google-protobuf 3.24.3-arm64-darwin → 3.25.0.rc.1-arm64-darwin

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