From 790aef0a9dbaef5ca4bfe9b6d5b8609630fc9ffa Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 18 May 2018 14:32:06 +0300 Subject: [PATCH] Make 'read' and 'append' easier to override for user-defined types The 'rlp.inspect' output have been improved. --- .gitignore | 1 + rlp.nim | 46 ++++++++++++++++++++++++++---------- rlp/object_serialization.nim | 5 ++++ rlp/writer.nim | 37 +++++++++++++++++------------ 4 files changed, 61 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 68e6fec..cc935b3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ * !*.* !*/ +*.exe nimcache/ diff --git a/rlp.nim b/rlp.nim index f0e2e89..153f9a4 100644 --- a/rlp.nim +++ b/rlp.nim @@ -267,19 +267,19 @@ proc listLen*(self: Rlp): int = for elem in rlp: inc result -proc read*(rlp: var Rlp, T: type string): string = +proc readImpl(rlp: var Rlp, T: type string): string = result = rlp.toString rlp.skipElem -proc read*(rlp: var Rlp, T: type Integer): Integer = +proc readImpl(rlp: var Rlp, T: type Integer): Integer = result = rlp.toInt(T) rlp.skipElem -proc read*(rlp: var Rlp, T: typedesc[enum]): T = +proc readImpl(rlp: var Rlp, T: typedesc[enum]): T = result = type(result)(rlp.toInt(int)) rlp.skipElem -proc read*[R, E](rlp: var Rlp, T: type array[R, E]): T = +proc readImpl[R, E](rlp: var Rlp, T: type array[R, E]): T = mixin read when E is (byte or char): @@ -306,7 +306,7 @@ proc read*[R, E](rlp: var Rlp, T: type array[R, E]): T = result[i] = rlp.read(E) inc i -proc read*[E](rlp: var Rlp, T: type seq[E]): T = +proc readImpl[E](rlp: var Rlp, T: type seq[E]): T = mixin read when E is (byte or char): @@ -323,11 +323,11 @@ proc read*[E](rlp: var Rlp, T: type seq[E]): T = for elem in rlp: result.add rlp.read(E) -proc read*[E](rlp: var Rlp, T: type openarray[E]): seq[E] = - result = read(rlp, seq[E]) +proc readImpl[E](rlp: var Rlp, T: type openarray[E]): seq[E] = + result = readImpl(rlp, seq[E]) -proc read*(rlp: var Rlp, T: typedesc[object|tuple], - wrappedInList = wrapObjectsInList): T = +proc readImpl(rlp: var Rlp, T: typedesc[object|tuple], + wrappedInList = wrapObjectsInList): T = mixin enumerateRlpFields, read if wrappedInList: @@ -356,14 +356,19 @@ proc toNodes*(self: var Rlp): RlpNode = result.bytes = toBytes() position = currentElemEnd() +# We define a single `read` template with a pretty low specifity +# score in order to facilitate easier overloading with user types: +template read*(rlp: var Rlp, T: typedesc): auto = + readImpl(rlp, T) + proc decode*(bytes: openarray[byte]): RlpNode = var bytesCopy = @bytes rlp = rlpFromBytes(bytesCopy.toRange()) return rlp.toNodes - template decode*(bytes: BytesRange, T: typedesc): untyped = + mixin read var rlp = rlpFromBytes bytes rlp.read(T) @@ -374,6 +379,13 @@ template decode*(bytes: openarray[byte], T: typedesc): T = proc append*(writer: var RlpWriter; rlp: Rlp) = append(writer, rlp.rawData) +proc isPrintable(s: string): bool = + for c in s: + if ord(c) < 32 or ord(c) >= 128: + return false + + return true + proc inspectAux(self: var Rlp, depth: int, output: var string) = if not hasData(): return @@ -388,9 +400,17 @@ proc inspectAux(self: var Rlp, depth: int, output: var string) = output.add "byte " output.add $bytes[position] elif self.isBlob: - output.add '"' - output.add self.toString - output.add '"' + let str = self.toString + if str.isPrintable: + output.add '"' + output.add str + output.add '"' + else: + output.add "blob(" & $str.len & ") [" + for c in str: + output.add $ord(c) + output.add "," + output[^1] = ']' else: output.add "{\n" for subitem in self: diff --git a/rlp/object_serialization.nim b/rlp/object_serialization.nim index 2effbc8..a38dbbe 100644 --- a/rlp/object_serialization.nim +++ b/rlp/object_serialization.nim @@ -15,3 +15,8 @@ macro rlpFields*(T: typedesc, fields: varargs[untyped]): untyped = result = quote do: template enumerateRlpFields*(`ins`: `T`, `op`: untyped) {.inject.} = `body` + +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. + diff --git a/rlp/writer.nim b/rlp/writer.nim index deca882..1502711 100644 --- a/rlp/writer.nim +++ b/rlp/writer.nim @@ -126,7 +126,7 @@ proc startList*(self; listSize: int) = else: pendingLists.add((listSize, output.len)) -template appendImpl(self; data, startMarker) = +template appendBlob(self; data, startMarker) = mixin baseAddr if data.len == 1 and byte(data[0]) < BLOB_START_MARKER: @@ -142,22 +142,22 @@ template appendImpl(self; data, startMarker) = maybeClosePendingLists() -proc append*(self; data: string) = - appendImpl(self, data, BLOB_START_MARKER) +proc appendImpl(self; data: string) = + appendBlob(self, data, BLOB_START_MARKER) proc appendBlob(self; data: openarray[byte]) = - appendImpl(self, data, BLOB_START_MARKER) + appendBlob(self, data, BLOB_START_MARKER) proc appendBlob(self; data: openarray[char]) = - appendImpl(self, data, BLOB_START_MARKER) + appendBlob(self, data, BLOB_START_MARKER) proc appendBytesRange(self; data: BytesRange) = - appendImpl(self, data, BLOB_START_MARKER) + appendBlob(self, data, BLOB_START_MARKER) -proc append*(self; data: MemRange) = - appendImpl(self, data, BLOB_START_MARKER) +proc appendImpl(self; data: MemRange) = + appendBlob(self, data, BLOB_START_MARKER) -proc append*(self; i: Integer) = +proc appendImpl(self; i: Integer) = type IntType = type(i) if i == IntType(0): @@ -171,10 +171,10 @@ proc append*(self; i: Integer) = self.maybeClosePendingLists() -template append*(self; e: enum) = - append(self, int(e)) +template appendImpl(self; e: enum) = + appendImpl(self, int(e)) -proc append*[T](self; listOrBlob: openarray[T]) = +proc appendImpl[T](self; listOrBlob: openarray[T]) = mixin append # TODO: This append proc should be overloaded by `openarray[byte]` after @@ -200,7 +200,7 @@ proc appendTupleOrObject(self; data: object|tuple, wrapInList: bool) = template op(x) = append(self, x) enumerateRlpFields(data, op) -proc append*(self; data: object, wrapInList = wrapObjectsInList) {.inline.} = +proc appendImpl(self; data: object, wrapInList = wrapObjectsInList) {.inline.} = # TODO: This append proc should be overloaded by `BytesRange` after # nim bug #7416 is fixed. when data is BytesRange: @@ -208,9 +208,13 @@ proc append*(self; data: object, wrapInList = wrapObjectsInList) {.inline.} = else: self.appendTupleOrObject(data, wrapInList) -proc append*(self; data: tuple, wrapInList = wrapObjectsInList) {.inline.} = +proc appendImpl(self; data: tuple, wrapInList = wrapObjectsInList) {.inline.} = self.appendTupleOrObject(data, wrapInList) +# We define a single `append` template with a pretty low specifity +# score in order to facilitate easier overloading with user types: +template append*[T](self; data: T) = appendImpl(self, data) + proc initRlpList*(listSize: int): RlpWriter = result = initRlpWriter() startList(result, listSize) @@ -222,6 +226,7 @@ proc finish*(self): BytesRange = result = output.toRange() proc encode*[T](v: T): BytesRange = + mixin append var writer = initRlpWriter() writer.append(v) return writer.finish @@ -231,10 +236,11 @@ macro encodeList*(args: varargs[untyped]): BytesRange = listLen = args.len writer = genSym(nskVar, "rlpWriter") body = newStmtList() + append = bindSym("append", brForceOpen) for arg in args: body.add quote do: - append(`writer`, `arg`) + `append`(`writer`, `arg`) result = quote do: var `writer` = initRlpList(`listLen`) @@ -244,6 +250,7 @@ macro encodeList*(args: varargs[untyped]): BytesRange = when false: # XXX: Currently fails with a malformed AST error on the args.len expression template encodeList*(args: varargs[untyped]): BytesRange = + mixin append var writer = initRlpList(args.len) for arg in args: writer.append(arg)