Class: ABI::Decoder
- Inherits:
-
Object
- Object
- ABI::Decoder
- Defined in:
- lib/abicoder/decoder.rb
Instance Method Summary collapse
-
#big_endian_to_int(bin) ⇒ Object
decoding helpers / utils.
-
#decode(types, data, raise_errors = false) ⇒ Object
Decodes multiple arguments using the head/tail mechanism.
- #decode_primitive_type(type, data) ⇒ Object
- #decode_type(type, arg) ⇒ Object
-
#encode_hex(bin) ⇒ Object
bin_to_hex.
- #zero_padding(data, pos, count, start_positions) ⇒ Object
Instance Method Details
#big_endian_to_int(bin) ⇒ Object
decoding helpers / utils
192 193 194 195 196 197 198 |
# File 'lib/abicoder/decoder.rb', line 192 def big_endian_to_int( bin ) bin = bin.sub( /\A(\x00)+/, '' ) ## keep "performance" shortcut - why? why not? ### todo/check - allow nil - why? why not? ## raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == BYTE_ZERO bin = bin || BYTE_ZERO bin.unpack("H*").first.to_i(16) end |
#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 |
# File 'lib/abicoder/decoder.rb', line 9 def decode( types, data, raise_errors = false ) ## 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 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] = big_endian_to_int(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?}" outputs[i] = zero_padding( data, pos, t.size, start_positions ) 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 |
#decode_primitive_type(type, data) ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/abicoder/decoder.rb', line 150 def decode_primitive_type(type, data) case type when Address encode_hex( data[12..-1] ) when String, Bytes if data.length == 32 data[0..32] else size = big_endian_to_int( data[0,32] ) data[32..-1][0,size] end when FixedBytes data[0, type.length] when Uint big_endian_to_int( data ) when Int u = big_endian_to_int( data ) u >= 2**(type.bits-1) ? (u - 2**type.bits) : u when Bool data[-1] == BYTE_ONE else raise DecodingError, "Unknown primitive type: #{type.class.name} #{type.format}" end end |
#decode_type(type, arg) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/abicoder/decoder.rb', line 104 def decode_type( type, arg ) return nil if arg.nil? || arg.empty? if type.is_a?( String ) || type.is_a?( Bytes ) l = big_endian_to_int( arg[0,32] ) data = arg[32..-1] data[0, l] elsif type.is_a?( Tuple ) arg ? decode(type.types, arg) : [] elsif type.is_a?( FixedArray ) # static-sized arrays l = type.dim subtype = type.subtype if subtype.dynamic? start_positions = (0...l).map {|i| big_endian_to_int(arg[32*i, 32]) } start_positions.push arg.size outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] } outputs.map {|out| decode_type(subtype, out) } else (0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) } end elsif type.is_a?( Array ) l = big_endian_to_int( arg[0,32] ) raise DecodingError, "Too long length: #{l}" if l > 100000 subtype = type.subtype if subtype.dynamic? raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l start_positions = (1..l).map {|i| 32+big_endian_to_int(arg[32*i, 32]) } start_positions.push arg.size outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] } outputs.map {|out| decode_type(subtype, out) } else (0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) } end else decode_primitive_type( type, arg ) end end |
#encode_hex(bin) ⇒ Object
bin_to_hex
200 201 202 203 |
# File 'lib/abicoder/decoder.rb', line 200 def encode_hex( bin ) ## bin_to_hex raise TypeError, "Value must be a String" unless bin.is_a?(::String) bin.unpack("H*").first end |
#zero_padding(data, pos, count, start_positions) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/abicoder/decoder.rb', line 176 def zero_padding( data, pos, count, start_positions ) if pos >= data.size start_positions[start_positions.size-1] += count "\x00"*count elsif pos + count > data.size start_positions[start_positions.size-1] += ( count - (data.size-pos)) data[pos,data.size-pos] + "\x00"*( count - (data.size-pos)) else data[pos, count] end end |