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