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 48
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 320
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, values: nil, array: nil) click to toggle source

@param field [FieldDescriptor] Descriptor of the field where the RepeatedField will be assigned @param values [Enumerable] Initial values; may be nil or empty @param arena [Arena] Owning message’s arena

# File lib/google/protobuf/ffi/repeated_field.rb, line 354
def self.construct_for_field(field, arena, 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 370
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 238
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 155
def <<(element)
  raise FrozenError if frozen?
  push element
end
==(other) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 199
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 74
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 134
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 166
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 257
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 189
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 67
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
Calls superclass method
# File lib/google/protobuf/ffi/repeated_field.rb, line 177
def freeze
  return self if frozen?
  super
  @arena.pin self
  if type == :message
    each do |element|
      element.freeze
    end
  end
  self
end
hash() click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 230
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 172
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 150
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 160
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 222
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 307
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 300
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 293
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 364
def fuse_arena(arena)
  arena.fuse(arena)
end
internal_push(*elements) click to toggle source
# File lib/google/protobuf/ffi/repeated_field.rb, line 267
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 274
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 314
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 284
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