Method: ABI::Decoder#decode

Defined in:
lib/abicoder/decoder.rb

#decode(types, data, raise_errors = false) ⇒ Object

Decodes multiple arguments using the head/tail mechanism.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/abicoder/decoder.rb', line 9

def decode( types, data, raise_errors = false )
   ##
   ##  todo/check:  always change data (string) to binary encoding (e.g. data = data.b )
   ##                    or such - why? why not?

  ## for convenience check if types is a String
  ##   otherwise assume ABI::Type already
  types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) }

  outputs = [nil] * types.size
  start_positions = [nil] * types.size + [data.size]

  # TODO: refactor, a reverse iteration will be better - why? why not?
  #   try to simplify / clean-up code - possible? why? why not?

  pos = 0
  types.each_with_index do |t, i|
    # If a type is static, grab the data directly, otherwise record its
    # start position
    if t.dynamic?

      if pos>data.size-1
        if raise_errors
          raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}"
        else
          puts "!! WARN - DecodingError: Position out of bounds #{pos}>#{data.size-1}"
        end
      end

      start_positions[i] = decode_uint256(data[pos, 32])

      if start_positions[i]>data.size-1
        if raise_errors
          raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}"
        else
          puts "!! WARN - DecodingError: Start position out of bounds #{start_positions[i]}>#{data.size-1}"
        end
      end


      j = i - 1
      while j >= 0 && start_positions[j].nil?
        start_positions[j] = start_positions[i]
        j -= 1
      end

      pos += 32
    else
      ## puts "step 1 - decode item [#{i}] - #{t.format}  size: #{t.size} dynamic? #{t.dynamic?}"

      count = t.size
      ## was zero_padding( data, pos, t.size, start_positions )
      ##   inline for now and clean-up later - why? why not?
      outputs[i] = if pos >= data.size
                      start_positions[start_positions.size-1] += count
                      BYTE_ZERO*count
                    elsif pos + count > data.size
                      start_positions[start_positions.size-1] += ( count - (data.size-pos))
                      data[pos,data.size-pos] + BYTE_ZERO*( count - (data.size-pos))
                    else
                      data[pos, count]
                    end

      pos += t.size
    end
  end



  # We add a start position equal to the length of the entire data for
  # convenience.
  j = types.size - 1
  while j >= 0 && start_positions[j].nil?
    start_positions[j] = start_positions[types.size]
    j -= 1
  end

  if pos > data.size
    if raise_errors
      raise DecodingError, "Not enough data for head"
    else
      puts "!! WARN - DecodingError: Not enough data for head"
    end
  end


  types.each_with_index do |t, i|
    if t.dynamic?
      offset, next_offset = start_positions[i, 2]
      if offset<=data.size && next_offset<=data.size
        outputs[i] = data[offset...next_offset]
      end
    end
  end

  if outputs.include?( nil )
    if raise_errors
      raise DecodingError, "Not all data can be parsed"
    else
      puts "!! WARN: DecodingError - Not all data can be parsed"
    end
  end


  types.zip(outputs).map do |(t, out)|
     ## puts "step 2 - decode item - #{t.format} got: #{out.size} byte(s) -  size: #{t.size} dynamic? #{t.dynamic?}"

     decode_type(t, out)
  end
end