mirror of
https://github.com/logos-storage/nim-nitro.git
synced 2026-01-06 23:53:11 +00:00
102 lines
2.9 KiB
Nim
102 lines
2.9 KiB
Nim
import pkg/stew/endians2
|
|
import pkg/stint
|
|
|
|
type
|
|
Abi* = object
|
|
AbiWriter* = object
|
|
bytes: seq[byte]
|
|
tuples: seq[Tuple]
|
|
Tuple = object
|
|
postponed: seq[Split]
|
|
Split = object
|
|
head: Slice[int]
|
|
tail: seq[byte]
|
|
|
|
proc isStatic*(_: type Abi, t: type SomeUnsignedInt): bool = true
|
|
proc isStatic*(_: type Abi, t: type StUint): bool = true
|
|
proc isStatic*(_: type Abi, t: type bool): bool = true
|
|
proc isStatic*(_: type Abi, t: type enum): bool = true
|
|
proc isStatic*[T](_: type Abi, t: type seq[T]): bool = false
|
|
proc isStatic*[I, T](_: type Abi, t: type array[I, T]): bool = Abi.isStatic(T)
|
|
|
|
proc encode*[T](_: type Abi, value: T): seq[byte]
|
|
|
|
proc pad(writer: var AbiWriter, len: int) =
|
|
let padlen = (32 - len mod 32) mod 32
|
|
for _ in 0..<padlen:
|
|
writer.bytes.add(0'u8)
|
|
|
|
proc padleft(writer: var AbiWriter, bytes: openArray[byte]) =
|
|
writer.pad(bytes.len)
|
|
writer.bytes.add(bytes)
|
|
|
|
proc padright(writer: var AbiWriter, bytes: openArray[byte]) =
|
|
writer.bytes.add(bytes)
|
|
writer.pad(bytes.len)
|
|
|
|
proc write*(writer: var AbiWriter, value: SomeUnsignedInt | StUint) =
|
|
writer.padleft(value.toBytesBE)
|
|
|
|
proc write*(writer: var AbiWriter, value: bool) =
|
|
writer.write(cast[uint8](value))
|
|
|
|
proc write*(writer: var AbiWriter, value: enum) =
|
|
writer.write(uint64(ord(value)))
|
|
|
|
proc write*[I](writer: var AbiWriter, bytes: array[I, byte]) =
|
|
writer.padright(bytes)
|
|
|
|
proc writeLater[T](writer: var AbiWriter, value: T) =
|
|
var split: Split
|
|
split.head.a = writer.bytes.high + 1
|
|
writer.write(0'u64)
|
|
split.head.b = writer.bytes.high
|
|
split.tail = Abi.encode(value)
|
|
writer.tuples[^1].postponed.add(split)
|
|
|
|
proc write*(writer: var AbiWriter, bytes: seq[byte]) =
|
|
if writer.tuples.len == 0:
|
|
writer.write(bytes.len.uint64)
|
|
writer.padright(bytes)
|
|
else:
|
|
writer.writeLater(bytes)
|
|
|
|
proc startTuple*(writer: var AbiWriter) =
|
|
writer.tuples.add(Tuple())
|
|
|
|
proc finishTuple*(writer: var AbiWriter) =
|
|
let tupl = writer.tuples.pop()
|
|
for split in tupl.postponed:
|
|
let offset = writer.bytes.len - split.head.a
|
|
writer.bytes[split.head] = Abi.encode(offset.uint64)
|
|
writer.bytes.add(split.tail)
|
|
|
|
proc write*[I, T](writer: var AbiWriter, value: array[I, T]) =
|
|
if writer.tuples.len == 0 or Abi.isStatic(T):
|
|
writer.startTuple()
|
|
for element in value:
|
|
writer.write(element)
|
|
writer.finishTuple()
|
|
else:
|
|
writer.writeLater(value)
|
|
|
|
proc write*[T](writer: var AbiWriter, value: seq[T]) =
|
|
if writer.tuples.len == 0:
|
|
writer.write(value.len.uint64)
|
|
writer.startTuple()
|
|
for element in value:
|
|
writer.write(element)
|
|
writer.finishTuple()
|
|
else:
|
|
writer.writeLater(value)
|
|
|
|
proc finish*(writer: var AbiWriter): seq[byte] =
|
|
doAssert writer.tuples.len == 0, "not all tuples were finished"
|
|
doAssert writer.bytes.len mod 32 == 0, "encoding invariant broken"
|
|
writer.bytes
|
|
|
|
proc encode*[T](_: type Abi, value: T): seq[byte] =
|
|
var writer: AbiWriter
|
|
writer.write(value)
|
|
writer.finish()
|