protobuf 3.8.5 → 3.9.0.pre

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.
@@ -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