protobuf 3.8.5 → 3.9.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,10 +3,14 @@ require 'protobuf/field/varint_field'
3
3
  module Protobuf
4
4
  module Field
5
5
  class BoolField < VarintField
6
+ ONE = 1
6
7
  FALSE_ENCODE = [0].pack('C')
7
8
  FALSE_STRING = "false".freeze
9
+ FALSE_VALUES = [false, FALSE_STRING].freeze
8
10
  TRUE_ENCODE = [1].pack('C')
9
11
  TRUE_STRING = "true".freeze
12
+ TRUE_VALUES = [true, TRUE_STRING].freeze
13
+ ACCEPTABLES = [true, false, TRUE_STRING, FALSE_STRING].freeze
10
14
 
11
15
  ##
12
16
  # Class Methods
@@ -21,20 +25,21 @@ module Protobuf
21
25
  # #
22
26
 
23
27
  def acceptable?(val)
24
- val == true || val == false || val == TRUE_STRING || val == FALSE_STRING
28
+ ACCEPTABLES.include?(val)
25
29
  end
26
30
 
27
31
  def coerce!(val)
28
- return true if val == true
29
- return false if val == false
30
- return true if val == TRUE_STRING
31
- return false if val == FALSE_STRING
32
-
33
- fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'"
32
+ if TRUE_VALUES.include?(val)
33
+ true
34
+ elsif FALSE_VALUES.include?(val)
35
+ false
36
+ else
37
+ fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'"
38
+ end
34
39
  end
35
40
 
36
41
  def decode(value)
37
- value == 1
42
+ value == ONE
38
43
  end
39
44
 
40
45
  def encode(value)
@@ -27,30 +27,19 @@ module Protobuf
27
27
  end
28
28
 
29
29
  def decode(bytes)
30
- bytes_to_decode = bytes.dup
31
- bytes_to_decode.force_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
32
- bytes_to_decode
30
+ bytes.force_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
31
+ bytes
33
32
  end
34
33
 
35
34
  def encode(value)
36
- value_to_encode =
37
- if value.is_a?(::Protobuf::Message)
38
- value.encode
39
- else
40
- value.dup
41
- end
35
+ value_to_encode = if value.is_a?(::Protobuf::Message)
36
+ value.encode
37
+ else
38
+ "" + value
39
+ end
42
40
 
43
41
  value_to_encode.force_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
44
- string_size = ::Protobuf::Field::VarintField.encode(value_to_encode.size)
45
-
46
- "#{string_size}#{value_to_encode}"
47
- end
48
-
49
- def encode_to_stream(value, stream)
50
- value = value.encode if value.is_a?(::Protobuf::Message)
51
- byte_size = ::Protobuf::Field::VarintField.encode(value.bytesize)
52
-
53
- stream << tag_encoded << byte_size << value
42
+ "#{::Protobuf::Field::VarintField.encode(value_to_encode.bytesize)}#{value_to_encode}"
54
43
  end
55
44
 
56
45
  def wire_type
@@ -15,18 +15,18 @@ module Protobuf
15
15
  ##
16
16
  # Public Instance Methods
17
17
  #
18
-
19
- def acceptable?(val)
20
- !type_class.fetch(val).nil?
21
- end
22
-
23
18
  def encode(value)
24
- super(value.to_i)
19
+ # original Google's library uses 64bits integer for negative value
20
+ ::Protobuf::Field::VarintField.encode(value.to_i & 0xffff_ffff_ffff_ffff)
25
21
  end
26
22
 
27
23
  def decode(value)
28
- decoded = super(value)
29
- decoded if acceptable?(decoded)
24
+ value -= 0x1_0000_0000_0000_0000 if (value & 0x8000_0000_0000_0000).nonzero?
25
+ value if acceptable?(value)
26
+ end
27
+
28
+ def acceptable?(val)
29
+ !type_class.fetch(val).nil?
30
30
  end
31
31
 
32
32
  def enum?
@@ -34,9 +34,7 @@ module Protobuf
34
34
  end
35
35
 
36
36
  def coerce!(value)
37
- enum_value = type_class.fetch(value)
38
- fail TypeError, "Invalid Enum value: #{value.inspect} for #{name}" unless enum_value
39
- enum_value
37
+ type_class.fetch(value) || fail(TypeError, "Invalid Enum value: #{value.inspect} for #{name}")
40
38
  end
41
39
 
42
40
  private
@@ -16,6 +16,19 @@ module Protobuf
16
16
  INT64_MIN
17
17
  end
18
18
 
19
+ ##
20
+ # Instance Methods
21
+ #
22
+ def acceptable?(val)
23
+ if val.is_a?(Integer) || val.is_a?(Numeric)
24
+ val >= INT64_MIN && val <= INT64_MAX
25
+ else
26
+ Integer(val, 10) >= INT64_MIN && Integer(val, 10) <= INT64_MAX
27
+ end
28
+ rescue
29
+ return false
30
+ end
31
+
19
32
  end
20
33
  end
21
34
  end
@@ -18,7 +18,7 @@ module Protobuf
18
18
 
19
19
  def encode(value)
20
20
  bytes = value.encode
21
- result = ::Protobuf::Field::VarintField.encode(bytes.size)
21
+ result = ::Protobuf::Field::VarintField.encode(bytes.bytesize)
22
22
  result << bytes
23
23
  end
24
24
 
@@ -14,28 +14,33 @@ module Protobuf
14
14
  # Public Instance Methods
15
15
  #
16
16
 
17
- def decode(bytes)
18
- bytes_to_decode = bytes.dup
19
- bytes_to_decode.force_encoding(::Protobuf::Field::StringField::ENCODING)
20
- bytes_to_decode
17
+ def acceptable?(val)
18
+ val.is_a?(String) || val.nil? || val.is_a?(Symbol)
21
19
  end
22
20
 
23
- def encode(value)
24
- value_to_encode = value.dup
25
- value_to_encode.encode!(::Protobuf::Field::StringField::ENCODING, :invalid => :replace, :undef => :replace, :replace => "")
26
- value_to_encode.force_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
21
+ def coerce!(value)
22
+ if value.nil?
23
+ nil
24
+ elsif acceptable?(value)
25
+ value.to_s
26
+ else
27
+ fail TypeError, "Unacceptable value #{value} for field #{name} of type #{type_class}"
28
+ end
29
+ end
27
30
 
28
- "#{::Protobuf::Field::VarintField.encode(value_to_encode.size)}#{value_to_encode}"
31
+ def decode(bytes)
32
+ bytes.force_encoding(::Protobuf::Field::StringField::ENCODING)
33
+ bytes
29
34
  end
30
35
 
31
- def encode_to_stream(value, stream)
32
- if value.encoding != ::Protobuf::Field::StringField::ENCODING
33
- value = value.dup
34
- value.encode!(::Protobuf::Field::StringField::ENCODING, :invalid => :replace, :undef => :replace, :replace => "")
36
+ def encode(value)
37
+ value_to_encode = "" + value # dup is slower
38
+ unless value_to_encode.encoding == ENCODING
39
+ value_to_encode.encode!(::Protobuf::Field::StringField::ENCODING, :invalid => :replace, :undef => :replace, :replace => "")
35
40
  end
41
+ value_to_encode.force_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
36
42
 
37
- byte_size = ::Protobuf::Field::VarintField.encode(value.bytesize)
38
- stream << tag_encoded << byte_size << value
43
+ "#{::Protobuf::Field::VarintField.encode(value_to_encode.bytesize)}#{value_to_encode}"
39
44
  end
40
45
 
41
46
  def json_encode(value)
@@ -7,8 +7,6 @@ module Protobuf
7
7
  ##
8
8
  # Constants
9
9
  #
10
-
11
- CACHE_LIMIT = 2048
12
10
  INT32_MAX = 2**31 - 1
13
11
  INT32_MIN = -2**31
14
12
  INT64_MAX = 2**63 - 1
@@ -24,27 +22,8 @@ module Protobuf
24
22
  0
25
23
  end
26
24
 
27
- # Because all tags and enums are calculated as VarInt it is "most common" to have
28
- # values < CACHE_LIMIT (low numbers) which is defaulting to 1024
29
- def self.cached_varint(value)
30
- @_varint_cache ||= {}
31
- (@_varint_cache[value] ||= encode(value, false)).dup
32
- end
33
-
34
- def self.encode(value, use_cache = true)
35
- return cached_varint(value) if use_cache && value >= 0 && value <= CACHE_LIMIT
36
-
37
- bytes = []
38
- until value < 128
39
- bytes << (0x80 | (value & 0x7f))
40
- value >>= 7
41
- end
42
- (bytes << value).pack('C*')
43
- end
44
-
45
- # Load the cache of VarInts on load of file
46
- (0..CACHE_LIMIT).each do |cached_value|
47
- cached_varint(cached_value)
25
+ def self.encode(value)
26
+ ::Protobuf::Varint.encode(value)
48
27
  end
49
28
 
50
29
  ##
@@ -66,9 +45,17 @@ module Protobuf
66
45
  end
67
46
 
68
47
  def coerce!(val)
69
- fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'" unless acceptable?(val)
70
- return val.to_i if val.is_a?(Numeric)
71
- Integer(val, 10)
48
+ if val.is_a?(Integer) && val >= 0 && val <= INT32_MAX
49
+ val
50
+ else
51
+ fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'" unless acceptable?(val)
52
+
53
+ if val.is_a?(Integer) || val.is_a?(Numeric)
54
+ val.to_i
55
+ else
56
+ Integer(val, 10)
57
+ end
58
+ end
72
59
  rescue ArgumentError
73
60
  fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'"
74
61
  end
@@ -1,15 +1,5 @@
1
1
  require 'protobuf/message/fields'
2
2
  require 'protobuf/message/serialization'
3
-
4
- # Under MRI, this optimizes proto decoding by around 15% in tests.
5
- # When unavailable, we fall to pure Ruby.
6
- # rubocop:disable Lint/HandleExceptions
7
- begin
8
- require 'varint/varint'
9
- rescue LoadError
10
- end
11
- # rubocop:enable Lint/HandleExceptions
12
-
13
3
  require 'protobuf/varint'
14
4
 
15
5
  module Protobuf
@@ -80,37 +70,26 @@ module Protobuf
80
70
  end
81
71
 
82
72
  def each_field_for_serialization
83
- self.class.all_fields.each do |field|
84
- value = @values[field.fully_qualified_name]
85
- if value.nil?
86
- fail ::Protobuf::SerializationError, "Required field #{self.class.name}##{field.name} does not have a value." if field.required?
87
- next
88
- end
89
- if field.map?
90
- # on-the-wire, maps are represented like an array of entries where
91
- # each entry is a message of two fields, key and value.
92
- array = Array.new(value.size)
93
- i = 0
94
- value.each do |k, v|
95
- array[i] = field.type_class.new(:key => k, :value => v)
96
- i += 1
97
- end
98
- value = array
99
- end
73
+ _protobuf_message_unset_required_field_tags.each do |tag|
74
+ fail ::Protobuf::SerializationError, "Required field #{self.class.name}##{_protobuf_message_field[tag].name} does not have a value."
75
+ end
100
76
 
101
- yield(field, value)
77
+ @values.each_key do |fully_qualified_name|
78
+ field = _protobuf_message_field[fully_qualified_name]
79
+ yield(field, field.value_from_values_for_serialization(@values))
102
80
  end
103
81
  end
104
82
 
105
83
  def field?(name)
106
- field = self.class.get_field(name, true)
107
- return false if field.nil?
108
- if field.repeated?
109
- @values.key?(field.fully_qualified_name) && @values[field.fully_qualified_name].present?
84
+ field = _protobuf_message_field[name]
85
+
86
+ if field
87
+ field.field?(@values)
110
88
  else
111
- @values.key?(field.fully_qualified_name)
89
+ false
112
90
  end
113
91
  end
92
+ alias :respond_to_has? field?
114
93
  ::Protobuf.deprecator.define_deprecated_methods(self, :has_field? => :field?)
115
94
 
116
95
  def inspect
@@ -121,13 +100,14 @@ module Protobuf
121
100
  "#<#{self.class} #{attrs}>"
122
101
  end
123
102
 
124
- def respond_to_has?(key)
125
- respond_to?(key) && field?(key)
126
- end
127
-
128
103
  def respond_to_has_and_present?(key)
129
- respond_to_has?(key) &&
130
- (self[key].present? || [true, false].include?(self[key]))
104
+ field = _protobuf_message_field[key]
105
+
106
+ if field
107
+ field.field_and_present?(@values)
108
+ else
109
+ false
110
+ end
131
111
  end
132
112
 
133
113
  # Return a hash-representation of the given fields for this message type.
@@ -135,10 +115,19 @@ module Protobuf
135
115
  result = {}
136
116
 
137
117
  @values.each_key do |field_name|
138
- value = self[field_name]
139
- field = self.class.get_field(field_name, true)
140
- hashed_value = value.respond_to?(:to_hash_value) ? value.to_hash_value : value
141
- result[field.name] = hashed_value
118
+ field = _protobuf_message_field[field_name]
119
+ field.to_message_hash(@values, result)
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ def to_hash_with_string_keys
126
+ result = {}
127
+
128
+ @values.each_key do |field_name|
129
+ field = _protobuf_message_field[field_name]
130
+ field.to_message_hash_with_string_key(@values, result)
142
131
  end
143
132
 
144
133
  result
@@ -186,11 +175,8 @@ module Protobuf
186
175
  end
187
176
 
188
177
  def [](name)
189
- field = self.class.get_field(name, true)
190
-
191
- return @values[field.fully_qualified_name] ||= ::Protobuf::Field::FieldHash.new(field) if field.map?
192
- return @values[field.fully_qualified_name] ||= ::Protobuf::Field::FieldArray.new(field) if field.repeated?
193
- @values.fetch(field.fully_qualified_name, field.default_value)
178
+ field = _protobuf_message_field[name]
179
+ field.value_from_values(@values)
194
180
  rescue # not having a field should be the exceptional state
195
181
  raise if field
196
182
  fail ArgumentError, "invalid field name=#{name.inspect}"
@@ -200,6 +186,16 @@ module Protobuf
200
186
  set_field(name, value, true)
201
187
  end
202
188
 
189
+ def set_field(name, value, ignore_nil_for_repeated, field = nil)
190
+ field ||= _protobuf_message_field[name]
191
+
192
+ if field
193
+ field.set_field(@values, value, ignore_nil_for_repeated, self)
194
+ else
195
+ fail(::Protobuf::FieldNotDefinedError, name) unless ::Protobuf.ignore_unknown_fields?
196
+ end
197
+ end
198
+
203
199
  ##
204
200
  # Instance Aliases
205
201
  #
@@ -222,57 +218,6 @@ module Protobuf
222
218
 
223
219
  private
224
220
 
225
- # rubocop:disable Metrics/MethodLength
226
- def set_field(name, value, ignore_nil_for_repeated)
227
- if (field = self.class.get_field(name, true))
228
- if field.map?
229
- unless value.is_a?(Hash)
230
- fail TypeError, <<-TYPE_ERROR
231
- Expected map value
232
- Got '#{value.class}' for map protobuf field #{field.name}
233
- TYPE_ERROR
234
- end
235
-
236
- if value.empty?
237
- @values.delete(field.fully_qualified_name)
238
- else
239
- @values[field.fully_qualified_name] ||= ::Protobuf::Field::FieldHash.new(field)
240
- @values[field.fully_qualified_name].replace(value)
241
- end
242
- elsif field.repeated?
243
- if value.nil? && ignore_nil_for_repeated
244
- ::Protobuf.deprecator.deprecation_warning("#{self.class}#[#{name}]=nil", "use an empty array instead of nil")
245
- return
246
- end
247
- unless value.is_a?(Array)
248
- fail TypeError, <<-TYPE_ERROR
249
- Expected repeated value of type '#{field.type_class}'
250
- Got '#{value.class}' for repeated protobuf field #{field.name}
251
- TYPE_ERROR
252
- end
253
-
254
- value = value.compact
255
-
256
- if value.empty?
257
- @values.delete(field.fully_qualified_name)
258
- else
259
- @values[field.fully_qualified_name] ||= ::Protobuf::Field::FieldArray.new(field)
260
- @values[field.fully_qualified_name].replace(value)
261
- end
262
- else
263
- if value.nil? # rubocop:disable Style/IfInsideElse
264
- @values.delete(field.fully_qualified_name)
265
- else
266
- @values[field.fully_qualified_name] = field.coerce!(value)
267
- end
268
- end
269
- else
270
- unless ::Protobuf.ignore_unknown_fields?
271
- fail ::Protobuf::FieldNotDefinedError, name
272
- end
273
- end
274
- end
275
-
276
221
  def copy_to(object, method)
277
222
  duplicate = proc do |obj|
278
223
  case obj