google-protobuf 3.22.2 → 4.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/google/protobuf_c/Rakefile +3 -0
- data/ext/google/protobuf_c/convert.c +60 -86
- data/ext/google/protobuf_c/convert.h +3 -28
- data/ext/google/protobuf_c/defs.c +692 -72
- data/ext/google/protobuf_c/defs.h +3 -28
- data/ext/google/protobuf_c/extconf.rb +14 -1
- data/ext/google/protobuf_c/glue.c +135 -0
- data/ext/google/protobuf_c/map.c +89 -45
- data/ext/google/protobuf_c/map.h +12 -30
- data/ext/google/protobuf_c/message.c +169 -169
- data/ext/google/protobuf_c/message.h +11 -33
- data/ext/google/protobuf_c/protobuf.c +65 -188
- data/ext/google/protobuf_c/protobuf.h +27 -39
- data/ext/google/protobuf_c/repeated_field.c +72 -38
- data/ext/google/protobuf_c/repeated_field.h +11 -29
- data/ext/google/protobuf_c/ruby-upb.c +15064 -11220
- data/ext/google/protobuf_c/ruby-upb.h +10643 -5403
- data/ext/google/protobuf_c/shared_convert.c +69 -0
- data/ext/google/protobuf_c/shared_convert.h +26 -0
- data/ext/google/protobuf_c/shared_message.c +37 -0
- data/ext/google/protobuf_c/shared_message.h +21 -0
- data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +207 -0
- data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -8
- data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_neon.inc +117 -0
- data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_sse.inc +272 -0
- data/ext/google/protobuf_c/wrap_memcpy.c +3 -26
- data/lib/google/protobuf/any_pb.rb +6 -8
- data/lib/google/protobuf/api_pb.rb +6 -26
- data/lib/google/protobuf/descriptor_pb.rb +21 -252
- data/lib/google/protobuf/duration_pb.rb +6 -8
- data/lib/google/protobuf/empty_pb.rb +6 -6
- data/lib/google/protobuf/ffi/descriptor.rb +175 -0
- data/lib/google/protobuf/ffi/descriptor_pool.rb +77 -0
- data/lib/google/protobuf/ffi/enum_descriptor.rb +183 -0
- data/lib/google/protobuf/ffi/ffi.rb +214 -0
- data/lib/google/protobuf/ffi/field_descriptor.rb +340 -0
- data/lib/google/protobuf/ffi/file_descriptor.rb +59 -0
- data/lib/google/protobuf/ffi/internal/arena.rb +60 -0
- data/lib/google/protobuf/ffi/internal/convert.rb +292 -0
- data/lib/google/protobuf/ffi/internal/pointer_helper.rb +35 -0
- data/lib/google/protobuf/ffi/internal/type_safety.rb +25 -0
- data/lib/google/protobuf/ffi/map.rb +433 -0
- data/lib/google/protobuf/ffi/message.rb +783 -0
- data/lib/google/protobuf/ffi/method_descriptor.rb +124 -0
- data/lib/google/protobuf/ffi/object_cache.rb +30 -0
- data/lib/google/protobuf/ffi/oneof_descriptor.rb +107 -0
- data/lib/google/protobuf/ffi/repeated_field.rb +411 -0
- data/lib/google/protobuf/ffi/service_descriptor.rb +117 -0
- data/lib/google/protobuf/field_mask_pb.rb +6 -7
- data/lib/google/protobuf/internal/object_cache.rb +99 -0
- data/lib/google/protobuf/message_exts.rb +3 -26
- data/lib/google/protobuf/plugin_pb.rb +6 -31
- data/lib/google/protobuf/repeated_field.rb +7 -31
- data/lib/google/protobuf/source_context_pb.rb +6 -7
- data/lib/google/protobuf/struct_pb.rb +6 -23
- data/lib/google/protobuf/timestamp_pb.rb +6 -8
- data/lib/google/protobuf/type_pb.rb +6 -71
- data/lib/google/protobuf/well_known_types.rb +5 -34
- data/lib/google/protobuf/wrappers_pb.rb +6 -31
- data/lib/google/protobuf.rb +27 -45
- data/lib/google/protobuf_ffi.rb +52 -0
- data/lib/google/protobuf_native.rb +19 -0
- data/lib/google/tasks/ffi.rake +100 -0
- metadata +92 -9
- data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
- data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
- data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
- data/lib/google/protobuf/descriptor_dsl.rb +0 -465
@@ -0,0 +1,783 @@
|
|
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_detecting_nonconformance, :upb_JsonDecodeDetectingNonconformance, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :int
|
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_WhichOneofByDef, [:Message, OneofDescriptor], FieldDescriptor
|
25
|
+
attach_function :message_discard_unknown, :upb_Message_DiscardUnknown, [:Message, Descriptor, :DefPool, :int], :bool
|
26
|
+
attach_function :message_next, :upb_Message_Next, [:Message, Descriptor, :DefPool, :FieldDefPointer, MessageValue.by_ref, :pointer], :bool
|
27
|
+
attach_function :message_freeze, :upb_Message_Freeze, [:Message, MiniTable.by_ref], :void
|
28
|
+
attach_function :message_frozen?, :upb_Message_IsFrozen, [:Message], :bool
|
29
|
+
|
30
|
+
# MessageValue
|
31
|
+
attach_function :message_value_equal, :shared_Msgval_IsEqual, [MessageValue.by_value, MessageValue.by_value, CType, Descriptor], :bool
|
32
|
+
attach_function :message_value_hash, :shared_Msgval_GetHash, [MessageValue.by_value, CType, Descriptor, :uint64_t], :uint64_t
|
33
|
+
end
|
34
|
+
|
35
|
+
class Descriptor
|
36
|
+
def build_message_class
|
37
|
+
descriptor = self
|
38
|
+
Class.new(Google::Protobuf::const_get(:AbstractMessage)) do
|
39
|
+
@descriptor = descriptor
|
40
|
+
class << self
|
41
|
+
attr_accessor :descriptor
|
42
|
+
private
|
43
|
+
attr_accessor :oneof_field_names
|
44
|
+
include ::Google::Protobuf::Internal::Convert
|
45
|
+
end
|
46
|
+
|
47
|
+
alias original_method_missing method_missing
|
48
|
+
def method_missing(method_name, *args)
|
49
|
+
method_missing_internal method_name, *args, mode: :method_missing
|
50
|
+
end
|
51
|
+
|
52
|
+
def respond_to_missing?(method_name, include_private = false)
|
53
|
+
method_missing_internal(method_name, mode: :respond_to_missing?) || super
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Public constructor. Automatically allocates from a new Arena.
|
58
|
+
def self.new(initial_value = nil)
|
59
|
+
instance = allocate
|
60
|
+
instance.send(:initialize, initial_value)
|
61
|
+
instance
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Is this object frozen?
|
66
|
+
# Returns true if either this Ruby wrapper or the underlying
|
67
|
+
# representation are frozen. Freezes the wrapper if the underlying
|
68
|
+
# representation is already frozen but this wrapper isn't.
|
69
|
+
def frozen?
|
70
|
+
unless Google::Protobuf::FFI.message_frozen? @msg
|
71
|
+
raise RuntimeError.new "Ruby frozen Message with mutable representation" if super
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
method(:freeze).super_method.call unless super
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Freezes the map object. We have to intercept this so we can freeze the
|
80
|
+
# underlying representation, not just the Ruby wrapper. Returns self.
|
81
|
+
def freeze
|
82
|
+
if method(:frozen?).super_method.call
|
83
|
+
unless Google::Protobuf::FFI.message_frozen? @msg
|
84
|
+
raise RuntimeError.new "Underlying representation of message still mutable despite frozen wrapper"
|
85
|
+
end
|
86
|
+
return self
|
87
|
+
end
|
88
|
+
unless Google::Protobuf::FFI.message_frozen? @msg
|
89
|
+
Google::Protobuf::FFI.message_freeze(@msg, Google::Protobuf::FFI.get_mini_table(self.class.descriptor))
|
90
|
+
end
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
def dup
|
95
|
+
duplicate = self.class.private_constructor(@arena)
|
96
|
+
mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
97
|
+
size = mini_table[:size]
|
98
|
+
duplicate.instance_variable_get(:@msg).write_string_length(@msg.read_string_length(size), size)
|
99
|
+
duplicate
|
100
|
+
end
|
101
|
+
alias clone dup
|
102
|
+
|
103
|
+
def eql?(other)
|
104
|
+
return false unless self.class === other
|
105
|
+
encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
|
106
|
+
temporary_arena = Google::Protobuf::FFI.create_arena
|
107
|
+
mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
108
|
+
size_one = ::FFI::MemoryPointer.new(:size_t, 1)
|
109
|
+
encoding_one = ::FFI::MemoryPointer.new(:pointer, 1)
|
110
|
+
encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table, encoding_options, temporary_arena, encoding_one.to_ptr, size_one)
|
111
|
+
raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding LHS of `eql?()`" unless encoding_status == :Ok
|
112
|
+
|
113
|
+
size_two = ::FFI::MemoryPointer.new(:size_t, 1)
|
114
|
+
encoding_two = ::FFI::MemoryPointer.new(:pointer, 1)
|
115
|
+
encoding_status = Google::Protobuf::FFI.encode_message(other.instance_variable_get(:@msg), mini_table, encoding_options, temporary_arena, encoding_two.to_ptr, size_two)
|
116
|
+
raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding RHS of `eql?()`" unless encoding_status == :Ok
|
117
|
+
|
118
|
+
if encoding_one.null? or encoding_two.null?
|
119
|
+
raise ParseError.new "Error comparing messages"
|
120
|
+
end
|
121
|
+
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?
|
122
|
+
end
|
123
|
+
alias == eql?
|
124
|
+
|
125
|
+
def hash
|
126
|
+
encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
|
127
|
+
temporary_arena = Google::Protobuf::FFI.create_arena
|
128
|
+
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
129
|
+
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
130
|
+
encoding = ::FFI::MemoryPointer.new(:pointer, 1)
|
131
|
+
encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table_ptr, encoding_options, temporary_arena, encoding.to_ptr, size_ptr)
|
132
|
+
if encoding_status != :Ok or encoding.null?
|
133
|
+
raise ParseError.new "Error calculating hash"
|
134
|
+
end
|
135
|
+
encoding.read(:pointer).read_string(size_ptr.read(:size_t)).hash
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_h
|
139
|
+
to_h_internal @msg, self.class.descriptor
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# call-seq:
|
144
|
+
# Message.inspect => string
|
145
|
+
#
|
146
|
+
# Returns a human-readable string representing this message. It will be
|
147
|
+
# formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
|
148
|
+
# field's value is represented according to its own #inspect method.
|
149
|
+
def inspect
|
150
|
+
self.class.inspect_internal @msg
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_s
|
154
|
+
self.inspect
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# call-seq:
|
159
|
+
# Message.[](index) => value
|
160
|
+
# Accesses a field's value by field name. The provided field name
|
161
|
+
# should be a string.
|
162
|
+
def [](name)
|
163
|
+
raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
|
164
|
+
index_internal name
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# call-seq:
|
169
|
+
# Message.[]=(index, value)
|
170
|
+
# Sets a field's value by field name. The provided field name should
|
171
|
+
# be a string.
|
172
|
+
# @param name [String] Name of the field to be set
|
173
|
+
# @param value [Object] Value to set the field to
|
174
|
+
def []=(name, value)
|
175
|
+
raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
|
176
|
+
index_assign_internal(value, name: name)
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# call-seq:
|
181
|
+
# MessageClass.decode(data, options) => message
|
182
|
+
#
|
183
|
+
# Decodes the given data (as a string containing bytes in protocol buffers wire
|
184
|
+
# format) under the interpretation given by this message class's definition
|
185
|
+
# and returns a message object with the corresponding field values.
|
186
|
+
# @param data [String] Binary string in Protobuf wire format to decode
|
187
|
+
# @param options [Hash] options for the decoder
|
188
|
+
# @option options [Integer] :recursion_limit Set to maximum decoding depth for message (default is 64)
|
189
|
+
def self.decode(data, options = {})
|
190
|
+
raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
|
191
|
+
raise ArgumentError.new "Expected string for binary protobuf data." unless data.is_a? String
|
192
|
+
decoding_options = 0
|
193
|
+
depth = options[:recursion_limit]
|
194
|
+
|
195
|
+
if depth.is_a? Numeric
|
196
|
+
decoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
|
197
|
+
end
|
198
|
+
|
199
|
+
message = new
|
200
|
+
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(message.class.descriptor)
|
201
|
+
status = Google::Protobuf::FFI.decode_message(
|
202
|
+
data,
|
203
|
+
data.bytesize,
|
204
|
+
message.instance_variable_get(:@msg),
|
205
|
+
mini_table_ptr,
|
206
|
+
Google::Protobuf::FFI.get_extension_registry(message.class.descriptor.send(:pool).descriptor_pool),
|
207
|
+
decoding_options,
|
208
|
+
message.instance_variable_get(:@arena)
|
209
|
+
)
|
210
|
+
raise ParseError.new "Error occurred during parsing" unless status == :Ok
|
211
|
+
message
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# call-seq:
|
216
|
+
# MessageClass.encode(msg, options) => bytes
|
217
|
+
#
|
218
|
+
# Encodes the given message object to its serialized form in protocol buffers
|
219
|
+
# wire format.
|
220
|
+
# @param options [Hash] options for the encoder
|
221
|
+
# @option options [Integer] :recursion_limit Set to maximum encoding depth for message (default is 64)
|
222
|
+
def self.encode(message, options = {})
|
223
|
+
raise ArgumentError.new "Message of wrong type." unless message.is_a? self
|
224
|
+
raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
|
225
|
+
|
226
|
+
encoding_options = 0
|
227
|
+
depth = options[:recursion_limit]
|
228
|
+
|
229
|
+
if depth.is_a? Numeric
|
230
|
+
encoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
|
231
|
+
end
|
232
|
+
|
233
|
+
encode_internal(message.instance_variable_get(:@msg), encoding_options) do |encoding, size, _|
|
234
|
+
if encoding.nil? or encoding.null?
|
235
|
+
raise RuntimeError.new "Exceeded maximum depth (possibly cycle)"
|
236
|
+
else
|
237
|
+
encoding.read_string_length(size).force_encoding("ASCII-8BIT").freeze
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# all-seq:
|
244
|
+
# MessageClass.decode_json(data, options = {}) => message
|
245
|
+
#
|
246
|
+
# Decodes the given data (as a string containing bytes in protocol buffers wire
|
247
|
+
# format) under the interpretation given by this message class's definition
|
248
|
+
# and returns a message object with the corresponding field values.
|
249
|
+
#
|
250
|
+
# @param options [Hash] options for the decoder
|
251
|
+
# @option options [Boolean] :ignore_unknown_fields Set true to ignore unknown fields (default is to raise an error)
|
252
|
+
# @return [Message]
|
253
|
+
def self.decode_json(data, options = {})
|
254
|
+
decoding_options = 0
|
255
|
+
unless options.is_a? Hash
|
256
|
+
if options.respond_to? :to_h
|
257
|
+
options options.to_h
|
258
|
+
else
|
259
|
+
#TODO can this error message be improve to include what was received?
|
260
|
+
raise ArgumentError.new "Expected hash arguments"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
raise ArgumentError.new "Expected string for JSON data." unless data.is_a? String
|
264
|
+
raise RuntimeError.new "Cannot parse a wrapper directly" if descriptor.send(:wrapper?)
|
265
|
+
|
266
|
+
if options[:ignore_unknown_fields]
|
267
|
+
decoding_options |= Google::Protobuf::FFI::Upb_JsonDecode_IgnoreUnknown
|
268
|
+
end
|
269
|
+
|
270
|
+
message = new
|
271
|
+
pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
|
272
|
+
status = Google::Protobuf::FFI::Status.new
|
273
|
+
result = Google::Protobuf::FFI.json_decode_message_detecting_nonconformance(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
|
274
|
+
case result
|
275
|
+
when Google::Protobuf::FFI::Upb_JsonDecodeResult_Error
|
276
|
+
raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
|
277
|
+
end
|
278
|
+
message
|
279
|
+
end
|
280
|
+
|
281
|
+
def self.encode_json(message, options = {})
|
282
|
+
encoding_options = 0
|
283
|
+
unless options.is_a? Hash
|
284
|
+
if options.respond_to? :to_h
|
285
|
+
options = options.to_h
|
286
|
+
else
|
287
|
+
#TODO can this error message be improve to include what was received?
|
288
|
+
raise ArgumentError.new "Expected hash arguments"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
if options[:preserve_proto_fieldnames]
|
293
|
+
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_UseProtoNames
|
294
|
+
end
|
295
|
+
if options[:emit_defaults]
|
296
|
+
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_EmitDefaults
|
297
|
+
end
|
298
|
+
if options[:format_enums_as_integers]
|
299
|
+
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_FormatEnumsAsIntegers
|
300
|
+
end
|
301
|
+
|
302
|
+
buffer_size = 1024
|
303
|
+
buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
|
304
|
+
status = Google::Protobuf::FFI::Status.new
|
305
|
+
msg = message.instance_variable_get(:@msg)
|
306
|
+
pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
|
307
|
+
size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
|
308
|
+
unless status[:ok]
|
309
|
+
raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
|
310
|
+
end
|
311
|
+
|
312
|
+
if size >= buffer_size
|
313
|
+
buffer_size = size + 1
|
314
|
+
buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
|
315
|
+
status.clear
|
316
|
+
size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
|
317
|
+
unless status[:ok]
|
318
|
+
raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
|
319
|
+
end
|
320
|
+
if size >= buffer_size
|
321
|
+
raise ParseError.new "Inconsistent JSON encoding sizes - was #{buffer_size - 1}, now #{size}"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
buffer.read_string_length(size).force_encoding("UTF-8").freeze
|
326
|
+
end
|
327
|
+
|
328
|
+
private
|
329
|
+
# Implementation details below are subject to breaking changes without
|
330
|
+
# warning and are intended for use only within the gem.
|
331
|
+
|
332
|
+
include Google::Protobuf::Internal::Convert
|
333
|
+
|
334
|
+
##
|
335
|
+
# Checks ObjectCache for a sentinel empty frozen Map of the key and
|
336
|
+
# value types matching the field descriptor's MessageDef and returns
|
337
|
+
# the cache entry. If an entry is not found, one is created and added
|
338
|
+
# to the cache keyed by the MessageDef pointer first.
|
339
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
340
|
+
def empty_frozen_map(field_descriptor)
|
341
|
+
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
|
342
|
+
frozen_map = OBJECT_CACHE.get sub_message_def
|
343
|
+
if frozen_map.nil?
|
344
|
+
frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
|
345
|
+
OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
|
346
|
+
end
|
347
|
+
raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
|
348
|
+
frozen_map
|
349
|
+
end
|
350
|
+
|
351
|
+
##
|
352
|
+
# Returns a frozen Map instance for the given field. If the message
|
353
|
+
# already has a value for that field, it is used. If not, a sentinel
|
354
|
+
# (per FieldDescriptor) empty frozen Map is returned instead.
|
355
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
356
|
+
def frozen_map_from_field_descriptor(field_descriptor)
|
357
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
358
|
+
return empty_frozen_map field_descriptor if message_value[:map_val].null?
|
359
|
+
get_map_field(message_value[:map_val], field_descriptor).freeze
|
360
|
+
end
|
361
|
+
|
362
|
+
##
|
363
|
+
# Returns a Map instance for the given field. If the message is frozen
|
364
|
+
# the return value is also frozen. If not, a mutable instance is
|
365
|
+
# returned instead.
|
366
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
367
|
+
def map_from_field_descriptor(field_descriptor)
|
368
|
+
return frozen_map_from_field_descriptor field_descriptor if frozen?
|
369
|
+
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
370
|
+
get_map_field(mutable_message_value[:map], field_descriptor)
|
371
|
+
end
|
372
|
+
|
373
|
+
##
|
374
|
+
# Checks ObjectCache for a sentinel empty frozen RepeatedField of the
|
375
|
+
# value type matching the field descriptor's MessageDef and returns
|
376
|
+
# the cache entry. If an entry is not found, one is created and added
|
377
|
+
# to the cache keyed by the MessageDef pointer first.
|
378
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
379
|
+
def empty_frozen_repeated_field(field_descriptor)
|
380
|
+
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
|
381
|
+
frozen_repeated_field = OBJECT_CACHE.get sub_message_def
|
382
|
+
if frozen_repeated_field.nil?
|
383
|
+
frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
|
384
|
+
OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
|
385
|
+
end
|
386
|
+
raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
|
387
|
+
frozen_repeated_field
|
388
|
+
end
|
389
|
+
|
390
|
+
##
|
391
|
+
# Returns a frozen RepeatedField instance for the given field. If the
|
392
|
+
# message already has a value for that field, it is used. If not, a
|
393
|
+
# sentinel (per FieldDescriptor) empty frozen RepeatedField is
|
394
|
+
# returned instead.
|
395
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
396
|
+
def frozen_repeated_field_from_field_descriptor(field_descriptor)
|
397
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
398
|
+
return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
|
399
|
+
get_repeated_field(message_value[:array_val], field_descriptor).freeze
|
400
|
+
end
|
401
|
+
|
402
|
+
##
|
403
|
+
# Returns a RepeatedField instance for the given field. If the message
|
404
|
+
# is frozen the return value is also frozen. If not, a mutable
|
405
|
+
# instance is returned instead.
|
406
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
407
|
+
def repeated_field_from_field_descriptor(field_descriptor)
|
408
|
+
return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
|
409
|
+
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
410
|
+
get_repeated_field(mutable_message_value[:array], field_descriptor)
|
411
|
+
end
|
412
|
+
|
413
|
+
##
|
414
|
+
# Returns a Message instance for the given field. If the message
|
415
|
+
# is frozen nil is always returned. Otherwise, a mutable instance is
|
416
|
+
# returned instead.
|
417
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
418
|
+
def message_from_field_descriptor(field_descriptor)
|
419
|
+
return nil if frozen?
|
420
|
+
return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
|
421
|
+
mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
422
|
+
sub_message = mutable_message[:msg]
|
423
|
+
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
|
424
|
+
Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
|
425
|
+
end
|
426
|
+
|
427
|
+
##
|
428
|
+
# Returns a scalar value for the given field. If the message
|
429
|
+
# is frozen the return value is also frozen.
|
430
|
+
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
431
|
+
def scalar_from_field_descriptor(field_descriptor)
|
432
|
+
c_type = field_descriptor.send(:c_type)
|
433
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
434
|
+
msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
|
435
|
+
return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
|
436
|
+
frozen? ? return_value.freeze : return_value
|
437
|
+
end
|
438
|
+
|
439
|
+
##
|
440
|
+
# Dynamically define accessors methods for every field of @descriptor.
|
441
|
+
# Methods with names that conflict with existing methods are skipped.
|
442
|
+
def self.setup_accessors!
|
443
|
+
@descriptor.each do |field_descriptor|
|
444
|
+
field_name = field_descriptor.name
|
445
|
+
unless instance_methods(true).include?(field_name.to_sym)
|
446
|
+
# Dispatching to either index_internal or get_field is logically
|
447
|
+
# correct, but slightly slower due to having to perform extra
|
448
|
+
# lookups on each invocation rather than doing it once here.
|
449
|
+
if field_descriptor.map?
|
450
|
+
define_method(field_name) do
|
451
|
+
map_from_field_descriptor field_descriptor
|
452
|
+
end
|
453
|
+
elsif field_descriptor.repeated?
|
454
|
+
define_method(field_name) do
|
455
|
+
repeated_field_from_field_descriptor field_descriptor
|
456
|
+
end
|
457
|
+
elsif field_descriptor.sub_message?
|
458
|
+
define_method(field_name) do
|
459
|
+
message_from_field_descriptor field_descriptor
|
460
|
+
end
|
461
|
+
else
|
462
|
+
define_method(field_name) do
|
463
|
+
scalar_from_field_descriptor field_descriptor
|
464
|
+
end
|
465
|
+
end
|
466
|
+
define_method("#{field_name}=") do |value|
|
467
|
+
index_assign_internal(value, field_descriptor: field_descriptor)
|
468
|
+
end
|
469
|
+
define_method("clear_#{field_name}") do
|
470
|
+
clear_internal(field_descriptor)
|
471
|
+
end
|
472
|
+
if field_descriptor.type == :enum
|
473
|
+
if field_descriptor.repeated?
|
474
|
+
define_method("#{field_name}_const") do
|
475
|
+
return_value = []
|
476
|
+
repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
|
477
|
+
return_value << msg_val[:int32_val]
|
478
|
+
end
|
479
|
+
return_value
|
480
|
+
end
|
481
|
+
else
|
482
|
+
define_method("#{field_name}_const") do
|
483
|
+
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
484
|
+
message_value[:int32_val]
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
if !field_descriptor.repeated? and field_descriptor.wrapper?
|
489
|
+
define_method("#{field_name}_as_value") do
|
490
|
+
get_field(field_descriptor, unwrap: true)
|
491
|
+
end
|
492
|
+
define_method("#{field_name}_as_value=") do |value|
|
493
|
+
if value.nil?
|
494
|
+
clear_internal(field_descriptor)
|
495
|
+
else
|
496
|
+
index_assign_internal(value, field_descriptor: field_descriptor, wrap: true)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
if field_descriptor.has_presence?
|
501
|
+
define_method("has_#{field_name}?") do
|
502
|
+
Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
##
|
510
|
+
# Dynamically define accessors methods for every OneOf field of
|
511
|
+
# @descriptor.
|
512
|
+
def self.setup_oneof_accessors!
|
513
|
+
@oneof_field_names = []
|
514
|
+
@descriptor.each_oneof do |oneof_descriptor|
|
515
|
+
self.add_oneof_accessors_for! oneof_descriptor
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
##
|
520
|
+
# Dynamically define accessors methods for the given OneOf field.
|
521
|
+
# Methods with names that conflict with existing methods are skipped.
|
522
|
+
# @param oneof_descriptor [OneofDescriptor] Field to create accessors
|
523
|
+
# for.
|
524
|
+
def self.add_oneof_accessors_for!(oneof_descriptor)
|
525
|
+
field_name = oneof_descriptor.name.to_sym
|
526
|
+
@oneof_field_names << field_name
|
527
|
+
unless instance_methods(true).include?(field_name)
|
528
|
+
define_method(field_name) do
|
529
|
+
field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
|
530
|
+
if field_descriptor.nil?
|
531
|
+
return
|
532
|
+
else
|
533
|
+
return field_descriptor.name.to_sym
|
534
|
+
end
|
535
|
+
end
|
536
|
+
define_method("clear_#{field_name}") do
|
537
|
+
field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
|
538
|
+
unless field_descriptor.nil?
|
539
|
+
clear_internal(field_descriptor)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
define_method("has_#{field_name}?") do
|
543
|
+
!Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor).nil?
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
setup_accessors!
|
549
|
+
setup_oneof_accessors!
|
550
|
+
|
551
|
+
def self.private_constructor(arena, msg: nil, initial_value: nil)
|
552
|
+
instance = allocate
|
553
|
+
instance.send(:initialize, initial_value, arena, msg)
|
554
|
+
instance
|
555
|
+
end
|
556
|
+
|
557
|
+
def self.inspect_field(field_descriptor, c_type, message_value)
|
558
|
+
if field_descriptor.sub_message?
|
559
|
+
sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
|
560
|
+
sub_msg_descriptor.msgclass.send(:inspect_internal, message_value[:msg_val])
|
561
|
+
else
|
562
|
+
convert_upb_to_ruby(message_value, c_type, field_descriptor.subtype).inspect
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
# @param msg [::FFI::Pointer] Pointer to the Message
|
567
|
+
def self.inspect_internal(msg)
|
568
|
+
field_output = []
|
569
|
+
descriptor.each do |field_descriptor|
|
570
|
+
next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
|
571
|
+
if field_descriptor.map?
|
572
|
+
# TODO Adapted - from map#each_msg_val and map#inspect- can this be refactored to reduce echo without introducing a arena allocation?
|
573
|
+
message_descriptor = field_descriptor.subtype
|
574
|
+
key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
|
575
|
+
key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
|
576
|
+
|
577
|
+
value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
|
578
|
+
value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
|
579
|
+
|
580
|
+
message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
|
581
|
+
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
582
|
+
iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
|
583
|
+
key_value_pairs = []
|
584
|
+
while Google::Protobuf::FFI.map_next(message_value[:map_val], iter) do
|
585
|
+
iter_size_t = iter.read(:size_t)
|
586
|
+
key_message_value = Google::Protobuf::FFI.map_key(message_value[:map_val], iter_size_t)
|
587
|
+
value_message_value = Google::Protobuf::FFI.map_value(message_value[:map_val], iter_size_t)
|
588
|
+
key_string = convert_upb_to_ruby(key_message_value, key_field_type).inspect
|
589
|
+
value_string = inspect_field(value_field_def, value_field_type, value_message_value)
|
590
|
+
key_value_pairs << "#{key_string}=>#{value_string}"
|
591
|
+
end
|
592
|
+
field_output << "#{field_descriptor.name}: {#{key_value_pairs.join(", ")}}"
|
593
|
+
elsif field_descriptor.repeated?
|
594
|
+
# TODO Adapted - from repeated_field#each - can this be refactored to reduce echo?
|
595
|
+
repeated_field_output = []
|
596
|
+
message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
|
597
|
+
array = message_value[:array_val]
|
598
|
+
n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
|
599
|
+
0.upto(n - 1) do |i|
|
600
|
+
element = Google::Protobuf::FFI.get_msgval_at(array, i)
|
601
|
+
repeated_field_output << inspect_field(field_descriptor, field_descriptor.send(:c_type), element)
|
602
|
+
end
|
603
|
+
field_output << "#{field_descriptor.name}: [#{repeated_field_output.join(", ")}]"
|
604
|
+
else
|
605
|
+
message_value = Google::Protobuf::FFI.get_message_value msg, field_descriptor
|
606
|
+
rendered_value = inspect_field(field_descriptor, field_descriptor.send(:c_type), message_value)
|
607
|
+
field_output << "#{field_descriptor.name}: #{rendered_value}"
|
608
|
+
end
|
609
|
+
end
|
610
|
+
"<#{name}: #{field_output.join(', ')}>"
|
611
|
+
end
|
612
|
+
|
613
|
+
def self.deep_copy(msg, arena = nil)
|
614
|
+
arena ||= Google::Protobuf::FFI.create_arena
|
615
|
+
encode_internal(msg) do |encoding, size, mini_table_ptr|
|
616
|
+
message = private_constructor(arena)
|
617
|
+
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
|
618
|
+
raise ParseError.new "Error occurred copying proto"
|
619
|
+
end
|
620
|
+
message
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
def self.encode_internal(msg, encoding_options = 0)
|
625
|
+
temporary_arena = Google::Protobuf::FFI.create_arena
|
626
|
+
|
627
|
+
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(descriptor)
|
628
|
+
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
629
|
+
pointer_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
|
630
|
+
encoding_status = Google::Protobuf::FFI.encode_message(msg, mini_table_ptr, encoding_options, temporary_arena, pointer_ptr.to_ptr, size_ptr)
|
631
|
+
raise "Encoding failed due to #{encoding_status}" unless encoding_status == :Ok
|
632
|
+
yield pointer_ptr.read(:pointer), size_ptr.read(:size_t), mini_table_ptr
|
633
|
+
end
|
634
|
+
|
635
|
+
def method_missing_internal(method_name, *args, mode: nil)
|
636
|
+
raise ArgumentError.new "method_missing_internal called with invalid mode #{mode.inspect}" unless [:respond_to_missing?, :method_missing].include? mode
|
637
|
+
|
638
|
+
#TODO not being allowed is not the same thing as not responding, but this is needed to pass tests
|
639
|
+
if method_name.to_s.end_with? '='
|
640
|
+
if self.class.send(:oneof_field_names).include? method_name.to_s[0..-2].to_sym
|
641
|
+
return false if mode == :respond_to_missing?
|
642
|
+
raise RuntimeError.new "Oneof accessors are read-only."
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
original_method_missing(method_name, *args) if mode == :method_missing
|
647
|
+
end
|
648
|
+
|
649
|
+
def clear_internal(field_def)
|
650
|
+
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
651
|
+
Google::Protobuf::FFI.clear_message_field(@msg, field_def)
|
652
|
+
end
|
653
|
+
|
654
|
+
# Accessor for field by name. Does not delegate to methods setup by
|
655
|
+
# self.setup_accessors! in order to avoid conflicts with bad field
|
656
|
+
# names e.g. `dup` or `class` which are perfectly valid for proto
|
657
|
+
# fields.
|
658
|
+
def index_internal(name)
|
659
|
+
field_descriptor = self.class.descriptor.lookup(name)
|
660
|
+
get_field field_descriptor unless field_descriptor.nil?
|
661
|
+
end
|
662
|
+
|
663
|
+
#TODO - well known types keeps us on our toes by overloading methods.
|
664
|
+
# How much of the public API needs to be defended?
|
665
|
+
def index_assign_internal(value, name: nil, field_descriptor: nil, wrap: false)
|
666
|
+
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
667
|
+
if field_descriptor.nil?
|
668
|
+
field_descriptor = self.class.descriptor.lookup(name)
|
669
|
+
if field_descriptor.nil?
|
670
|
+
raise ArgumentError.new "Unknown field: #{name}"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
unless field_descriptor.send :set_value_on_message, value, @msg, @arena, wrap: wrap
|
674
|
+
raise RuntimeError.new "allocation failed"
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
##
|
679
|
+
# @param initial_value [Object] initial value of this Message
|
680
|
+
# @param arena [Arena] Optional; Arena where this message will be allocated
|
681
|
+
# @param msg [::FFI::Pointer] Optional; Message to initialize; creates
|
682
|
+
# one if omitted or nil.
|
683
|
+
def initialize(initial_value = nil, arena = nil, msg = nil)
|
684
|
+
@arena = arena || Google::Protobuf::FFI.create_arena
|
685
|
+
@msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
|
686
|
+
|
687
|
+
unless initial_value.nil?
|
688
|
+
raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
|
689
|
+
|
690
|
+
field_def_ptr = ::FFI::MemoryPointer.new :pointer
|
691
|
+
oneof_def_ptr = ::FFI::MemoryPointer.new :pointer
|
692
|
+
|
693
|
+
initial_value.each do |key, value|
|
694
|
+
raise ArgumentError.new "Expected string or symbols as hash keys when initializing proto from hash." unless [String, Symbol].include? key.class
|
695
|
+
|
696
|
+
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
|
697
|
+
raise ArgumentError.new "Unknown field name '#{key}' in initialization map entry."
|
698
|
+
end
|
699
|
+
raise NotImplementedError.new "Haven't added oneofsupport yet" unless oneof_def_ptr.get_pointer(0).null?
|
700
|
+
raise NotImplementedError.new "Expected a field def" if field_def_ptr.get_pointer(0).null?
|
701
|
+
|
702
|
+
field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
|
703
|
+
|
704
|
+
next if value.nil?
|
705
|
+
if field_descriptor.map?
|
706
|
+
index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
|
707
|
+
elsif field_descriptor.repeated?
|
708
|
+
index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
|
709
|
+
else
|
710
|
+
index_assign_internal(value, name: key.to_s)
|
711
|
+
end
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
# Should always be the last expression of the initializer to avoid
|
716
|
+
# leaking references to this object before construction is complete.
|
717
|
+
Google::Protobuf::OBJECT_CACHE.try_add @msg.address, self
|
718
|
+
end
|
719
|
+
|
720
|
+
##
|
721
|
+
# Gets a field of this message identified by FieldDescriptor.
|
722
|
+
#
|
723
|
+
# @param field [FieldDescriptor] Field to retrieve.
|
724
|
+
# @param unwrap [Boolean](false) If true, unwraps wrappers.
|
725
|
+
def get_field(field, unwrap: false)
|
726
|
+
if field.map?
|
727
|
+
map_from_field_descriptor field
|
728
|
+
elsif field.repeated?
|
729
|
+
repeated_field_from_field_descriptor field
|
730
|
+
elsif field.sub_message?
|
731
|
+
return nil unless Google::Protobuf::FFI.get_message_has @msg, field
|
732
|
+
if unwrap
|
733
|
+
if field.has?(self)
|
734
|
+
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
|
735
|
+
wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
|
736
|
+
fields = Google::Protobuf::FFI.field_count(sub_message_def)
|
737
|
+
raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
|
738
|
+
value_field_def = Google::Protobuf::FFI.get_field_by_number sub_message_def, 1
|
739
|
+
message_value = Google::Protobuf::FFI.get_message_value wrapper_message_value[:msg_val], value_field_def
|
740
|
+
convert_upb_to_ruby message_value, Google::Protobuf::FFI.get_c_type(value_field_def)
|
741
|
+
else
|
742
|
+
nil
|
743
|
+
end
|
744
|
+
else
|
745
|
+
message_from_field_descriptor field
|
746
|
+
end
|
747
|
+
else
|
748
|
+
scalar_from_field_descriptor field
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
##
|
753
|
+
# Gets a RepeatedField from the ObjectCache or creates a new one.
|
754
|
+
# @param array [::FFI::Pointer] Pointer to the upb_Array
|
755
|
+
# @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
|
756
|
+
def get_repeated_field(array, field)
|
757
|
+
return nil if array.nil? or array.null?
|
758
|
+
repeated_field = OBJECT_CACHE.get(array.address)
|
759
|
+
if repeated_field.nil?
|
760
|
+
repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
|
761
|
+
repeated_field.freeze if frozen?
|
762
|
+
end
|
763
|
+
repeated_field
|
764
|
+
end
|
765
|
+
|
766
|
+
##
|
767
|
+
# Gets a Map from the ObjectCache or creates a new one.
|
768
|
+
# @param map [::FFI::Pointer] Pointer to the upb_Map
|
769
|
+
# @param field [Google::Protobuf::FieldDescriptor] Type of the map field
|
770
|
+
def get_map_field(map, field)
|
771
|
+
return nil if map.nil? or map.null?
|
772
|
+
map_field = OBJECT_CACHE.get(map.address)
|
773
|
+
if map_field.nil?
|
774
|
+
map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
|
775
|
+
map_field.freeze if frozen?
|
776
|
+
end
|
777
|
+
map_field
|
778
|
+
end
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end
|
782
|
+
end
|
783
|
+
end
|