google-protobuf 3.25.0 → 4.29.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/convert.c +46 -18
  3. data/ext/google/protobuf_c/defs.c +499 -26
  4. data/ext/google/protobuf_c/extconf.rb +1 -1
  5. data/ext/google/protobuf_c/glue.c +53 -2
  6. data/ext/google/protobuf_c/map.c +82 -17
  7. data/ext/google/protobuf_c/map.h +9 -2
  8. data/ext/google/protobuf_c/message.c +144 -104
  9. data/ext/google/protobuf_c/message.h +8 -5
  10. data/ext/google/protobuf_c/protobuf.c +30 -17
  11. data/ext/google/protobuf_c/protobuf.h +3 -7
  12. data/ext/google/protobuf_c/repeated_field.c +64 -10
  13. data/ext/google/protobuf_c/repeated_field.h +8 -1
  14. data/ext/google/protobuf_c/ruby-upb.c +13774 -11526
  15. data/ext/google/protobuf_c/ruby-upb.h +11198 -9048
  16. data/ext/google/protobuf_c/shared_convert.c +10 -5
  17. data/ext/google/protobuf_c/shared_convert.h +2 -2
  18. data/ext/google/protobuf_c/shared_message.c +3 -31
  19. data/ext/google/protobuf_c/shared_message.h +0 -4
  20. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +467 -0
  21. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -8
  22. data/lib/google/protobuf/any_pb.rb +1 -22
  23. data/lib/google/protobuf/api_pb.rb +1 -24
  24. data/lib/google/protobuf/descriptor_pb.rb +3 -23
  25. data/lib/google/protobuf/duration_pb.rb +1 -22
  26. data/lib/google/protobuf/empty_pb.rb +1 -22
  27. data/lib/google/protobuf/ffi/descriptor.rb +13 -2
  28. data/lib/google/protobuf/ffi/descriptor_pool.rb +16 -9
  29. data/lib/google/protobuf/ffi/enum_descriptor.rb +13 -1
  30. data/lib/google/protobuf/ffi/ffi.rb +8 -6
  31. data/lib/google/protobuf/ffi/field_descriptor.rb +37 -16
  32. data/lib/google/protobuf/ffi/file_descriptor.rb +13 -12
  33. data/lib/google/protobuf/ffi/internal/arena.rb +0 -6
  34. data/lib/google/protobuf/ffi/internal/convert.rb +21 -30
  35. data/lib/google/protobuf/ffi/map.rb +50 -13
  36. data/lib/google/protobuf/ffi/message.rb +202 -58
  37. data/lib/google/protobuf/ffi/method_descriptor.rb +114 -0
  38. data/lib/google/protobuf/ffi/object_cache.rb +3 -3
  39. data/lib/google/protobuf/ffi/oneof_descriptor.rb +20 -11
  40. data/lib/google/protobuf/ffi/repeated_field.rb +50 -142
  41. data/lib/google/protobuf/ffi/service_descriptor.rb +107 -0
  42. data/lib/google/protobuf/field_mask_pb.rb +1 -22
  43. data/lib/google/protobuf/internal/object_cache.rb +99 -0
  44. data/lib/google/protobuf/plugin_pb.rb +2 -24
  45. data/lib/google/protobuf/repeated_field.rb +4 -5
  46. data/lib/google/protobuf/source_context_pb.rb +1 -22
  47. data/lib/google/protobuf/struct_pb.rb +1 -22
  48. data/lib/google/protobuf/timestamp_pb.rb +1 -22
  49. data/lib/google/protobuf/type_pb.rb +1 -24
  50. data/lib/google/protobuf/wrappers_pb.rb +1 -22
  51. data/lib/google/protobuf.rb +1 -1
  52. data/lib/google/protobuf_ffi.rb +3 -2
  53. data/lib/google/protobuf_native.rb +0 -1
  54. data/lib/google/tasks/ffi.rake +1 -3
  55. metadata +25 -12
  56. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
  57. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
  58. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
  59. data/lib/google/protobuf/descriptor_dsl.rb +0 -465
  60. data/lib/google/protobuf/object_cache.rb +0 -97
@@ -9,18 +9,20 @@ module Google
9
9
  module Protobuf
10
10
  class FFI
11
11
  # Map
12
- attach_function :map_clear, :upb_Map_Clear, [:Map], :void
13
- attach_function :map_delete, :upb_Map_Delete, [:Map, MessageValue.by_value, MessageValue.by_ref], :bool
14
- attach_function :map_get, :upb_Map_Get, [:Map, MessageValue.by_value, MessageValue.by_ref], :bool
15
- attach_function :create_map, :upb_Map_New, [Internal::Arena, CType, CType], :Map
16
- attach_function :map_size, :upb_Map_Size, [:Map], :size_t
17
- attach_function :map_set, :upb_Map_Set, [:Map, MessageValue.by_value, MessageValue.by_value, Internal::Arena], :bool
12
+ attach_function :map_clear, :upb_Map_Clear, [:Map], :void
13
+ attach_function :map_delete, :upb_Map_Delete, [:Map, MessageValue.by_value, MessageValue.by_ref], :bool
14
+ attach_function :map_get, :upb_Map_Get, [:Map, MessageValue.by_value, MessageValue.by_ref], :bool
15
+ attach_function :create_map, :upb_Map_New, [Internal::Arena, CType, CType], :Map
16
+ attach_function :map_size, :upb_Map_Size, [:Map], :size_t
17
+ attach_function :map_set, :upb_Map_Set, [:Map, MessageValue.by_value, MessageValue.by_value, Internal::Arena], :bool
18
+ attach_function :map_freeze, :upb_Map_Freeze, [:Map, MiniTable.by_ref], :void
19
+ attach_function :map_frozen?, :upb_Map_IsFrozen, [:Map], :bool
18
20
 
19
21
  # MapIterator
20
- attach_function :map_next, :upb_MapIterator_Next, [:Map, :pointer], :bool
21
- attach_function :map_done, :upb_MapIterator_Done, [:Map, :size_t], :bool
22
- attach_function :map_key, :upb_MapIterator_Key, [:Map, :size_t], MessageValue.by_value
23
- attach_function :map_value, :upb_MapIterator_Value, [:Map, :size_t], MessageValue.by_value
22
+ attach_function :map_next, :upb_MapIterator_Next, [:Map, :pointer], :bool
23
+ attach_function :map_done, :upb_MapIterator_Done, [:Map, :size_t], :bool
24
+ attach_function :map_key, :upb_MapIterator_Key, [:Map, :size_t], MessageValue.by_value
25
+ attach_function :map_value, :upb_MapIterator_Value, [:Map, :size_t], MessageValue.by_value
24
26
  end
25
27
  class Map
26
28
  include Enumerable
@@ -155,6 +157,37 @@ module Google
155
157
  end
156
158
  alias size length
157
159
 
160
+ ##
161
+ # Is this object frozen?
162
+ # Returns true if either this Ruby wrapper or the underlying
163
+ # representation are frozen. Freezes the wrapper if the underlying
164
+ # representation is already frozen but this wrapper isn't.
165
+ def frozen?
166
+ unless Google::Protobuf::FFI.map_frozen? @map_ptr
167
+ raise RuntimeError.new "Ruby frozen Map with mutable representation" if super
168
+ return false
169
+ end
170
+ method(:freeze).super_method.call unless super
171
+ true
172
+ end
173
+
174
+ ##
175
+ # Freezes the map object. We have to intercept this so we can freeze the
176
+ # underlying representation, not just the Ruby wrapper. Returns self.
177
+ def freeze
178
+ if method(:frozen?).super_method.call
179
+ unless Google::Protobuf::FFI.map_frozen? @map_ptr
180
+ raise RuntimeError.new "Underlying representation of map still mutable despite frozen wrapper"
181
+ end
182
+ return self
183
+ end
184
+ unless Google::Protobuf::FFI.map_frozen? @map_ptr
185
+ mini_table = (value_type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
186
+ Google::Protobuf::FFI.map_freeze(@map_ptr, mini_table)
187
+ end
188
+ super
189
+ end
190
+
158
191
  ##
159
192
  # call-seq:
160
193
  # Map.dup => new_map
@@ -358,10 +391,14 @@ module Google
358
391
  OBJECT_CACHE.try_add(@map_ptr.address, self)
359
392
  end
360
393
 
361
- # @param field [FieldDescriptor] Descriptor of the field where the RepeatedField will be assigned
362
- # @param values [Hash|Map] Initial value; may be nil or empty
394
+ ##
395
+ # Constructor that uses the type information from the given
396
+ # FieldDescriptor to configure the new Map instance.
397
+ # @param field [FieldDescriptor] Type information for the new Map
363
398
  # @param arena [Arena] Owning message's arena
364
- def self.construct_for_field(field, arena, value: nil, map: nil)
399
+ # @param value [Hash|Map] Initial value
400
+ # @param map [::FFI::Pointer] Existing upb_Map
401
+ def self.construct_for_field(field, arena: nil, value: nil, map: nil)
365
402
  raise ArgumentError.new "Expected Hash object as initializer value for map field '#{field.name}' (given #{value.class})." unless value.nil? or value.is_a? Hash
366
403
  instance = allocate
367
404
  raise ArgumentError.new "Expected field with type :message, instead got #{field.class}" unless field.type == :message
@@ -17,12 +17,16 @@ module Google
17
17
  attach_function :get_message_has, :upb_Message_HasFieldByDef, [:Message, FieldDescriptor], :bool
18
18
  attach_function :set_message_field, :upb_Message_SetFieldByDef, [:Message, FieldDescriptor, MessageValue.by_value, Internal::Arena], :bool
19
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
20
+ attach_function :json_decode_message_detecting_nonconformance, :upb_JsonDecodeDetectingNonconformance, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :int
21
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
22
+ attach_function :decode_message, :upb_Decode, [:binary_string, :size_t, :Message, MiniTable.by_ref, :ExtensionRegistry, :int, Internal::Arena], DecodeStatus
23
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
24
+ attach_function :get_message_which_oneof, :upb_Message_WhichOneofByDef, [:Message, OneofDescriptor], FieldDescriptor
25
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
+ attach_function :message_freeze, :upb_Message_Freeze, [:Message, MiniTable.by_ref], :void
28
+ attach_function :message_frozen?, :upb_Message_IsFrozen, [:Message], :bool
29
+
26
30
  # MessageValue
27
31
  attach_function :message_value_equal, :shared_Msgval_IsEqual, [MessageValue.by_value, MessageValue.by_value, CType, Descriptor], :bool
28
32
  attach_function :message_value_hash, :shared_Msgval_GetHash, [MessageValue.by_value, CType, Descriptor, :uint64_t], :uint64_t
@@ -57,11 +61,35 @@ module Google
57
61
  instance
58
62
  end
59
63
 
64
+ ##
65
+ # Is this object frozen?
66
+ # Returns true if either this Ruby wrapper or the underlying
67
+ # representation are frozen. Freezes the wrapper if the underlying
68
+ # representation is already frozen but this wrapper isn't.
69
+ def frozen?
70
+ unless Google::Protobuf::FFI.message_frozen? @msg
71
+ raise RuntimeError.new "Ruby frozen Message with mutable representation" if super
72
+ return false
73
+ end
74
+ method(:freeze).super_method.call unless super
75
+ true
76
+ end
77
+
78
+ ##
79
+ # Freezes the map object. We have to intercept this so we can freeze the
80
+ # underlying representation, not just the Ruby wrapper. Returns self.
60
81
  def freeze
82
+ if method(:frozen?).super_method.call
83
+ unless Google::Protobuf::FFI.message_frozen? @msg
84
+ raise RuntimeError.new "Underlying representation of message still mutable despite frozen wrapper"
85
+ end
86
+ return self
87
+ end
88
+ unless Google::Protobuf::FFI.message_frozen? @msg
89
+ Google::Protobuf::FFI.message_freeze(@msg, Google::Protobuf::FFI.get_mini_table(self.class.descriptor))
90
+ end
61
91
  super
62
- @arena.pin self
63
- self
64
- end
92
+ end
65
93
 
66
94
  def dup
67
95
  duplicate = self.class.private_constructor(@arena)
@@ -170,7 +198,15 @@ module Google
170
198
 
171
199
  message = new
172
200
  mini_table_ptr = Google::Protobuf::FFI.get_mini_table(message.class.descriptor)
173
- status = Google::Protobuf::FFI.decode_message(data, data.bytesize, message.instance_variable_get(:@msg), mini_table_ptr, nil, decoding_options, message.instance_variable_get(:@arena))
201
+ status = Google::Protobuf::FFI.decode_message(
202
+ data,
203
+ data.bytesize,
204
+ message.instance_variable_get(:@msg),
205
+ mini_table_ptr,
206
+ Google::Protobuf::FFI.get_extension_registry(message.class.descriptor.send(:pool).descriptor_pool),
207
+ decoding_options,
208
+ message.instance_variable_get(:@arena)
209
+ )
174
210
  raise ParseError.new "Error occurred during parsing" unless status == :Ok
175
211
  message
176
212
  end
@@ -234,7 +270,11 @@ module Google
234
270
  message = new
235
271
  pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
236
272
  status = Google::Protobuf::FFI::Status.new
237
- 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)
273
+ result = Google::Protobuf::FFI.json_decode_message_detecting_nonconformance(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
274
+ case result
275
+ when Google::Protobuf::FFI::Upb_JsonDecodeResult_OkWithEmptyStringNumerics
276
+ warn Google::Protobuf::FFI.error_message(status)
277
+ when Google::Protobuf::FFI::Upb_JsonDecodeResult_Error
238
278
  raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
239
279
  end
240
280
  message
@@ -293,42 +333,136 @@ module Google
293
333
 
294
334
  include Google::Protobuf::Internal::Convert
295
335
 
336
+ ##
337
+ # Checks ObjectCache for a sentinel empty frozen Map of the key and
338
+ # value types matching the field descriptor's MessageDef and returns
339
+ # the cache entry. If an entry is not found, one is created and added
340
+ # to the cache keyed by the MessageDef pointer first.
341
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
342
+ def empty_frozen_map(field_descriptor)
343
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
344
+ frozen_map = OBJECT_CACHE.get sub_message_def
345
+ if frozen_map.nil?
346
+ frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
347
+ OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
348
+ end
349
+ raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
350
+ frozen_map
351
+ end
352
+
353
+ ##
354
+ # Returns a frozen Map instance for the given field. If the message
355
+ # already has a value for that field, it is used. If not, a sentinel
356
+ # (per FieldDescriptor) empty frozen Map is returned instead.
357
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
358
+ def frozen_map_from_field_descriptor(field_descriptor)
359
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
360
+ return empty_frozen_map field_descriptor if message_value[:map_val].null?
361
+ get_map_field(message_value[:map_val], field_descriptor).freeze
362
+ end
363
+
364
+ ##
365
+ # Returns a Map instance for the given field. If the message is frozen
366
+ # the return value is also frozen. If not, a mutable instance is
367
+ # returned instead.
368
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
369
+ def map_from_field_descriptor(field_descriptor)
370
+ return frozen_map_from_field_descriptor field_descriptor if frozen?
371
+ mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
372
+ get_map_field(mutable_message_value[:map], field_descriptor)
373
+ end
374
+
375
+ ##
376
+ # Checks ObjectCache for a sentinel empty frozen RepeatedField of the
377
+ # value type matching the field descriptor's MessageDef and returns
378
+ # the cache entry. If an entry is not found, one is created and added
379
+ # to the cache keyed by the MessageDef pointer first.
380
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
381
+ def empty_frozen_repeated_field(field_descriptor)
382
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
383
+ frozen_repeated_field = OBJECT_CACHE.get sub_message_def
384
+ if frozen_repeated_field.nil?
385
+ frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
386
+ OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
387
+ end
388
+ raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
389
+ frozen_repeated_field
390
+ end
391
+
392
+ ##
393
+ # Returns a frozen RepeatedField instance for the given field. If the
394
+ # message already has a value for that field, it is used. If not, a
395
+ # sentinel (per FieldDescriptor) empty frozen RepeatedField is
396
+ # returned instead.
397
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
398
+ def frozen_repeated_field_from_field_descriptor(field_descriptor)
399
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
400
+ return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
401
+ get_repeated_field(message_value[:array_val], field_descriptor).freeze
402
+ end
403
+
404
+ ##
405
+ # Returns a RepeatedField instance for the given field. If the message
406
+ # is frozen the return value is also frozen. If not, a mutable
407
+ # instance is returned instead.
408
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
409
+ def repeated_field_from_field_descriptor(field_descriptor)
410
+ return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
411
+ mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
412
+ get_repeated_field(mutable_message_value[:array], field_descriptor)
413
+ end
414
+
415
+ ##
416
+ # Returns a Message instance for the given field. If the message
417
+ # is frozen nil is always returned. Otherwise, a mutable instance is
418
+ # returned instead.
419
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
420
+ def message_from_field_descriptor(field_descriptor)
421
+ return nil if frozen?
422
+ return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
423
+ mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
424
+ sub_message = mutable_message[:msg]
425
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
426
+ Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
427
+ end
428
+
429
+ ##
430
+ # Returns a scalar value for the given field. If the message
431
+ # is frozen the return value is also frozen.
432
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
433
+ def scalar_from_field_descriptor(field_descriptor)
434
+ c_type = field_descriptor.send(:c_type)
435
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
436
+ msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
437
+ return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
438
+ frozen? ? return_value.freeze : return_value
439
+ end
440
+
441
+ ##
442
+ # Dynamically define accessors methods for every field of @descriptor.
443
+ # Methods with names that conflict with existing methods are skipped.
296
444
  def self.setup_accessors!
297
445
  @descriptor.each do |field_descriptor|
298
446
  field_name = field_descriptor.name
299
447
  unless instance_methods(true).include?(field_name.to_sym)
300
- #TODO - at a high level, dispatching to either
301
- # index_internal or get_field would be logically correct, but slightly slower.
448
+ # Dispatching to either index_internal or get_field is logically
449
+ # correct, but slightly slower due to having to perform extra
450
+ # lookups on each invocation rather than doing it once here.
302
451
  if field_descriptor.map?
303
452
  define_method(field_name) do
304
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
305
- get_map_field(mutable_message_value[:map], field_descriptor)
453
+ map_from_field_descriptor field_descriptor
306
454
  end
307
455
  elsif field_descriptor.repeated?
308
456
  define_method(field_name) do
309
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
310
- get_repeated_field(mutable_message_value[:array], field_descriptor)
457
+ repeated_field_from_field_descriptor field_descriptor
311
458
  end
312
459
  elsif field_descriptor.sub_message?
313
460
  define_method(field_name) do
314
- return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
315
- mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
316
- sub_message = mutable_message[:msg]
317
- sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
318
- Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
461
+ message_from_field_descriptor field_descriptor
319
462
  end
320
463
  else
321
- c_type = field_descriptor.send(:c_type)
322
- if c_type == :enum
323
- define_method(field_name) do
324
- message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
325
- convert_upb_to_ruby message_value, c_type, Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor)
326
- end
327
- else
328
- define_method(field_name) do
329
- message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
330
- convert_upb_to_ruby message_value, c_type
331
- end
464
+ define_method(field_name) do
465
+ scalar_from_field_descriptor field_descriptor
332
466
  end
333
467
  end
334
468
  define_method("#{field_name}=") do |value|
@@ -338,14 +472,16 @@ module Google
338
472
  clear_internal(field_descriptor)
339
473
  end
340
474
  if field_descriptor.type == :enum
341
- define_method("#{field_name}_const") do
342
- if field_descriptor.repeated?
475
+ if field_descriptor.repeated?
476
+ define_method("#{field_name}_const") do
343
477
  return_value = []
344
- get_field(field_descriptor).send(:each_msg_val) do |msg_val|
478
+ repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
345
479
  return_value << msg_val[:int32_val]
346
480
  end
347
481
  return_value
348
- else
482
+ end
483
+ else
484
+ define_method("#{field_name}_const") do
349
485
  message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
350
486
  message_value[:int32_val]
351
487
  end
@@ -372,12 +508,21 @@ module Google
372
508
  end
373
509
  end
374
510
 
511
+ ##
512
+ # Dynamically define accessors methods for every OneOf field of
513
+ # @descriptor.
375
514
  def self.setup_oneof_accessors!
376
515
  @oneof_field_names = []
377
516
  @descriptor.each_oneof do |oneof_descriptor|
378
517
  self.add_oneof_accessors_for! oneof_descriptor
379
518
  end
380
519
  end
520
+
521
+ ##
522
+ # Dynamically define accessors methods for the given OneOf field.
523
+ # Methods with names that conflict with existing methods are skipped.
524
+ # @param oneof_descriptor [OneofDescriptor] Field to create accessors
525
+ # for.
381
526
  def self.add_oneof_accessors_for!(oneof_descriptor)
382
527
  field_name = oneof_descriptor.name.to_sym
383
528
  @oneof_field_names << field_name
@@ -508,6 +653,10 @@ module Google
508
653
  Google::Protobuf::FFI.clear_message_field(@msg, field_def)
509
654
  end
510
655
 
656
+ # Accessor for field by name. Does not delegate to methods setup by
657
+ # self.setup_accessors! in order to avoid conflicts with bad field
658
+ # names e.g. `dup` or `class` which are perfectly valid for proto
659
+ # fields.
511
660
  def index_internal(name)
512
661
  field_descriptor = self.class.descriptor.lookup(name)
513
662
  get_field field_descriptor unless field_descriptor.nil?
@@ -535,7 +684,7 @@ module Google
535
684
  # one if omitted or nil.
536
685
  def initialize(initial_value = nil, arena = nil, msg = nil)
537
686
  @arena = arena || Google::Protobuf::FFI.create_arena
538
- @msg = msg || Google::Protobuf::FFI.new_message_from_def(self.class.descriptor, @arena)
687
+ @msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
539
688
 
540
689
  unless initial_value.nil?
541
690
  raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
@@ -556,9 +705,9 @@ module Google
556
705
 
557
706
  next if value.nil?
558
707
  if field_descriptor.map?
559
- index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, @arena, value: value), name: key.to_s)
708
+ index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
560
709
  elsif field_descriptor.repeated?
561
- index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, @arena, values: value), name: key.to_s)
710
+ index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
562
711
  else
563
712
  index_assign_internal(value, name: key.to_s)
564
713
  end
@@ -571,21 +720,20 @@ module Google
571
720
  end
572
721
 
573
722
  ##
574
- # Gets a field of this message identified by the argument definition.
723
+ # Gets a field of this message identified by FieldDescriptor.
575
724
  #
576
- # @param field [FieldDescriptor] Descriptor of the field to get
725
+ # @param field [FieldDescriptor] Field to retrieve.
726
+ # @param unwrap [Boolean](false) If true, unwraps wrappers.
577
727
  def get_field(field, unwrap: false)
578
728
  if field.map?
579
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
580
- get_map_field(mutable_message_value[:map], field)
729
+ map_from_field_descriptor field
581
730
  elsif field.repeated?
582
- mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
583
- get_repeated_field(mutable_message_value[:array], field)
731
+ repeated_field_from_field_descriptor field
584
732
  elsif field.sub_message?
585
733
  return nil unless Google::Protobuf::FFI.get_message_has @msg, field
586
- sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field)
587
734
  if unwrap
588
735
  if field.has?(self)
736
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
589
737
  wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
590
738
  fields = Google::Protobuf::FFI.field_count(sub_message_def)
591
739
  raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
@@ -596,41 +744,37 @@ module Google
596
744
  nil
597
745
  end
598
746
  else
599
- mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field, @arena
600
- sub_message = mutable_message[:msg]
601
- Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
747
+ message_from_field_descriptor field
602
748
  end
603
749
  else
604
- c_type = field.send(:c_type)
605
- message_value = Google::Protobuf::FFI.get_message_value @msg, field
606
- if c_type == :enum
607
- convert_upb_to_ruby message_value, c_type, Google::Protobuf::FFI.get_subtype_as_enum(field)
608
- else
609
- convert_upb_to_ruby message_value, c_type
610
- end
750
+ scalar_from_field_descriptor field
611
751
  end
612
752
  end
613
753
 
614
754
  ##
615
- # @param array [::FFI::Pointer] Pointer to the Array
755
+ # Gets a RepeatedField from the ObjectCache or creates a new one.
756
+ # @param array [::FFI::Pointer] Pointer to the upb_Array
616
757
  # @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
617
758
  def get_repeated_field(array, field)
618
759
  return nil if array.nil? or array.null?
619
760
  repeated_field = OBJECT_CACHE.get(array.address)
620
761
  if repeated_field.nil?
621
- repeated_field = RepeatedField.send(:construct_for_field, field, @arena, array: array)
762
+ repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
763
+ repeated_field.freeze if frozen?
622
764
  end
623
765
  repeated_field
624
766
  end
625
767
 
626
768
  ##
627
- # @param map [::FFI::Pointer] Pointer to the Map
769
+ # Gets a Map from the ObjectCache or creates a new one.
770
+ # @param map [::FFI::Pointer] Pointer to the upb_Map
628
771
  # @param field [Google::Protobuf::FieldDescriptor] Type of the map field
629
772
  def get_map_field(map, field)
630
773
  return nil if map.nil? or map.null?
631
774
  map_field = OBJECT_CACHE.get(map.address)
632
775
  if map_field.nil?
633
- map_field = Google::Protobuf::Map.send(:construct_for_field, field, @arena, map: map)
776
+ map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
777
+ map_field.freeze if frozen?
634
778
  end
635
779
  map_field
636
780
  end
@@ -0,0 +1,114 @@
1
+ # Protocol Buffers - Google's data interchange format
2
+ # Copyright 2024 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
+ module Google
9
+ module Protobuf
10
+ class MethodDescriptor
11
+ attr :method_def, :descriptor_pool
12
+
13
+ include Google::Protobuf::Internal::Convert
14
+
15
+ # FFI Interface methods and setup
16
+ extend ::FFI::DataConverter
17
+ native_type ::FFI::Type::POINTER
18
+
19
+ class << self
20
+ prepend Google::Protobuf::Internal::TypeSafety
21
+ include Google::Protobuf::Internal::PointerHelper
22
+
23
+ # @param value [MethodDescriptor] MethodDescriptor to convert to an FFI native type
24
+ # @param _ [Object] Unused
25
+ def to_native(value, _)
26
+ method_def_ptr = value.nil? ? nil : value.instance_variable_get(:@method_def)
27
+ return ::FFI::Pointer::NULL if method_def_ptr.nil?
28
+ raise "Underlying method_def was null!" if method_def_ptr.null?
29
+ method_def_ptr
30
+ end
31
+
32
+ ##
33
+ # @param service_def [::FFI::Pointer] MethodDef pointer to be wrapped
34
+ # @param _ [Object] Unused
35
+ def from_native(method_def, _ = nil)
36
+ return nil if method_def.nil? or method_def.null?
37
+ service_def = Google::Protobuf::FFI.raw_service_def_by_raw_method_def(method_def)
38
+ file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def)
39
+ descriptor_from_file_def(file_def, method_def)
40
+ end
41
+ end
42
+
43
+ def self.new(*arguments, &block)
44
+ raise "Descriptor objects may not be created from Ruby."
45
+ end
46
+
47
+ def to_s
48
+ inspect
49
+ end
50
+
51
+ def inspect
52
+ "#{self.class.name}: #{name}"
53
+ end
54
+
55
+ def name
56
+ @name ||= Google::Protobuf::FFI.get_method_name(self)
57
+ end
58
+
59
+ def options
60
+ @options ||= begin
61
+ size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
62
+ temporary_arena = Google::Protobuf::FFI.create_arena
63
+ buffer = Google::Protobuf::FFI.method_options(self, size_ptr, temporary_arena)
64
+ Google::Protobuf::MethodOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze
65
+ end
66
+ end
67
+
68
+ def input_type
69
+ @input_type ||= Google::Protobuf::FFI.method_input_type(self)
70
+ end
71
+
72
+ def output_type
73
+ @output_type ||= Google::Protobuf::FFI.method_output_type(self)
74
+ end
75
+
76
+ def client_streaming
77
+ @client_streaming ||= Google::Protobuf::FFI.method_client_streaming(self)
78
+ end
79
+
80
+ def server_streaming
81
+ @server_streaming ||= Google::Protobuf::FFI.method_server_streaming(self)
82
+ end
83
+
84
+ private
85
+
86
+ def initialize(method_def, descriptor_pool)
87
+ @method_def = method_def
88
+ @descriptor_pool = descriptor_pool
89
+ end
90
+
91
+ def self.private_constructor(method_def, descriptor_pool)
92
+ instance = allocate
93
+ instance.send(:initialize, method_def, descriptor_pool)
94
+ instance
95
+ end
96
+
97
+ def c_type
98
+ @c_type ||= Google::Protobuf::FFI.get_c_type(self)
99
+ end
100
+ end
101
+
102
+ class FFI
103
+ # MethodDef
104
+ attach_function :raw_service_def_by_raw_method_def, :upb_MethodDef_Service, [:pointer], :pointer
105
+ attach_function :get_method_name, :upb_MethodDef_Name, [MethodDescriptor], :string
106
+ attach_function :method_options, :MethodDescriptor_serialized_options, [MethodDescriptor, :pointer, Internal::Arena], :pointer
107
+ attach_function :method_input_type, :upb_MethodDef_InputType, [MethodDescriptor], Descriptor
108
+ attach_function :method_output_type, :upb_MethodDef_OutputType, [MethodDescriptor], Descriptor
109
+ attach_function :method_client_streaming, :upb_MethodDef_ClientStreaming, [MethodDescriptor], :bool
110
+ attach_function :method_server_streaming, :upb_MethodDef_ServerStreaming, [MethodDescriptor], :bool
111
+ end
112
+ end
113
+ end
114
+
@@ -18,13 +18,13 @@ module Google
18
18
 
19
19
  def self.cache_implementation
20
20
  if interpreter_supports_non_finalized_keys_in_weak_map? and SIZEOF_LONG >= SIZEOF_VALUE
21
- Google::Protobuf::ObjectCache
21
+ Google::Protobuf::Internal::ObjectCache
22
22
  else
23
- Google::Protobuf::LegacyObjectCache
23
+ Google::Protobuf::Internal::LegacyObjectCache
24
24
  end
25
25
  end
26
26
 
27
27
  public
28
28
  OBJECT_CACHE = cache_implementation.new
29
29
  end
30
- end
30
+ end