module RLP::DecodeLazy

Public Instance Methods

consume_item_lazy(rlp, start) click to toggle source

Read an item from an RLP string lazily.

If the length prefix announces a string, the string is read; if it announces a list, a {LazyList} is created.

@param rlp [String] the rlp string to read from @param start [Integer] the position at which to start reading

@return [Array] A pair ‘[item, next_start]` where `item` is the read

string or a {LazyList} and `next_start` is the position of the first
unprocessed byte
# File lib/rlp/decode_lazy.rb, line 62
def consume_item_lazy(rlp, start)
  t, l, s = consume_length_prefix(rlp, start)
  if t == :str
    consume_payload(rlp, s, :str, l)
  elsif t == :list
    [LazyList.new(rlp, s, s+l), s+l]
  else
    raise "Invalid item type: #{t}"
  end
end
decode_lazy(rlp, sedes: nil, sedes_options: {}) click to toggle source

Decode an RLP encoded object in a lazy fashion.

If the encoded object is a byte string, this function acts similar to {RLP::Decode#decode}. If it is a list however, a {LazyList} is returned instead. This object will decode the string lazily, avoiding both horizontal and vertical traversing as much as possible.

The way ‘sedes` is applied depends on the decoded object: If it is a string `sedes` deserializes it as a whole; if it is a list, each element is deserialized individually. In both cases, `sedes_options` are passed on. Note that, if a deserializer is used, only “horizontal” but not “vertical lazyness” can be preserved.

@param rlp [String] the RLP string to decode @param sedes [Object] an object implementing a method ‘deserialize(code)`

which is used as described above, or `nil` if no deserialization should
be performed

@param sedes_options [Hash] additional keyword arguments that will be

passed to the deserializers

@return [Object] either the already decoded and deserialized object (if

encoded as a string) or an instance of {RLP::LazyList}
# File lib/rlp/decode_lazy.rb, line 30
def decode_lazy(rlp, sedes: nil, sedes_options: {})
  item, next_start = consume_item_lazy(rlp, 0)

  raise DecodingError.new("RLP length prefix announced wrong length", rlp) if next_start != rlp.size

  if item.instance_of?(LazyList)
    item.sedes = sedes
    item.sedes_options = sedes_options
    item
  elsif sedes
    # FIXME: lazy man's kwargs
    sedes_options.empty? ?
      sedes.deserialize(item) :
      sedes.deserialize(item, **sedes_options)
  else
    item
  end
end
peek(rlp, index, sedes: nil) click to toggle source

Get a specific element from an rlp encoded nested list.

This method uses {RLP::DecodeLazy#decode_lazy} and, thus, decodes only the necessary parts of the string.

@example Usage

rlpdata = RLP.encode([1, 2, [3, [4, 5]]])
RLP.peek(rlpdata, 0, sedes: RLP::Sedes.big_endian_int) # => 1
RLP.peek(rlpdata, [2, 0], sedes: RLP::Sedes.big_endian_int) # => 3

@param rlp [String] the rlp string @param index [Integer, Array] the index of the element to peek at (can be

a list for nested data)

@param sedes [#deserialize] a sedes used to deserialize the peeked at

object, or `nil` if no deserialization should be performed

@raise [IndexError] if ‘index` is invalid (out of range or too many levels)

# File lib/rlp/decode_lazy.rb, line 92
def peek(rlp, index, sedes: nil)
  ll = decode_lazy(rlp)
  index = Array(index)

  index.each do |i|
    raise IndexError, "Too many indices given" if primitive?(ll)
    ll = ll.fetch(i)
  end

  sedes ? sedes.deserialize(ll) : ll
end