Skip to content
Open
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
98 changes: 74 additions & 24 deletions cbor_serialization/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ template read(p: CborParser): byte =
p.raiseUnexpectedValue("unexpected eof")
inputs.read(p.stream)

template read(p: CborParser, T: type): untyped =
read(p).T

func minorLen(minor: uint8): int =
assert minor in cborMinorLens
if minor < cborMinorLen1:
Expand Down Expand Up @@ -64,7 +67,7 @@ func `$`(major: CborMajor): string =
of CborMajor.SimpleOrFloat: "simple/float/break"

template parseStringLikeImpl(
p: var CborParser, majorExpected: CborMajor, body: untyped
p: var CborParser, majorExpected: CborMajor, strLen, prelude, body: untyped
) =
let c = p.read()
if c.major != majorExpected:
Expand All @@ -75,42 +78,64 @@ template parseStringLikeImpl(
let c2 = p.read()
if c2.major != majorExpected:
p.raiseUnexpectedValue($majorExpected, $c2.major)
for _ in 0 ..< readMinorValue(p, c2.minor):
let strLen {.inject.} = readMinorValue(p, c2.minor)
prelude
for _ in 0 ..< strLen:
body
discard p.read() # stop code
else:
# https://www.rfc-editor.org/rfc/rfc8949#section-3-3.2
for _ in 0 ..< readMinorValue(p, c.minor):
let strLen {.inject.} = readMinorValue(p, c.minor)
prelude
for _ in 0 ..< strLen:
body

template parseStringLikeImpl(
p: var CborParser,
majorExpected: CborMajor,
limit: int,
strLen, prelude, body: untyped,
) =
var i = 0'u64
parseStringLikeImpl(p, majorExpected, strLen):
i += strLen
if limit > 0 and i > limit.uint64:
p.raiseUnexpectedValue($majorExpected & " length limit reached")
prelude
do:
body

iterator parseStringLikeIt(
p: var CborParser, majorExpected: CborMajor, limit: int
): byte {.inline, raises: [IOError, CborReaderError].} =
var strLen = 0
parseStringLikeImpl(p, majorExpected):
inc strLen
if limit > 0 and strLen > limit:
p.raiseUnexpectedValue($majorExpected & " length limit reached")
parseStringLikeImpl(p, majorExpected, limit, strLen):
discard strLen
do:
yield p.read()

proc parseStringLike[T](
proc parseStringLike[T: string or seq[byte]](
p: var CborParser, majorExpected: CborMajor, limit: int, val: var T
) {.raises: [IOError, CborReaderError].} =
when T isnot (string or seq[byte] or CborVoid):
{.fatal: "`parseStringLike` only accepts string or `seq[byte]` or `CborVoid`".}
for v in parseStringLikeIt(p, majorExpected, limit):
when T is CborVoid:
discard v
elif T is string:
val.add v.char
else:
val.add v
type ElmType = typeof val[0]
val.setLen 0
var i = 0
parseStringLikeImpl(p, majorExpected, limit, strLen):
val.setLen val.len.uint64 + strLen
do:
val[i] = p.read ElmType
inc i

proc parseStringLike(
p: var CborParser, majorExpected: CborMajor, limit: int, val: var CborVoid
) {.raises: [IOError, CborReaderError].} =
for _ in parseStringLikeIt(p, majorExpected, limit):
discard val

# https://www.rfc-editor.org/rfc/rfc8949#section-3.1-2.6
proc parseByteString[T](
p: var CborParser, limit: int, val: var T
) {.raises: [IOError, CborReaderError].} =
parseStringLike[T](p, CborMajor.Bytes, limit, val)
parseStringLike(p, CborMajor.Bytes, limit, val)

proc parseByteString[T](
p: var CborParser, val: var T
Expand All @@ -121,7 +146,7 @@ proc parseByteString[T](
proc parseString[T](
p: var CborParser, limit: int, val: var T
) {.raises: [IOError, CborReaderError].} =
parseStringLike[T](p, CborMajor.Text, limit, val)
parseStringLike(p, CborMajor.Text, limit, val)

proc parseString[T](
p: var CborParser, val: var T
Expand All @@ -136,26 +161,48 @@ template enterNestedStructure(p: CborParser) =
template exitNestedStructure(p: CborParser) =
dec p.currDepth

template parseArrayLike(p: var CborParser, majorExpected: CborMajor, body: untyped) =
template parseArrayLikeImpl(
p: var CborParser, majorExpected: CborMajor, arrLen, prelude, body: untyped
) =
enterNestedStructure(p)
let c = p.read()
if c.major != majorExpected:
p.raiseUnexpectedValue($majorExpected, $c.major)
if c.minor == cborMinorIndef:
# https://www.rfc-editor.org/rfc/rfc8949#section-3.2.2
while p.peek() != cborBreakStopCode:
let arrLen {.inject.} = 1'u64
prelude
body
discard p.read() # stop code
else:
# https://www.rfc-editor.org/rfc/rfc8949#section-3-3.2
for _ in 0 ..< readMinorValue(p, c.minor):
let arrLen {.inject.} = readMinorValue(p, c.minor)
prelude
for _ in 0 ..< arrLen:
body
exitNestedStructure(p)

template parseArrayLikeImpl(p: var CborParser, majorExpected: CborMajor, body: untyped) =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nph] reported by reviewdog 🐶

Suggested change
template parseArrayLikeImpl(p: var CborParser, majorExpected: CborMajor, body: untyped) =
template parseArrayLikeImpl(
p: var CborParser, majorExpected: CborMajor, body: untyped
) =

parseArrayLikeImpl(p, majorExpected, arrLen):
discard arrLen
do:
body

# https://www.rfc-editor.org/rfc/rfc8949#section-3.1-2.10
template parseArray(p: var CborParser, arrLen, prelude, body: untyped) =
var i = 0'u64
parseArrayLikeImpl(p, CborMajor.Array, arrLen):
i += arrLen
if p.conf.arrayElementsLimit > 0 and i > p.conf.arrayElementsLimit.uint64:
p.raiseUnexpectedValue("`arrayElementsLimit` reached")
prelude
do:
body

template parseArray(p: var CborParser, idx, body: untyped) =
var idx {.inject.} = 0
parseArrayLike(p, CborMajor.Array):
parseArrayLikeImpl(p, CborMajor.Array):
if p.conf.arrayElementsLimit > 0 and idx + 1 > p.conf.arrayElementsLimit:
p.raiseUnexpectedValue("`arrayElementsLimit` reached")
body
Expand All @@ -164,7 +211,7 @@ template parseArray(p: var CborParser, idx, body: untyped) =
# https://www.rfc-editor.org/rfc/rfc8949#section-3.1-2.12
template parseObjectImpl(p: var CborParser, skipNullFields, keyAction, body: untyped) =
var numElem = 0
parseArrayLike(p, CborMajor.Map):
parseArrayLikeImpl(p, CborMajor.Map):
inc numElem
if p.conf.objectFieldsLimit > 0 and numElem > p.conf.objectFieldsLimit:
p.raiseUnexpectedValue("`objectFieldsLimit` reached")
Expand Down Expand Up @@ -450,6 +497,9 @@ template parseArray*(r: var CborReader, body: untyped) =
template parseArray*(r: var CborReader, idx, body: untyped) =
parseArray(r.parser, idx, body)

template parseArray*(r: var CborReader, arrLen, prelude, body: untyped) =
parseArray(r.parser, arrLen, prelude, body)

template skipNullFields(r: CborReader): untyped =
mixin flavorSkipNullFields

Expand Down
11 changes: 7 additions & 4 deletions cbor_serialization/reader_impl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,13 @@ proc read*[T](
) {.raises: [SerializationError, IOError].} =
mixin readValue

r.parseArray:
let lastPos = value.len
value.setLen(lastPos + 1)
readValue(r, value[lastPos])
value.setLen 0
var i = 0
r.parseArray(arrLen):
value.setLen value.len.uint64 + arrLen
do:
readValue(r, value[i])
inc i

proc read*[T: array](
r: var CborReader, value: var T
Expand Down
Loading