21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
# File 'lib/google/protobuf/ffi/internal/convert.rb', line 21
def convert_ruby_to_upb(value, arena, c_type, msg_or_enum_def)
raise ArgumentError.new "Expected Descriptor or EnumDescriptor, instead got #{msg_or_enum_def.class}" unless [NilClass, Descriptor, EnumDescriptor].include? msg_or_enum_def.class
return_value = Google::Protobuf::FFI::MessageValue.new
case c_type
when :float
raise TypeError.new "Expected number type for float field '#{name}' (given #{value.class})." unless value.respond_to? :to_f
return_value[:float_val] = value.to_f
when :double
raise TypeError.new "Expected number type for double field '#{name}' (given #{value.class})." unless value.respond_to? :to_f
return_value[:double_val] = value.to_f
when :bool
raise TypeError.new "Invalid argument for boolean field '#{name}' (given #{value.class})." unless [TrueClass, FalseClass].include? value.class
return_value[:bool_val] = value
when :string
raise TypeError.new "Invalid argument for string field '#{name}' (given #{value.class})." unless value.is_a?(String) or value.is_a?(Symbol)
value = value.to_s if value.is_a?(Symbol)
if value.encoding == Encoding::UTF_8
unless value.valid_encoding?
warn "String is invalid UTF-8. This will be an error in a future version."
end
string_value = value
else
string_value = value.to_s.encode("UTF-8")
end
return_value[:str_val][:size] = string_value.bytesize
return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
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?
return_value[:str_val][:data].write_string(string_value)
when :bytes
raise TypeError.new "Invalid argument for bytes field '#{name}' (given #{value.class})." unless value.is_a? String
string_value = value.encode("ASCII-8BIT")
return_value[:str_val][:size] = string_value.bytesize
return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
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?
return_value[:str_val][:data].write_string_length(string_value, string_value.bytesize)
when :message
raise TypeError.new "nil message not allowed here." if value.nil?
if value.is_a? Hash
raise RuntimeError.new "Attempted to initialize message from Hash for field #{name} but have no definition" if msg_or_enum_def.nil?
new_message = msg_or_enum_def.msgclass.
send(:private_constructor, arena, initial_value: value)
return_value[:msg_val] = new_message.instance_variable_get(:@msg)
return return_value
end
descriptor = value.class.respond_to?(:descriptor) ? value.class.descriptor : nil
if descriptor != msg_or_enum_def
wkt = Google::Protobuf::FFI.get_well_known_type(msg_or_enum_def)
case wkt
when :Timestamp
raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Time
new_message = Google::Protobuf::FFI.new_message_from_def Google::Protobuf::FFI.get_mini_table(msg_or_enum_def), arena
sec = Google::Protobuf::FFI::MessageValue.new
sec[:int64_val] = value.tv_sec
sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
nsec = Google::Protobuf::FFI::MessageValue.new
nsec[:int32_val] = value.tv_nsec
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
return_value[:msg_val] = new_message
when :Duration
raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Numeric
new_message = Google::Protobuf::FFI.new_message_from_def Google::Protobuf::FFI.get_mini_table(msg_or_enum_def), arena
sec = Google::Protobuf::FFI::MessageValue.new
sec[:int64_val] = value
sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
nsec = Google::Protobuf::FFI::MessageValue.new
nsec[:int32_val] = ((value.to_f - value.to_i) * 1000000000).round
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
return_value[:msg_val] = new_message
else
raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'."
end
else
arena.fuse(value.instance_variable_get(:@arena))
return_value[:msg_val] = value.instance_variable_get :@msg
end
when :enum
return_value[:int32_val] = case value
when Numeric
value.to_i
when String, Symbol
enum_number = EnumDescriptor.send(:lookup_name, msg_or_enum_def, value.to_s)
raise RangeError.new "Unknown symbol value for enum field '#{name}'." if enum_number.nil?
enum_number
else
raise TypeError.new "Expected number or symbol type for enum field '#{name}'."
end
when :int32
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
raise RangeError.new "Value assigned to int32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 32
return_value[:int32_val] = value.to_i
when :uint32
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
raise RangeError.new "Value assigned to uint32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 33
return_value[:uint32_val] = value.to_i
when :int64
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
raise RangeError.new "Value assigned to int64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 64
return_value[:int64_val] = value.to_i
when :uint64
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
raise RangeError.new "Value assigned to uint64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 65
return_value[:uint64_val] = value.to_i
else
raise RuntimeError.new "Unsupported type #{c_type}"
end
return_value
end
|