Custom encoding can use startTuple() and finishTuple()

This commit is contained in:
Mark Spanbroek 2022-04-13 06:29:07 +02:00 committed by markspanbroek
parent fd377e59da
commit 01c11eecb0
3 changed files with 76 additions and 28 deletions

View File

@ -43,11 +43,10 @@ func index(decoder: var AbiDecoder): var int =
func `index=`(decoder: var AbiDecoder, value: int) =
decoder.currentTuple.index = value
func startTuple(decoder: var AbiDecoder): ?!void =
func startTuple*(decoder: var AbiDecoder) =
decoder.stack.add(Tuple.init(decoder.index))
success()
func finishTuple(decoder: var AbiDecoder) =
func finishTuple*(decoder: var AbiDecoder) =
doAssert decoder.stack.len > 1, "unable to finish a tuple that hasn't started"
let tupl = decoder.stack.pop()
decoder.index = tupl.index
@ -134,7 +133,7 @@ func decode(decoder: var AbiDecoder, T: type seq[byte]): ?!T =
func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
var tupl: T
?decoder.startTuple()
decoder.startTuple()
for element in tupl.fields:
element = ?decoder.read(typeof(element))
decoder.finishTuple()
@ -143,7 +142,7 @@ func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
var sequence: seq[T]
let len = ?decoder.read(uint64)
?decoder.startTuple()
decoder.startTuple()
for _ in 0..<len:
sequence.add(?decoder.read(T))
decoder.finishTuple()
@ -151,7 +150,7 @@ func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): ?!array[I,T] =
var arr: array[I, T]
?decoder.startTuple()
decoder.startTuple()
for i in 0..<arr.len:
arr[i] = ?decoder.read(T)
decoder.finishTuple()

View File

@ -57,7 +57,7 @@ func postpone(encoder: var AbiEncoder, bytes: seq[byte]) =
func setDynamic(encoder: var AbiEncoder) =
encoder.stack[^1].dynamic = true
func startTuple(encoder: var AbiEncoder) =
func startTuple*(encoder: var AbiEncoder) =
encoder.stack.add(Tuple())
func encode(encoder: var AbiEncoder, tupl: Tuple) =
@ -67,7 +67,7 @@ func encode(encoder: var AbiEncoder, tupl: Tuple) =
else:
encoder.append(tupl.finish())
func finishTuple(encoder: var AbiEncoder) =
func finishTuple*(encoder: var AbiEncoder) =
encoder.encode(encoder.stack.pop())
func pad(encoder: var AbiEncoder, len: int, padding=0'u8) =
@ -131,25 +131,25 @@ func encode(encoder: var AbiEncoder, tupl: tuple) =
encoder.write(element)
encoder.finishTuple()
func finish(encoder: var AbiEncoder): Tuple =
doAssert encoder.stack.len == 1, "not all tuples were finished"
doAssert encoder.stack[0].bytes.len mod 32 == 0, "encoding invariant broken"
encoder.stack[0]
func write*[T](encoder: var AbiEncoder, value: T) =
var writer = AbiEncoder.init()
writer.encode(value)
encoder.encode(writer.stack[0])
func finish(encoder: var AbiEncoder): seq[byte] =
doAssert encoder.stack.len == 1, "not all tuples were finished"
doAssert encoder.stack[0].bytes.len mod 32 == 0, "encoding invariant broken"
encoder.stack[0].bytes
encoder.encode(writer.finish())
func encode*[T](_: type AbiEncoder, value: T): seq[byte] =
var encoder = AbiEncoder.init()
encoder.write(value)
encoder.finish()
encoder.finish().bytes
proc isDynamic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
var encoder = AbiEncoder.init()
encoder.encode(T.default)
encoder.stack[^1].dynamic
encoder.write(T.default)
encoder.finish().dynamic
proc isStatic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
not AbiEncoder.isDynamic(T)

View File

@ -2,30 +2,79 @@ import std/unittest
import pkg/questionable/results
import contractabi
type CustomType = object
a: uint16
b: string
type
Custom1 = object
a: uint16
b: string
Custom2 = object
a: uint16
b: string
Custom3 = object
a: uint16
b: string
func encode(encoder: var AbiEncoder, custom: CustomType) =
func encode(encoder: var AbiEncoder, custom: Custom1) =
encoder.write( (custom.a, custom.b) )
func decode(decoder: var AbiDecoder, T: type CustomType): ?!T =
func decode(decoder: var AbiDecoder, T: type Custom1): ?!T =
let (a, b) = ?decoder.read( (uint16, string) )
success CustomType(a: a, b: b)
success Custom1(a: a, b: b)
func encode(encoder: var AbiEncoder, custom: Custom2) =
encoder.startTuple()
encoder.write(custom.a)
encoder.write(custom.b)
encoder.finishTuple()
func decode(decoder: var AbiDecoder, T: type Custom2): ?!T =
var custom: T
decoder.startTuple()
custom.a = ?decoder.read(uint16)
custom.b = ?decoder.read(string)
decoder.finishTuple()
success custom
func encode(encoder: var AbiEncoder, custom: Custom3) =
encoder.startTuple()
encoder.write(custom.a)
encoder.write(custom.b)
# missing: encoder.finishTuple()
func decode(decoder: var AbiDecoder, T: type Custom3): ?!T =
var custom: T
decoder.startTuple()
custom.a = ?decoder.read(uint16)
custom.b = ?decoder.read(string)
# missing: decoder.finishTuple()
success custom
suite "custom types":
let custom = CustomType(a: 42, b: "ultimate answer")
let custom1 = Custom1(a: 42, b: "ultimate answer")
let custom2 = Custom2(a: 42, b: "ultimate answer")
let custom3 = Custom3(a: 42, b: "ultimate answer")
test "can be encoded":
check AbiEncoder.encode(custom) == AbiEncoder.encode( (custom.a, custom.b) )
check:
AbiEncoder.encode(custom1) == AbiEncoder.encode( (custom1.a, custom1.b) )
test "can be decoded":
let encoding = AbiEncoder.encode(custom)
check AbiDecoder.decode(encoding, CustomType) == success custom
let encoding = AbiEncoder.encode(custom1)
check AbiDecoder.decode(encoding, Custom1) == success custom1
test "can be embedded in tuples, arrays and sequences":
let embedding = (custom, [custom], @[custom])
let embedding = (custom1, [custom1], @[custom1])
let encoding = AbiEncoder.encode(embedding)
let decoded = AbiDecoder.decode(encoding, typeof(embedding))
check !decoded == embedding
test "can use startTuple() and finishTuple()":
let encoding = AbiEncoder.encode(custom2)
check AbiDecoder.decode(encoding, Custom2) == success custom2
test "fail when finishTuple() is missing":
expect AssertionDefect:
discard AbiEncoder.encode(custom3)
let encoding = AbiEncoder.encode( (custom3.a, custom3.b) )
expect AssertionDefect:
discard AbiDecoder.decode(encoding, Custom3)