diff --git a/lib/protobuf/field/base_field.rb b/lib/protobuf/field/base_field.rb index 3ae505a4..f2b8426a 100644 --- a/lib/protobuf/field/base_field.rb +++ b/lib/protobuf/field/base_field.rb @@ -176,16 +176,18 @@ def define_accessor else define_getter define_setter + define_decode_setter end end def define_array_getter field = self + field_name = field.name method_name = field.getter message_class.class_eval do define_method(method_name) do - @values[field.name] ||= ::Protobuf::Field::FieldArray.new(field) + @values[field_name] ||= ::Protobuf::Field::FieldArray.new(field) end end @@ -194,6 +196,7 @@ def define_array_getter def define_array_setter field = self + field_name = field.name method_name = field.setter message_class.class_eval do @@ -205,15 +208,15 @@ def define_array_setter else fail TypeError, <<-TYPE_ERROR Expected repeated value of type '#{field.type_class}' - Got '#{val.class}' for repeated protobuf field #{field.name} + Got '#{val.class}' for repeated protobuf field #{field_name} TYPE_ERROR end if val.nil? || (val.respond_to?(:empty?) && val.empty?) - @values.delete(field.name) + @values.delete(field_name) else - @values[field.name] ||= ::Protobuf::Field::FieldArray.new(field) - @values[field.name].replace(val) + @values[field_name] ||= ::Protobuf::Field::FieldArray.new(field) + @values[field_name].replace(val) end end end @@ -234,17 +237,22 @@ def define_getter ::Protobuf.field_deprecator.deprecate_method(message_class, method_name) if field.deprecated? end + def define_decode_setter + # empty for now + end + def define_setter field = self + field_name = field.name method_name = field.setter message_class.class_eval do define_method(method_name) do |val| @encode = nil if val.nil? || (val.respond_to?(:empty?) && val.empty?) - @values.delete(field.name) + @values.delete(field_name) elsif field.acceptable?(val) - @values[field.name] = field.coerce!(val) + @values[field_name] = field.coerce!(val) else fail TypeError, "Unacceptable value #{val} for field #{field.name} of type #{field.type_class}" end diff --git a/lib/protobuf/field/bool_field.rb b/lib/protobuf/field/bool_field.rb index afe6c112..4422d93e 100644 --- a/lib/protobuf/field/bool_field.rb +++ b/lib/protobuf/field/bool_field.rb @@ -35,6 +35,19 @@ def decode(value) value == 1 end + def define_decode_setter + field = self + field_name = field.name + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + @values[field_name] = field.decode(val) + end + end + end + def encode(value) [value ? 1 : 0].pack('C') end diff --git a/lib/protobuf/field/bytes_field.rb b/lib/protobuf/field/bytes_field.rb index d7f4aee4..7c61a136 100644 --- a/lib/protobuf/field/bytes_field.rb +++ b/lib/protobuf/field/bytes_field.rb @@ -56,8 +56,23 @@ def wire_type # Private Instance Methods # + def define_decode_setter + field = self + field_name = field.name + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + @values[field_name] = field.decode(val) + end + end + end + def define_setter field = self + field_name = field.name + field_type_class = field.type_class method_name = field.setter message_class.class_eval do @@ -65,13 +80,13 @@ def define_setter @encode = nil case val when String, Symbol - @values[field.name] = "#{val}" + @values[field_name] = "#{val}" when NilClass - @values.delete(field.name) + @values.delete(field_name) when ::Protobuf::Message - @values[field.name] = val.dup + @values[field_name] = val.dup else - fail TypeError, "Unacceptable value #{val} for field #{field.name} of type #{field.type_class}" + fail TypeError, "Unacceptable value #{val} for field #{field_name} of type #{field_type_class}" end end end diff --git a/lib/protobuf/field/enum_field.rb b/lib/protobuf/field/enum_field.rb index 71632cde..96e13e9e 100644 --- a/lib/protobuf/field/enum_field.rb +++ b/lib/protobuf/field/enum_field.rb @@ -38,19 +38,36 @@ def enum? # Private Instance Methods # + def define_decode_setter + field = self + field_name = field.name + field_type_class = field.type_class + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + @values[field_name] = field_type_class.fetch(val) + end + end + end + def define_setter field = self + field_name = field.name + field_type_class = field.type_class + message_class.class_eval do - define_method("#{field.name}=") do |value| + define_method("#{field_name}=") do |value| @encode = nil orig_value = value if value.nil? - @values.delete(field.name) + @values.delete(field_name) else - value = field.type_class.fetch(value) + value = field_type_class.fetch(value) fail TypeError, "Invalid Enum value: #{orig_value.inspect} for #{field.name}" unless value - @values[field.name] = value + @values[field_name] = value end end end diff --git a/lib/protobuf/field/integer_field.rb b/lib/protobuf/field/integer_field.rb index 5eb3b064..254a2a9d 100644 --- a/lib/protobuf/field/integer_field.rb +++ b/lib/protobuf/field/integer_field.rb @@ -9,10 +9,23 @@ class IntegerField < VarintField # def decode(value) - value -= 0x1_0000_0000_0000_0000 if (value & 0x8000_0000_0000_0000).nonzero? + value -= 0x1_0000_0000_0000_0000 if (value & 0x8000_0000_0000_0000) != 0 value end + def define_decode_setter + field = self + field_name = field.name + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + @values[field_name] = field.decode(val) + end + end + end + def encode(value) # original Google's library uses 64bits integer for negative value ::Protobuf::Field::VarintField.encode(value & 0xffff_ffff_ffff_ffff) diff --git a/lib/protobuf/field/message_field.rb b/lib/protobuf/field/message_field.rb index 8cb37bd7..c24f8104 100644 --- a/lib/protobuf/field/message_field.rb +++ b/lib/protobuf/field/message_field.rb @@ -17,9 +17,7 @@ def decode(bytes) end def encode(value) - bytes = value.encode - result = ::Protobuf::Field::VarintField.encode(bytes.size) - result << bytes + "#{::Protobuf::Field::VarintField.encode(value.encode.size)}#{value.encode}" end def message? @@ -38,20 +36,23 @@ def wire_type def define_setter field = self + field_name = field.name + field_type_class = field.type_class + message_class.class_eval do - define_method("#{field.name}=") do |val| + define_method("#{field_name}=") do |val| @encode = nil case when val.nil? - @values.delete(field.name) - when val.is_a?(field.type_class) - @values[field.name] = val + @values.delete(field_name) + when val.is_a?(field_type_class) + @values[field_name] = val when val.respond_to?(:to_proto) - @values[field.name] = val.to_proto + @values[field_name] = val.to_proto when val.respond_to?(:to_hash) - @values[field.name] = field.type_class.new(val.to_hash) + @values[field_name] = field_type_class.new(val.to_hash) else - fail TypeError, "Expected value of type '#{field.type_class}' for field #{field.name}, but got '#{val.class}'" + fail TypeError, "Expected value of type '#{field_type_class}' for field #{field_name}, but got '#{val.class}'" end end end diff --git a/lib/protobuf/field/sfixed32_field.rb b/lib/protobuf/field/sfixed32_field.rb index 9f9b64e9..c8ca1f3a 100644 --- a/lib/protobuf/field/sfixed32_field.rb +++ b/lib/protobuf/field/sfixed32_field.rb @@ -10,7 +10,7 @@ class Sfixed32Field < Int32Field def decode(bytes) value = bytes.unpack('V').first - value -= 0x1_0000_0000 if (value & 0x8000_0000).nonzero? + value -= 0x1_0000_0000 if (value & 0x8000_0000) != 0 value end diff --git a/lib/protobuf/field/sfixed64_field.rb b/lib/protobuf/field/sfixed64_field.rb index fc296483..d7ab755e 100644 --- a/lib/protobuf/field/sfixed64_field.rb +++ b/lib/protobuf/field/sfixed64_field.rb @@ -11,7 +11,7 @@ class Sfixed64Field < Int64Field def decode(bytes) values = bytes.unpack('VV') # 'Q' is machine-dependent, don't use value = values[0] + (values[1] << 32) - value -= 0x1_0000_0000_0000_0000 if (value & 0x8000_0000_0000_0000).nonzero? + value -= 0x1_0000_0000_0000_0000 if (value & 0x8000_0000_0000_0000) != 0 value end diff --git a/lib/protobuf/field/signed_integer_field.rb b/lib/protobuf/field/signed_integer_field.rb index 8945f771..44f5730c 100644 --- a/lib/protobuf/field/signed_integer_field.rb +++ b/lib/protobuf/field/signed_integer_field.rb @@ -16,6 +16,19 @@ def decode(value) end end + def define_decode_setter + field = self + field_name = field.name + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + @values[field_name] = field.decode(val) + end + end + end + def encode(value) if value >= 0 VarintField.encode(value << 1) diff --git a/lib/protobuf/field/string_field.rb b/lib/protobuf/field/string_field.rb index ac36ea70..5050daba 100644 --- a/lib/protobuf/field/string_field.rb +++ b/lib/protobuf/field/string_field.rb @@ -20,6 +20,20 @@ def decode(bytes) bytes_to_decode end + def define_decode_setter + field = self + field_name = field.name + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + val.force_encoding(::Protobuf::Field::StringField::ENCODING) + @values[field_name] = val + end + end + end + def encode(value) value_to_encode = value.dup value_to_encode.encode!(::Protobuf::Field::StringField::ENCODING, :invalid => :replace, :undef => :replace, :replace => "") diff --git a/lib/protobuf/field/varint_field.rb b/lib/protobuf/field/varint_field.rb index 59fabdcb..9b5ba415 100644 --- a/lib/protobuf/field/varint_field.rb +++ b/lib/protobuf/field/varint_field.rb @@ -52,6 +52,7 @@ def self.encode(value, use_cache = true) # def acceptable?(val) + return true if val.is_a?(Integer) && val >= 0 && val < INT32_MAX int_val = coerce!(val) int_val >= self.class.min && int_val <= self.class.max rescue @@ -63,6 +64,19 @@ def coerce!(val) Integer(val, 10) end + def define_decode_setter + field = self + field_name = field.name + tag_method_name = "_protobuf_decode_setter_#{field.tag}" + + message_class.class_eval do + define_method(tag_method_name) do |val| + @encode = nil + @values[field_name] = val + end + end + end + def decode(value) value end diff --git a/lib/protobuf/message.rb b/lib/protobuf/message.rb index 9ac1332e..944fc8c4 100644 --- a/lib/protobuf/message.rb +++ b/lib/protobuf/message.rb @@ -77,14 +77,13 @@ def each_field return to_enum(:each_field) unless block_given? self.class.all_fields.each do |field| - value = __send__(field.getter) - yield(field, value) + yield(field, __send__(field.getter)) end end def each_field_for_serialization self.class.all_fields.each do |field| - value = @values[field.getter] + value = @values[field.name] if value.nil? fail ::Protobuf::SerializationError, "Required field #{self.class.name}##{field.name} does not have a value." if field.required? next @@ -146,18 +145,16 @@ def ==(other) end def [](name) - if (field = self.class.get_field(name, true)) - __send__(field.getter) - end + __send__(name) if respond_to?(name) end def []=(name, value) - if (field = self.class.get_field(name, true)) - __send__(field.setter, value) unless value.nil? + setter = "#{name}=" + + if respond_to?(setter) + __send__(setter, value) unless value.nil? else - unless ::Protobuf.ignore_unknown_fields? - fail ::Protobuf::FieldNotDefinedError, name - end + fail ::Protobuf::FieldNotDefinedError, name unless ::Protobuf.ignore_unknown_fields? end end diff --git a/lib/protobuf/message/serialization.rb b/lib/protobuf/message/serialization.rb index 668b6cea..68670bc0 100644 --- a/lib/protobuf/message/serialization.rb +++ b/lib/protobuf/message/serialization.rb @@ -78,6 +78,9 @@ def encode_to(stream) private def set_field_bytes(tag, bytes) + tag_setter_name = "_protobuf_decode_setter_#{tag}" + return __send__(tag_setter_name, bytes) if respond_to?(tag_setter_name) + field = self.class.get_field(tag, true) field.set(self, bytes) if field end diff --git a/lib/protobuf/varint_pure.rb b/lib/protobuf/varint_pure.rb index eeffbecc..6fe38aad 100644 --- a/lib/protobuf/varint_pure.rb +++ b/lib/protobuf/varint_pure.rb @@ -1,12 +1,12 @@ module Protobuf module VarintPure def decode(stream) - value = index = 0 + value = shift = 0 begin byte = stream.readbyte - value |= (byte & 0x7f) << (7 * index) - index += 1 - end while (byte & 0x80).nonzero? + value |= (byte & 127) << shift + shift += 7 + end while byte > 127 value end end diff --git a/spec/benchmark/tasks.rb b/spec/benchmark/tasks.rb index 631439d6..a98b9683 100644 --- a/spec/benchmark/tasks.rb +++ b/spec/benchmark/tasks.rb @@ -119,7 +119,7 @@ def profile_code(output, &block) when :jruby profile_data = JRuby::Profiler.profile(&block) File.open(output, 'w') do |f| - JRuby::Profiler::FlatProfilePrinter.new(profile_data).printProfile(f) + JRuby::Profiler::GraphProfilePrinter.new(profile_data).printProfile(f) end end end