module Ciri::RLP::Decode

Public Instance Methods

decode(input, type = Raw) click to toggle source

Decode input from rlp encoding, only produce string or array

Examples:

Ciri::RLP.decode(input)
# File lib/ciri/rlp/decode.rb, line 38
def decode(input, type = Raw)
  decode_with_type(input, type)
end
decode_with_type(s, type) click to toggle source

Use this method after RLP.decode, decode values from string or array to specific types see Ciri::RLP::Serializable::TYPES for supported types

Examples:

item = Ciri::RLP.decode(encoded_text)
decode_with_type(item, Integer)
# File lib/ciri/rlp/decode.rb, line 50
def decode_with_type(s, type)
  s = StringIO.new(s) if s.is_a?(String)
  if type == Integer
    item = s.read(1)
    if item.nil?
      raise InvalidError.new "invalid Integer value nil"
    elsif item == "\x80".b || item.empty?
      0
    elsif item.ord < 0x80
      item.ord
    else
      size = item[0].ord - 0x80
      Ciri::Utils.big_endian_decode(s.read(size))
    end
  elsif type == Bool
    item = s.read(1)
    if item == Bool::ENCODED_TRUE
      true
    elsif item == Bool::ENCODED_FALSE
      false
    else
      raise InvalidError.new "invalid bool value #{item}"
    end
  elsif type.is_a?(Class) && type.respond_to?(:rlp_decode)
    type.rlp_decode(s)
  elsif type.is_a?(Array)
    decode_list(s) do |list, s2|
      i = 0
      until s2.eof?
        t = type.size > i ? type[i] : type[-1]
        list << decode_with_type(s2, t)
        i += 1
      end
    end
  elsif type == Bytes
    str = decode_stream(s)
    raise RLP::InvalidError.new "decode #{str.class} from Bytes" unless str.is_a?(String)
    str
  elsif type == List
    list = decode_stream(s)
    raise RLP::InvalidError.new "decode #{list.class} from List" unless list.is_a?(Array)
    list
  elsif type == Raw
    decode_stream(s)
  else
    raise RLP::InvalidError.new "unknown type #{type}"
  end
rescue
  error "when decoding #{s} into #{type}"
  raise
end

Protected Instance Methods

decode_list(s, first_char = nil, &decoder) click to toggle source
# File lib/ciri/rlp/decode.rb, line 104
def decode_list(s, first_char = nil, &decoder)
  s = StringIO.new(s) if s.is_a?(String)
  c = first_char || s.read(1)
  list = []
  # list is empty
  # return list if c.nil?
  sub_s = case c.ord
          when 0xc0..0xf7
            length = c.ord - 0xc0
            s.read(length)
          when 0xf8..0xff
            length_binary = s.read(c.ord - 0xf7)
            length = int_from_binary(length_binary)
            check_range_and_read(s, length)
          else
            raise InvalidError.new("invalid char #{c}")
          end

  raise InvalidError.new("stream EOF when encode_list") unless sub_s

  decoder.call(list, StringIO.new(sub_s))
  list
end

Private Instance Methods

check_range_and_read(s, length) click to toggle source
# File lib/ciri/rlp/decode.rb, line 152
def check_range_and_read(s, length)
  s.read(length)
rescue RangeError
  raise InvalidError.new("length too long: #{length}")
end
decode_stream(s) click to toggle source
# File lib/ciri/rlp/decode.rb, line 130
def decode_stream(s)
  c = s.read(1)
  raise InvalidError.new("read none char from stream") unless c
  case c.ord
  when 0x00..0x7f
    c
  when 0x80..0xb7
    length = c.ord - 0x80
    s.read(length)
  when 0xb8..0xbf
    length_binary = s.read(c.ord - 0xb7)
    length = int_from_binary(length_binary)
    check_range_and_read(s, length)
  else
    decode_list(s, c) do |list, s2|
      until s2.eof?
        list << decode_stream(s2)
      end
    end
  end
end
int_from_binary(input) click to toggle source
# File lib/ciri/rlp/decode.rb, line 158
def int_from_binary(input)
  Ciri::Utils.big_endian_decode(input)
end