google-protobuf 3.24.4-x86_64-linux → 3.25.0.rc.1-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (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.so +0 -0
  25. data/lib/google/3.0/protobuf_c.so +0 -0
  26. data/lib/google/3.1/protobuf_c.so +0 -0
  27. data/lib/google/3.2/protobuf_c.so +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