google-protobuf 3.22.5 → 3.25.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/Rakefile +3 -0
  3. data/ext/google/protobuf_c/convert.c +23 -70
  4. data/ext/google/protobuf_c/convert.h +3 -28
  5. data/ext/google/protobuf_c/defs.c +175 -52
  6. data/ext/google/protobuf_c/defs.h +3 -28
  7. data/ext/google/protobuf_c/extconf.rb +2 -1
  8. data/ext/google/protobuf_c/glue.c +56 -0
  9. data/ext/google/protobuf_c/map.c +27 -28
  10. data/ext/google/protobuf_c/map.h +6 -28
  11. data/ext/google/protobuf_c/message.c +83 -83
  12. data/ext/google/protobuf_c/message.h +10 -28
  13. data/ext/google/protobuf_c/protobuf.c +39 -176
  14. data/ext/google/protobuf_c/protobuf.h +24 -32
  15. data/ext/google/protobuf_c/repeated_field.c +28 -29
  16. data/ext/google/protobuf_c/repeated_field.h +6 -28
  17. data/ext/google/protobuf_c/ruby-upb.c +2982 -2494
  18. data/ext/google/protobuf_c/ruby-upb.h +5838 -3467
  19. data/ext/google/protobuf_c/shared_convert.c +64 -0
  20. data/ext/google/protobuf_c/shared_convert.h +26 -0
  21. data/ext/google/protobuf_c/shared_message.c +65 -0
  22. data/ext/google/protobuf_c/shared_message.h +25 -0
  23. data/ext/google/protobuf_c/wrap_memcpy.c +3 -26
  24. data/lib/google/protobuf/any_pb.rb +24 -5
  25. data/lib/google/protobuf/api_pb.rb +26 -23
  26. data/lib/google/protobuf/descriptor_pb.rb +40 -252
  27. data/lib/google/protobuf/duration_pb.rb +24 -5
  28. data/lib/google/protobuf/empty_pb.rb +24 -3
  29. data/lib/google/protobuf/ffi/descriptor.rb +165 -0
  30. data/lib/google/protobuf/ffi/descriptor_pool.rb +75 -0
  31. data/lib/google/protobuf/ffi/enum_descriptor.rb +171 -0
  32. data/lib/google/protobuf/ffi/ffi.rb +213 -0
  33. data/lib/google/protobuf/ffi/field_descriptor.rb +319 -0
  34. data/lib/google/protobuf/ffi/file_descriptor.rb +59 -0
  35. data/lib/google/protobuf/ffi/internal/arena.rb +66 -0
  36. data/lib/google/protobuf/ffi/internal/convert.rb +305 -0
  37. data/lib/google/protobuf/ffi/internal/pointer_helper.rb +35 -0
  38. data/lib/google/protobuf/ffi/internal/type_safety.rb +25 -0
  39. data/lib/google/protobuf/ffi/map.rb +407 -0
  40. data/lib/google/protobuf/ffi/message.rb +662 -0
  41. data/lib/google/protobuf/ffi/object_cache.rb +30 -0
  42. data/lib/google/protobuf/ffi/oneof_descriptor.rb +95 -0
  43. data/lib/google/protobuf/ffi/repeated_field.rb +383 -0
  44. data/lib/google/protobuf/field_mask_pb.rb +24 -4
  45. data/lib/google/protobuf/message_exts.rb +3 -26
  46. data/lib/google/protobuf/object_cache.rb +97 -0
  47. data/lib/google/protobuf/plugin_pb.rb +25 -28
  48. data/lib/google/protobuf/repeated_field.rb +3 -26
  49. data/lib/google/protobuf/source_context_pb.rb +24 -4
  50. data/lib/google/protobuf/struct_pb.rb +24 -20
  51. data/lib/google/protobuf/timestamp_pb.rb +24 -5
  52. data/lib/google/protobuf/type_pb.rb +26 -68
  53. data/lib/google/protobuf/well_known_types.rb +5 -34
  54. data/lib/google/protobuf/wrappers_pb.rb +24 -28
  55. data/lib/google/protobuf.rb +27 -45
  56. data/lib/google/protobuf_ffi.rb +50 -0
  57. data/lib/google/protobuf_native.rb +20 -0
  58. data/lib/google/tasks/ffi.rake +102 -0
  59. metadata +72 -4
@@ -0,0 +1,59 @@
1
+ # Protocol Buffers - Google's data interchange format
2
+ # Copyright 2022 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 FFI
11
+ # FileDescriptor
12
+ attach_function :file_def_name, :upb_FileDef_Name, [:FileDef], :string
13
+ attach_function :file_def_syntax, :upb_FileDef_Syntax, [:FileDef], Syntax
14
+ attach_function :file_def_pool, :upb_FileDef_Pool, [:FileDef], :DefPool
15
+ attach_function :file_options, :FileDescriptor_serialized_options, [:FileDef, :pointer, Internal::Arena], :pointer
16
+ end
17
+
18
+ class FileDescriptor
19
+ attr :descriptor_pool, :file_def
20
+
21
+ def initialize(file_def, descriptor_pool)
22
+ @descriptor_pool = descriptor_pool
23
+ @file_def = file_def
24
+ end
25
+
26
+ def to_s
27
+ inspect
28
+ end
29
+
30
+ def inspect
31
+ "#{self.class.name}: #{name}"
32
+ end
33
+
34
+ def syntax
35
+ case Google::Protobuf::FFI.file_def_syntax(@file_def)
36
+ when :Proto3
37
+ :proto3
38
+ when :Proto2
39
+ :proto2
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ def name
46
+ Google::Protobuf::FFI.file_def_name(@file_def)
47
+ end
48
+
49
+ def options
50
+ @options ||= begin
51
+ size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
52
+ temporary_arena = Google::Protobuf::FFI.create_arena
53
+ buffer = Google::Protobuf::FFI.file_options(@file_def, size_ptr, temporary_arena)
54
+ Google::Protobuf::FileOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).send(:internal_deep_freeze)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,66 @@
1
+ # Protocol Buffers - Google's data interchange format
2
+ # Copyright 2022 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
+ # Implementation details below are subject to breaking changes without
10
+ # warning and are intended for use only within the gem.
11
+ module Google
12
+ module Protobuf
13
+ module Internal
14
+ class Arena
15
+ attr :pinned_messages
16
+
17
+ # FFI Interface methods and setup
18
+ extend ::FFI::DataConverter
19
+ native_type ::FFI::Type::POINTER
20
+
21
+ class << self
22
+ prepend Google::Protobuf::Internal::TypeSafety
23
+
24
+ # @param value [Arena] Arena to convert to an FFI native type
25
+ # @param _ [Object] Unused
26
+ def to_native(value, _)
27
+ value.instance_variable_get(:@arena) || ::FFI::Pointer::NULL
28
+ end
29
+
30
+ ##
31
+ # @param value [::FFI::Pointer] Arena pointer to be wrapped
32
+ # @param _ [Object] Unused
33
+ def from_native(value, _)
34
+ new(value)
35
+ end
36
+ end
37
+
38
+ def initialize(pointer)
39
+ @arena = ::FFI::AutoPointer.new(pointer, Google::Protobuf::FFI.method(:free_arena))
40
+ @pinned_messages = []
41
+ end
42
+
43
+ def fuse(other_arena)
44
+ return if other_arena == self
45
+ unless Google::Protobuf::FFI.fuse_arena(self, other_arena)
46
+ raise RuntimeError.new "Unable to fuse arenas. This should never happen since Ruby does not use initial blocks"
47
+ end
48
+ end
49
+
50
+ def pin(message)
51
+ pinned_messages.push message
52
+ end
53
+ end
54
+ end
55
+
56
+ class FFI
57
+ # Arena
58
+ attach_function :create_arena, :Arena_create, [], Internal::Arena
59
+ attach_function :fuse_arena, :upb_Arena_Fuse, [Internal::Arena, Internal::Arena], :bool
60
+ # Argument takes a :pointer rather than a typed Arena here due to
61
+ # implementation details of FFI::AutoPointer.
62
+ attach_function :free_arena, :upb_Arena_Free, [:pointer], :void
63
+ attach_function :arena_malloc, :upb_Arena_Malloc, [Internal::Arena, :size_t], :pointer
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,305 @@
1
+ # Protocol Buffers - Google's data interchange format
2
+ # Copyright 2022 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
+ # Implementation details below are subject to breaking changes without
10
+ # warning and are intended for use only within the gem.
11
+ module Google
12
+ module Protobuf
13
+ module Internal
14
+ module Convert
15
+
16
+ # Arena should be the
17
+ # @param value [Object] Value to convert
18
+ # @param arena [Arena] Arena that owns the Message where the MessageValue
19
+ # will be set
20
+ # @return [Google::Protobuf::FFI::MessageValue]
21
+ def convert_ruby_to_upb(value, arena, c_type, msg_or_enum_def)
22
+ raise ArgumentError.new "Expected Descriptor or EnumDescriptor, instead got #{msg_or_enum_def.class}" unless [NilClass, Descriptor, EnumDescriptor].include? msg_or_enum_def.class
23
+ return_value = Google::Protobuf::FFI::MessageValue.new
24
+ case c_type
25
+ when :float
26
+ raise TypeError.new "Expected number type for float field '#{name}' (given #{value.class})." unless value.respond_to? :to_f
27
+ return_value[:float_val] = value.to_f
28
+ when :double
29
+ raise TypeError.new "Expected number type for double field '#{name}' (given #{value.class})." unless value.respond_to? :to_f
30
+ return_value[:double_val] = value.to_f
31
+ when :bool
32
+ raise TypeError.new "Invalid argument for boolean field '#{name}' (given #{value.class})." unless [TrueClass, FalseClass].include? value.class
33
+ return_value[:bool_val] = value
34
+ when :string
35
+ raise TypeError.new "Invalid argument for string field '#{name}' (given #{value.class})." unless [Symbol, String].include? value.class
36
+ begin
37
+ string_value = value.to_s.encode("UTF-8")
38
+ rescue Encoding::UndefinedConversionError
39
+ # TODO - why not include the field name here?
40
+ raise Encoding::UndefinedConversionError.new "String is invalid UTF-8"
41
+ end
42
+ return_value[:str_val][:size] = string_value.bytesize
43
+ return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
44
+ # TODO - how important is it to still use arena malloc, versus the following?
45
+ # buffer = ::FFI::MemoryPointer.new(:char, string_value.bytesize)
46
+ # buffer.put_bytes(0, string_value)
47
+ # return_value[:str_val][:data] = buffer
48
+ raise NoMemoryError.new "Cannot allocate #{string_value.bytesize} bytes for string on Arena" if return_value[:str_val][:data].nil? || return_value[:str_val][:data].null?
49
+ return_value[:str_val][:data].write_string(string_value)
50
+ when :bytes
51
+ raise TypeError.new "Invalid argument for bytes field '#{name}' (given #{value.class})." unless value.is_a? String
52
+ string_value = value.encode("ASCII-8BIT")
53
+ return_value[:str_val][:size] = string_value.bytesize
54
+ return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
55
+ raise NoMemoryError.new "Cannot allocate #{string_value.bytesize} bytes for bytes on Arena" if return_value[:str_val][:data].nil? || return_value[:str_val][:data].null?
56
+ return_value[:str_val][:data].write_string_length(string_value, string_value.bytesize)
57
+ when :message
58
+ raise TypeError.new "nil message not allowed here." if value.nil?
59
+ if value.is_a? Hash
60
+ raise RuntimeError.new "Attempted to initialize message from Hash for field #{name} but have no definition" if msg_or_enum_def.nil?
61
+ new_message = msg_or_enum_def.msgclass.
62
+ send(:private_constructor, arena, initial_value: value)
63
+ return_value[:msg_val] = new_message.instance_variable_get(:@msg)
64
+ return return_value
65
+ end
66
+
67
+ descriptor = value.class.respond_to?(:descriptor) ? value.class.descriptor : nil
68
+ if descriptor != msg_or_enum_def
69
+ wkt = Google::Protobuf::FFI.get_well_known_type(msg_or_enum_def)
70
+ case wkt
71
+ when :Timestamp
72
+ raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Time
73
+ new_message = Google::Protobuf::FFI.new_message_from_def msg_or_enum_def, arena
74
+ sec = Google::Protobuf::FFI::MessageValue.new
75
+ sec[:int64_val] = value.tv_sec
76
+ sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
77
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
78
+ nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
79
+ nsec = Google::Protobuf::FFI::MessageValue.new
80
+ nsec[:int32_val] = value.tv_nsec
81
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
82
+ return_value[:msg_val] = new_message
83
+ when :Duration
84
+ raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Numeric
85
+ new_message = Google::Protobuf::FFI.new_message_from_def msg_or_enum_def, arena
86
+ sec = Google::Protobuf::FFI::MessageValue.new
87
+ sec[:int64_val] = value
88
+ sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
89
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
90
+ nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
91
+ nsec = Google::Protobuf::FFI::MessageValue.new
92
+ nsec[:int32_val] = ((value.to_f - value.to_i) * 1000000000).round
93
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
94
+ return_value[:msg_val] = new_message
95
+ else
96
+ raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'."
97
+ end
98
+ else
99
+ arena.fuse(value.instance_variable_get(:@arena))
100
+ return_value[:msg_val] = value.instance_variable_get :@msg
101
+ end
102
+ when :enum
103
+ return_value[:int32_val] = case value
104
+ when Numeric
105
+ value.to_i
106
+ when String, Symbol
107
+ enum_number = EnumDescriptor.send(:lookup_name, msg_or_enum_def, value.to_s)
108
+ #TODO add the bad value to the error message after tests pass
109
+ raise RangeError.new "Unknown symbol value for enum field '#{name}'." if enum_number.nil?
110
+ enum_number
111
+ else
112
+ raise TypeError.new "Expected number or symbol type for enum field '#{name}'."
113
+ end
114
+ #TODO After all tests pass, improve error message across integer type by including actual offending value
115
+ when :int32
116
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
117
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
118
+ raise RangeError.new "Value assigned to int32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 32
119
+ return_value[:int32_val] = value.to_i
120
+ when :uint32
121
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
122
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
123
+ raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
124
+ raise RangeError.new "Value assigned to uint32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 33
125
+ return_value[:uint32_val] = value.to_i
126
+ when :int64
127
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
128
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
129
+ raise RangeError.new "Value assigned to int64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 64
130
+ return_value[:int64_val] = value.to_i
131
+ when :uint64
132
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
133
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
134
+ raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
135
+ raise RangeError.new "Value assigned to uint64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 65
136
+ return_value[:uint64_val] = value.to_i
137
+ else
138
+ raise RuntimeError.new "Unsupported type #{c_type}"
139
+ end
140
+ return_value
141
+ end
142
+
143
+ ##
144
+ # Safe to call without an arena if the caller has checked that c_type
145
+ # is not :message.
146
+ # @param message_value [Google::Protobuf::FFI::MessageValue] Value to be converted.
147
+ # @param c_type [Google::Protobuf::FFI::CType] Enum representing the type of message_value
148
+ # @param msg_or_enum_def [::FFI::Pointer] Pointer to the MsgDef or EnumDef definition
149
+ # @param arena [Google::Protobuf::Internal::Arena] Arena to create Message instances, if needed
150
+ def convert_upb_to_ruby(message_value, c_type, msg_or_enum_def = nil, arena = nil)
151
+ throw TypeError.new "Expected MessageValue but got #{message_value.class}" unless message_value.is_a? Google::Protobuf::FFI::MessageValue
152
+
153
+ case c_type
154
+ when :bool
155
+ message_value[:bool_val]
156
+ when :int32
157
+ message_value[:int32_val]
158
+ when :uint32
159
+ message_value[:uint32_val]
160
+ when :double
161
+ message_value[:double_val]
162
+ when :int64
163
+ message_value[:int64_val]
164
+ when :uint64
165
+ message_value[:uint64_val]
166
+ when :string
167
+ if message_value[:str_val][:size].zero?
168
+ ""
169
+ else
170
+ message_value[:str_val][:data].read_string_length(message_value[:str_val][:size]).force_encoding("UTF-8").freeze
171
+ end
172
+ when :bytes
173
+ if message_value[:str_val][:size].zero?
174
+ ""
175
+ else
176
+ message_value[:str_val][:data].read_string_length(message_value[:str_val][:size]).force_encoding("ASCII-8BIT").freeze
177
+ end
178
+ when :float
179
+ message_value[:float_val]
180
+ when :enum
181
+ EnumDescriptor.send(:lookup_value, msg_or_enum_def, message_value[:int32_val]) || message_value[:int32_val]
182
+ when :message
183
+ raise "Null Arena for message" if arena.nil?
184
+ Descriptor.send(:get_message, message_value[:msg_val], msg_or_enum_def, arena)
185
+ else
186
+ raise RuntimeError.new "Unexpected type #{c_type}"
187
+ end
188
+ end
189
+
190
+ def to_h_internal(msg, message_descriptor)
191
+ return nil if msg.nil? or msg.null?
192
+ hash = {}
193
+ is_proto2 = Google::Protobuf::FFI.message_def_syntax(message_descriptor) == :Proto2
194
+ message_descriptor.each do |field_descriptor|
195
+ # TODO: Legacy behavior, remove when we fix the is_proto2 differences.
196
+ if !is_proto2 and
197
+ field_descriptor.sub_message? and
198
+ !field_descriptor.repeated? and
199
+ !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
200
+ hash[field_descriptor.name.to_sym] = nil
201
+ next
202
+ end
203
+
204
+ # Do not include fields that are not present (oneof or optional fields).
205
+ if is_proto2 and field_descriptor.has_presence? and !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
206
+ next
207
+ end
208
+
209
+ message_value = Google::Protobuf::FFI.get_message_value msg, field_descriptor
210
+
211
+ # Proto2 omits empty map/repeated fields also.
212
+ if field_descriptor.map?
213
+ hash_entry = map_create_hash(message_value[:map_val], field_descriptor)
214
+ elsif field_descriptor.repeated?
215
+ array = message_value[:array_val]
216
+ if is_proto2 and (array.null? || Google::Protobuf::FFI.array_size(array).zero?)
217
+ next
218
+ end
219
+ hash_entry = repeated_field_create_array(array, field_descriptor, field_descriptor.type)
220
+ else
221
+ hash_entry = scalar_create_hash(message_value, field_descriptor.type, field_descriptor: field_descriptor)
222
+ end
223
+
224
+ hash[field_descriptor.name.to_sym] = hash_entry
225
+
226
+ end
227
+
228
+ hash
229
+ end
230
+
231
+ def map_create_hash(map_ptr, field_descriptor)
232
+ return {} if map_ptr.nil? or map_ptr.null?
233
+ return_value = {}
234
+
235
+ message_descriptor = field_descriptor.send(:subtype)
236
+ key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
237
+ key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
238
+
239
+ value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
240
+ value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
241
+
242
+ iter = ::FFI::MemoryPointer.new(:size_t, 1)
243
+ iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
244
+ while Google::Protobuf::FFI.map_next(map_ptr, iter) do
245
+ iter_size_t = iter.read(:size_t)
246
+ key_message_value = Google::Protobuf::FFI.map_key(map_ptr, iter_size_t)
247
+ value_message_value = Google::Protobuf::FFI.map_value(map_ptr, iter_size_t)
248
+ hash_key = convert_upb_to_ruby(key_message_value, key_field_type)
249
+ hash_value = scalar_create_hash(value_message_value, value_field_type, msg_or_enum_descriptor: value_field_def.subtype)
250
+ return_value[hash_key] = hash_value
251
+ end
252
+ return_value
253
+ end
254
+
255
+ def repeated_field_create_array(array, field_descriptor, type)
256
+ return_value = []
257
+ n = (array.nil? || array.null?) ? 0 : Google::Protobuf::FFI.array_size(array)
258
+ 0.upto(n - 1) do |i|
259
+ message_value = Google::Protobuf::FFI.get_msgval_at(array, i)
260
+ return_value << scalar_create_hash(message_value, type, field_descriptor: field_descriptor)
261
+ end
262
+ return_value
263
+ end
264
+
265
+ # @param field_descriptor [FieldDescriptor] Descriptor of the field to convert to a hash.
266
+ def scalar_create_hash(message_value, type, field_descriptor: nil, msg_or_enum_descriptor: nil)
267
+ if [:message, :enum].include? type
268
+ if field_descriptor.nil?
269
+ if msg_or_enum_descriptor.nil?
270
+ raise "scalar_create_hash requires either a FieldDescriptor, MessageDescriptor, or EnumDescriptor as an argument, but received only nil"
271
+ end
272
+ else
273
+ msg_or_enum_descriptor = field_descriptor.subtype
274
+ end
275
+ if type == :message
276
+ to_h_internal(message_value[:msg_val], msg_or_enum_descriptor)
277
+ elsif type == :enum
278
+ convert_upb_to_ruby message_value, type, msg_or_enum_descriptor
279
+ end
280
+ else
281
+ convert_upb_to_ruby message_value, type
282
+ end
283
+ end
284
+
285
+ def message_value_deep_copy(message_value, type, descriptor, arena)
286
+ raise unless message_value.is_a? Google::Protobuf::FFI::MessageValue
287
+ new_message_value = Google::Protobuf::FFI::MessageValue.new
288
+ case type
289
+ when :string, :bytes
290
+ # TODO - how important is it to still use arena malloc, versus using FFI MemoryPointers?
291
+ new_message_value[:str_val][:size] = message_value[:str_val][:size]
292
+ new_message_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, message_value[:str_val][:size])
293
+ raise NoMemoryError.new "Allocation failed" if new_message_value[:str_val][:data].nil? or new_message_value[:str_val][:data].null?
294
+ Google::Protobuf::FFI.memcpy(new_message_value[:str_val][:data], message_value[:str_val][:data], message_value[:str_val][:size])
295
+ when :message
296
+ new_message_value[:msg_val] = descriptor.msgclass.send(:deep_copy, message_value[:msg_val], arena).instance_variable_get(:@msg)
297
+ else
298
+ Google::Protobuf::FFI.memcpy(new_message_value.to_ptr, message_value.to_ptr, Google::Protobuf::FFI::MessageValue.size)
299
+ end
300
+ new_message_value
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,35 @@
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
+ module Google
9
+ module Protobuf
10
+ module Internal
11
+ module PointerHelper
12
+ # Utility code to defensively walk the object graph from a file_def to
13
+ # the pool, and either retrieve the wrapper object for the given pointer
14
+ # or create one. Assumes that the caller is the wrapper class for the
15
+ # given pointer and that it implements `private_constructor`.
16
+ def descriptor_from_file_def(file_def, pointer)
17
+ raise RuntimeError.new "FileDef is nil" if file_def.nil?
18
+ raise RuntimeError.new "FileDef is null" if file_def.null?
19
+ pool_def = Google::Protobuf::FFI.file_def_pool file_def
20
+ raise RuntimeError.new "PoolDef is nil" if pool_def.nil?
21
+ raise RuntimeError.new "PoolDef is null" if pool_def.null?
22
+ pool = Google::Protobuf::OBJECT_CACHE.get(pool_def.address)
23
+ raise "Cannot find pool in ObjectCache!" if pool.nil?
24
+ descriptor = pool.descriptor_class_by_def[pointer.address]
25
+ if descriptor.nil?
26
+ pool.descriptor_class_by_def[pointer.address] = private_constructor(pointer, pool)
27
+ else
28
+ descriptor
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,25 @@
1
+ # Protocol Buffers - Google's data interchange format
2
+ # Copyright 2022 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
+ # A to_native DataConverter method that raises an error if the value is not of the same type.
9
+ # Adapted from to https://www.varvet.com/blog/advanced-topics-in-ruby-ffi/
10
+ module Google
11
+ module Protobuf
12
+ module Internal
13
+ module TypeSafety
14
+ def to_native(value, ctx = nil)
15
+ if value.kind_of?(self) or value.nil?
16
+ super
17
+ else
18
+ raise TypeError.new "Expected a kind of #{name}, was #{value.class}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+