Make 'read' and 'append' easier to override for user-defined types

The 'rlp.inspect' output have been improved.
This commit is contained in:
Zahary Karadjov 2018-05-18 14:32:06 +03:00
parent 078aa161dd
commit 790aef0a9d
4 changed files with 61 additions and 28 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
* *
!*.* !*.*
!*/ !*/
*.exe
nimcache/ nimcache/

46
rlp.nim
View File

@ -267,19 +267,19 @@ proc listLen*(self: Rlp): int =
for elem in rlp: for elem in rlp:
inc result inc result
proc read*(rlp: var Rlp, T: type string): string = proc readImpl(rlp: var Rlp, T: type string): string =
result = rlp.toString result = rlp.toString
rlp.skipElem rlp.skipElem
proc read*(rlp: var Rlp, T: type Integer): Integer = proc readImpl(rlp: var Rlp, T: type Integer): Integer =
result = rlp.toInt(T) result = rlp.toInt(T)
rlp.skipElem 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)) result = type(result)(rlp.toInt(int))
rlp.skipElem 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 mixin read
when E is (byte or char): 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) result[i] = rlp.read(E)
inc i 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 mixin read
when E is (byte or char): 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: for elem in rlp:
result.add rlp.read(E) result.add rlp.read(E)
proc read*[E](rlp: var Rlp, T: type openarray[E]): seq[E] = proc readImpl[E](rlp: var Rlp, T: type openarray[E]): seq[E] =
result = read(rlp, seq[E]) result = readImpl(rlp, seq[E])
proc read*(rlp: var Rlp, T: typedesc[object|tuple], proc readImpl(rlp: var Rlp, T: typedesc[object|tuple],
wrappedInList = wrapObjectsInList): T = wrappedInList = wrapObjectsInList): T =
mixin enumerateRlpFields, read mixin enumerateRlpFields, read
if wrappedInList: if wrappedInList:
@ -356,14 +356,19 @@ proc toNodes*(self: var Rlp): RlpNode =
result.bytes = toBytes() result.bytes = toBytes()
position = currentElemEnd() 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 = proc decode*(bytes: openarray[byte]): RlpNode =
var var
bytesCopy = @bytes bytesCopy = @bytes
rlp = rlpFromBytes(bytesCopy.toRange()) rlp = rlpFromBytes(bytesCopy.toRange())
return rlp.toNodes return rlp.toNodes
template decode*(bytes: BytesRange, T: typedesc): untyped = template decode*(bytes: BytesRange, T: typedesc): untyped =
mixin read
var rlp = rlpFromBytes bytes var rlp = rlpFromBytes bytes
rlp.read(T) rlp.read(T)
@ -374,6 +379,13 @@ template decode*(bytes: openarray[byte], T: typedesc): T =
proc append*(writer: var RlpWriter; rlp: Rlp) = proc append*(writer: var RlpWriter; rlp: Rlp) =
append(writer, rlp.rawData) 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) = proc inspectAux(self: var Rlp, depth: int, output: var string) =
if not hasData(): if not hasData():
return return
@ -388,9 +400,17 @@ proc inspectAux(self: var Rlp, depth: int, output: var string) =
output.add "byte " output.add "byte "
output.add $bytes[position] output.add $bytes[position]
elif self.isBlob: elif self.isBlob:
output.add '"' let str = self.toString
output.add self.toString if str.isPrintable:
output.add '"' 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: else:
output.add "{\n" output.add "{\n"
for subitem in self: for subitem in self:

View File

@ -15,3 +15,8 @@ macro rlpFields*(T: typedesc, fields: varargs[untyped]): untyped =
result = quote do: result = quote do:
template enumerateRlpFields*(`ins`: `T`, `op`: untyped) {.inject.} = template enumerateRlpFields*(`ins`: `T`, `op`: untyped) {.inject.} =
`body` `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.

View File

@ -126,7 +126,7 @@ proc startList*(self; listSize: int) =
else: else:
pendingLists.add((listSize, output.len)) pendingLists.add((listSize, output.len))
template appendImpl(self; data, startMarker) = template appendBlob(self; data, startMarker) =
mixin baseAddr mixin baseAddr
if data.len == 1 and byte(data[0]) < BLOB_START_MARKER: if data.len == 1 and byte(data[0]) < BLOB_START_MARKER:
@ -142,22 +142,22 @@ template appendImpl(self; data, startMarker) =
maybeClosePendingLists() maybeClosePendingLists()
proc append*(self; data: string) = proc appendImpl(self; data: string) =
appendImpl(self, data, BLOB_START_MARKER) appendBlob(self, data, BLOB_START_MARKER)
proc appendBlob(self; data: openarray[byte]) = proc appendBlob(self; data: openarray[byte]) =
appendImpl(self, data, BLOB_START_MARKER) appendBlob(self, data, BLOB_START_MARKER)
proc appendBlob(self; data: openarray[char]) = proc appendBlob(self; data: openarray[char]) =
appendImpl(self, data, BLOB_START_MARKER) appendBlob(self, data, BLOB_START_MARKER)
proc appendBytesRange(self; data: BytesRange) = proc appendBytesRange(self; data: BytesRange) =
appendImpl(self, data, BLOB_START_MARKER) appendBlob(self, data, BLOB_START_MARKER)
proc append*(self; data: MemRange) = proc appendImpl(self; data: MemRange) =
appendImpl(self, data, BLOB_START_MARKER) appendBlob(self, data, BLOB_START_MARKER)
proc append*(self; i: Integer) = proc appendImpl(self; i: Integer) =
type IntType = type(i) type IntType = type(i)
if i == IntType(0): if i == IntType(0):
@ -171,10 +171,10 @@ proc append*(self; i: Integer) =
self.maybeClosePendingLists() self.maybeClosePendingLists()
template append*(self; e: enum) = template appendImpl(self; e: enum) =
append(self, int(e)) appendImpl(self, int(e))
proc append*[T](self; listOrBlob: openarray[T]) = proc appendImpl[T](self; listOrBlob: openarray[T]) =
mixin append mixin append
# TODO: This append proc should be overloaded by `openarray[byte]` after # 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) template op(x) = append(self, x)
enumerateRlpFields(data, op) 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 # TODO: This append proc should be overloaded by `BytesRange` after
# nim bug #7416 is fixed. # nim bug #7416 is fixed.
when data is BytesRange: when data is BytesRange:
@ -208,9 +208,13 @@ proc append*(self; data: object, wrapInList = wrapObjectsInList) {.inline.} =
else: else:
self.appendTupleOrObject(data, wrapInList) self.appendTupleOrObject(data, wrapInList)
proc append*(self; data: tuple, wrapInList = wrapObjectsInList) {.inline.} = proc appendImpl(self; data: tuple, wrapInList = wrapObjectsInList) {.inline.} =
self.appendTupleOrObject(data, wrapInList) 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 = proc initRlpList*(listSize: int): RlpWriter =
result = initRlpWriter() result = initRlpWriter()
startList(result, listSize) startList(result, listSize)
@ -222,6 +226,7 @@ proc finish*(self): BytesRange =
result = output.toRange() result = output.toRange()
proc encode*[T](v: T): BytesRange = proc encode*[T](v: T): BytesRange =
mixin append
var writer = initRlpWriter() var writer = initRlpWriter()
writer.append(v) writer.append(v)
return writer.finish return writer.finish
@ -231,10 +236,11 @@ macro encodeList*(args: varargs[untyped]): BytesRange =
listLen = args.len listLen = args.len
writer = genSym(nskVar, "rlpWriter") writer = genSym(nskVar, "rlpWriter")
body = newStmtList() body = newStmtList()
append = bindSym("append", brForceOpen)
for arg in args: for arg in args:
body.add quote do: body.add quote do:
append(`writer`, `arg`) `append`(`writer`, `arg`)
result = quote do: result = quote do:
var `writer` = initRlpList(`listLen`) var `writer` = initRlpList(`listLen`)
@ -244,6 +250,7 @@ macro encodeList*(args: varargs[untyped]): BytesRange =
when false: when false:
# XXX: Currently fails with a malformed AST error on the args.len expression # XXX: Currently fails with a malformed AST error on the args.len expression
template encodeList*(args: varargs[untyped]): BytesRange = template encodeList*(args: varargs[untyped]): BytesRange =
mixin append
var writer = initRlpList(args.len) var writer = initRlpList(args.len)
for arg in args: for arg in args:
writer.append(arg) writer.append(arg)