class Google::Protobuf::RepeatedField

Attributes

name[RW]

Public Class Methods

new(type, type_class = nil, initial_values = []) click to toggle source

Creates a new repeated field. The provided type must be a Ruby symbol, and an take on the same values as those accepted by FieldDescriptor#type=. If the type is :message or :enum, type_class must be non-nil, and must be the Ruby class or module returned by Descriptor#msgclass or EnumDescriptor#enummodule, respectively. An initial list of elements may also be provided.

# File lib/google/protobuf/ffi/repeated_field.rb, line 50
def self.new(type, type_class = nil, initial_values = [])
  instance = allocate
  # TODO This argument mangling doesn't agree with the type signature in the comments
  # but is required to make unit tests pass;
  if type_class.is_a?(Enumerable) and initial_values.empty? and ![:enum, :message].include?(type)
    initial_values = type_class
    type_class = nil
  end
  instance.send(:initialize, type, type_class: type_class, initial_values: initial_values)
  instance
end
new(type, type_class: nil, initial_values: nil, name: nil, arena: nil, array: nil, descriptor: nil) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 342
def initialize(type, type_class: nil, initial_values: nil, name: nil, arena: nil, array: nil, descriptor: nil)
  @name = name || 'RepeatedField'
  raise ArgumentError.new "Expected argument type to be a Symbol" unless type.is_a? Symbol
  field_number = Google::Protobuf::FFI::FieldType[type]
  raise ArgumentError.new "Unsupported type '#{type}'" if field_number.nil?
  if !descriptor.nil?
    @descriptor = descriptor
  elsif [:message, :enum].include? type
    raise ArgumentError.new "Expected at least 2 arguments for message/enum." if type_class.nil?
    descriptor = type_class.respond_to?(:descriptor) ? type_class.descriptor : nil
    raise ArgumentError.new "Type class #{type_class} has no descriptor. Please pass a class or enum as returned by the DescriptorPool." if descriptor.nil?
    @descriptor = descriptor
  else
    @descriptor = nil
  end
  @type = type

  @arena = arena || Google::Protobuf::FFI.create_arena
  @array = array || Google::Protobuf::FFI.create_array(@arena, @type)
  unless initial_values.nil?
    unless initial_values.is_a? Enumerable
      raise ArgumentError.new "Expected array as initializer value for repeated field '#{name}' (given #{initial_values.class})."
    end
    internal_push(*initial_values)
  end

  # Should always be the last expression of the initializer to avoid
  # leaking references to this object before construction is complete.
  OBJECT_CACHE.try_add(@array.address, self)
end

Private Class Methods

construct_for_field(field, arena: nil, values: nil, array: nil) click to toggle source

Constructor that uses the type information from the given FieldDescriptor to configure the new RepeatedField instance. @param field [FieldDescriptor] Type information for the new RepeatedField @param arena [Arena] Owning message’s arena @param values [Enumerable] Initial values @param array [::FFI::Pointer] Existing upb_Array

# File lib/google/protobuf/ffi/repeated_field.rb, line 380
def self.construct_for_field(field, arena: nil, values: nil, array: nil)
  instance = allocate
  options = {initial_values: values, name: field.name, arena: arena, array: array}
  if [:enum, :message].include? field.type
    options[:descriptor] = field.subtype
  end
  instance.send(:initialize, field.type, **options)
  instance
end
deep_copy(repeated_field) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 396
def self.deep_copy(repeated_field)
  instance = allocate
  instance.send(:initialize, repeated_field.send(:type), descriptor: repeated_field.send(:descriptor))
  instance.send(:resize, repeated_field.length)
  new_array = instance.send(:array)
  repeated_field.send(:each_msg_val_with_index) do |element, i|
    Google::Protobuf::FFI.array_set(new_array, i, message_value_deep_copy(element, repeated_field.send(:type), repeated_field.send(:descriptor), instance.send(:arena)))
  end
  instance
end
define_array_wrapper_method(method_name) click to toggle source
# File lib/google/protobuf/repeated_field.rb, line 103
def define_array_wrapper_method(method_name)
  define_method(method_name) do |*args, &block|
    arr = self.to_a
    result = arr.send(method_name, *args)
    self.replace(arr)
    return result if result
    return block ? block.call : result
  end
end
define_array_wrapper_with_result_method(method_name) click to toggle source
# File lib/google/protobuf/repeated_field.rb, line 115
def define_array_wrapper_with_result_method(method_name)
  define_method(method_name) do |*args, &block|
    # result can be an Enumerator, Array, or nil
    # Enumerator can sometimes be returned if a block is an optional argument and it is not passed in
    # nil usually specifies that no change was made
    result = self.to_a.send(method_name, *args, &block)
    if result
      new_arr = result.to_a
      self.replace(new_arr)
      if result.is_a?(Enumerator)
        # generate a fresh enum; rewinding the exiting one, in Ruby 2.2, will
        # reset the enum with the same length, but all the #next calls will
        # return nil
        result = new_arr.to_enum
        # generate a wrapper enum so any changes which occur by a chained
        # enum can be captured
        ie = ProxyingEnumerator.new(self, result)
        result = ie.to_enum
      end
    end
    result
  end
end

Public Instance Methods

+(other) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 260
def +(other)
  if other.is_a? RepeatedField
    if type != other.instance_variable_get(:@type) or descriptor != other.instance_variable_get(:@descriptor)
      raise ArgumentError.new "Attempt to append RepeatedField with different element type."
    end
    fuse_arena(other.send(:arena))
    super_set = dup
    other.send(:each_msg_val) do |msg_val|
      super_set.send(:append_msg_val, msg_val)
    end
    super_set
  elsif other.is_a? Enumerable
    super_set = dup
    super_set.push(*other.to_a)
  else
    raise ArgumentError.new "Unknown type appending to RepeatedField"
  end
end
<<(element) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 157
def <<(element)
  raise FrozenError if frozen?
  push element
end
==(other) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 221
def ==(other)
  return true if other.object_id == object_id
  if other.is_a? RepeatedField
    return false unless other.length == length
    each_msg_val_with_index do |msg_val, i|
      other_msg_val = Google::Protobuf::FFI.get_msgval_at(other.send(:array), i)
      unless Google::Protobuf::FFI.message_value_equal(msg_val, other_msg_val, type, descriptor)
        return false
      end
    end
    return true
  elsif other.is_a? Enumerable
    return to_ary == other.to_a
  end
  false
end
[](*args) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 76
def [](*args)
  count = length
  if args.size < 1
    raise ArgumentError.new "Index or range is a required argument."
  end
  if args[0].is_a? Range
    if args.size > 1
      raise ArgumentError.new "Expected 1 when passing Range argument, but got #{args.size}"
    end
    range = args[0]
    # Handle begin-less and/or endless ranges, when supported.
    index_of_first = range.respond_to?(:begin) ? range.begin : range.last
    index_of_first = 0 if index_of_first.nil?
    end_of_range = range.respond_to?(:end) ? range.end : range.last
    index_of_last = end_of_range.nil? ? -1 : end_of_range

    if index_of_last < 0
      index_of_last += count
    end
    unless range.exclude_end? and !end_of_range.nil?
      index_of_last += 1
    end
    index_of_first += count if index_of_first < 0
    length = index_of_last - index_of_first
    return [] if length.zero?
  elsif args[0].is_a? Integer
    index_of_first = args[0]
    index_of_first += count if index_of_first < 0
    if args.size > 2
      raise ArgumentError.new "Expected 1 or 2 arguments, but got #{args.size}"
    end
    if args.size == 1 # No length specified, return one element
      if array.null? or index_of_first < 0 or index_of_first >= count
        return nil
      else
        return convert_upb_to_ruby(Google::Protobuf::FFI.get_msgval_at(array, index_of_first), type, descriptor, arena)
      end
    else
      length = [args[1],count].min
    end
  else
    raise NotImplementedError
  end

  if array.null? or index_of_first < 0 or index_of_first >= count
    nil
  else
    if index_of_first + length > count
      length = count - index_of_first
    end
    if length < 0
      nil
    else
      subarray(index_of_first, length)
    end
  end
end
Also aliased as: at, slice
[]=(index, value) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 136
def []=(index, value)
  raise FrozenError if frozen?
  count = length
  index += count if index < 0
  return nil if index < 0
  if index >= count
    resize(index+1)
    empty_message_value = Google::Protobuf::FFI::MessageValue.new # Implicitly clear
    count.upto(index-1) do |i|
      Google::Protobuf::FFI.array_set(array, i, empty_message_value)
    end
  end
  Google::Protobuf::FFI.array_set(array, index, convert_ruby_to_upb(value, arena, type, descriptor))
  nil
end
at(*args)
Alias for: []
clear() click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 168
def clear
  raise FrozenError if frozen?
  resize 0
  self
end
clone()
Alias for: dup
concat(other) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 279
def concat(other)
  raise ArgumentError.new "Expected Enumerable, but got #{other.class}" unless other.is_a? Enumerable
  push(*other.to_a)
end
dup() click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 211
def dup
  instance = self.class.allocate
  instance.send(:initialize, type, descriptor: descriptor, arena: arena)
  each_msg_val do |element|
    instance.send(:append_msg_val, element)
  end
  instance
end
Also aliased as: clone
each(&block) click to toggle source

Invokes the block once for each element of the repeated field. RepeatedField also includes Enumerable; combined with this method, the repeated field thus acts like an ordinary Ruby sequence.

# File lib/google/protobuf/ffi/repeated_field.rb, line 69
def each &block
  each_msg_val do |element|
    yield(convert_upb_to_ruby(element, type, descriptor, arena))
  end
  self
end
empty?() click to toggle source
# File lib/google/protobuf/repeated_field.rb, line 92
def empty?
  self.size == 0
end
first(n=nil) click to toggle source
# File lib/google/protobuf/repeated_field.rb, line 58
def first(n=nil)
  if n.nil?
    return self[0]
  elsif n < 0
    raise ArgumentError, "negative array size"
  else
    return self[0...n]
  end
end
freeze() click to toggle source

Freezes the RepeatedField object. We have to intercept this so we can freeze the underlying representation, not just the Ruby wrapper. Returns self.

Calls superclass method
# File lib/google/protobuf/ffi/repeated_field.rb, line 197
def freeze
  if method(:frozen?).super_method.call
    unless Google::Protobuf::FFI.array_frozen? array
      raise RuntimeError.new "Underlying representation of repeated field still mutable despite frozen wrapper"
    end
    return self
  end
  unless Google::Protobuf::FFI.array_frozen? array
    mini_table = (type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
    Google::Protobuf::FFI.array_freeze(array, mini_table)
  end
  super
end
frozen?() click to toggle source

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.

Calls superclass method
# File lib/google/protobuf/ffi/repeated_field.rb, line 184
def frozen?
  unless Google::Protobuf::FFI.array_frozen? array
    raise RuntimeError.new "Ruby frozen RepeatedField with mutable representation" if super
    return false
  end
  method(:freeze).super_method.call unless super
  true
end
hash() click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 252
def hash
  return_value = 0
  each_msg_val do |msg_val|
    return_value = Google::Protobuf::FFI.message_value_hash(msg_val, type, descriptor, return_value)
  end
  return_value
end
last(n=nil) click to toggle source
# File lib/google/protobuf/repeated_field.rb, line 69
def last(n=nil)
  if n.nil?
    return self[-1]
  elsif n < 0
    raise ArgumentError, "negative array size"
  else
    start = [self.size-n, 0].max
    return self[start...self.size]
  end
end
length() click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 174
def length
  array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
end
Also aliased as: size
pop(n=nil) click to toggle source
# File lib/google/protobuf/repeated_field.rb, line 81
def pop(n=nil)
  if n
    results = []
    n.times{ results << pop_one }
    return results
  else
    return pop_one
  end
end
push(*elements) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 152
def push(*elements)
  raise FrozenError if frozen?
  internal_push(*elements)
end
replace(replacements) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 162
def replace(replacements)
  raise FrozenError if frozen?
  clear
  push(*replacements)
end
size()
Alias for: length
slice(*args)

array aliases into enumerable

Alias for: []
to_ary → array click to toggle source

Used when converted implicitly into array, e.g. compared to an Array. Also called as a fallback of Object#to_a

# File lib/google/protobuf/ffi/repeated_field.rb, line 244
def to_ary
  return_value = []
  each do |element|
    return_value << element
  end
  return_value
end

Private Instance Methods

append_msg_val(msg_val) click to toggle source

@param msg_val [Google::Protobuf::FFI::MessageValue] Value to append

# File lib/google/protobuf/ffi/repeated_field.rb, line 329
def append_msg_val(msg_val)
  unless Google::Protobuf::FFI.append_array(array, msg_val, arena)
    raise NoMemoryError.new "Could not allocate room for #{msg_val} in Arena"
  end
end
each_msg_val() { |msg_val| ... } click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 322
def each_msg_val &block
  each_msg_val_with_index do |msg_val, _|
    yield msg_val
  end
end
each_msg_val_with_index() { |get_msgval_at, i| ... } click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 315
def each_msg_val_with_index &block
  n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
  0.upto(n-1) do |i|
    yield Google::Protobuf::FFI.get_msgval_at(array, i), i
  end
end
fuse_arena(arena) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 390
def fuse_arena(arena)
  arena.fuse(arena)
end
internal_push(*elements) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 289
def internal_push(*elements)
  elements.each do |element|
    append_msg_val convert_ruby_to_upb(element, arena, type, descriptor)
  end
  self
end
pop_one() click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 296
def pop_one
  raise FrozenError if frozen?
  count = length
  return nil if length.zero?
  last_element = Google::Protobuf::FFI.get_msgval_at(array, count-1)
  return_value = convert_upb_to_ruby(last_element, type, descriptor, arena)
  resize(count-1)
  return_value
end
resize(new_size) click to toggle source

@param new_size [Integer] New size of the array

# File lib/google/protobuf/ffi/repeated_field.rb, line 336
def resize(new_size)
  unless Google::Protobuf::FFI.array_resize(array, new_size, arena)
    raise NoMemoryError.new "Array resize to #{new_size} failed!"
  end
end
subarray(start, length) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 306
def subarray(start, length)
  return_result = []
  (start..(start + length - 1)).each do |i|
    element = Google::Protobuf::FFI.get_msgval_at(array, i)
    return_result << convert_upb_to_ruby(element, type, descriptor, arena)
  end
  return_result
end