google-protobuf 3.22.0 → 3.25.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/google/protobuf_c/Rakefile +3 -0
- data/ext/google/protobuf_c/convert.c +23 -70
- data/ext/google/protobuf_c/convert.h +3 -28
- data/ext/google/protobuf_c/defs.c +175 -52
- data/ext/google/protobuf_c/defs.h +3 -28
- data/ext/google/protobuf_c/extconf.rb +2 -1
- data/ext/google/protobuf_c/glue.c +56 -0
- data/ext/google/protobuf_c/map.c +27 -28
- data/ext/google/protobuf_c/map.h +6 -28
- data/ext/google/protobuf_c/message.c +83 -83
- data/ext/google/protobuf_c/message.h +10 -28
- data/ext/google/protobuf_c/protobuf.c +39 -176
- data/ext/google/protobuf_c/protobuf.h +24 -32
- data/ext/google/protobuf_c/repeated_field.c +28 -29
- data/ext/google/protobuf_c/repeated_field.h +6 -28
- data/ext/google/protobuf_c/ruby-upb.c +2982 -2494
- data/ext/google/protobuf_c/ruby-upb.h +5838 -3467
- data/ext/google/protobuf_c/shared_convert.c +64 -0
- data/ext/google/protobuf_c/shared_convert.h +26 -0
- data/ext/google/protobuf_c/shared_message.c +65 -0
- data/ext/google/protobuf_c/shared_message.h +25 -0
- data/ext/google/protobuf_c/wrap_memcpy.c +3 -26
- data/lib/google/protobuf/any_pb.rb +24 -5
- data/lib/google/protobuf/api_pb.rb +26 -23
- data/lib/google/protobuf/descriptor_pb.rb +40 -252
- data/lib/google/protobuf/duration_pb.rb +24 -5
- data/lib/google/protobuf/empty_pb.rb +24 -3
- data/lib/google/protobuf/ffi/descriptor.rb +165 -0
- data/lib/google/protobuf/ffi/descriptor_pool.rb +75 -0
- data/lib/google/protobuf/ffi/enum_descriptor.rb +171 -0
- data/lib/google/protobuf/ffi/ffi.rb +213 -0
- data/lib/google/protobuf/ffi/field_descriptor.rb +319 -0
- data/lib/google/protobuf/ffi/file_descriptor.rb +59 -0
- data/lib/google/protobuf/ffi/internal/arena.rb +66 -0
- data/lib/google/protobuf/ffi/internal/convert.rb +305 -0
- data/lib/google/protobuf/ffi/internal/pointer_helper.rb +35 -0
- data/lib/google/protobuf/ffi/internal/type_safety.rb +25 -0
- data/lib/google/protobuf/ffi/map.rb +407 -0
- data/lib/google/protobuf/ffi/message.rb +662 -0
- data/lib/google/protobuf/ffi/object_cache.rb +30 -0
- data/lib/google/protobuf/ffi/oneof_descriptor.rb +95 -0
- data/lib/google/protobuf/ffi/repeated_field.rb +383 -0
- data/lib/google/protobuf/field_mask_pb.rb +24 -4
- data/lib/google/protobuf/message_exts.rb +3 -26
- data/lib/google/protobuf/object_cache.rb +97 -0
- data/lib/google/protobuf/plugin_pb.rb +25 -28
- data/lib/google/protobuf/repeated_field.rb +3 -26
- data/lib/google/protobuf/source_context_pb.rb +24 -4
- data/lib/google/protobuf/struct_pb.rb +24 -20
- data/lib/google/protobuf/timestamp_pb.rb +24 -5
- data/lib/google/protobuf/type_pb.rb +26 -68
- data/lib/google/protobuf/well_known_types.rb +5 -34
- data/lib/google/protobuf/wrappers_pb.rb +24 -28
- data/lib/google/protobuf.rb +27 -45
- data/lib/google/protobuf_ffi.rb +50 -0
- data/lib/google/protobuf_native.rb +20 -0
- data/lib/google/tasks/ffi.rake +102 -0
- metadata +72 -4
@@ -0,0 +1,662 @@
|
|
1
|
+
# Protocol Buffers - Google's data interchange format
|
2
|
+
# Copyright 2023 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Use of this source code is governed by a BSD-style
|
5
|
+
# license that can be found in the LICENSE file or at
|
6
|
+
# https://developers.google.com/open-source/licenses/bsd
|
7
|
+
|
8
|
+
|
9
|
+
# Decorates Descriptor with the `build_message_class` method that defines
|
10
|
+
# Message classes.
|
11
|
+
module Google
|
12
|
+
module Protobuf
|
13
|
+
class FFI
|
14
|
+
# Message
|
15
|
+
attach_function :clear_message_field, :upb_Message_ClearFieldByDef, [:Message, FieldDescriptor], :void
|
16
|
+
attach_function :get_message_value, :upb_Message_GetFieldByDef, [:Message, FieldDescriptor], MessageValue.by_value
|
17
|
+
attach_function :get_message_has, :upb_Message_HasFieldByDef, [:Message, FieldDescriptor], :bool
|
18
|
+
attach_function :set_message_field, :upb_Message_SetFieldByDef, [:Message, FieldDescriptor, MessageValue.by_value, Internal::Arena], :bool
|
19
|
+
attach_function :encode_message, :upb_Encode, [:Message, MiniTable.by_ref, :size_t, Internal::Arena, :pointer, :pointer], EncodeStatus
|
20
|
+
attach_function :json_decode_message, :upb_JsonDecode, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :bool
|
21
|
+
attach_function :json_encode_message, :upb_JsonEncode, [:Message, Descriptor, :DefPool, :int, :binary_string, :size_t, Status.by_ref], :size_t
|
22
|
+
attach_function :decode_message, :upb_Decode, [:binary_string, :size_t, :Message, MiniTable.by_ref, :ExtensionRegistry, :int, Internal::Arena], DecodeStatus
|
23
|
+
attach_function :get_mutable_message, :upb_Message_Mutable, [:Message, FieldDescriptor, Internal::Arena], MutableMessageValue.by_value
|
24
|
+
attach_function :get_message_which_oneof, :upb_Message_WhichOneof, [:Message, OneofDescriptor], FieldDescriptor
|
25
|
+
attach_function :message_discard_unknown, :upb_Message_DiscardUnknown, [:Message, Descriptor, :int], :bool
|
26
|
+
# MessageValue
|
27
|
+
attach_function :message_value_equal, :shared_Msgval_IsEqual, [MessageValue.by_value, MessageValue.by_value, CType, Descriptor], :bool
|
28
|
+
attach_function :message_value_hash, :shared_Msgval_GetHash, [MessageValue.by_value, CType, Descriptor, :uint64_t], :uint64_t
|
29
|
+
end
|
30
|
+
|
31
|
+
class Descriptor
|
32
|
+
def build_message_class
|
33
|
+
descriptor = self
|
34
|
+
Class.new(Google::Protobuf::const_get(:AbstractMessage)) do
|
35
|
+
@descriptor = descriptor
|
36
|
+
class << self
|
37
|
+
attr_accessor :descriptor
|
38
|
+
private
|
39
|
+
attr_accessor :oneof_field_names
|
40
|
+
include ::Google::Protobuf::Internal::Convert
|
41
|
+
end
|
42
|
+
|
43
|
+
alias original_method_missing method_missing
|
44
|
+
def method_missing(method_name, *args)
|
45
|
+
method_missing_internal method_name, *args, mode: :method_missing
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to_missing?(method_name, include_private = false)
|
49
|
+
method_missing_internal(method_name, mode: :respond_to_missing?) || super
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Public constructor. Automatically allocates from a new Arena.
|
54
|
+
def self.new(initial_value = nil)
|
55
|
+
instance = allocate
|
56
|
+
instance.send(:initialize, initial_value)
|
57
|
+
instance
|
58
|
+
end
|
59
|
+
|
60
|
+
def freeze
|
61
|
+
super
|
62
|
+
@arena.pin self
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def dup
|
67
|
+
duplicate = self.class.private_constructor(@arena)
|
68
|
+
mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
69
|
+
size = mini_table[:size]
|
70
|
+
duplicate.instance_variable_get(:@msg).write_string_length(@msg.read_string_length(size), size)
|
71
|
+
duplicate
|
72
|
+
end
|
73
|
+
alias clone dup
|
74
|
+
|
75
|
+
def eql?(other)
|
76
|
+
return false unless self.class === other
|
77
|
+
encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
|
78
|
+
temporary_arena = Google::Protobuf::FFI.create_arena
|
79
|
+
mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
80
|
+
size_one = ::FFI::MemoryPointer.new(:size_t, 1)
|
81
|
+
encoding_one = ::FFI::MemoryPointer.new(:pointer, 1)
|
82
|
+
encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table, encoding_options, temporary_arena, encoding_one.to_ptr, size_one)
|
83
|
+
raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding LHS of `eql?()`" unless encoding_status == :Ok
|
84
|
+
|
85
|
+
size_two = ::FFI::MemoryPointer.new(:size_t, 1)
|
86
|
+
encoding_two = ::FFI::MemoryPointer.new(:pointer, 1)
|
87
|
+
encoding_status = Google::Protobuf::FFI.encode_message(other.instance_variable_get(:@msg), mini_table, encoding_options, temporary_arena, encoding_two.to_ptr, size_two)
|
88
|
+
raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding RHS of `eql?()`" unless encoding_status == :Ok
|
89
|
+
|
90
|
+
if encoding_one.null? or encoding_two.null?
|
91
|
+
raise ParseError.new "Error comparing messages"
|
92
|
+
end
|
93
|
+
size_one.read(:size_t) == size_two.read(:size_t) and Google::Protobuf::FFI.memcmp(encoding_one.read(:pointer), encoding_two.read(:pointer), size_one.read(:size_t)).zero?
|
94
|
+
end
|
95
|
+
alias == eql?
|
96
|
+
|
97
|
+
def hash
|
98
|
+
encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
|
99
|
+
temporary_arena = Google::Protobuf::FFI.create_arena
|
100
|
+
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
101
|
+
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
102
|
+
encoding = ::FFI::MemoryPointer.new(:pointer, 1)
|
103
|
+
encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table_ptr, encoding_options, temporary_arena, encoding.to_ptr, size_ptr)
|
104
|
+
if encoding_status != :Ok or encoding.null?
|
105
|
+
raise ParseError.new "Error calculating hash"
|
106
|
+
end
|
107
|
+
encoding.read(:pointer).read_string(size_ptr.read(:size_t)).hash
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_h
|
111
|
+
to_h_internal @msg, self.class.descriptor
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# call-seq:
|
116
|
+
# Message.inspect => string
|
117
|
+
#
|
118
|
+
# Returns a human-readable string representing this message. It will be
|
119
|
+
# formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
|
120
|
+
# field's value is represented according to its own #inspect method.
|
121
|
+
def inspect
|
122
|
+
self.class.inspect_internal @msg
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_s
|
126
|
+
self.inspect
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# call-seq:
|
131
|
+
# Message.[](index) => value
|
132
|
+
# Accesses a field's value by field name. The provided field name
|
133
|
+
# should be a string.
|
134
|
+
def [](name)
|
135
|
+
raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
|
136
|
+
index_internal name
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# call-seq:
|
141
|
+
# Message.[]=(index, value)
|
142
|
+
# Sets a field's value by field name. The provided field name should
|
143
|
+
# be a string.
|
144
|
+
# @param name [String] Name of the field to be set
|
145
|
+
# @param value [Object] Value to set the field to
|
146
|
+
def []=(name, value)
|
147
|
+
raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
|
148
|
+
index_assign_internal(value, name: name)
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# call-seq:
|
153
|
+
# MessageClass.decode(data, options) => message
|
154
|
+
#
|
155
|
+
# Decodes the given data (as a string containing bytes in protocol buffers wire
|
156
|
+
# format) under the interpretation given by this message class's definition
|
157
|
+
# and returns a message object with the corresponding field values.
|
158
|
+
# @param data [String] Binary string in Protobuf wire format to decode
|
159
|
+
# @param options [Hash] options for the decoder
|
160
|
+
# @option options [Integer] :recursion_limit Set to maximum decoding depth for message (default is 64)
|
161
|
+
def self.decode(data, options = {})
|
162
|
+
raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
|
163
|
+
raise ArgumentError.new "Expected string for binary protobuf data." unless data.is_a? String
|
164
|
+
decoding_options = 0
|
165
|
+
depth = options[:recursion_limit]
|
166
|
+
|
167
|
+
if depth.is_a? Numeric
|
168
|
+
decoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
|
169
|
+
end
|
170
|
+
|
171
|
+
message = new
|
172
|
+
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(message.class.descriptor)
|
173
|
+
status = Google::Protobuf::FFI.decode_message(
|
174
|
+
data,
|
175
|
+
data.bytesize,
|
176
|
+
message.instance_variable_get(:@msg),
|
177
|
+
mini_table_ptr,
|
178
|
+
Google::Protobuf::FFI.get_extension_registry(message.class.descriptor.send(:pool).descriptor_pool),
|
179
|
+
decoding_options,
|
180
|
+
message.instance_variable_get(:@arena)
|
181
|
+
)
|
182
|
+
raise ParseError.new "Error occurred during parsing" unless status == :Ok
|
183
|
+
message
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# call-seq:
|
188
|
+
# MessageClass.encode(msg, options) => bytes
|
189
|
+
#
|
190
|
+
# Encodes the given message object to its serialized form in protocol buffers
|
191
|
+
# wire format.
|
192
|
+
# @param options [Hash] options for the encoder
|
193
|
+
# @option options [Integer] :recursion_limit Set to maximum encoding depth for message (default is 64)
|
194
|
+
def self.encode(message, options = {})
|
195
|
+
raise ArgumentError.new "Message of wrong type." unless message.is_a? self
|
196
|
+
raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
|
197
|
+
|
198
|
+
encoding_options = 0
|
199
|
+
depth = options[:recursion_limit]
|
200
|
+
|
201
|
+
if depth.is_a? Numeric
|
202
|
+
encoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
|
203
|
+
end
|
204
|
+
|
205
|
+
encode_internal(message.instance_variable_get(:@msg), encoding_options) do |encoding, size, _|
|
206
|
+
if encoding.nil? or encoding.null?
|
207
|
+
raise RuntimeError.new "Exceeded maximum depth (possibly cycle)"
|
208
|
+
else
|
209
|
+
encoding.read_string_length(size).force_encoding("ASCII-8BIT").freeze
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# all-seq:
|
216
|
+
# MessageClass.decode_json(data, options = {}) => message
|
217
|
+
#
|
218
|
+
# Decodes the given data (as a string containing bytes in protocol buffers wire
|
219
|
+
# format) under the interpretation given by this message class's definition
|
220
|
+
# and returns a message object with the corresponding field values.
|
221
|
+
#
|
222
|
+
# @param options [Hash] options for the decoder
|
223
|
+
# @option options [Boolean] :ignore_unknown_fields Set true to ignore unknown fields (default is to raise an error)
|
224
|
+
# @return [Message]
|
225
|
+
def self.decode_json(data, options = {})
|
226
|
+
decoding_options = 0
|
227
|
+
unless options.is_a? Hash
|
228
|
+
if options.respond_to? :to_h
|
229
|
+
options options.to_h
|
230
|
+
else
|
231
|
+
#TODO can this error message be improve to include what was received?
|
232
|
+
raise ArgumentError.new "Expected hash arguments"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
raise ArgumentError.new "Expected string for JSON data." unless data.is_a? String
|
236
|
+
raise RuntimeError.new "Cannot parse a wrapper directly" if descriptor.send(:wrapper?)
|
237
|
+
|
238
|
+
if options[:ignore_unknown_fields]
|
239
|
+
decoding_options |= Google::Protobuf::FFI::Upb_JsonDecode_IgnoreUnknown
|
240
|
+
end
|
241
|
+
|
242
|
+
message = new
|
243
|
+
pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
|
244
|
+
status = Google::Protobuf::FFI::Status.new
|
245
|
+
unless Google::Protobuf::FFI.json_decode_message(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
|
246
|
+
raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
|
247
|
+
end
|
248
|
+
message
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.encode_json(message, options = {})
|
252
|
+
encoding_options = 0
|
253
|
+
unless options.is_a? Hash
|
254
|
+
if options.respond_to? :to_h
|
255
|
+
options = options.to_h
|
256
|
+
else
|
257
|
+
#TODO can this error message be improve to include what was received?
|
258
|
+
raise ArgumentError.new "Expected hash arguments"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
if options[:preserve_proto_fieldnames]
|
263
|
+
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_UseProtoNames
|
264
|
+
end
|
265
|
+
if options[:emit_defaults]
|
266
|
+
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_EmitDefaults
|
267
|
+
end
|
268
|
+
if options[:format_enums_as_integers]
|
269
|
+
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_FormatEnumsAsIntegers
|
270
|
+
end
|
271
|
+
|
272
|
+
buffer_size = 1024
|
273
|
+
buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
|
274
|
+
status = Google::Protobuf::FFI::Status.new
|
275
|
+
msg = message.instance_variable_get(:@msg)
|
276
|
+
pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
|
277
|
+
size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
|
278
|
+
unless status[:ok]
|
279
|
+
raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
|
280
|
+
end
|
281
|
+
|
282
|
+
if size >= buffer_size
|
283
|
+
buffer_size = size + 1
|
284
|
+
buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
|
285
|
+
status.clear
|
286
|
+
size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
|
287
|
+
unless status[:ok]
|
288
|
+
raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
|
289
|
+
end
|
290
|
+
if size >= buffer_size
|
291
|
+
raise ParseError.new "Inconsistent JSON encoding sizes - was #{buffer_size - 1}, now #{size}"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
buffer.read_string_length(size).force_encoding("UTF-8").freeze
|
296
|
+
end
|
297
|
+
|
298
|
+
private
|
299
|
+
# Implementation details below are subject to breaking changes without
|
300
|
+
# warning and are intended for use only within the gem.
|
301
|
+
|
302
|
+
include Google::Protobuf::Internal::Convert
|
303
|
+
|
304
|
+
def internal_deep_freeze
|
305
|
+
freeze
|
306
|
+
self.class.descriptor.each do |field_descriptor|
|
307
|
+
next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
|
308
|
+
if field_descriptor.map? or field_descriptor.repeated? or field_descriptor.sub_message?
|
309
|
+
get_field(field_descriptor).send :internal_deep_freeze
|
310
|
+
end
|
311
|
+
end
|
312
|
+
self
|
313
|
+
end
|
314
|
+
|
315
|
+
def self.setup_accessors!
|
316
|
+
@descriptor.each do |field_descriptor|
|
317
|
+
field_name = field_descriptor.name
|
318
|
+
unless instance_methods(true).include?(field_name.to_sym)
|
319
|
+
#TODO - at a high level, dispatching to either
|
320
|
+
# index_internal or get_field would be logically correct, but slightly slower.
|
321
|
+
if field_descriptor.map?
|
322
|
+
define_method(field_name) do
|
323
|
+
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
324
|
+
get_map_field(mutable_message_value[:map], field_descriptor)
|
325
|
+
end
|
326
|
+
elsif field_descriptor.repeated?
|
327
|
+
define_method(field_name) do
|
328
|
+
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
329
|
+
get_repeated_field(mutable_message_value[:array], field_descriptor)
|
330
|
+
end
|
331
|
+
elsif field_descriptor.sub_message?
|
332
|
+
define_method(field_name) do
|
333
|
+
return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
|
334
|
+
mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
335
|
+
sub_message = mutable_message[:msg]
|
336
|
+
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
|
337
|
+
Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
|
338
|
+
end
|
339
|
+
else
|
340
|
+
c_type = field_descriptor.send(:c_type)
|
341
|
+
if c_type == :enum
|
342
|
+
define_method(field_name) do
|
343
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
344
|
+
convert_upb_to_ruby message_value, c_type, Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor)
|
345
|
+
end
|
346
|
+
else
|
347
|
+
define_method(field_name) do
|
348
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
349
|
+
convert_upb_to_ruby message_value, c_type
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
define_method("#{field_name}=") do |value|
|
354
|
+
index_assign_internal(value, field_descriptor: field_descriptor)
|
355
|
+
end
|
356
|
+
define_method("clear_#{field_name}") do
|
357
|
+
clear_internal(field_descriptor)
|
358
|
+
end
|
359
|
+
if field_descriptor.type == :enum
|
360
|
+
define_method("#{field_name}_const") do
|
361
|
+
if field_descriptor.repeated?
|
362
|
+
return_value = []
|
363
|
+
get_field(field_descriptor).send(:each_msg_val) do |msg_val|
|
364
|
+
return_value << msg_val[:int32_val]
|
365
|
+
end
|
366
|
+
return_value
|
367
|
+
else
|
368
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
369
|
+
message_value[:int32_val]
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
if !field_descriptor.repeated? and field_descriptor.wrapper?
|
374
|
+
define_method("#{field_name}_as_value") do
|
375
|
+
get_field(field_descriptor, unwrap: true)
|
376
|
+
end
|
377
|
+
define_method("#{field_name}_as_value=") do |value|
|
378
|
+
if value.nil?
|
379
|
+
clear_internal(field_descriptor)
|
380
|
+
else
|
381
|
+
index_assign_internal(value, field_descriptor: field_descriptor, wrap: true)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
if field_descriptor.has_presence?
|
386
|
+
define_method("has_#{field_name}?") do
|
387
|
+
Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def self.setup_oneof_accessors!
|
395
|
+
@oneof_field_names = []
|
396
|
+
@descriptor.each_oneof do |oneof_descriptor|
|
397
|
+
self.add_oneof_accessors_for! oneof_descriptor
|
398
|
+
end
|
399
|
+
end
|
400
|
+
def self.add_oneof_accessors_for!(oneof_descriptor)
|
401
|
+
field_name = oneof_descriptor.name.to_sym
|
402
|
+
@oneof_field_names << field_name
|
403
|
+
unless instance_methods(true).include?(field_name)
|
404
|
+
define_method(field_name) do
|
405
|
+
field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
|
406
|
+
if field_descriptor.nil?
|
407
|
+
return
|
408
|
+
else
|
409
|
+
return field_descriptor.name.to_sym
|
410
|
+
end
|
411
|
+
end
|
412
|
+
define_method("clear_#{field_name}") do
|
413
|
+
field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
|
414
|
+
unless field_descriptor.nil?
|
415
|
+
clear_internal(field_descriptor)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
define_method("has_#{field_name}?") do
|
419
|
+
!Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor).nil?
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
setup_accessors!
|
425
|
+
setup_oneof_accessors!
|
426
|
+
|
427
|
+
def self.private_constructor(arena, msg: nil, initial_value: nil)
|
428
|
+
instance = allocate
|
429
|
+
instance.send(:initialize, initial_value, arena, msg)
|
430
|
+
instance
|
431
|
+
end
|
432
|
+
|
433
|
+
def self.inspect_field(field_descriptor, c_type, message_value)
|
434
|
+
if field_descriptor.sub_message?
|
435
|
+
sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
|
436
|
+
sub_msg_descriptor.msgclass.send(:inspect_internal, message_value[:msg_val])
|
437
|
+
else
|
438
|
+
convert_upb_to_ruby(message_value, c_type, field_descriptor.subtype).inspect
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
# @param msg [::FFI::Pointer] Pointer to the Message
|
443
|
+
def self.inspect_internal(msg)
|
444
|
+
field_output = []
|
445
|
+
descriptor.each do |field_descriptor|
|
446
|
+
next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
|
447
|
+
if field_descriptor.map?
|
448
|
+
# TODO Adapted - from map#each_msg_val and map#inspect- can this be refactored to reduce echo without introducing a arena allocation?
|
449
|
+
message_descriptor = field_descriptor.subtype
|
450
|
+
key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
|
451
|
+
key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
|
452
|
+
|
453
|
+
value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
|
454
|
+
value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
|
455
|
+
|
456
|
+
message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
|
457
|
+
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
458
|
+
iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
|
459
|
+
key_value_pairs = []
|
460
|
+
while Google::Protobuf::FFI.map_next(message_value[:map_val], iter) do
|
461
|
+
iter_size_t = iter.read(:size_t)
|
462
|
+
key_message_value = Google::Protobuf::FFI.map_key(message_value[:map_val], iter_size_t)
|
463
|
+
value_message_value = Google::Protobuf::FFI.map_value(message_value[:map_val], iter_size_t)
|
464
|
+
key_string = convert_upb_to_ruby(key_message_value, key_field_type).inspect
|
465
|
+
value_string = inspect_field(value_field_def, value_field_type, value_message_value)
|
466
|
+
key_value_pairs << "#{key_string}=>#{value_string}"
|
467
|
+
end
|
468
|
+
field_output << "#{field_descriptor.name}: {#{key_value_pairs.join(", ")}}"
|
469
|
+
elsif field_descriptor.repeated?
|
470
|
+
# TODO Adapted - from repeated_field#each - can this be refactored to reduce echo?
|
471
|
+
repeated_field_output = []
|
472
|
+
message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
|
473
|
+
array = message_value[:array_val]
|
474
|
+
n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
|
475
|
+
0.upto(n - 1) do |i|
|
476
|
+
element = Google::Protobuf::FFI.get_msgval_at(array, i)
|
477
|
+
repeated_field_output << inspect_field(field_descriptor, field_descriptor.send(:c_type), element)
|
478
|
+
end
|
479
|
+
field_output << "#{field_descriptor.name}: [#{repeated_field_output.join(", ")}]"
|
480
|
+
else
|
481
|
+
message_value = Google::Protobuf::FFI.get_message_value msg, field_descriptor
|
482
|
+
rendered_value = inspect_field(field_descriptor, field_descriptor.send(:c_type), message_value)
|
483
|
+
field_output << "#{field_descriptor.name}: #{rendered_value}"
|
484
|
+
end
|
485
|
+
end
|
486
|
+
"<#{name}: #{field_output.join(', ')}>"
|
487
|
+
end
|
488
|
+
|
489
|
+
def self.deep_copy(msg, arena = nil)
|
490
|
+
arena ||= Google::Protobuf::FFI.create_arena
|
491
|
+
encode_internal(msg) do |encoding, size, mini_table_ptr|
|
492
|
+
message = private_constructor(arena)
|
493
|
+
if encoding.nil? or encoding.null? or Google::Protobuf::FFI.decode_message(encoding, size, message.instance_variable_get(:@msg), mini_table_ptr, nil, 0, arena) != :Ok
|
494
|
+
raise ParseError.new "Error occurred copying proto"
|
495
|
+
end
|
496
|
+
message
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
def self.encode_internal(msg, encoding_options = 0)
|
501
|
+
temporary_arena = Google::Protobuf::FFI.create_arena
|
502
|
+
|
503
|
+
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(descriptor)
|
504
|
+
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
505
|
+
pointer_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
|
506
|
+
encoding_status = Google::Protobuf::FFI.encode_message(msg, mini_table_ptr, encoding_options, temporary_arena, pointer_ptr.to_ptr, size_ptr)
|
507
|
+
raise "Encoding failed due to #{encoding_status}" unless encoding_status == :Ok
|
508
|
+
yield pointer_ptr.read(:pointer), size_ptr.read(:size_t), mini_table_ptr
|
509
|
+
end
|
510
|
+
|
511
|
+
def method_missing_internal(method_name, *args, mode: nil)
|
512
|
+
raise ArgumentError.new "method_missing_internal called with invalid mode #{mode.inspect}" unless [:respond_to_missing?, :method_missing].include? mode
|
513
|
+
|
514
|
+
#TODO not being allowed is not the same thing as not responding, but this is needed to pass tests
|
515
|
+
if method_name.to_s.end_with? '='
|
516
|
+
if self.class.send(:oneof_field_names).include? method_name.to_s[0..-2].to_sym
|
517
|
+
return false if mode == :respond_to_missing?
|
518
|
+
raise RuntimeError.new "Oneof accessors are read-only."
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
original_method_missing(method_name, *args) if mode == :method_missing
|
523
|
+
end
|
524
|
+
|
525
|
+
def clear_internal(field_def)
|
526
|
+
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
527
|
+
Google::Protobuf::FFI.clear_message_field(@msg, field_def)
|
528
|
+
end
|
529
|
+
|
530
|
+
def index_internal(name)
|
531
|
+
field_descriptor = self.class.descriptor.lookup(name)
|
532
|
+
get_field field_descriptor unless field_descriptor.nil?
|
533
|
+
end
|
534
|
+
|
535
|
+
#TODO - well known types keeps us on our toes by overloading methods.
|
536
|
+
# How much of the public API needs to be defended?
|
537
|
+
def index_assign_internal(value, name: nil, field_descriptor: nil, wrap: false)
|
538
|
+
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
539
|
+
if field_descriptor.nil?
|
540
|
+
field_descriptor = self.class.descriptor.lookup(name)
|
541
|
+
if field_descriptor.nil?
|
542
|
+
raise ArgumentError.new "Unknown field: #{name}"
|
543
|
+
end
|
544
|
+
end
|
545
|
+
unless field_descriptor.send :set_value_on_message, value, @msg, @arena, wrap: wrap
|
546
|
+
raise RuntimeError.new "allocation failed"
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# @param initial_value [Object] initial value of this Message
|
552
|
+
# @param arena [Arena] Optional; Arena where this message will be allocated
|
553
|
+
# @param msg [::FFI::Pointer] Optional; Message to initialize; creates
|
554
|
+
# one if omitted or nil.
|
555
|
+
def initialize(initial_value = nil, arena = nil, msg = nil)
|
556
|
+
@arena = arena || Google::Protobuf::FFI.create_arena
|
557
|
+
@msg = msg || Google::Protobuf::FFI.new_message_from_def(self.class.descriptor, @arena)
|
558
|
+
|
559
|
+
unless initial_value.nil?
|
560
|
+
raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
|
561
|
+
|
562
|
+
field_def_ptr = ::FFI::MemoryPointer.new :pointer
|
563
|
+
oneof_def_ptr = ::FFI::MemoryPointer.new :pointer
|
564
|
+
|
565
|
+
initial_value.each do |key, value|
|
566
|
+
raise ArgumentError.new "Expected string or symbols as hash keys when initializing proto from hash." unless [String, Symbol].include? key.class
|
567
|
+
|
568
|
+
unless Google::Protobuf::FFI.find_msg_def_by_name self.class.descriptor, key.to_s, key.to_s.bytesize, field_def_ptr, oneof_def_ptr
|
569
|
+
raise ArgumentError.new "Unknown field name '#{key}' in initialization map entry."
|
570
|
+
end
|
571
|
+
raise NotImplementedError.new "Haven't added oneofsupport yet" unless oneof_def_ptr.get_pointer(0).null?
|
572
|
+
raise NotImplementedError.new "Expected a field def" if field_def_ptr.get_pointer(0).null?
|
573
|
+
|
574
|
+
field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
|
575
|
+
|
576
|
+
next if value.nil?
|
577
|
+
if field_descriptor.map?
|
578
|
+
index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, @arena, value: value), name: key.to_s)
|
579
|
+
elsif field_descriptor.repeated?
|
580
|
+
index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, @arena, values: value), name: key.to_s)
|
581
|
+
else
|
582
|
+
index_assign_internal(value, name: key.to_s)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
# Should always be the last expression of the initializer to avoid
|
588
|
+
# leaking references to this object before construction is complete.
|
589
|
+
Google::Protobuf::OBJECT_CACHE.try_add @msg.address, self
|
590
|
+
end
|
591
|
+
|
592
|
+
##
|
593
|
+
# Gets a field of this message identified by the argument definition.
|
594
|
+
#
|
595
|
+
# @param field [FieldDescriptor] Descriptor of the field to get
|
596
|
+
def get_field(field, unwrap: false)
|
597
|
+
if field.map?
|
598
|
+
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
|
599
|
+
get_map_field(mutable_message_value[:map], field)
|
600
|
+
elsif field.repeated?
|
601
|
+
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
|
602
|
+
get_repeated_field(mutable_message_value[:array], field)
|
603
|
+
elsif field.sub_message?
|
604
|
+
return nil unless Google::Protobuf::FFI.get_message_has @msg, field
|
605
|
+
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field)
|
606
|
+
if unwrap
|
607
|
+
if field.has?(self)
|
608
|
+
wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
|
609
|
+
fields = Google::Protobuf::FFI.field_count(sub_message_def)
|
610
|
+
raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
|
611
|
+
value_field_def = Google::Protobuf::FFI.get_field_by_number sub_message_def, 1
|
612
|
+
message_value = Google::Protobuf::FFI.get_message_value wrapper_message_value[:msg_val], value_field_def
|
613
|
+
convert_upb_to_ruby message_value, Google::Protobuf::FFI.get_c_type(value_field_def)
|
614
|
+
else
|
615
|
+
nil
|
616
|
+
end
|
617
|
+
else
|
618
|
+
mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
|
619
|
+
sub_message = mutable_message[:msg]
|
620
|
+
Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
|
621
|
+
end
|
622
|
+
else
|
623
|
+
c_type = field.send(:c_type)
|
624
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field
|
625
|
+
if c_type == :enum
|
626
|
+
convert_upb_to_ruby message_value, c_type, Google::Protobuf::FFI.get_subtype_as_enum(field)
|
627
|
+
else
|
628
|
+
convert_upb_to_ruby message_value, c_type
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
##
|
634
|
+
# @param array [::FFI::Pointer] Pointer to the Array
|
635
|
+
# @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
|
636
|
+
def get_repeated_field(array, field)
|
637
|
+
return nil if array.nil? or array.null?
|
638
|
+
repeated_field = OBJECT_CACHE.get(array.address)
|
639
|
+
if repeated_field.nil?
|
640
|
+
repeated_field = RepeatedField.send(:construct_for_field, field, @arena, array: array)
|
641
|
+
repeated_field.send :internal_deep_freeze if frozen?
|
642
|
+
end
|
643
|
+
repeated_field
|
644
|
+
end
|
645
|
+
|
646
|
+
##
|
647
|
+
# @param map [::FFI::Pointer] Pointer to the Map
|
648
|
+
# @param field [Google::Protobuf::FieldDescriptor] Type of the map field
|
649
|
+
def get_map_field(map, field)
|
650
|
+
return nil if map.nil? or map.null?
|
651
|
+
map_field = OBJECT_CACHE.get(map.address)
|
652
|
+
if map_field.nil?
|
653
|
+
map_field = Google::Protobuf::Map.send(:construct_for_field, field, @arena, map: map)
|
654
|
+
map_field.send :internal_deep_freeze if frozen?
|
655
|
+
end
|
656
|
+
map_field
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|