google-protobuf 3.25.6 → 4.30.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/convert.c +33 -15
  3. data/ext/google/protobuf_c/defs.c +526 -29
  4. data/ext/google/protobuf_c/extconf.rb +13 -1
  5. data/ext/google/protobuf_c/glue.c +79 -0
  6. data/ext/google/protobuf_c/map.c +72 -27
  7. data/ext/google/protobuf_c/map.h +7 -3
  8. data/ext/google/protobuf_c/message.c +113 -116
  9. data/ext/google/protobuf_c/message.h +2 -6
  10. data/ext/google/protobuf_c/protobuf.c +32 -18
  11. data/ext/google/protobuf_c/protobuf.h +3 -7
  12. data/ext/google/protobuf_c/repeated_field.c +58 -23
  13. data/ext/google/protobuf_c/repeated_field.h +6 -2
  14. data/ext/google/protobuf_c/ruby-upb.c +14926 -11596
  15. data/ext/google/protobuf_c/ruby-upb.h +8701 -5832
  16. data/ext/google/protobuf_c/shared_convert.c +7 -2
  17. data/ext/google/protobuf_c/shared_message.c +3 -32
  18. data/ext/google/protobuf_c/shared_message.h +0 -4
  19. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +207 -0
  20. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -8
  21. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_neon.inc +117 -0
  22. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_sse.inc +272 -0
  23. data/lib/google/protobuf/any_pb.rb +1 -22
  24. data/lib/google/protobuf/api_pb.rb +1 -24
  25. data/lib/google/protobuf/descriptor_pb.rb +4 -23
  26. data/lib/google/protobuf/duration_pb.rb +1 -22
  27. data/lib/google/protobuf/empty_pb.rb +1 -22
  28. data/lib/google/protobuf/ffi/descriptor.rb +14 -4
  29. data/lib/google/protobuf/ffi/descriptor_pool.rb +3 -1
  30. data/lib/google/protobuf/ffi/enum_descriptor.rb +13 -1
  31. data/lib/google/protobuf/ffi/ffi.rb +7 -6
  32. data/lib/google/protobuf/ffi/field_descriptor.rb +23 -2
  33. data/lib/google/protobuf/ffi/file_descriptor.rb +13 -13
  34. data/lib/google/protobuf/ffi/internal/arena.rb +0 -6
  35. data/lib/google/protobuf/ffi/internal/convert.rb +17 -30
  36. data/lib/google/protobuf/ffi/map.rb +50 -24
  37. data/lib/google/protobuf/ffi/message.rb +188 -67
  38. data/lib/google/protobuf/ffi/method_descriptor.rb +124 -0
  39. data/lib/google/protobuf/ffi/object_cache.rb +3 -3
  40. data/lib/google/protobuf/ffi/oneof_descriptor.rb +13 -1
  41. data/lib/google/protobuf/ffi/repeated_field.rb +47 -19
  42. data/lib/google/protobuf/ffi/service_descriptor.rb +117 -0
  43. data/lib/google/protobuf/field_mask_pb.rb +1 -22
  44. data/lib/google/protobuf/internal/object_cache.rb +99 -0
  45. data/lib/google/protobuf/plugin_pb.rb +2 -24
  46. data/lib/google/protobuf/repeated_field.rb +4 -5
  47. data/lib/google/protobuf/source_context_pb.rb +1 -22
  48. data/lib/google/protobuf/struct_pb.rb +1 -22
  49. data/lib/google/protobuf/timestamp_pb.rb +1 -22
  50. data/lib/google/protobuf/type_pb.rb +1 -24
  51. data/lib/google/protobuf/wrappers_pb.rb +1 -22
  52. data/lib/google/protobuf.rb +1 -1
  53. data/lib/google/protobuf_ffi.rb +5 -3
  54. data/lib/google/protobuf_native.rb +0 -1
  55. data/lib/google/tasks/ffi.rake +1 -3
  56. metadata +28 -13
  57. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
  58. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
  59. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
  60. data/lib/google/protobuf/descriptor_dsl.rb +0 -465
  61. data/lib/google/protobuf/object_cache.rb +0 -97
@@ -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
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
25
- attach_function :message_discard_unknown, :upb_Message_DiscardUnknown, [:Message, Descriptor, :int], :bool
24
+ attach_function :get_message_which_oneof, :upb_Message_WhichOneofByDef, [:Message, OneofDescriptor], FieldDescriptor
25
+ attach_function :message_discard_unknown, :upb_Message_DiscardUnknown, [:Message, Descriptor, :DefPool, :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)
@@ -242,7 +270,9 @@ module Google
242
270
  message = new
243
271
  pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
244
272
  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)
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_Error
246
276
  raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
247
277
  end
248
278
  message
@@ -301,53 +331,136 @@ module Google
301
331
 
302
332
  include Google::Protobuf::Internal::Convert
303
333
 
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
334
+ ##
335
+ # Checks ObjectCache for a sentinel empty frozen Map of the key and
336
+ # value types matching the field descriptor's MessageDef and returns
337
+ # the cache entry. If an entry is not found, one is created and added
338
+ # to the cache keyed by the MessageDef pointer first.
339
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
340
+ def empty_frozen_map(field_descriptor)
341
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
342
+ frozen_map = OBJECT_CACHE.get sub_message_def
343
+ if frozen_map.nil?
344
+ frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
345
+ OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
311
346
  end
312
- self
347
+ raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
348
+ frozen_map
349
+ end
350
+
351
+ ##
352
+ # Returns a frozen Map instance for the given field. If the message
353
+ # already has a value for that field, it is used. If not, a sentinel
354
+ # (per FieldDescriptor) empty frozen Map is returned instead.
355
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
356
+ def frozen_map_from_field_descriptor(field_descriptor)
357
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
358
+ return empty_frozen_map field_descriptor if message_value[:map_val].null?
359
+ get_map_field(message_value[:map_val], field_descriptor).freeze
360
+ end
361
+
362
+ ##
363
+ # Returns a Map instance for the given field. If the message is frozen
364
+ # the return value is also frozen. If not, a mutable instance is
365
+ # returned instead.
366
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
367
+ def map_from_field_descriptor(field_descriptor)
368
+ return frozen_map_from_field_descriptor field_descriptor if frozen?
369
+ mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
370
+ get_map_field(mutable_message_value[:map], field_descriptor)
371
+ end
372
+
373
+ ##
374
+ # Checks ObjectCache for a sentinel empty frozen RepeatedField of the
375
+ # value type matching the field descriptor's MessageDef and returns
376
+ # the cache entry. If an entry is not found, one is created and added
377
+ # to the cache keyed by the MessageDef pointer first.
378
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
379
+ def empty_frozen_repeated_field(field_descriptor)
380
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
381
+ frozen_repeated_field = OBJECT_CACHE.get sub_message_def
382
+ if frozen_repeated_field.nil?
383
+ frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
384
+ OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
385
+ end
386
+ raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
387
+ frozen_repeated_field
388
+ end
389
+
390
+ ##
391
+ # Returns a frozen RepeatedField instance for the given field. If the
392
+ # message already has a value for that field, it is used. If not, a
393
+ # sentinel (per FieldDescriptor) empty frozen RepeatedField is
394
+ # returned instead.
395
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
396
+ def frozen_repeated_field_from_field_descriptor(field_descriptor)
397
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
398
+ return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
399
+ get_repeated_field(message_value[:array_val], field_descriptor).freeze
313
400
  end
314
401
 
402
+ ##
403
+ # Returns a RepeatedField instance for the given field. If the message
404
+ # is frozen the return value is also frozen. If not, a mutable
405
+ # instance is returned instead.
406
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
407
+ def repeated_field_from_field_descriptor(field_descriptor)
408
+ return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
409
+ mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
410
+ get_repeated_field(mutable_message_value[:array], field_descriptor)
411
+ end
412
+
413
+ ##
414
+ # Returns a Message instance for the given field. If the message
415
+ # is frozen nil is always returned. Otherwise, a mutable instance is
416
+ # returned instead.
417
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
418
+ def message_from_field_descriptor(field_descriptor)
419
+ return nil if frozen?
420
+ return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
421
+ mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
422
+ sub_message = mutable_message[:msg]
423
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
424
+ Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
425
+ end
426
+
427
+ ##
428
+ # Returns a scalar value for the given field. If the message
429
+ # is frozen the return value is also frozen.
430
+ # @param field_descriptor [FieldDescriptor] Field to retrieve.
431
+ def scalar_from_field_descriptor(field_descriptor)
432
+ c_type = field_descriptor.send(:c_type)
433
+ message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
434
+ msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
435
+ return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
436
+ frozen? ? return_value.freeze : return_value
437
+ end
438
+
439
+ ##
440
+ # Dynamically define accessors methods for every field of @descriptor.
441
+ # Methods with names that conflict with existing methods are skipped.
315
442
  def self.setup_accessors!
316
443
  @descriptor.each do |field_descriptor|
317
444
  field_name = field_descriptor.name
318
445
  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.
446
+ # Dispatching to either index_internal or get_field is logically
447
+ # correct, but slightly slower due to having to perform extra
448
+ # lookups on each invocation rather than doing it once here.
321
449
  if field_descriptor.map?
322
450
  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)
451
+ map_from_field_descriptor field_descriptor
325
452
  end
326
453
  elsif field_descriptor.repeated?
327
454
  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)
455
+ repeated_field_from_field_descriptor field_descriptor
330
456
  end
331
457
  elsif field_descriptor.sub_message?
332
458
  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)
459
+ message_from_field_descriptor field_descriptor
338
460
  end
339
461
  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
462
+ define_method(field_name) do
463
+ scalar_from_field_descriptor field_descriptor
351
464
  end
352
465
  end
353
466
  define_method("#{field_name}=") do |value|
@@ -357,14 +470,16 @@ module Google
357
470
  clear_internal(field_descriptor)
358
471
  end
359
472
  if field_descriptor.type == :enum
360
- define_method("#{field_name}_const") do
361
- if field_descriptor.repeated?
473
+ if field_descriptor.repeated?
474
+ define_method("#{field_name}_const") do
362
475
  return_value = []
363
- get_field(field_descriptor).send(:each_msg_val) do |msg_val|
476
+ repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
364
477
  return_value << msg_val[:int32_val]
365
478
  end
366
479
  return_value
367
- else
480
+ end
481
+ else
482
+ define_method("#{field_name}_const") do
368
483
  message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
369
484
  message_value[:int32_val]
370
485
  end
@@ -391,12 +506,21 @@ module Google
391
506
  end
392
507
  end
393
508
 
509
+ ##
510
+ # Dynamically define accessors methods for every OneOf field of
511
+ # @descriptor.
394
512
  def self.setup_oneof_accessors!
395
513
  @oneof_field_names = []
396
514
  @descriptor.each_oneof do |oneof_descriptor|
397
515
  self.add_oneof_accessors_for! oneof_descriptor
398
516
  end
399
517
  end
518
+
519
+ ##
520
+ # Dynamically define accessors methods for the given OneOf field.
521
+ # Methods with names that conflict with existing methods are skipped.
522
+ # @param oneof_descriptor [OneofDescriptor] Field to create accessors
523
+ # for.
400
524
  def self.add_oneof_accessors_for!(oneof_descriptor)
401
525
  field_name = oneof_descriptor.name.to_sym
402
526
  @oneof_field_names << field_name
@@ -527,6 +651,10 @@ module Google
527
651
  Google::Protobuf::FFI.clear_message_field(@msg, field_def)
528
652
  end
529
653
 
654
+ # Accessor for field by name. Does not delegate to methods setup by
655
+ # self.setup_accessors! in order to avoid conflicts with bad field
656
+ # names e.g. `dup` or `class` which are perfectly valid for proto
657
+ # fields.
530
658
  def index_internal(name)
531
659
  field_descriptor = self.class.descriptor.lookup(name)
532
660
  get_field field_descriptor unless field_descriptor.nil?
@@ -554,7 +682,7 @@ module Google
554
682
  # one if omitted or nil.
555
683
  def initialize(initial_value = nil, arena = nil, msg = nil)
556
684
  @arena = arena || Google::Protobuf::FFI.create_arena
557
- @msg = msg || Google::Protobuf::FFI.new_message_from_def(self.class.descriptor, @arena)
685
+ @msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
558
686
 
559
687
  unless initial_value.nil?
560
688
  raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
@@ -575,9 +703,9 @@ module Google
575
703
 
576
704
  next if value.nil?
577
705
  if field_descriptor.map?
578
- index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, @arena, value: value), name: key.to_s)
706
+ index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
579
707
  elsif field_descriptor.repeated?
580
- index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, @arena, values: value), name: key.to_s)
708
+ index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
581
709
  else
582
710
  index_assign_internal(value, name: key.to_s)
583
711
  end
@@ -590,21 +718,20 @@ module Google
590
718
  end
591
719
 
592
720
  ##
593
- # Gets a field of this message identified by the argument definition.
721
+ # Gets a field of this message identified by FieldDescriptor.
594
722
  #
595
- # @param field [FieldDescriptor] Descriptor of the field to get
723
+ # @param field [FieldDescriptor] Field to retrieve.
724
+ # @param unwrap [Boolean](false) If true, unwraps wrappers.
596
725
  def get_field(field, unwrap: false)
597
726
  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)
727
+ map_from_field_descriptor field
600
728
  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)
729
+ repeated_field_from_field_descriptor field
603
730
  elsif field.sub_message?
604
731
  return nil unless Google::Protobuf::FFI.get_message_has @msg, field
605
- sub_message_def = Google::Protobuf::FFI.get_subtype_as_message(field)
606
732
  if unwrap
607
733
  if field.has?(self)
734
+ sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
608
735
  wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
609
736
  fields = Google::Protobuf::FFI.field_count(sub_message_def)
610
737
  raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
@@ -615,43 +742,37 @@ module Google
615
742
  nil
616
743
  end
617
744
  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)
745
+ message_from_field_descriptor field
621
746
  end
622
747
  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
748
+ scalar_from_field_descriptor field
630
749
  end
631
750
  end
632
751
 
633
752
  ##
634
- # @param array [::FFI::Pointer] Pointer to the Array
753
+ # Gets a RepeatedField from the ObjectCache or creates a new one.
754
+ # @param array [::FFI::Pointer] Pointer to the upb_Array
635
755
  # @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
636
756
  def get_repeated_field(array, field)
637
757
  return nil if array.nil? or array.null?
638
758
  repeated_field = OBJECT_CACHE.get(array.address)
639
759
  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?
760
+ repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
761
+ repeated_field.freeze if frozen?
642
762
  end
643
763
  repeated_field
644
764
  end
645
765
 
646
766
  ##
647
- # @param map [::FFI::Pointer] Pointer to the Map
767
+ # Gets a Map from the ObjectCache or creates a new one.
768
+ # @param map [::FFI::Pointer] Pointer to the upb_Map
648
769
  # @param field [Google::Protobuf::FieldDescriptor] Type of the map field
649
770
  def get_map_field(map, field)
650
771
  return nil if map.nil? or map.null?
651
772
  map_field = OBJECT_CACHE.get(map.address)
652
773
  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?
774
+ map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
775
+ map_field.freeze if frozen?
655
776
  end
656
777
  map_field
657
778
  end
@@ -0,0 +1,124 @@
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
+ def to_proto
85
+ @to_proto ||= begin
86
+ size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
87
+ temporary_arena = Google::Protobuf::FFI.create_arena
88
+ buffer = Google::Protobuf::FFI.method_to_proto(self, size_ptr, temporary_arena)
89
+ Google::Protobuf::MethodDescriptorProto.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def initialize(method_def, descriptor_pool)
96
+ @method_def = method_def
97
+ @descriptor_pool = descriptor_pool
98
+ end
99
+
100
+ def self.private_constructor(method_def, descriptor_pool)
101
+ instance = allocate
102
+ instance.send(:initialize, method_def, descriptor_pool)
103
+ instance
104
+ end
105
+
106
+ def c_type
107
+ @c_type ||= Google::Protobuf::FFI.get_c_type(self)
108
+ end
109
+ end
110
+
111
+ class FFI
112
+ # MethodDef
113
+ attach_function :raw_service_def_by_raw_method_def, :upb_MethodDef_Service, [:pointer], :pointer
114
+ attach_function :get_method_name, :upb_MethodDef_Name, [MethodDescriptor], :string
115
+ attach_function :method_options, :MethodDescriptor_serialized_options, [MethodDescriptor, :pointer, Internal::Arena], :pointer
116
+ attach_function :method_input_type, :upb_MethodDef_InputType, [MethodDescriptor], Descriptor
117
+ attach_function :method_output_type, :upb_MethodDef_OutputType, [MethodDescriptor], Descriptor
118
+ attach_function :method_client_streaming, :upb_MethodDef_ClientStreaming, [MethodDescriptor], :bool
119
+ attach_function :method_server_streaming, :upb_MethodDef_ServerStreaming, [MethodDescriptor], :bool
120
+ attach_function :method_to_proto, :MethodDescriptor_serialized_to_proto, [MethodDescriptor, :pointer, Internal::Arena], :pointer
121
+ end
122
+ end
123
+ end
124
+
@@ -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
@@ -58,10 +58,21 @@ module Google
58
58
  size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
59
59
  temporary_arena = Google::Protobuf::FFI.create_arena
60
60
  buffer = Google::Protobuf::FFI.oneof_options(self, size_ptr, temporary_arena)
61
- Google::Protobuf::OneofOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).send(:internal_deep_freeze)
61
+ opts = Google::Protobuf::OneofOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
62
+ opts.clear_features()
63
+ opts.freeze
62
64
  end
63
65
  end
64
66
 
67
+ def to_proto
68
+ @to_proto ||= begin
69
+ size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
70
+ temporary_arena = Google::Protobuf::FFI.create_arena
71
+ buffer = Google::Protobuf::FFI.oneof_to_proto(self, size_ptr, temporary_arena)
72
+ Google::Protobuf::OneofDescriptorProto.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
73
+ end
74
+ end
75
+
65
76
  private
66
77
 
67
78
  def initialize(oneof_def, descriptor_pool)
@@ -87,6 +98,7 @@ module Google
87
98
  attach_function :get_oneof_field_by_index, :upb_OneofDef_Field, [OneofDescriptor, :int], FieldDescriptor
88
99
  attach_function :get_oneof_containing_type,:upb_OneofDef_ContainingType, [:pointer], Descriptor
89
100
  attach_function :oneof_options, :OneOfDescriptor_serialized_options, [OneofDescriptor, :pointer, Internal::Arena], :pointer
101
+ attach_function :oneof_to_proto, :OneOfDescriptor_serialized_to_proto, [OneofDescriptor, :pointer, Internal::Arena], :pointer
90
102
 
91
103
  # FieldDescriptor
92
104
  attach_function :real_containing_oneof, :upb_FieldDef_RealContainingOneof, [FieldDescriptor], OneofDescriptor
@@ -24,12 +24,14 @@ module Google
24
24
  module Protobuf
25
25
  class FFI
26
26
  # Array
27
- attach_function :append_array, :upb_Array_Append, [:Array, MessageValue.by_value, Internal::Arena], :bool
28
- attach_function :get_msgval_at,:upb_Array_Get, [:Array, :size_t], MessageValue.by_value
29
- attach_function :create_array, :upb_Array_New, [Internal::Arena, CType], :Array
30
- attach_function :array_resize, :upb_Array_Resize, [:Array, :size_t, Internal::Arena], :bool
31
- attach_function :array_set, :upb_Array_Set, [:Array, :size_t, MessageValue.by_value], :void
32
- attach_function :array_size, :upb_Array_Size, [:Array], :size_t
27
+ attach_function :append_array, :upb_Array_Append, [:Array, MessageValue.by_value, Internal::Arena], :bool
28
+ attach_function :get_msgval_at, :upb_Array_Get, [:Array, :size_t], MessageValue.by_value
29
+ attach_function :create_array, :upb_Array_New, [Internal::Arena, CType], :Array
30
+ attach_function :array_resize, :upb_Array_Resize, [:Array, :size_t, Internal::Arena], :bool
31
+ attach_function :array_set, :upb_Array_Set, [:Array, :size_t, MessageValue.by_value], :void
32
+ attach_function :array_size, :upb_Array_Size, [:Array], :size_t
33
+ attach_function :array_freeze, :upb_Array_Freeze, [:Array, MiniTable.by_ref], :void
34
+ attach_function :array_frozen?, :upb_Array_IsFrozen, [:Array], :bool
33
35
  end
34
36
 
35
37
  class RepeatedField
@@ -174,6 +176,38 @@ module Google
174
176
  end
175
177
  alias size :length
176
178
 
179
+ ##
180
+ # Is this object frozen?
181
+ # Returns true if either this Ruby wrapper or the underlying
182
+ # representation are frozen. Freezes the wrapper if the underlying
183
+ # representation is already frozen but this wrapper isn't.
184
+ def frozen?
185
+ unless Google::Protobuf::FFI.array_frozen? array
186
+ raise RuntimeError.new "Ruby frozen RepeatedField with mutable representation" if super
187
+ return false
188
+ end
189
+ method(:freeze).super_method.call unless super
190
+ true
191
+ end
192
+
193
+ ##
194
+ # Freezes the RepeatedField object. We have to intercept this so we can
195
+ # freeze the underlying representation, not just the Ruby wrapper. Returns
196
+ # self.
197
+ def freeze
198
+ if method(:frozen?).super_method.call
199
+ unless Google::Protobuf::FFI.array_frozen? array
200
+ raise RuntimeError.new "Underlying representation of repeated field still mutable despite frozen wrapper"
201
+ end
202
+ return self
203
+ end
204
+ unless Google::Protobuf::FFI.array_frozen? array
205
+ mini_table = (type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
206
+ Google::Protobuf::FFI.array_freeze(array, mini_table)
207
+ end
208
+ super
209
+ end
210
+
177
211
  def dup
178
212
  instance = self.class.allocate
179
213
  instance.send(:initialize, type, descriptor: descriptor, arena: arena)
@@ -252,16 +286,6 @@ module Google
252
286
 
253
287
  attr :name, :arena, :array, :type, :descriptor
254
288
 
255
- def internal_deep_freeze
256
- freeze
257
- if type == :message
258
- each do |element|
259
- element.send :internal_deep_freeze
260
- end
261
- end
262
- self
263
- end
264
-
265
289
  def internal_push(*elements)
266
290
  elements.each do |element|
267
291
  append_msg_val convert_ruby_to_upb(element, arena, type, descriptor)
@@ -346,10 +370,14 @@ module Google
346
370
  OBJECT_CACHE.try_add(@array.address, self)
347
371
  end
348
372
 
349
- # @param field [FieldDescriptor] Descriptor of the field where the RepeatedField will be assigned
350
- # @param values [Enumerable] Initial values; may be nil or empty
373
+ ##
374
+ # Constructor that uses the type information from the given
375
+ # FieldDescriptor to configure the new RepeatedField instance.
376
+ # @param field [FieldDescriptor] Type information for the new RepeatedField
351
377
  # @param arena [Arena] Owning message's arena
352
- def self.construct_for_field(field, arena, values: nil, array: nil)
378
+ # @param values [Enumerable] Initial values
379
+ # @param array [::FFI::Pointer] Existing upb_Array
380
+ def self.construct_for_field(field, arena: nil, values: nil, array: nil)
353
381
  instance = allocate
354
382
  options = {initial_values: values, name: field.name, arena: arena, array: array}
355
383
  if [:enum, :message].include? field.type