mirror of
https://github.com/logos-storage/nim-contract-abi.git
synced 2026-01-03 22:23:08 +00:00
Since the introduction of the new API for encoding and decoding of tuples, these procs are no longer required to be on the external API.
136 lines
4.0 KiB
Nim
136 lines
4.0 KiB
Nim
import pkg/stint
|
|
import pkg/stew/endians2
|
|
import pkg/stew/byteutils
|
|
import pkg/upraises
|
|
import ./encoding
|
|
|
|
push: {.upraises:[].}
|
|
|
|
type
|
|
AbiDecoder* = object
|
|
bytes: seq[byte]
|
|
stack: seq[Tuple]
|
|
last: int
|
|
Tuple = object
|
|
start: int
|
|
index: int
|
|
dynamic: bool
|
|
Padding = enum
|
|
padLeft,
|
|
padRight
|
|
UInt = SomeUnsignedInt | StUint
|
|
|
|
func read*(decoder: var AbiDecoder, T: type): T
|
|
|
|
func init(_: type Tuple, offset: int, dynamic: bool): Tuple =
|
|
Tuple(start: offset, index: offset, dynamic: dynamic)
|
|
|
|
func init(_: type AbiDecoder, bytes: seq[byte], offset=0): AbiDecoder =
|
|
AbiDecoder(bytes: bytes, stack: @[Tuple.init(offset, dynamic=false)])
|
|
|
|
func currentTuple(decoder: var AbiDecoder): var Tuple =
|
|
decoder.stack[^1]
|
|
|
|
func index(decoder: var AbiDecoder): var int =
|
|
decoder.currentTuple.index
|
|
|
|
func `index=`(decoder: var AbiDecoder, value: int) =
|
|
decoder.currentTuple.index = value
|
|
|
|
func updateLast(decoder: var AbiDecoder, index: int) =
|
|
if index > decoder.last:
|
|
decoder.last = index
|
|
|
|
func advance(decoder: var AbiDecoder, amount: int) =
|
|
decoder.index += amount
|
|
decoder.updateLast(decoder.index)
|
|
|
|
func read(decoder: var AbiDecoder, amount: int, padding = padLeft): seq[byte] =
|
|
let padlen = (32 - amount mod 32) mod 32
|
|
if padding == padLeft:
|
|
decoder.advance(padlen)
|
|
result = decoder.bytes[decoder.index..<decoder.index+amount]
|
|
decoder.advance(amount)
|
|
if padding == padRight:
|
|
decoder.advance(padlen)
|
|
|
|
func decode(decoder: var AbiDecoder, T: type UInt): T =
|
|
T.fromBytesBE(decoder.read(sizeof(T)))
|
|
|
|
func decode(decoder: var AbiDecoder, T: type bool): T =
|
|
decoder.read(uint8) != 0
|
|
|
|
func decode(decoder: var AbiDecoder, T: type enum): T =
|
|
T(decoder.read(uint64))
|
|
|
|
func decode[I](decoder: var AbiDecoder, T: type array[I, byte]): T =
|
|
result[0..<result.len] = decoder.read(result.len, padRight)
|
|
|
|
func readOffset(decoder: var AbiDecoder): int =
|
|
let offset = decoder.read(uint64)
|
|
decoder.currentTuple.start + offset.int
|
|
|
|
func readTail(decoder: var AbiDecoder, T: type seq[byte]): T =
|
|
let offset = decoder.readOffset()
|
|
var tailDecoder = AbiDecoder.init(decoder.bytes, offset)
|
|
result = tailDecoder.read(T)
|
|
decoder.updateLast(tailDecoder.index)
|
|
|
|
func decode(decoder: var AbiDecoder, T: type seq[byte]): T =
|
|
if decoder.currentTuple.dynamic:
|
|
decoder.readTail(T)
|
|
else:
|
|
let len = decoder.read(uint64).int
|
|
decoder.read(len, padRight)
|
|
|
|
func startTuple(decoder: var AbiDecoder, dynamic: bool) =
|
|
var start: int
|
|
if decoder.currentTuple.dynamic and dynamic:
|
|
start = decoder.readOffset()
|
|
else:
|
|
start = decoder.index
|
|
decoder.stack.add(Tuple.init(start, dynamic))
|
|
|
|
func finishTuple(decoder: var AbiDecoder) =
|
|
doAssert decoder.stack.len > 1, "unable to finish a tuple that hasn't started"
|
|
let tupl = decoder.stack.pop()
|
|
if not tupl.dynamic:
|
|
decoder.index = tupl.index
|
|
|
|
func decode(decoder: var AbiDecoder, T: type tuple): T =
|
|
const dynamic = AbiEncoder.isDynamic(typeof(result))
|
|
decoder.startTuple(dynamic)
|
|
for element in result.fields:
|
|
element = decoder.read(typeof(element))
|
|
decoder.finishTuple()
|
|
|
|
func read*(decoder: var AbiDecoder, T: type): T =
|
|
decoder.decode(T)
|
|
|
|
func finish(decoder: var AbiDecoder) =
|
|
doAssert decoder.stack.len == 1, "not all tuples were finished"
|
|
doAssert decoder.last == decoder.bytes.len, "unread trailing bytes found"
|
|
doAssert decoder.last mod 32 == 0, "encoding variant broken"
|
|
|
|
func decode[T](decoder: var AbiDecoder, _: type seq[T]): seq[T] =
|
|
let len = decoder.read(uint64)
|
|
decoder.startTuple(dynamic=true)
|
|
for _ in 0..<len:
|
|
result.add(decoder.read(T))
|
|
decoder.finishTuple()
|
|
|
|
func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): array[I,T] =
|
|
const dynamic = AbiEncoder.isDynamic(T)
|
|
decoder.startTuple(dynamic)
|
|
for i in 0..<result.len:
|
|
result[i] = decoder.read(T)
|
|
decoder.finishTuple()
|
|
|
|
func decode(decoder: var AbiDecoder, T: type string): T =
|
|
string.fromBytes(decoder.read(seq[byte]))
|
|
|
|
func decode*(_: type AbiDecoder, bytes: seq[byte], T: type): T =
|
|
var decoder = AbiDecoder.init(bytes)
|
|
result = decoder.decode(T)
|
|
decoder.finish()
|