Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ template decodeImpl[InputType](
# from the fact that the dynamic dispatch mechanisms used in
# faststreams may be reading from a file or a network device.
{.noSideEffect.}:
var reader = unpackForwarded(init, [ReaderType, stream, params])
var reader = unpackForwarded(init, [ReaderType, stream], params)
reader.readValue(result)
except IOError:
raiseAssert "memory input doesn't raise IOError"
Expand All @@ -90,7 +90,7 @@ template decodeImpl[InputType](
# Something's terribly wrong if we're reaching this point
raiseAssert "negative memory input length"

unpackForwarded(decodeProc, [inputParam, params])
unpackArgs(decodeProc, [inputParam, params])

template decode*(
Format: type SerializationFormat,
Expand Down
26 changes: 12 additions & 14 deletions serialization/macros.nim
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,22 @@ macro forward*(args, prc: untyped): untyped =
# this exact ident instance ..
prc.params.add nnkIdentDefs.newTree(ident $arg[0], nnkCall.newTree(ident "typeof", arg[1]), newEmptyNode())
else:
prc.params.add nnkIdentDefs.newTree(ident "fwd" & $i, nnkCall.newTree(ident "typeof", arg[0]), newEmptyNode())
i += 1
prc.params.add nnkIdentDefs.newTree(ident "fwd" & $i, nnkCall.newTree(ident "typeof", arg), newEmptyNode())
i += 1
prc

macro unpackForwarded*(callee: untyped, args: untyped): untyped =
macro unpackForwarded*(callee: untyped, args: untyped, params: varargs[untyped]): untyped =
# pass on `args` to callee - args should be an array of parameters to pass
# on to callee where one of them should be the `varargs[untyped]` passed to
# the forward macro. Messy.
# on to callee; `varargs[untyped]` should be the params passed to the forward macro.
result = newCall(callee)
var i = 0

for arg in usefulArgs(args):
if arg.kind == nnkArgList:
for subarg in usefulArgs(arg):
if subarg.kind == nnkExprEqExpr:
result.add nnkExprEqExpr.newTree(ident $subarg[0], ident $subarg[0])
else:
result.add ident "fwd" & $i
i += 1
result.add arg

var i = 0
for arg in usefulArgs(params):
if arg.kind == nnkExprEqExpr:
result.add nnkExprEqExpr.newTree(ident $arg[0], ident $arg[0])
else:
result.add arg
result.add ident "fwd" & $i
i += 1
98 changes: 98 additions & 0 deletions tests/test_serializer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,101 @@ suite "Rountrips":
var val: ref string
let ser = Ser.encode(val)
check Ser.decode(ser, typeof(val)).isNil()

type StringLimErr = object of SerializationError
type StringLim = distinct string

proc `==`(a, b: StringLim): bool {.borrow.}
proc add(a: var StringLim, b: char) {.borrow.}

proc readValue(r: var SerReader, val: var StringLim) {.raises: [IOError, SerializationError].} =
consumeKind r, SerKind.String
let L = r.readUint64()
if L > r.conf.limit.uint64:
raise newException(StringLimErr, "limit err")
for _ in 0 ..< L:
val.add r.stream.read().char

suite "Config":
test "pass let conf":
let val = "1234567890"
let ser = Ser.encode(val)

let conf10 = SerConf(limit: 10)
check Ser.decode(ser, StringLim, conf = conf10) == val.StringLim
check Ser.decode(ser, StringLim, conf10) == val.StringLim

let conf5 = SerConf(limit: 5)
expect StringLimErr:
discard Ser.decode(ser, StringLim, conf = conf5)
expect StringLimErr:
discard Ser.decode(ser, StringLim, conf5)

test "pass const conf":
let val = "1234567890"
let ser = Ser.encode(val)

const conf10 = SerConf(limit: 10)
check Ser.decode(ser, StringLim, conf = conf10) == val.StringLim
check Ser.decode(ser, StringLim, conf10) == val.StringLim

const conf5 = SerConf(limit: 5)
expect StringLimErr:
discard Ser.decode(ser, StringLim, conf = conf5)
expect StringLimErr:
discard Ser.decode(ser, StringLim, conf5)

test "pass inlined conf":
let val = "1234567890"
let ser = Ser.encode(val)

check Ser.decode(ser, StringLim, conf = SerConf(limit: 10)) == val.StringLim
check Ser.decode(ser, StringLim, SerConf(limit: 10)) == val.StringLim

expect StringLimErr:
discard Ser.decode(ser, StringLim, conf = SerConf(limit: 5))
expect StringLimErr:
discard Ser.decode(ser, StringLim, SerConf(limit: 5))

test "pass expression conf":
let val = "1234567890"
let ser = Ser.encode(val)

template conf10: untyped =
var conf = SerConf(limit: 10)
conf

check Ser.decode(ser, StringLim, conf = conf10) == val.StringLim
check Ser.decode(ser, StringLim, conf10) == val.StringLim

template conf5: untyped =
var conf = SerConf(limit: 5)
conf

expect StringLimErr:
discard Ser.decode(ser, StringLim, conf = conf5)
expect StringLimErr:
discard Ser.decode(ser, StringLim, conf5)

test "multi params":
func init(
R: type SerReader,
stream: InputStream,
limit1: int,
limit2: int,
conf = default(SerConf)
): R =
R(stream: stream, conf: SerConf(limit: conf.limit + limit1 + limit2))

let val = "1234567890"
let ser = Ser.encode(val)

let lim1 = 10
let lim2 = 0
check:
Ser.decode(ser, StringLim, 10, 0) == val.StringLim
Ser.decode(ser, StringLim, 0, 10) == val.StringLim
Ser.decode(ser, StringLim, 0, 0, SerConf(limit: 10)) == val.StringLim
Ser.decode(ser, StringLim, limit1 = 10, limit2 = 0) == val.StringLim
Ser.decode(ser, StringLim, limit1 = 0, limit2 = 10) == val.StringLim
Ser.decode(ser, StringLim, lim1, lim2) == val.StringLim
8 changes: 6 additions & 2 deletions tests/utils/serializer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ Ser.setWriter SerWriter, PreferredOutput = seq[byte]
func init*(W: type SerWriter, stream: OutputStream): W =
W(stream: stream)

type SerConf* = object
limit*: int

type SerReader* = object
stream*: InputStream
conf*: SerConf

Ser.setReader SerReader

func init*(R: type SerReader, stream: InputStream): R =
R(stream: stream)
func init*(R: type SerReader, stream: InputStream, conf = default(SerConf)): R =
R(stream: stream, conf: conf)

type SerKind* {.pure.} = enum
Int = 0
Expand Down