Module: RLP::DecodeLazy

Includes:
Decode
Included in:
RLP, LazyList
Defined in:
lib/rlp/decode_lazy.rb

Constant Summary

Constants included from Constant

Constant::BYTE_EMPTY, Constant::BYTE_ZERO, Constant::LIST_PREFIX_OFFSET, Constant::LONG_LENGTH_LIMIT, Constant::PRIMITIVE_PREFIX_OFFSET, Constant::SHORT_LENGTH_LIMIT

Instance Method Summary collapse

Methods included from Decode

#decode

Methods included from Utils

#big_endian_to_int, #bytes_to_str, #encode_hex, #int_to_big_endian, #list?, make_immutable!, #primitive?, #str_to_bytes

Instance Method Details

#consume_item_lazy(rlp, start) ⇒ Array

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.


61
62
63
64
65
66
67
68
69
70
# File 'lib/rlp/decode_lazy.rb', line 61

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: {}) ⇒ Object

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.

Raises:


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/rlp/decode_lazy.rb', line 29

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) ⇒ Object

Get a specific element from an rlp encoded nested list.

This method uses #decode_lazy and, thus, decodes only the necessary parts of the string.

Examples:

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

Raises:

  • (IndexError)

    if `index` is invalid (out of range or too many levels)


91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rlp/decode_lazy.rb', line 91

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