google-protobuf 3.24.4 → 3.25.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of google-protobuf might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/ext/google/protobuf_c/Rakefile +3 -0
- data/ext/google/protobuf_c/convert.c +23 -70
- data/ext/google/protobuf_c/convert.h +3 -26
- data/ext/google/protobuf_c/defs.c +3 -26
- data/ext/google/protobuf_c/defs.h +3 -26
- data/ext/google/protobuf_c/extconf.rb +2 -1
- data/ext/google/protobuf_c/glue.c +21 -0
- data/ext/google/protobuf_c/map.c +3 -26
- data/ext/google/protobuf_c/map.h +3 -26
- data/ext/google/protobuf_c/message.c +21 -69
- data/ext/google/protobuf_c/message.h +3 -26
- data/ext/google/protobuf_c/protobuf.c +3 -26
- data/ext/google/protobuf_c/protobuf.h +3 -26
- data/ext/google/protobuf_c/repeated_field.c +4 -27
- data/ext/google/protobuf_c/repeated_field.h +3 -26
- data/ext/google/protobuf_c/ruby-upb.c +8351 -8160
- data/ext/google/protobuf_c/ruby-upb.h +4077 -3784
- data/ext/google/protobuf_c/shared_convert.c +64 -0
- data/ext/google/protobuf_c/shared_convert.h +26 -0
- data/ext/google/protobuf_c/shared_message.c +65 -0
- data/ext/google/protobuf_c/shared_message.h +25 -0
- data/ext/google/protobuf_c/wrap_memcpy.c +3 -26
- data/lib/google/protobuf/any_pb.rb +1 -1
- data/lib/google/protobuf/api_pb.rb +1 -1
- data/lib/google/protobuf/descriptor_pb.rb +6 -3
- data/lib/google/protobuf/duration_pb.rb +1 -1
- data/lib/google/protobuf/empty_pb.rb +1 -1
- data/lib/google/protobuf/ffi/descriptor.rb +154 -0
- data/lib/google/protobuf/ffi/descriptor_pool.rb +70 -0
- data/lib/google/protobuf/ffi/enum_descriptor.rb +161 -0
- data/lib/google/protobuf/ffi/ffi.rb +213 -0
- data/lib/google/protobuf/ffi/field_descriptor.rb +309 -0
- data/lib/google/protobuf/ffi/file_descriptor.rb +48 -0
- data/lib/google/protobuf/ffi/internal/arena.rb +66 -0
- data/lib/google/protobuf/ffi/internal/convert.rb +305 -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 +396 -0
- data/lib/google/protobuf/ffi/message.rb +641 -0
- data/lib/google/protobuf/ffi/object_cache.rb +30 -0
- data/lib/google/protobuf/ffi/oneof_descriptor.rb +88 -0
- data/lib/google/protobuf/ffi/repeated_field.rb +503 -0
- data/lib/google/protobuf/field_mask_pb.rb +1 -1
- data/lib/google/protobuf/message_exts.rb +3 -26
- data/lib/google/protobuf/object_cache.rb +3 -26
- data/lib/google/protobuf/plugin_pb.rb +1 -1
- data/lib/google/protobuf/repeated_field.rb +3 -26
- data/lib/google/protobuf/source_context_pb.rb +1 -1
- data/lib/google/protobuf/struct_pb.rb +1 -1
- data/lib/google/protobuf/timestamp_pb.rb +1 -1
- data/lib/google/protobuf/type_pb.rb +1 -1
- data/lib/google/protobuf/well_known_types.rb +3 -26
- data/lib/google/protobuf/wrappers_pb.rb +1 -1
- data/lib/google/protobuf.rb +26 -45
- data/lib/google/protobuf_ffi.rb +50 -0
- data/lib/google/protobuf_native.rb +20 -0
- data/lib/google/tasks/ffi.rake +94 -0
- metadata +72 -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
|