diff --git a/rlp.nim b/rlp.nim index 0c6fc91..2d9af06 100644 --- a/rlp.nim +++ b/rlp.nim @@ -3,7 +3,7 @@ ## https://ethereum.github.io/yellowpaper/paper.pdf import - strutils, parseutils, + macros, strutils, parseutils, rlp/[types, writer, object_serialization], rlp/priv/defs @@ -357,7 +357,11 @@ proc readImpl(rlp: var Rlp, T: typedesc[object|tuple], rlp.position += payloadOffset template op(field) = - field = rlp.read(type(field)) + when hasCustomPragma(field, rlpCustomSerialization): + field = rlp.read(type(field), + getCustomPragmaVal(field, rlpCustomSerialization)) + else: + field = rlp.read(type(field)) enumerateRlpFields(result, op) diff --git a/rlp/object_serialization.nim b/rlp/object_serialization.nim index a38dbbe..33d5ee7 100644 --- a/rlp/object_serialization.nim +++ b/rlp/object_serialization.nim @@ -20,3 +20,9 @@ template rlpInline* {.pragma.} ## This can be specified on a record field in order to avoid the ## default behavior of wrapping the record in a RLP list. +template rlpCustomSerialization*(T: typedesc) {.pragma.} + ## This can be specified on a record field to trigger the use of a + ## custom `read` and `append` serialization functions. + ## The custom functions will be passed an additional `type` argument + ## specefied in the pragma (it is used as an overload tag) + diff --git a/rlp/writer.nim b/rlp/writer.nim index 9935dc2..441e889 100644 --- a/rlp/writer.nim +++ b/rlp/writer.nim @@ -217,7 +217,12 @@ proc appendTupleOrObject(self; data: object|tuple, wrapInList: bool) = enumerateRlpFields(data, countFields) self.startList(fieldsCount) - template op(x) = append(self, x) + template op(field) = + when hasCustomPragma(field, rlpCustomSerialization): + append(self, field, getCustomPragmaVal(field, rlpCustomSerialization)) + else: + append(self, field) + enumerateRlpFields(data, op) proc appendImpl(self; data: object, wrapInList = wrapObjectsInList) {.inline.} = diff --git a/tests/test_object_serialization.nim b/tests/test_object_serialization.nim index aed34c2..b74abaa 100644 --- a/tests/test_object_serialization.nim +++ b/tests/test_object_serialization.nim @@ -17,6 +17,11 @@ type b: string f: Foo + CompressedFoo = object + + Bar2 = object + f {.rlpCustomSerialization: CompressedFoo}: Foo + rlpFields Foo, x, y, z @@ -25,6 +30,14 @@ rlpFields Transaction, proc default(T: typedesc): T = discard +proc append*(rlpWriter: var RlpWriter, f: Foo, tag: type CompressedFoo) = + rlpWriter.append(f.x) + rlpWriter.append(f.y.len) + +proc read*(rlp: var Rlp, T: type Foo, tag: type CompressedFoo): Foo = + result.x = rlp.read(uint64) + result.y = newString(rlp.read(int)) + test "encoding and decoding an object": var originalBar = Bar(b: "abracadabra", f: Foo(x: 5'u64, y: "hocus pocus", z: @[100, 200, 300])) @@ -47,3 +60,13 @@ test "encoding and decoding an object": t2.receiver == "Bob" t2.amount == 1000 +test "custom field serialization": + var origVal = Bar2(f: Foo(x: 10'u64, y: "y", z: @[])) + var bytes = encode(origVal) + var r = rlpFromBytes(bytes) + var restored = r.read(Bar2) + + check: + origVal.f.x == restored.f.x + origVal.f.y.len == restored.f.y.len +