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/

46
rlp.nim
View File

@ -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:

View File

@ -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.

View File

@ -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)