google-protobuf 3.14.0 → 4.26.1

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.

Potentially problematic release.


This version of google-protobuf might be problematic. Click here for more details.

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