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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/Rakefile +3 -0
  3. data/ext/google/protobuf_c/convert.c +60 -86
  4. data/ext/google/protobuf_c/convert.h +3 -28
  5. data/ext/google/protobuf_c/defs.c +692 -72
  6. data/ext/google/protobuf_c/defs.h +3 -28
  7. data/ext/google/protobuf_c/extconf.rb +14 -1
  8. data/ext/google/protobuf_c/glue.c +135 -0
  9. data/ext/google/protobuf_c/map.c +89 -45
  10. data/ext/google/protobuf_c/map.h +12 -30
  11. data/ext/google/protobuf_c/message.c +169 -169
  12. data/ext/google/protobuf_c/message.h +11 -33
  13. data/ext/google/protobuf_c/protobuf.c +65 -188
  14. data/ext/google/protobuf_c/protobuf.h +27 -39
  15. data/ext/google/protobuf_c/repeated_field.c +72 -38
  16. data/ext/google/protobuf_c/repeated_field.h +11 -29
  17. data/ext/google/protobuf_c/ruby-upb.c +15064 -11220
  18. data/ext/google/protobuf_c/ruby-upb.h +10643 -5403
  19. data/ext/google/protobuf_c/shared_convert.c +69 -0
  20. data/ext/google/protobuf_c/shared_convert.h +26 -0
  21. data/ext/google/protobuf_c/shared_message.c +37 -0
  22. data/ext/google/protobuf_c/shared_message.h +21 -0
  23. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +207 -0
  24. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -8
  25. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_neon.inc +117 -0
  26. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_sse.inc +272 -0
  27. data/ext/google/protobuf_c/wrap_memcpy.c +3 -26
  28. data/lib/google/protobuf/any_pb.rb +6 -8
  29. data/lib/google/protobuf/api_pb.rb +6 -26
  30. data/lib/google/protobuf/descriptor_pb.rb +21 -252
  31. data/lib/google/protobuf/duration_pb.rb +6 -8
  32. data/lib/google/protobuf/empty_pb.rb +6 -6
  33. data/lib/google/protobuf/ffi/descriptor.rb +175 -0
  34. data/lib/google/protobuf/ffi/descriptor_pool.rb +77 -0
  35. data/lib/google/protobuf/ffi/enum_descriptor.rb +183 -0
  36. data/lib/google/protobuf/ffi/ffi.rb +214 -0
  37. data/lib/google/protobuf/ffi/field_descriptor.rb +340 -0
  38. data/lib/google/protobuf/ffi/file_descriptor.rb +59 -0
  39. data/lib/google/protobuf/ffi/internal/arena.rb +60 -0
  40. data/lib/google/protobuf/ffi/internal/convert.rb +292 -0
  41. data/lib/google/protobuf/ffi/internal/pointer_helper.rb +35 -0
  42. data/lib/google/protobuf/ffi/internal/type_safety.rb +25 -0
  43. data/lib/google/protobuf/ffi/map.rb +433 -0
  44. data/lib/google/protobuf/ffi/message.rb +783 -0
  45. data/lib/google/protobuf/ffi/method_descriptor.rb +124 -0
  46. data/lib/google/protobuf/ffi/object_cache.rb +30 -0
  47. data/lib/google/protobuf/ffi/oneof_descriptor.rb +107 -0
  48. data/lib/google/protobuf/ffi/repeated_field.rb +411 -0
  49. data/lib/google/protobuf/ffi/service_descriptor.rb +117 -0
  50. data/lib/google/protobuf/field_mask_pb.rb +6 -7
  51. data/lib/google/protobuf/internal/object_cache.rb +99 -0
  52. data/lib/google/protobuf/message_exts.rb +3 -26
  53. data/lib/google/protobuf/plugin_pb.rb +6 -31
  54. data/lib/google/protobuf/repeated_field.rb +7 -31
  55. data/lib/google/protobuf/source_context_pb.rb +6 -7
  56. data/lib/google/protobuf/struct_pb.rb +6 -23
  57. data/lib/google/protobuf/timestamp_pb.rb +6 -8
  58. data/lib/google/protobuf/type_pb.rb +6 -71
  59. data/lib/google/protobuf/well_known_types.rb +5 -34
  60. data/lib/google/protobuf/wrappers_pb.rb +6 -31
  61. data/lib/google/protobuf.rb +27 -45
  62. data/lib/google/protobuf_ffi.rb +52 -0
  63. data/lib/google/protobuf_native.rb +19 -0
  64. data/lib/google/tasks/ffi.rake +100 -0
  65. metadata +92 -9
  66. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
  67. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
  68. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
  69. data/lib/google/protobuf/descriptor_dsl.rb +0 -465
@@ -0,0 +1,292 @@
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 value.is_a?(String) or value.is_a?(Symbol)
36
+ value = value.to_s if value.is_a?(Symbol)
37
+ if value.encoding == Encoding::UTF_8
38
+ unless value.valid_encoding?
39
+ raise Encoding::InvalidByteSequenceError.new "String is invalid UTF-8"
40
+ end
41
+ string_value = value
42
+ else
43
+ string_value = value.to_s.encode("UTF-8")
44
+ end
45
+ return_value[:str_val][:size] = string_value.bytesize
46
+ return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
47
+ # TODO - how important is it to still use arena malloc, versus the following?
48
+ # buffer = ::FFI::MemoryPointer.new(:char, string_value.bytesize)
49
+ # buffer.put_bytes(0, string_value)
50
+ # return_value[:str_val][:data] = buffer
51
+ 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?
52
+ return_value[:str_val][:data].write_string(string_value)
53
+ when :bytes
54
+ raise TypeError.new "Invalid argument for bytes field '#{name}' (given #{value.class})." unless value.is_a? String
55
+ string_value = value.encode("ASCII-8BIT")
56
+ return_value[:str_val][:size] = string_value.bytesize
57
+ return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
58
+ 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?
59
+ return_value[:str_val][:data].write_string_length(string_value, string_value.bytesize)
60
+ when :message
61
+ raise TypeError.new "nil message not allowed here." if value.nil?
62
+ if value.is_a? Hash
63
+ raise RuntimeError.new "Attempted to initialize message from Hash for field #{name} but have no definition" if msg_or_enum_def.nil?
64
+ new_message = msg_or_enum_def.msgclass.
65
+ send(:private_constructor, arena, initial_value: value)
66
+ return_value[:msg_val] = new_message.instance_variable_get(:@msg)
67
+ return return_value
68
+ end
69
+
70
+ descriptor = value.class.respond_to?(:descriptor) ? value.class.descriptor : nil
71
+ if descriptor != msg_or_enum_def
72
+ wkt = Google::Protobuf::FFI.get_well_known_type(msg_or_enum_def)
73
+ case wkt
74
+ when :Timestamp
75
+ raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Time
76
+ new_message = Google::Protobuf::FFI.new_message_from_def Google::Protobuf::FFI.get_mini_table(msg_or_enum_def), arena
77
+ sec = Google::Protobuf::FFI::MessageValue.new
78
+ sec[:int64_val] = value.tv_sec
79
+ sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
80
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
81
+ nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
82
+ nsec = Google::Protobuf::FFI::MessageValue.new
83
+ nsec[:int32_val] = value.tv_nsec
84
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
85
+ return_value[:msg_val] = new_message
86
+ when :Duration
87
+ raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Numeric
88
+ new_message = Google::Protobuf::FFI.new_message_from_def Google::Protobuf::FFI.get_mini_table(msg_or_enum_def), arena
89
+ sec = Google::Protobuf::FFI::MessageValue.new
90
+ sec[:int64_val] = value
91
+ sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
92
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
93
+ nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
94
+ nsec = Google::Protobuf::FFI::MessageValue.new
95
+ nsec[:int32_val] = ((value.to_f - value.to_i) * 1000000000).round
96
+ raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
97
+ return_value[:msg_val] = new_message
98
+ else
99
+ raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'."
100
+ end
101
+ else
102
+ arena.fuse(value.instance_variable_get(:@arena))
103
+ return_value[:msg_val] = value.instance_variable_get :@msg
104
+ end
105
+ when :enum
106
+ return_value[:int32_val] = case value
107
+ when Numeric
108
+ value.to_i
109
+ when String, Symbol
110
+ enum_number = EnumDescriptor.send(:lookup_name, msg_or_enum_def, value.to_s)
111
+ #TODO add the bad value to the error message after tests pass
112
+ raise RangeError.new "Unknown symbol value for enum field '#{name}'." if enum_number.nil?
113
+ enum_number
114
+ else
115
+ raise TypeError.new "Expected number or symbol type for enum field '#{name}'."
116
+ end
117
+ #TODO After all tests pass, improve error message across integer type by including actual offending value
118
+ when :int32
119
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
120
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
121
+ raise RangeError.new "Value assigned to int32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 32
122
+ return_value[:int32_val] = value.to_i
123
+ when :uint32
124
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
125
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
126
+ raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
127
+ raise RangeError.new "Value assigned to uint32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 33
128
+ return_value[:uint32_val] = value.to_i
129
+ when :int64
130
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
131
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
132
+ raise RangeError.new "Value assigned to int64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 64
133
+ return_value[:int64_val] = value.to_i
134
+ when :uint64
135
+ raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
136
+ raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
137
+ raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
138
+ raise RangeError.new "Value assigned to uint64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 65
139
+ return_value[:uint64_val] = value.to_i
140
+ else
141
+ raise RuntimeError.new "Unsupported type #{c_type}"
142
+ end
143
+ return_value
144
+ end
145
+
146
+ ##
147
+ # Safe to call without an arena if the caller has checked that c_type
148
+ # is not :message.
149
+ # @param message_value [Google::Protobuf::FFI::MessageValue] Value to be converted.
150
+ # @param c_type [Google::Protobuf::FFI::CType] Enum representing the type of message_value
151
+ # @param msg_or_enum_def [::FFI::Pointer] Pointer to the MsgDef or EnumDef definition
152
+ # @param arena [Google::Protobuf::Internal::Arena] Arena to create Message instances, if needed
153
+ def convert_upb_to_ruby(message_value, c_type, msg_or_enum_def = nil, arena = nil)
154
+ throw TypeError.new "Expected MessageValue but got #{message_value.class}" unless message_value.is_a? Google::Protobuf::FFI::MessageValue
155
+
156
+ case c_type
157
+ when :bool
158
+ message_value[:bool_val]
159
+ when :int32
160
+ message_value[:int32_val]
161
+ when :uint32
162
+ message_value[:uint32_val]
163
+ when :double
164
+ message_value[:double_val]
165
+ when :int64
166
+ message_value[:int64_val]
167
+ when :uint64
168
+ message_value[:uint64_val]
169
+ when :string
170
+ if message_value[:str_val][:size].zero?
171
+ ""
172
+ else
173
+ message_value[:str_val][:data].read_string_length(message_value[:str_val][:size]).force_encoding("UTF-8").freeze
174
+ end
175
+ when :bytes
176
+ if message_value[:str_val][:size].zero?
177
+ ""
178
+ else
179
+ message_value[:str_val][:data].read_string_length(message_value[:str_val][:size]).force_encoding("ASCII-8BIT").freeze
180
+ end
181
+ when :float
182
+ message_value[:float_val]
183
+ when :enum
184
+ EnumDescriptor.send(:lookup_value, msg_or_enum_def, message_value[:int32_val]) || message_value[:int32_val]
185
+ when :message
186
+ raise "Null Arena for message" if arena.nil?
187
+ Descriptor.send(:get_message, message_value[:msg_val], msg_or_enum_def, arena)
188
+ else
189
+ raise RuntimeError.new "Unexpected type #{c_type}"
190
+ end
191
+ end
192
+
193
+ def to_h_internal(msg, message_descriptor)
194
+ return nil if msg.nil? or msg.null?
195
+ hash = {}
196
+ iter = ::FFI::MemoryPointer.new(:size_t, 1)
197
+ iter.write(:size_t, Google::Protobuf::FFI::Upb_Message_Begin)
198
+ message_value = Google::Protobuf::FFI::MessageValue.new
199
+ field_def_ptr = ::FFI::MemoryPointer.new :pointer
200
+
201
+ while Google::Protobuf::FFI::message_next(msg, message_descriptor, nil, field_def_ptr, message_value, iter) do
202
+ field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
203
+
204
+ if field_descriptor.map?
205
+ hash_entry = map_create_hash(message_value[:map_val], field_descriptor)
206
+ elsif field_descriptor.repeated?
207
+ hash_entry = repeated_field_create_array(message_value[:array_val], field_descriptor, field_descriptor.type)
208
+ else
209
+ hash_entry = scalar_create_hash(message_value, field_descriptor.type, field_descriptor: field_descriptor)
210
+ end
211
+
212
+ hash[field_descriptor.name.to_sym] = hash_entry
213
+ end
214
+
215
+ hash
216
+ end
217
+
218
+ def map_create_hash(map_ptr, field_descriptor)
219
+ return {} if map_ptr.nil? or map_ptr.null?
220
+ return_value = {}
221
+
222
+ message_descriptor = field_descriptor.send(:subtype)
223
+ key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
224
+ key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
225
+
226
+ value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
227
+ value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
228
+
229
+ iter = ::FFI::MemoryPointer.new(:size_t, 1)
230
+ iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
231
+ while Google::Protobuf::FFI.map_next(map_ptr, iter) do
232
+ iter_size_t = iter.read(:size_t)
233
+ key_message_value = Google::Protobuf::FFI.map_key(map_ptr, iter_size_t)
234
+ value_message_value = Google::Protobuf::FFI.map_value(map_ptr, iter_size_t)
235
+ hash_key = convert_upb_to_ruby(key_message_value, key_field_type)
236
+ hash_value = scalar_create_hash(value_message_value, value_field_type, msg_or_enum_descriptor: value_field_def.subtype)
237
+ return_value[hash_key] = hash_value
238
+ end
239
+ return_value
240
+ end
241
+
242
+ def repeated_field_create_array(array, field_descriptor, type)
243
+ return_value = []
244
+ n = (array.nil? || array.null?) ? 0 : Google::Protobuf::FFI.array_size(array)
245
+ 0.upto(n - 1) do |i|
246
+ message_value = Google::Protobuf::FFI.get_msgval_at(array, i)
247
+ return_value << scalar_create_hash(message_value, type, field_descriptor: field_descriptor)
248
+ end
249
+ return_value
250
+ end
251
+
252
+ # @param field_descriptor [FieldDescriptor] Descriptor of the field to convert to a hash.
253
+ def scalar_create_hash(message_value, type, field_descriptor: nil, msg_or_enum_descriptor: nil)
254
+ if [:message, :enum].include? type
255
+ if field_descriptor.nil?
256
+ if msg_or_enum_descriptor.nil?
257
+ raise "scalar_create_hash requires either a FieldDescriptor, MessageDescriptor, or EnumDescriptor as an argument, but received only nil"
258
+ end
259
+ else
260
+ msg_or_enum_descriptor = field_descriptor.subtype
261
+ end
262
+ if type == :message
263
+ to_h_internal(message_value[:msg_val], msg_or_enum_descriptor)
264
+ elsif type == :enum
265
+ convert_upb_to_ruby message_value, type, msg_or_enum_descriptor
266
+ end
267
+ else
268
+ convert_upb_to_ruby message_value, type
269
+ end
270
+ end
271
+
272
+ def message_value_deep_copy(message_value, type, descriptor, arena)
273
+ raise unless message_value.is_a? Google::Protobuf::FFI::MessageValue
274
+ new_message_value = Google::Protobuf::FFI::MessageValue.new
275
+ case type
276
+ when :string, :bytes
277
+ # TODO - how important is it to still use arena malloc, versus using FFI MemoryPointers?
278
+ new_message_value[:str_val][:size] = message_value[:str_val][:size]
279
+ new_message_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, message_value[:str_val][:size])
280
+ raise NoMemoryError.new "Allocation failed" if new_message_value[:str_val][:data].nil? or new_message_value[:str_val][:data].null?
281
+ Google::Protobuf::FFI.memcpy(new_message_value[:str_val][:data], message_value[:str_val][:data], message_value[:str_val][:size])
282
+ when :message
283
+ new_message_value[:msg_val] = descriptor.msgclass.send(:deep_copy, message_value[:msg_val], arena).instance_variable_get(:@msg)
284
+ else
285
+ Google::Protobuf::FFI.memcpy(new_message_value.to_ptr, message_value.to_ptr, Google::Protobuf::FFI::MessageValue.size)
286
+ end
287
+ new_message_value
288
+ end
289
+ end
290
+ end
291
+ end
292
+ 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
+