module RLP::Decode

def append(rlp, obj)

def append(rlp, obj)
  type, len, pos = consume_length_prefix rlp, 0
  raise DecodingError.new("Trying to append to a non-list!", rlp) if type != :list
  rlpdata = rlp[pos..-1] + RLP.encode(obj)
  prefix = length_prefix rlpdata.size, LIST_PREFIX_OFFSET
  prefix + rlpdata
end

def compare_length(rlp, length)

def compare_length(rlp, length)
  type, len, pos = consume_length_prefix rlp, 0
  raise DecodingError.new("Trying to compare length of non-list!", rlp) if type != :list
  return (length == 0 ? 0 : -length/length.abs) if rlp == EMPTYLIST
  beginpos = pos
  len = 0
  loop do
    return 1 if len > length
    _, _len, _pos = consume_length_prefix rlp, pos
    len += 1
    break if _len + _pos >= rlp.size
    pos = _len + _pos
  end
  len == length ? 0 : -1
end

def consume_item(rlp, start)


the position of the first unprocessed byte.
Returns a pair `[item, end]` where `item` is the read item and `end` is

* `start` - the position at which to start reading`
* `rlp` - the string to read from

Read an item from an RLP string.
#
def consume_item(rlp, start)
  t, l, s = consume_length_prefix(rlp, start)
  consume_payload(rlp, s, t, l)
end

def consume_length_prefix(rlp, start)


first payload byte in the rlp string (thus the end of length prefix).
the length of the payload in bytes, and `end` is the position of the
or `:list` depending on the type of the following payload, `length` is
Returns an array `[type, length, end]`, where `type` is either `:str`

* `start` - the position at which to start reading
* `rlp` - the rlp string to read from

Read a length prefix from an RLP string.
#
def consume_length_prefix(rlp, start)
  b0 = rlp[start].ord
  if b0 < PRIMITIVE_PREFIX_OFFSET # single byte
    [:str, 1, start]
  elsif b0 < PRIMITIVE_PREFIX_OFFSET + SHORT_LENGTH_LIMIT # short string
    raise DecodingError.new("Encoded as short string although single byte was possible", rlp) if (b0 - PRIMITIVE_PREFIX_OFFSET == 1) && rlp[start+1].ord < PRIMITIVE_PREFIX_OFFSET
    [:str, b0 - PRIMITIVE_PREFIX_OFFSET, start + 1]
  elsif b0 < LIST_PREFIX_OFFSET # long string
    raise DecodingError.new("Length starts with zero bytes", rlp) if rlp.slice(start+1) == BYTE_ZERO
    ll = b0 - PRIMITIVE_PREFIX_OFFSET - SHORT_LENGTH_LIMIT + 1
    l = big_endian_to_int rlp[(start+1)...(start+1+ll)]
    [:str, l, start+1+ll]
  elsif b0 < LIST_PREFIX_OFFSET + SHORT_LENGTH_LIMIT # short list
    [:list, b0 - LIST_PREFIX_OFFSET, start + 1]
  else # long list
    raise DecodingError.new('Length starts with zero bytes', rlp) if rlp.slice(start+1) == BYTE_ZERO
    ll = b0 - LIST_PREFIX_OFFSET - SHORT_LENGTH_LIMIT + 1
    l = big_endian_to_int rlp[(start+1)...(start+1+ll)]
    raise DecodingError.new('Long list prefix used for short list', rlp) if l < 56
    [:list, l, start+1+ll]
  end
end

def consume_payload(rlp, start, type, length)


the position of the first unprocessed byte.
Returns a pair `[item, end]`, where `item` is the read item and `end` is

* `length` - the length of the payload in bytes
* `start` - the position at which to start reading
* `type` - the type of the payload (`:str` or `:list`)
* `rlp` - the rlp string to read from

Read the payload of an item from an RLP string.
#
def consume_payload(rlp, start, type, length)
  case type
  when :str
    [rlp[start...(start+length)], start+length]
  when :list
    items = []
    next_item_start = start
    payload_end = next_item_start + length
    while next_item_start < payload_end
      item, next_item_start = consume_item rlp, next_item_start
      items.push item
    end
    raise DecodingError.new('List length prefix announced a too small length', rlp) if next_item_start > payload_end
    [items, next_item_start]
  else
    raise TypeError, 'Type must be either :str or :list'
  end
end

def decode(rlp, **options)

Raises:
  • (RLP::Error::DeserializationError) - if the deserialization fails
  • (RLP::Error::DecodingError) - if the input string does not end after

Returns:
  • (Object) - the decoded and maybe deserialized object

Parameters:
  • options (Hash) -- deserialization options:
def decode(rlp, **options)
  rlp = str_to_bytes(rlp)
  sedes = options.delete(:sedes)
  strict = options.has_key?(:strict) ? options.delete(:strict) : true
  begin
    item, next_start = consume_item(rlp, 0)
  rescue Exception => e
    raise DecodingError.new("Cannot decode rlp string: #{e}", rlp)
  end
  raise DecodingError.new("RLP string ends with #{rlp.size - next_start} superfluous bytes", rlp) if next_start != rlp.size && strict
  if sedes
    obj = sedes.instance_of?(Class) && sedes.include?(Sedes::Serializable) ?
      sedes.deserialize(item, **options) :
      sedes.deserialize(item)
    if obj.respond_to?(:_cached_rlp)
      obj._cached_rlp = rlp
      raise "RLP::Sedes::Serializable object must be immutable after decode" if obj.is_a?(Sedes::Serializable) && obj.mutable?
    end
    obj
  else
    item
  end
end

def descend(rlp, *path)

def descend(rlp, *path)
  rlp = str_to_bytes(rlp)
  path.each do |pa|
    pos = 0
    type, len, pos = consume_length_prefix rlp, pos
    raise DecodingError.new("Trying to descend through a non-list!", rlp) if type != :list
    pa.times do |i|
      t, l, s = consume_length_prefix(rlp, pos)
      pos = l + s
    end
    t, l, s = consume_length_prefix rlp, pos
    rlp = rlp[pos...(l+s)]
  end
  rlp
end

def insert(rlp, index, obj)

def insert(rlp, index, obj)
  type, len, pos = consume_length_prefix rlp, 0
  raise DecodingError.new("Trying to insert to a non-list!", rlp) if type != :list
  beginpos = pos
  index.times do |i|
    _, _len, _pos = consume_length_prefix rlp, pos
    pos = _pos + _len
    break if _pos >= rlp.size
  end
  rlpdata = rlp[beginpos...pos] + RLP.encode(obj) + rlp[pos..-1]
  prefix = length_prefix rlpdata.size, LIST_PREFIX_OFFSET
  prefix + rlpdata
end

def pop(rlp, index=2**50)

def pop(rlp, index=2**50)
  type, len, pos = consume_length_prefix rlp, 0
  raise DecodingError.new("Trying to pop from a non-list!", rlp) if type != :list
  beginpos = pos
  index.times do |i|
    _, _len, _pos = consume_length_prefix rlp, pos
    break if _len + _pos >= rlp.size
    pos = _len + _pos
  end
  _, _len, _pos = consume_length_prefix rlp, pos
  rlpdata = rlp[beginpos...pos] + rlp[(_len+_pos)..-1]
  prefix = length_prefix rlpdata.size, LIST_PREFIX_OFFSET
  prefix + rlpdata
end