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)
-
(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