class Google::Protobuf::Descriptor

Message Descriptor - Descriptor for short.
#

def self.add_oneof_accessors_for!(oneof_descriptor)

Parameters:
  • oneof_descriptor (OneofDescriptor) -- Field to create accessors
def self.add_oneof_accessors_for!(oneof_descriptor)
  field_name = oneof_descriptor.name.to_sym
  @oneof_field_names << field_name
  unless instance_methods(true).include?(field_name)
    define_method(field_name) do
      field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
      if field_descriptor.nil?
        return
      else
        return field_descriptor.name.to_sym
      end
    end
    define_method("clear_#{field_name}") do
      field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
      unless field_descriptor.nil?
        clear_internal(field_descriptor)
      end
    end
    define_method("has_#{field_name}?") do
      !Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor).nil?
    end
  end
end

def self.decode(data, options = {})

Options Hash: (**options)
  • :recursion_limit (Integer) -- Set to maximum decoding depth for message (default is 64)

Parameters:
  • options (Hash) -- options for the decoder
  • data (String) -- Binary string in Protobuf wire format to decode
def self.decode(data, options = {})
  raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
  raise ArgumentError.new "Expected string for binary protobuf data." unless data.is_a? String
  decoding_options = 0
  depth = options[:recursion_limit]
  if depth.is_a? Numeric
    decoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
  end
  message = new
  mini_table_ptr = Google::Protobuf::FFI.get_mini_table(message.class.descriptor)
  status = Google::Protobuf::FFI.decode_message(
    data,
    data.bytesize,
    message.instance_variable_get(:@msg),
    mini_table_ptr,
    Google::Protobuf::FFI.get_extension_registry(message.class.descriptor.send(:pool).descriptor_pool),
    decoding_options,
    message.instance_variable_get(:@arena)
  )
  raise ParseError.new "Error occurred during parsing" unless status == :Ok
  message
end

def self.decode_json(data, options = {})

Returns:
  • (Message) -

Options Hash: (**options)
  • :ignore_unknown_fields (Boolean) -- Set true to ignore unknown fields (default is to raise an error)

Parameters:
  • options (Hash) -- options for the decoder
def self.decode_json(data, options = {})
  decoding_options = 0
  unless options.is_a? Hash
    if options.respond_to? :to_h
      options options.to_h
    else
      #TODO can this error message be improve to include what was received?
      raise ArgumentError.new "Expected hash arguments"
    end
  end
  raise ArgumentError.new "Expected string for JSON data." unless data.is_a? String
  raise RuntimeError.new "Cannot parse a wrapper directly" if descriptor.send(:wrapper?)
  if options[:ignore_unknown_fields]
    decoding_options |= Google::Protobuf::FFI::Upb_JsonDecode_IgnoreUnknown
  end
  message = new
  pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
  status = Google::Protobuf::FFI::Status.new
  result = Google::Protobuf::FFI.json_decode_message_detecting_nonconformance(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
  case result
  when Google::Protobuf::FFI::Upb_JsonDecodeResult_Error
    raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
  end
  message
end

def self.deep_copy(msg, arena = nil)

def self.deep_copy(msg, arena = nil)
  arena ||= Google::Protobuf::FFI.create_arena
  encode_internal(msg) do |encoding, size, mini_table_ptr|
    message = private_constructor(arena)
    if encoding.nil? or encoding.null? or Google::Protobuf::FFI.decode_message(encoding, size, message.instance_variable_get(:@msg), mini_table_ptr, nil, 0, arena) != :Ok
      raise ParseError.new "Error occurred copying proto"
    end
    message
  end
end

def self.encode(message, options = {})

Options Hash: (**options)
  • :recursion_limit (Integer) -- Set to maximum encoding depth for message (default is 64)

Parameters:
  • options (Hash) -- options for the encoder
def self.encode(message, options = {})
  raise ArgumentError.new "Message of wrong type." unless message.is_a? self
  raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
  encoding_options = 0
  depth = options[:recursion_limit]
  if depth.is_a? Numeric
    encoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
  end
  encode_internal(message.instance_variable_get(:@msg), encoding_options) do |encoding, size, _|
    if encoding.nil? or encoding.null?
      raise RuntimeError.new "Exceeded maximum depth (possibly cycle)"
    else
      encoding.read_string_length(size).force_encoding("ASCII-8BIT").freeze
    end
  end
end

def self.encode_internal(msg, encoding_options = 0)

def self.encode_internal(msg, encoding_options = 0)
  temporary_arena = Google::Protobuf::FFI.create_arena
  mini_table_ptr = Google::Protobuf::FFI.get_mini_table(descriptor)
  size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
  pointer_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
  encoding_status = Google::Protobuf::FFI.encode_message(msg, mini_table_ptr, encoding_options, temporary_arena, pointer_ptr.to_ptr, size_ptr)
  raise "Encoding failed due to #{encoding_status}" unless encoding_status == :Ok
  yield pointer_ptr.read(:pointer), size_ptr.read(:size_t), mini_table_ptr
end

def self.encode_json(message, options = {})

def self.encode_json(message, options = {})
  encoding_options = 0
  unless options.is_a? Hash
    if options.respond_to? :to_h
      options = options.to_h
    else
      #TODO can this error message be improve to include what was received?
      raise ArgumentError.new "Expected hash arguments"
    end
  end
  if options[:preserve_proto_fieldnames]
    encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_UseProtoNames
  end
  if options[:emit_defaults]
    encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_EmitDefaults
  end
  if options[:format_enums_as_integers]
    encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_FormatEnumsAsIntegers
  end
  buffer_size = 1024
  buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
  status = Google::Protobuf::FFI::Status.new
  msg = message.instance_variable_get(:@msg)
  pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
  size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
  unless status[:ok]
    raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
  end
  if size >= buffer_size
    buffer_size = size + 1
    buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
    status.clear
    size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
    unless status[:ok]
      raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
    end
    if size >= buffer_size
      raise ParseError.new "Inconsistent JSON encoding sizes - was #{buffer_size - 1}, now #{size}"
    end
  end
  buffer.read_string_length(size).force_encoding("UTF-8").freeze
end

def self.get_message(msg, descriptor, arena)

def self.get_message(msg, descriptor, arena)
  return nil if msg.nil? or msg.null?
  message = OBJECT_CACHE.get(msg.address)
  if message.nil?
    message = descriptor.msgclass.send(:private_constructor, arena, msg: msg)
  end
  message
end

def self.inspect_field(field_descriptor, c_type, message_value)

def self.inspect_field(field_descriptor, c_type, message_value)
  if field_descriptor.sub_message?
    sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
    sub_msg_descriptor.msgclass.send(:inspect_internal, message_value[:msg_val])
  else
    convert_upb_to_ruby(message_value, c_type, field_descriptor.subtype).inspect
  end
end

def self.inspect_internal(msg)

Parameters:
  • msg (::FFI::Pointer) -- Pointer to the Message
def self.inspect_internal(msg)
  field_output = []
  descriptor.each do |field_descriptor|
    next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
    if field_descriptor.map?
      # TODO Adapted - from map#each_msg_val and map#inspect- can this be refactored to reduce echo without introducing a arena allocation?
      message_descriptor = field_descriptor.subtype
      key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
      key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
      value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
      value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
      message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
      iter = ::FFI::MemoryPointer.new(:size_t, 1)
      iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
      key_value_pairs = []
      while Google::Protobuf::FFI.map_next(message_value[:map_val], iter) do
        iter_size_t = iter.read(:size_t)
        key_message_value = Google::Protobuf::FFI.map_key(message_value[:map_val], iter_size_t)
        value_message_value = Google::Protobuf::FFI.map_value(message_value[:map_val], iter_size_t)
        key_string = convert_upb_to_ruby(key_message_value, key_field_type).inspect
        value_string = inspect_field(value_field_def, value_field_type, value_message_value)
        key_value_pairs << "#{key_string}=>#{value_string}"
      end
      field_output << "#{field_descriptor.name}: {#{key_value_pairs.join(", ")}}"
    elsif field_descriptor.repeated?
      # TODO Adapted - from repeated_field#each - can this be refactored to reduce echo?
      repeated_field_output = []
      message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
      array = message_value[:array_val]
      n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
      0.upto(n - 1) do |i|
        element = Google::Protobuf::FFI.get_msgval_at(array, i)
        repeated_field_output << inspect_field(field_descriptor, field_descriptor.send(:c_type), element)
      end
      field_output << "#{field_descriptor.name}: [#{repeated_field_output.join(", ")}]"
    else
      message_value = Google::Protobuf::FFI.get_message_value msg, field_descriptor
      rendered_value = inspect_field(field_descriptor, field_descriptor.send(:c_type), message_value)
      field_output << "#{field_descriptor.name}: #{rendered_value}"
    end
  end
  "<#{name}: #{field_output.join(', ')}>"
end

def self.new(*arguments, &block)

See https://blog.appsignal.com/2018/08/07/ruby-magic-changing-the-way-ruby-creates-objects.html
Great write up of this strategy:
#
def self.new(*arguments, &block)
  raise "Descriptor objects may not be created from Ruby."
end

def self.new(initial_value = nil)

Public constructor. Automatically allocates from a new Arena.
#
def self.new(initial_value = nil)
  instance = allocate
  instance.send(:initialize, initial_value)
  instance
end

def self.private_constructor(msg_def, descriptor_pool)

def self.private_constructor(msg_def, descriptor_pool)
  instance = allocate
  instance.send(:initialize, msg_def, descriptor_pool)
  instance
end

def self.private_constructor(arena, msg: nil, initial_value: nil)

def self.private_constructor(arena, msg: nil, initial_value: nil)
  instance = allocate
  instance.send(:initialize, initial_value, arena, msg)
  instance
end

def self.setup_accessors!

Methods with names that conflict with existing methods are skipped.
Dynamically define accessors methods for every field of @descriptor.
#
def self.setup_accessors!
  @descriptor.each do |field_descriptor|
    field_name = field_descriptor.name
    unless instance_methods(true).include?(field_name.to_sym)
      # Dispatching to either index_internal or get_field is logically
      # correct, but slightly slower due to having to perform extra
      # lookups on each invocation rather than doing it once here.
      if field_descriptor.map?
        define_method(field_name) do
          map_from_field_descriptor field_descriptor
        end
      elsif field_descriptor.repeated?
        define_method(field_name) do
          repeated_field_from_field_descriptor field_descriptor
        end
      elsif field_descriptor.sub_message?
        define_method(field_name) do
          message_from_field_descriptor field_descriptor
        end
      else
        define_method(field_name) do
          scalar_from_field_descriptor field_descriptor
        end
      end
      define_method("#{field_name}=") do |value|
        index_assign_internal(value, field_descriptor: field_descriptor)
      end
      define_method("clear_#{field_name}") do
        clear_internal(field_descriptor)
      end
      if field_descriptor.type == :enum
        if field_descriptor.repeated?
          define_method("#{field_name}_const") do
            return_value = []
            repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
              return_value << msg_val[:int32_val]
            end
            return_value
          end
        else
          define_method("#{field_name}_const") do
            message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
            message_value[:int32_val]
          end
        end
      end
      if !field_descriptor.repeated? and field_descriptor.wrapper?
        define_method("#{field_name}_as_value") do
          get_field(field_descriptor, unwrap: true)
        end
        define_method("#{field_name}_as_value=") do |value|
          if value.nil?
            clear_internal(field_descriptor)
          else
            index_assign_internal(value, field_descriptor: field_descriptor, wrap: true)
          end
        end
      end
      if field_descriptor.has_presence?
        define_method("has_#{field_name}?") do
          Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
        end
      end
    end
  end
end

def self.setup_oneof_accessors!

@descriptor.
Dynamically define accessors methods for every OneOf field of
#
def self.setup_oneof_accessors!
  @oneof_field_names = []
  @descriptor.each_oneof do |oneof_descriptor|
    self.add_oneof_accessors_for! oneof_descriptor
  end
end

def [](name)

should be a string.
Accesses a field's value by field name. The provided field name
Message.[](index) => value
call-seq:
#
def [](name)
  raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
  index_internal name
end

def []=(name, value)

Parameters:
  • value (Object) -- Value to set the field to
  • name (String) -- Name of the field to be set
def []=(name, value)
  raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
  index_assign_internal(value, name: name)
end

def build_message_class

def build_message_class
  descriptor = self
  Class.new(Google::Protobuf::const_get(:AbstractMessage)) do
    @descriptor = descriptor
    class << self
      attr_accessor :descriptor
      private
      attr_accessor :oneof_field_names
      include ::Google::Protobuf::Internal::Convert
    end
    alias original_method_missing method_missing
    def method_missing(method_name, *args)
      method_missing_internal method_name, *args, mode: :method_missing
    end
    def respond_to_missing?(method_name, include_private = false)
      method_missing_internal(method_name, mode: :respond_to_missing?) || super
    end
    ##
    # Public constructor. Automatically allocates from a new Arena.
    def self.new(initial_value = nil)
      instance = allocate
      instance.send(:initialize, initial_value)
      instance
    end
    ##
    # Is this object frozen?
    # Returns true if either this Ruby wrapper or the underlying
    # representation are frozen. Freezes the wrapper if the underlying
    # representation is already frozen but this wrapper isn't.
    def frozen?
      unless Google::Protobuf::FFI.message_frozen? @msg
        raise RuntimeError.new "Ruby frozen Message with mutable representation" if super
        return false
      end
      method(:freeze).super_method.call unless super
      true
    end
    ##
    # Freezes the map object. We have to intercept this so we can freeze the
    # underlying representation, not just the Ruby wrapper. Returns self.
    def freeze
      if method(:frozen?).super_method.call
        unless Google::Protobuf::FFI.message_frozen? @msg
          raise RuntimeError.new "Underlying representation of message still mutable despite frozen wrapper"
        end
        return self
      end
      unless Google::Protobuf::FFI.message_frozen? @msg
        Google::Protobuf::FFI.message_freeze(@msg, Google::Protobuf::FFI.get_mini_table(self.class.descriptor))
      end
      super
   end
    def dup
      duplicate = self.class.private_constructor(@arena)
      mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
      size = mini_table[:size]
      duplicate.instance_variable_get(:@msg).write_string_length(@msg.read_string_length(size), size)
      duplicate
    end
    alias clone dup
    def eql?(other)
      return false unless self.class === other
      encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
      temporary_arena = Google::Protobuf::FFI.create_arena
      mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
      size_one = ::FFI::MemoryPointer.new(:size_t, 1)
      encoding_one = ::FFI::MemoryPointer.new(:pointer, 1)
      encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table, encoding_options, temporary_arena, encoding_one.to_ptr, size_one)
      raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding LHS of `eql?()`" unless encoding_status == :Ok
      size_two = ::FFI::MemoryPointer.new(:size_t, 1)
      encoding_two = ::FFI::MemoryPointer.new(:pointer, 1)
      encoding_status = Google::Protobuf::FFI.encode_message(other.instance_variable_get(:@msg), mini_table, encoding_options, temporary_arena, encoding_two.to_ptr, size_two)
      raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding RHS of `eql?()`" unless encoding_status == :Ok
      if encoding_one.null? or encoding_two.null?
        raise ParseError.new "Error comparing messages"
      end
      size_one.read(:size_t) == size_two.read(:size_t) and Google::Protobuf::FFI.memcmp(encoding_one.read(:pointer), encoding_two.read(:pointer), size_one.read(:size_t)).zero?
    end
    alias == eql?
    def hash
      encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
      temporary_arena = Google::Protobuf::FFI.create_arena
      mini_table_ptr = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
      size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
      encoding = ::FFI::MemoryPointer.new(:pointer, 1)
      encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table_ptr, encoding_options, temporary_arena, encoding.to_ptr, size_ptr)
      if encoding_status != :Ok or encoding.null?
        raise ParseError.new "Error calculating hash"
      end
      encoding.read(:pointer).read_string(size_ptr.read(:size_t)).hash
    end
    def to_h
      to_h_internal @msg, self.class.descriptor
    end
    ##
    # call-seq:
    #     Message.inspect => string
    #
    # Returns a human-readable string representing this message. It will be
    # formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
    # field's value is represented according to its own #inspect method.
    def inspect
      self.class.inspect_internal @msg
    end
    def to_s
      self.inspect
    end
    ##
    # call-seq:
    #     Message.[](index) => value
    # Accesses a field's value by field name. The provided field name
    # should be a string.
    def [](name)
      raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
      index_internal name
    end
    ##
    # call-seq:
    #     Message.[]=(index, value)
    # Sets a field's value by field name. The provided field name should
    # be a string.
    # @param name [String] Name of the field to be set
    # @param value [Object] Value to set the field to
    def []=(name, value)
      raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
      index_assign_internal(value, name: name)
    end
    ##
    # call-seq:
    #    MessageClass.decode(data, options) => message
    #
    # Decodes the given data (as a string containing bytes in protocol buffers wire
    # format) under the interpretation given by this message class's definition
    # and returns a message object with the corresponding field values.
    # @param data [String] Binary string in Protobuf wire format to decode
    # @param options [Hash] options for the decoder
    # @option options [Integer] :recursion_limit Set to maximum decoding depth for message (default is 64)
    def self.decode(data, options = {})
      raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
      raise ArgumentError.new "Expected string for binary protobuf data." unless data.is_a? String
      decoding_options = 0
      depth = options[:recursion_limit]
      if depth.is_a? Numeric
        decoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
      end
      message = new
      mini_table_ptr = Google::Protobuf::FFI.get_mini_table(message.class.descriptor)
      status = Google::Protobuf::FFI.decode_message(
        data,
        data.bytesize,
        message.instance_variable_get(:@msg),
        mini_table_ptr,
        Google::Protobuf::FFI.get_extension_registry(message.class.descriptor.send(:pool).descriptor_pool),
        decoding_options,
        message.instance_variable_get(:@arena)
      )
      raise ParseError.new "Error occurred during parsing" unless status == :Ok
      message
    end
    ##
    # call-seq:
    #    MessageClass.encode(msg, options) => bytes
    #
    # Encodes the given message object to its serialized form in protocol buffers
    # wire format.
    # @param options [Hash] options for the encoder
    # @option options [Integer] :recursion_limit Set to maximum encoding depth for message (default is 64)
    def self.encode(message, options = {})
      raise ArgumentError.new "Message of wrong type." unless message.is_a? self
      raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
      encoding_options = 0
      depth = options[:recursion_limit]
      if depth.is_a? Numeric
        encoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
      end
      encode_internal(message.instance_variable_get(:@msg), encoding_options) do |encoding, size, _|
        if encoding.nil? or encoding.null?
          raise RuntimeError.new "Exceeded maximum depth (possibly cycle)"
        else
          encoding.read_string_length(size).force_encoding("ASCII-8BIT").freeze
        end
      end
    end
    ##
    # all-seq:
    #    MessageClass.decode_json(data, options = {}) => message
    #
    # Decodes the given data (as a string containing bytes in protocol buffers wire
    # format) under the interpretation given by this message class's definition
    # and returns a message object with the corresponding field values.
    #
    # @param options [Hash] options for the decoder
    # @option options [Boolean] :ignore_unknown_fields Set true to ignore unknown fields (default is to raise an error)
    # @return [Message]
    def self.decode_json(data, options = {})
      decoding_options = 0
      unless options.is_a? Hash
        if options.respond_to? :to_h
          options options.to_h
        else
          #TODO can this error message be improve to include what was received?
          raise ArgumentError.new "Expected hash arguments"
        end
      end
      raise ArgumentError.new "Expected string for JSON data." unless data.is_a? String
      raise RuntimeError.new "Cannot parse a wrapper directly" if descriptor.send(:wrapper?)
      if options[:ignore_unknown_fields]
        decoding_options |= Google::Protobuf::FFI::Upb_JsonDecode_IgnoreUnknown
      end
      message = new
      pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
      status = Google::Protobuf::FFI::Status.new
      result = Google::Protobuf::FFI.json_decode_message_detecting_nonconformance(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
      case result
      when Google::Protobuf::FFI::Upb_JsonDecodeResult_Error
        raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
      end
      message
    end
    def self.encode_json(message, options = {})
      encoding_options = 0
      unless options.is_a? Hash
        if options.respond_to? :to_h
          options = options.to_h
        else
          #TODO can this error message be improve to include what was received?
          raise ArgumentError.new "Expected hash arguments"
        end
      end
      if options[:preserve_proto_fieldnames]
        encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_UseProtoNames
      end
      if options[:emit_defaults]
        encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_EmitDefaults
      end
      if options[:format_enums_as_integers]
        encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_FormatEnumsAsIntegers
      end
      buffer_size = 1024
      buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
      status = Google::Protobuf::FFI::Status.new
      msg = message.instance_variable_get(:@msg)
      pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
      size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
      unless status[:ok]
        raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
      end
      if size >= buffer_size
        buffer_size = size + 1
        buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
        status.clear
        size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
        unless status[:ok]
          raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
        end
        if size >= buffer_size
          raise ParseError.new "Inconsistent JSON encoding sizes - was #{buffer_size - 1}, now #{size}"
        end
      end
      buffer.read_string_length(size).force_encoding("UTF-8").freeze
    end
    private
    # Implementation details below are subject to breaking changes without
    # warning and are intended for use only within the gem.
    include Google::Protobuf::Internal::Convert
    ##
    # Checks ObjectCache for a sentinel empty frozen Map of the key and
    # value types matching the field descriptor's MessageDef and returns
    # the cache entry. If an entry is not found, one is created and added
    # to the cache keyed by the MessageDef pointer first.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def empty_frozen_map(field_descriptor)
      sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
      frozen_map = OBJECT_CACHE.get sub_message_def
      if frozen_map.nil?
        frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
        OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
      end
      raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
      frozen_map
    end
    ##
    # Returns a frozen Map instance for the given field. If the message
    # already has a value for that field, it is used. If not, a sentinel
    # (per FieldDescriptor) empty frozen Map is returned instead.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def frozen_map_from_field_descriptor(field_descriptor)
      message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
      return empty_frozen_map field_descriptor if message_value[:map_val].null?
      get_map_field(message_value[:map_val], field_descriptor).freeze
    end
    ##
    # Returns a Map instance for the given field. If the message is frozen
    # the return value is also frozen. If not, a mutable instance is
    # returned instead.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def map_from_field_descriptor(field_descriptor)
      return frozen_map_from_field_descriptor field_descriptor if frozen?
      mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
      get_map_field(mutable_message_value[:map], field_descriptor)
    end
    ##
    # Checks ObjectCache for a sentinel empty frozen RepeatedField of the
    # value type matching the field descriptor's MessageDef and returns
    # the cache entry. If an entry is not found, one is created and added
    # to the cache keyed by the MessageDef pointer first.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def empty_frozen_repeated_field(field_descriptor)
      sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
      frozen_repeated_field = OBJECT_CACHE.get sub_message_def
      if frozen_repeated_field.nil?
        frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
        OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
      end
      raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
      frozen_repeated_field
    end
    ##
    # Returns a frozen RepeatedField instance for the given field. If the
    # message already has a value for that field, it is used. If not, a
    # sentinel (per FieldDescriptor) empty frozen RepeatedField is
    # returned instead.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def frozen_repeated_field_from_field_descriptor(field_descriptor)
      message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
      return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
      get_repeated_field(message_value[:array_val], field_descriptor).freeze
    end
    ##
    # Returns a RepeatedField instance for the given field. If the message
    # is frozen the return value is also frozen. If not, a mutable
    # instance is returned instead.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def repeated_field_from_field_descriptor(field_descriptor)
      return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
      mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
      get_repeated_field(mutable_message_value[:array], field_descriptor)
    end
    ##
    # Returns a Message instance for the given field. If the message
    # is frozen nil is always returned. Otherwise, a mutable instance is
    # returned instead.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def message_from_field_descriptor(field_descriptor)
      return nil if frozen?
      return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
      mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
      sub_message = mutable_message[:msg]
      sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
      Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
    end
    ##
    # Returns a scalar value for the given field. If the message
    # is frozen the return value is also frozen.
    # @param field_descriptor [FieldDescriptor] Field to retrieve.
    def scalar_from_field_descriptor(field_descriptor)
      c_type = field_descriptor.send(:c_type)
      message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
      msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
      return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
      frozen? ? return_value.freeze : return_value
    end
    ##
    # Dynamically define accessors methods for every field of @descriptor.
    # Methods with names that conflict with existing methods are skipped.
    def self.setup_accessors!
      @descriptor.each do |field_descriptor|
        field_name = field_descriptor.name
        unless instance_methods(true).include?(field_name.to_sym)
          # Dispatching to either index_internal or get_field is logically
          # correct, but slightly slower due to having to perform extra
          # lookups on each invocation rather than doing it once here.
          if field_descriptor.map?
            define_method(field_name) do
              map_from_field_descriptor field_descriptor
            end
          elsif field_descriptor.repeated?
            define_method(field_name) do
              repeated_field_from_field_descriptor field_descriptor
            end
          elsif field_descriptor.sub_message?
            define_method(field_name) do
              message_from_field_descriptor field_descriptor
            end
          else
            define_method(field_name) do
              scalar_from_field_descriptor field_descriptor
            end
          end
          define_method("#{field_name}=") do |value|
            index_assign_internal(value, field_descriptor: field_descriptor)
          end
          define_method("clear_#{field_name}") do
            clear_internal(field_descriptor)
          end
          if field_descriptor.type == :enum
            if field_descriptor.repeated?
              define_method("#{field_name}_const") do
                return_value = []
                repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
                  return_value << msg_val[:int32_val]
                end
                return_value
              end
            else
              define_method("#{field_name}_const") do
                message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
                message_value[:int32_val]
              end
            end
          end
          if !field_descriptor.repeated? and field_descriptor.wrapper?
            define_method("#{field_name}_as_value") do
              get_field(field_descriptor, unwrap: true)
            end
            define_method("#{field_name}_as_value=") do |value|
              if value.nil?
                clear_internal(field_descriptor)
              else
                index_assign_internal(value, field_descriptor: field_descriptor, wrap: true)
              end
            end
          end
          if field_descriptor.has_presence?
            define_method("has_#{field_name}?") do
              Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
            end
          end
        end
      end
    end
    ##
    # Dynamically define accessors methods for every OneOf field of
    # @descriptor.
    def self.setup_oneof_accessors!
      @oneof_field_names = []
      @descriptor.each_oneof do |oneof_descriptor|
        self.add_oneof_accessors_for! oneof_descriptor
      end
    end
    ##
    # Dynamically define accessors methods for the given OneOf field.
    # Methods with names that conflict with existing methods are skipped.
    # @param oneof_descriptor [OneofDescriptor] Field to create accessors
    # for.
    def self.add_oneof_accessors_for!(oneof_descriptor)
      field_name = oneof_descriptor.name.to_sym
      @oneof_field_names << field_name
      unless instance_methods(true).include?(field_name)
        define_method(field_name) do
          field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
          if field_descriptor.nil?
            return
          else
            return field_descriptor.name.to_sym
          end
        end
        define_method("clear_#{field_name}") do
          field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
          unless field_descriptor.nil?
            clear_internal(field_descriptor)
          end
        end
        define_method("has_#{field_name}?") do
          !Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor).nil?
        end
      end
    end
    setup_accessors!
    setup_oneof_accessors!
    def self.private_constructor(arena, msg: nil, initial_value: nil)
      instance = allocate
      instance.send(:initialize, initial_value, arena, msg)
      instance
    end
    def self.inspect_field(field_descriptor, c_type, message_value)
      if field_descriptor.sub_message?
        sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
        sub_msg_descriptor.msgclass.send(:inspect_internal, message_value[:msg_val])
      else
        convert_upb_to_ruby(message_value, c_type, field_descriptor.subtype).inspect
      end
    end
    # @param msg [::FFI::Pointer] Pointer to the Message
    def self.inspect_internal(msg)
      field_output = []
      descriptor.each do |field_descriptor|
        next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
        if field_descriptor.map?
          # TODO Adapted - from map#each_msg_val and map#inspect- can this be refactored to reduce echo without introducing a arena allocation?
          message_descriptor = field_descriptor.subtype
          key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
          key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
          value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
          value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
          message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
          iter = ::FFI::MemoryPointer.new(:size_t, 1)
          iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
          key_value_pairs = []
          while Google::Protobuf::FFI.map_next(message_value[:map_val], iter) do
            iter_size_t = iter.read(:size_t)
            key_message_value = Google::Protobuf::FFI.map_key(message_value[:map_val], iter_size_t)
            value_message_value = Google::Protobuf::FFI.map_value(message_value[:map_val], iter_size_t)
            key_string = convert_upb_to_ruby(key_message_value, key_field_type).inspect
            value_string = inspect_field(value_field_def, value_field_type, value_message_value)
            key_value_pairs << "#{key_string}=>#{value_string}"
          end
          field_output << "#{field_descriptor.name}: {#{key_value_pairs.join(", ")}}"
        elsif field_descriptor.repeated?
          # TODO Adapted - from repeated_field#each - can this be refactored to reduce echo?
          repeated_field_output = []
          message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
          array = message_value[:array_val]
          n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
          0.upto(n - 1) do |i|
            element = Google::Protobuf::FFI.get_msgval_at(array, i)
            repeated_field_output << inspect_field(field_descriptor, field_descriptor.send(:c_type), element)
          end
          field_output << "#{field_descriptor.name}: [#{repeated_field_output.join(", ")}]"
        else
          message_value = Google::Protobuf::FFI.get_message_value msg, field_descriptor
          rendered_value = inspect_field(field_descriptor, field_descriptor.send(:c_type), message_value)
          field_output << "#{field_descriptor.name}: #{rendered_value}"
        end
      end
      "<#{name}: #{field_output.join(', ')}>"
    end
    def self.deep_copy(msg, arena = nil)
      arena ||= Google::Protobuf::FFI.create_arena
      encode_internal(msg) do |encoding, size, mini_table_ptr|
        message = private_constructor(arena)
        if encoding.nil? or encoding.null? or Google::Protobuf::FFI.decode_message(encoding, size, message.instance_variable_get(:@msg), mini_table_ptr, nil, 0, arena) != :Ok
          raise ParseError.new "Error occurred copying proto"
        end
        message
      end
    end
    def self.encode_internal(msg, encoding_options = 0)
      temporary_arena = Google::Protobuf::FFI.create_arena
      mini_table_ptr = Google::Protobuf::FFI.get_mini_table(descriptor)
      size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
      pointer_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
      encoding_status = Google::Protobuf::FFI.encode_message(msg, mini_table_ptr, encoding_options, temporary_arena, pointer_ptr.to_ptr, size_ptr)
      raise "Encoding failed due to #{encoding_status}" unless encoding_status == :Ok
      yield pointer_ptr.read(:pointer), size_ptr.read(:size_t), mini_table_ptr
    end
    def method_missing_internal(method_name, *args, mode: nil)
      raise ArgumentError.new "method_missing_internal called with invalid mode #{mode.inspect}" unless [:respond_to_missing?, :method_missing].include? mode
      #TODO not being allowed is not the same thing as not responding, but this is needed to pass tests
      if method_name.to_s.end_with? '='
        if self.class.send(:oneof_field_names).include? method_name.to_s[0..-2].to_sym
          return false if mode == :respond_to_missing?
          raise RuntimeError.new "Oneof accessors are read-only."
        end
      end
      original_method_missing(method_name, *args) if mode == :method_missing
    end
    def clear_internal(field_def)
      raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
      Google::Protobuf::FFI.clear_message_field(@msg, field_def)
    end
    # Accessor for field by name. Does not delegate to methods setup by
    # self.setup_accessors! in order to avoid conflicts with bad field
    # names e.g. `dup` or `class` which are perfectly valid for proto
    # fields.
    def index_internal(name)
      field_descriptor = self.class.descriptor.lookup(name)
      get_field field_descriptor unless field_descriptor.nil?
    end
    #TODO - well known types keeps us on our toes by overloading methods.
    # How much of the public API needs to be defended?
    def index_assign_internal(value, name: nil, field_descriptor: nil, wrap: false)
      raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
      if field_descriptor.nil?
        field_descriptor = self.class.descriptor.lookup(name)
        if field_descriptor.nil?
          raise ArgumentError.new "Unknown field: #{name}"
        end
      end
      unless field_descriptor.send :set_value_on_message, value, @msg, @arena, wrap: wrap
        raise RuntimeError.new "allocation failed"
      end
    end
    ##
    # @param initial_value [Object] initial value of this Message
    # @param arena [Arena] Optional; Arena where this message will be allocated
    # @param msg [::FFI::Pointer] Optional; Message to initialize; creates
    #   one if omitted or nil.
    def initialize(initial_value = nil, arena = nil, msg = nil)
      @arena = arena || Google::Protobuf::FFI.create_arena
      @msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
      unless initial_value.nil?
        raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
        field_def_ptr = ::FFI::MemoryPointer.new :pointer
        oneof_def_ptr = ::FFI::MemoryPointer.new :pointer
        initial_value.each do |key, value|
          raise ArgumentError.new "Expected string or symbols as hash keys when initializing proto from hash." unless [String, Symbol].include? key.class
          unless Google::Protobuf::FFI.find_msg_def_by_name self.class.descriptor, key.to_s, key.to_s.bytesize, field_def_ptr, oneof_def_ptr
            raise ArgumentError.new "Unknown field name '#{key}' in initialization map entry."
          end
          raise NotImplementedError.new "Haven't added oneofsupport yet" unless oneof_def_ptr.get_pointer(0).null?
          raise NotImplementedError.new "Expected a field def" if field_def_ptr.get_pointer(0).null?
          field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
          next if value.nil?
          if field_descriptor.map?
            index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
          elsif field_descriptor.repeated?
            index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
          else
            index_assign_internal(value, name: key.to_s)
          end
        end
      end
      # Should always be the last expression of the initializer to avoid
      # leaking references to this object before construction is complete.
      Google::Protobuf::OBJECT_CACHE.try_add @msg.address, self
    end
    ##
    # Gets a field of this message identified by FieldDescriptor.
    #
    # @param field [FieldDescriptor] Field to retrieve.
    # @param unwrap [Boolean](false) If true, unwraps wrappers.
    def get_field(field, unwrap: false)
      if field.map?
        map_from_field_descriptor field
      elsif field.repeated?
        repeated_field_from_field_descriptor field
      elsif field.sub_message?
        return nil unless Google::Protobuf::FFI.get_message_has @msg, field
        if unwrap
          if field.has?(self)
            sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
            wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
            fields = Google::Protobuf::FFI.field_count(sub_message_def)
            raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
            value_field_def = Google::Protobuf::FFI.get_field_by_number sub_message_def, 1
            message_value = Google::Protobuf::FFI.get_message_value wrapper_message_value[:msg_val], value_field_def
            convert_upb_to_ruby message_value, Google::Protobuf::FFI.get_c_type(value_field_def)
          else
            nil
          end
        else
          message_from_field_descriptor field
        end
      else
        scalar_from_field_descriptor field
      end
    end
    ##
    # Gets a RepeatedField from the ObjectCache or creates a new one.
    # @param array [::FFI::Pointer] Pointer to the upb_Array
    # @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
    def get_repeated_field(array, field)
      return nil if array.nil? or array.null?
      repeated_field = OBJECT_CACHE.get(array.address)
      if repeated_field.nil?
        repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
        repeated_field.freeze if frozen?
      end
      repeated_field
    end
    ##
    # Gets a Map from the ObjectCache or creates a new one.
    # @param map [::FFI::Pointer] Pointer to the upb_Map
    # @param field [Google::Protobuf::FieldDescriptor] Type of the map field
    def get_map_field(map, field)
      return nil if map.nil? or map.null?
      map_field = OBJECT_CACHE.get(map.address)
      if map_field.nil?
        map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
        map_field.freeze if frozen?
      end
      map_field
    end
  end
end

def clear_internal(field_def)

def clear_internal(field_def)
  raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
  Google::Protobuf::FFI.clear_message_field(@msg, field_def)
end

def dup

def dup
  duplicate = self.class.private_constructor(@arena)
  mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
  size = mini_table[:size]
  duplicate.instance_variable_get(:@msg).write_string_length(@msg.read_string_length(size), size)
  duplicate
end

def each &block

def each &block
  n = Google::Protobuf::FFI.field_count(self)
  0.upto(n-1) do |i|
    yield(Google::Protobuf::FFI.get_field_by_index(self, i))
  end
  nil
end

def each_oneof &block

def each_oneof &block
  n = Google::Protobuf::FFI.oneof_count(self)
  0.upto(n-1) do |i|
    yield(Google::Protobuf::FFI.get_oneof_by_index(self, i))
  end
  nil
end

def empty_frozen_map(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def empty_frozen_map(field_descriptor)
  sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
  frozen_map = OBJECT_CACHE.get sub_message_def
  if frozen_map.nil?
    frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
    OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
  end
  raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
  frozen_map
end

def empty_frozen_repeated_field(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def empty_frozen_repeated_field(field_descriptor)
  sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
  frozen_repeated_field = OBJECT_CACHE.get sub_message_def
  if frozen_repeated_field.nil?
    frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
    OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
  end
  raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
  frozen_repeated_field
end

def eql?(other)

def eql?(other)
  return false unless self.class === other
  encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
  temporary_arena = Google::Protobuf::FFI.create_arena
  mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
  size_one = ::FFI::MemoryPointer.new(:size_t, 1)
  encoding_one = ::FFI::MemoryPointer.new(:pointer, 1)
  encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table, encoding_options, temporary_arena, encoding_one.to_ptr, size_one)
  raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding LHS of `eql?()`" unless encoding_status == :Ok
  size_two = ::FFI::MemoryPointer.new(:size_t, 1)
  encoding_two = ::FFI::MemoryPointer.new(:pointer, 1)
  encoding_status = Google::Protobuf::FFI.encode_message(other.instance_variable_get(:@msg), mini_table, encoding_options, temporary_arena, encoding_two.to_ptr, size_two)
  raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding RHS of `eql?()`" unless encoding_status == :Ok
  if encoding_one.null? or encoding_two.null?
    raise ParseError.new "Error comparing messages"
  end
  size_one.read(:size_t) == size_two.read(:size_t) and Google::Protobuf::FFI.memcmp(encoding_one.read(:pointer), encoding_two.read(:pointer), size_one.read(:size_t)).zero?
end

def file_descriptor

def file_descriptor
  @descriptor_pool.send(:get_file_descriptor, Google::Protobuf::FFI.get_message_file_def(@msg_def))
end

def freeze

underlying representation, not just the Ruby wrapper. Returns self.
Freezes the map object. We have to intercept this so we can freeze the
#
def freeze
  if method(:frozen?).super_method.call
    unless Google::Protobuf::FFI.message_frozen? @msg
      raise RuntimeError.new "Underlying representation of message still mutable despite frozen wrapper"
    end
    return self
  end
  unless Google::Protobuf::FFI.message_frozen? @msg
    Google::Protobuf::FFI.message_freeze(@msg, Google::Protobuf::FFI.get_mini_table(self.class.descriptor))
  end
  super
nd

def from_native(msg_def, _ = nil)

Parameters:
  • _ (Object) -- Unused
  • msg_def (::FFI::Pointer) -- MsgDef pointer to be wrapped
def from_native(msg_def, _ = nil)
  return nil if msg_def.nil? or msg_def.null?
  file_def = Google::Protobuf::FFI.get_message_file_def msg_def
  descriptor_from_file_def(file_def, msg_def)
end

def frozen?

representation is already frozen but this wrapper isn't.
representation are frozen. Freezes the wrapper if the underlying
Returns true if either this Ruby wrapper or the underlying
Is this object frozen?
#
def frozen?
  unless Google::Protobuf::FFI.message_frozen? @msg
    raise RuntimeError.new "Ruby frozen Message with mutable representation" if super
    return false
  end
  method(:freeze).super_method.call unless super
  true
end

def frozen_map_from_field_descriptor(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def frozen_map_from_field_descriptor(field_descriptor)
  message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
  return empty_frozen_map field_descriptor if message_value[:map_val].null?
  get_map_field(message_value[:map_val], field_descriptor).freeze
end

def frozen_repeated_field_from_field_descriptor(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def frozen_repeated_field_from_field_descriptor(field_descriptor)
  message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
  return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
  get_repeated_field(message_value[:array_val], field_descriptor).freeze
end

def get_field(field, unwrap: false)

Parameters:
  • unwrap (Boolean) -- (false) If true, unwraps wrappers.
  • field (FieldDescriptor) -- Field to retrieve.
def get_field(field, unwrap: false)
  if field.map?
    map_from_field_descriptor field
  elsif field.repeated?
    repeated_field_from_field_descriptor field
  elsif field.sub_message?
    return nil unless Google::Protobuf::FFI.get_message_has @msg, field
    if unwrap
      if field.has?(self)
        sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
        wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
        fields = Google::Protobuf::FFI.field_count(sub_message_def)
        raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
        value_field_def = Google::Protobuf::FFI.get_field_by_number sub_message_def, 1
        message_value = Google::Protobuf::FFI.get_message_value wrapper_message_value[:msg_val], value_field_def
        convert_upb_to_ruby message_value, Google::Protobuf::FFI.get_c_type(value_field_def)
      else
        nil
      end
    else
      message_from_field_descriptor field
    end
  else
    scalar_from_field_descriptor field
  end
end

def get_map_field(map, field)

Parameters:
  • field (Google::Protobuf::FieldDescriptor) -- Type of the map field
  • map (::FFI::Pointer) -- Pointer to the upb_Map
def get_map_field(map, field)
  return nil if map.nil? or map.null?
  map_field = OBJECT_CACHE.get(map.address)
  if map_field.nil?
    map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
    map_field.freeze if frozen?
  end
  map_field
end

def get_repeated_field(array, field)

Parameters:
  • field (Google::Protobuf::FieldDescriptor) -- Type of the repeated field
  • array (::FFI::Pointer) -- Pointer to the upb_Array
def get_repeated_field(array, field)
  return nil if array.nil? or array.null?
  repeated_field = OBJECT_CACHE.get(array.address)
  if repeated_field.nil?
    repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
    repeated_field.freeze if frozen?
  end
  repeated_field
end

def hash

def hash
  encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
  temporary_arena = Google::Protobuf::FFI.create_arena
  mini_table_ptr = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
  size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
  encoding = ::FFI::MemoryPointer.new(:pointer, 1)
  encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table_ptr, encoding_options, temporary_arena, encoding.to_ptr, size_ptr)
  if encoding_status != :Ok or encoding.null?
    raise ParseError.new "Error calculating hash"
  end
  encoding.read(:pointer).read_string(size_ptr.read(:size_t)).hash
end

def index_assign_internal(value, name: nil, field_descriptor: nil, wrap: false)

How much of the public API needs to be defended?
TODO - well known types keeps us on our toes by overloading methods.
def index_assign_internal(value, name: nil, field_descriptor: nil, wrap: false)
  raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
  if field_descriptor.nil?
    field_descriptor = self.class.descriptor.lookup(name)
    if field_descriptor.nil?
      raise ArgumentError.new "Unknown field: #{name}"
    end
  end
  unless field_descriptor.send :set_value_on_message, value, @msg, @arena, wrap: wrap
    raise RuntimeError.new "allocation failed"
  end
end

def index_internal(name)

fields.
names e.g. `dup` or `class` which are perfectly valid for proto
self.setup_accessors! in order to avoid conflicts with bad field
Accessor for field by name. Does not delegate to methods setup by
def index_internal(name)
  field_descriptor = self.class.descriptor.lookup(name)
  get_field field_descriptor unless field_descriptor.nil?
end

def initialize(msg_def, descriptor_pool)

def initialize(msg_def, descriptor_pool)
  @msg_def = msg_def
  @msg_class = nil
  @descriptor_pool = descriptor_pool
end

def initialize(initial_value = nil, arena = nil, msg = nil)

Parameters:
  • msg (::FFI::Pointer) -- Optional; Message to initialize; creates
  • arena (Arena) -- Optional; Arena where this message will be allocated
  • initial_value (Object) -- initial value of this Message
def initialize(initial_value = nil, arena = nil, msg = nil)
  @arena = arena || Google::Protobuf::FFI.create_arena
  @msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
  unless initial_value.nil?
    raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
    field_def_ptr = ::FFI::MemoryPointer.new :pointer
    oneof_def_ptr = ::FFI::MemoryPointer.new :pointer
    initial_value.each do |key, value|
      raise ArgumentError.new "Expected string or symbols as hash keys when initializing proto from hash." unless [String, Symbol].include? key.class
      unless Google::Protobuf::FFI.find_msg_def_by_name self.class.descriptor, key.to_s, key.to_s.bytesize, field_def_ptr, oneof_def_ptr
        raise ArgumentError.new "Unknown field name '#{key}' in initialization map entry."
      end
      raise NotImplementedError.new "Haven't added oneofsupport yet" unless oneof_def_ptr.get_pointer(0).null?
      raise NotImplementedError.new "Expected a field def" if field_def_ptr.get_pointer(0).null?
      field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
      next if value.nil?
      if field_descriptor.map?
        index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
      elsif field_descriptor.repeated?
        index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
      else
        index_assign_internal(value, name: key.to_s)
      end
    end
  end
  # Should always be the last expression of the initializer to avoid
  # leaking references to this object before construction is complete.
  Google::Protobuf::OBJECT_CACHE.try_add @msg.address, self
end

def inspect

def inspect
  "Descriptor - (not the message class) #{name}"
end

def inspect

field's value is represented according to its own #inspect method.
formatted as "". Each
Returns a human-readable string representing this message. It will be

Message.inspect => string
call-seq:
#
def inspect
  self.class.inspect_internal @msg
end

def lookup(name)

def lookup(name)
  Google::Protobuf::FFI.get_field_by_name(self, name, name.size)
end

def lookup_oneof(name)

def lookup_oneof(name)
  Google::Protobuf::FFI.get_oneof_by_name(self, name, name.size)
end

def map_from_field_descriptor(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def map_from_field_descriptor(field_descriptor)
  return frozen_map_from_field_descriptor field_descriptor if frozen?
  mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
  get_map_field(mutable_message_value[:map], field_descriptor)
end

def message_from_field_descriptor(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def message_from_field_descriptor(field_descriptor)
  return nil if frozen?
  return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
  mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
  sub_message = mutable_message[:msg]
  sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
  Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
end

def method_missing(method_name, *args)

def method_missing(method_name, *args)
  method_missing_internal method_name, *args, mode: :method_missing
end

def method_missing_internal(method_name, *args, mode: nil)

def method_missing_internal(method_name, *args, mode: nil)
  raise ArgumentError.new "method_missing_internal called with invalid mode #{mode.inspect}" unless [:respond_to_missing?, :method_missing].include? mode
  #TODO not being allowed is not the same thing as not responding, but this is needed to pass tests
  if method_name.to_s.end_with? '='
    if self.class.send(:oneof_field_names).include? method_name.to_s[0..-2].to_sym
      return false if mode == :respond_to_missing?
      raise RuntimeError.new "Oneof accessors are read-only."
    end
  end
  original_method_missing(method_name, *args) if mode == :method_missing
end

def msgclass

def msgclass
  @msg_class ||= build_message_class
end

def name

def name
  @name ||= Google::Protobuf::FFI.get_message_fullname(self)
end

def options

def options
  @options ||= begin
    size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
    temporary_arena = Google::Protobuf::FFI.create_arena
    buffer = Google::Protobuf::FFI.message_options(self, size_ptr, temporary_arena)
    opts = Google::Protobuf::MessageOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
    opts.clear_features()
    opts.freeze
  end
end

def pool

def pool
  @descriptor_pool
end

def repeated_field_from_field_descriptor(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def repeated_field_from_field_descriptor(field_descriptor)
  return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
  mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
  get_repeated_field(mutable_message_value[:array], field_descriptor)
end

def respond_to_missing?(method_name, include_private = false)

def respond_to_missing?(method_name, include_private = false)
  method_missing_internal(method_name, mode: :respond_to_missing?) || super
end

def scalar_from_field_descriptor(field_descriptor)

Parameters:
  • field_descriptor (FieldDescriptor) -- Field to retrieve.
def scalar_from_field_descriptor(field_descriptor)
  c_type = field_descriptor.send(:c_type)
  message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
  msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
  return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
  frozen? ? return_value.freeze : return_value
end

def to_h

def to_h
  to_h_internal @msg, self.class.descriptor
end

def to_native(value, _ = nil)

Parameters:
  • _ (Object) -- Unused
  • value (Descriptor) -- Descriptor to convert to an FFI native type
def to_native(value, _ = nil)
  msg_def_ptr = value.nil? ? nil : value.instance_variable_get(:@msg_def)
  return ::FFI::Pointer::NULL if msg_def_ptr.nil?
  raise "Underlying msg_def was null!" if msg_def_ptr.null?
  msg_def_ptr
end

def to_native

def to_native
  self.class.to_native(self)
end

def to_proto

def to_proto
  @to_proto ||= begin
    size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
    temporary_arena = Google::Protobuf::FFI.create_arena
    buffer = Google::Protobuf::FFI.message_to_proto(self, size_ptr, temporary_arena)
    Google::Protobuf::DescriptorProto.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
  end
end

def to_s

def to_s
  inspect
end

def to_s

def to_s
  self.inspect
end

def wrapper?

def wrapper?
  if defined? @wrapper
    @wrapper
  else
    @wrapper = case Google::Protobuf::FFI.get_well_known_type self
    when :DoubleValue, :FloatValue, :Int64Value, :UInt64Value, :Int32Value, :UInt32Value, :StringValue, :BytesValue, :BoolValue
      true
    else
      false
    end
  end
end