mirror of
https://github.com/logos-storage/nim-serde.git
synced 2026-01-07 08:03:07 +00:00
improve error handling for cbor serialization
This commit is contained in:
parent
beac0e2a32
commit
52bc943948
@ -9,11 +9,11 @@ import pkg/questionable/results
|
||||
export results
|
||||
export types
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
func isIndefinite*(c: CborParser): bool {.inline.} = c.minor == 31
|
||||
## Return true if the parser is positioned on an item of indefinite length.
|
||||
|
||||
{.push raises: [].}
|
||||
proc open*(c: var CborParser; s: Stream) =
|
||||
## Begin parsing a stream of CBOR in binary form.
|
||||
## The parser will be initialized in an EOF state, call
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
{.push checks: off.}
|
||||
|
||||
import std/[streams, options, tables, typetraits, math, endians, times, base64]
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import ../utils/errors
|
||||
import ./types
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
func isHalfPrecise(single: float32): bool =
|
||||
# TODO: check for subnormal false-positives
|
||||
let val = cast[uint32](single)
|
||||
@ -34,298 +37,335 @@ func floatHalf(single: float32): uint16 =
|
||||
func initialByte(major, minor: Natural): uint8 {.inline.} =
|
||||
uint8((major shl 5) or (minor and 0b11111))
|
||||
|
||||
proc writeInitial[T: SomeInteger](str: Stream; m: uint8; n: T) =
|
||||
proc writeInitial[T: SomeInteger](str: Stream; m: uint8; n: T): ?!void =
|
||||
## Write the initial integer of a CBOR item.
|
||||
|
||||
let m = m shl 5
|
||||
when T is byte:
|
||||
if n < 24:
|
||||
str.write(m or n.uint8)
|
||||
try:
|
||||
let m = m shl 5
|
||||
when T is byte:
|
||||
if n < 24:
|
||||
str.write(m or n.uint8)
|
||||
else:
|
||||
str.write(m or 24'u8)
|
||||
str.write(n)
|
||||
else:
|
||||
str.write(m or 24'u8)
|
||||
str.write(n)
|
||||
else:
|
||||
if n < 24:
|
||||
str.write(m or n.uint8)
|
||||
elif uint64(n) <= uint64(uint8.high):
|
||||
str.write(m or 24'u8)
|
||||
str.write(n.uint8)
|
||||
elif uint64(n) <= uint64(uint16.high):
|
||||
str.write(m or 25'u8)
|
||||
str.write((uint8)n shr 8)
|
||||
str.write((uint8)n)
|
||||
elif uint64(n) <= uint64(uint32.high):
|
||||
str.write(m or 26'u8)
|
||||
for i in countdown(24, 8, 8):
|
||||
{.unroll.}
|
||||
str.write((uint8)n shr i)
|
||||
str.write((uint8)n)
|
||||
else:
|
||||
str.write(m or 27'u8)
|
||||
for i in countdown(56, 8, 8):
|
||||
{.unroll.}
|
||||
str.write((uint8)n shr i)
|
||||
str.write((uint8)n)
|
||||
# {.pop.}
|
||||
if n < 24:
|
||||
str.write(m or n.uint8)
|
||||
elif uint64(n) <= uint64(uint8.high):
|
||||
str.write(m or 24'u8)
|
||||
str.write(n.uint8)
|
||||
elif uint64(n) <= uint64(uint16.high):
|
||||
str.write(m or 25'u8)
|
||||
str.write((uint8)n shr 8)
|
||||
str.write((uint8)n)
|
||||
elif uint64(n) <= uint64(uint32.high):
|
||||
str.write(m or 26'u8)
|
||||
for i in countdown(24, 8, 8):
|
||||
{.unroll.}
|
||||
str.write((uint8)n shr i)
|
||||
str.write((uint8)n)
|
||||
else:
|
||||
str.write(m or 27'u8)
|
||||
for i in countdown(56, 8, 8):
|
||||
{.unroll.}
|
||||
str.write((uint8)n shr i)
|
||||
str.write((uint8)n)
|
||||
success()
|
||||
except IOError as e:
|
||||
return failure(e.msg)
|
||||
except OSError as o:
|
||||
return failure(o.msg)
|
||||
|
||||
proc writeCborArrayLen*(str: Stream; len: Natural) =
|
||||
proc writeCborArrayLen*(str: Stream; len: Natural): ?!void =
|
||||
## Write a marker to the stream that initiates an array of ``len`` items.
|
||||
str.writeInitial(4, len)
|
||||
|
||||
proc writeCborIndefiniteArrayLen*(str: Stream) =
|
||||
proc writeCborIndefiniteArrayLen*(str: Stream): ?!void =
|
||||
## Write a marker to the stream that initiates an array of indefinite length.
|
||||
## Indefinite length arrays are composed of an indefinite amount of arrays
|
||||
## of definite lengths.
|
||||
str.write(initialByte(4, 31))
|
||||
catch str.write(initialByte(4, 31))
|
||||
|
||||
proc writeCborMapLen*(str: Stream; len: Natural) =
|
||||
proc writeCborMapLen*(str: Stream; len: Natural): ?!void =
|
||||
## Write a marker to the stream that initiates an map of ``len`` pairs.
|
||||
str.writeInitial(5, len)
|
||||
|
||||
proc writeCborIndefiniteMapLen*(str: Stream) =
|
||||
proc writeCborIndefiniteMapLen*(str: Stream): ?!void =
|
||||
## Write a marker to the stream that initiates a map of indefinite length.
|
||||
## Indefinite length maps are composed of an indefinite amount of maps
|
||||
## of definite length.
|
||||
str.write(initialByte(5, 31))
|
||||
catch str.write(initialByte(5, 31))
|
||||
|
||||
proc writeCborBreak*(str: Stream) =
|
||||
proc writeCborBreak*(str: Stream): ?!void =
|
||||
## Write a marker to the stream that ends an indefinite array or map.
|
||||
str.write(initialByte(7, 31))
|
||||
catch str.write(initialByte(7, 31))
|
||||
|
||||
proc writeCborTag*(str: Stream; tag: Natural) {.inline.} =
|
||||
proc writeCborTag*(str: Stream; tag: Natural): ?!void {.inline.} =
|
||||
## Write a tag for the next CBOR item to a binary stream.
|
||||
str.writeInitial(6, tag)
|
||||
|
||||
proc writeCbor*(str: Stream; buf: pointer; len: int) =
|
||||
proc writeCbor*(str: Stream; buf: pointer; len: int): ?!void =
|
||||
## Write a raw buffer to a CBOR `Stream`.
|
||||
str.writeInitial(BytesMajor, len)
|
||||
if len > 0: str.writeData(buf, len)
|
||||
?str.writeInitial(BytesMajor, len)
|
||||
if len > 0:
|
||||
return catch str.writeData(buf, len)
|
||||
|
||||
proc isSorted*(n: CborNode): bool {.gcsafe.}
|
||||
proc isSorted*(n: CborNode): ?!bool {.gcsafe.}
|
||||
|
||||
proc writeCbor*[T](str: Stream; v: T) =
|
||||
proc writeCbor*[T](str: Stream; v: T): ?!void =
|
||||
## Write the CBOR binary representation of a `T` to a `Stream`.
|
||||
## The behavior of this procedure can be extended or overriden
|
||||
## by defining `proc writeCborHook(str: Stream; v: T)` for specific
|
||||
## types `T`.
|
||||
when T is CborNode:
|
||||
if v.tag.isSome:
|
||||
str.writeCborTag(v.tag.get)
|
||||
case v.kind:
|
||||
of cborUnsigned:
|
||||
str.writeCbor(v.uint)
|
||||
of cborNegative:
|
||||
str.writeCbor(v.int)
|
||||
of cborBytes:
|
||||
str.writeInitial(cborBytes.uint8, v.bytes.len)
|
||||
for b in v.bytes.items:
|
||||
str.write(b)
|
||||
of cborText:
|
||||
str.writeInitial(cborText.uint8, v.text.len)
|
||||
str.write(v.text)
|
||||
of cborArray:
|
||||
str.writeInitial(4, v.seq.len)
|
||||
for e in v.seq:
|
||||
str.writeCbor(e)
|
||||
of cborMap:
|
||||
assert(v.isSorted, "refusing to write unsorted map to stream")
|
||||
str.writeInitial(5, v.map.len)
|
||||
for k, f in v.map.pairs:
|
||||
str.writeCbor(k)
|
||||
str.writeCbor(f)
|
||||
of cborTag:
|
||||
discard
|
||||
of cborSimple:
|
||||
if v.simple > 31'u or v.simple == 24:
|
||||
str.write(initialByte(cborSimple.uint8, 24))
|
||||
str.write(v.simple)
|
||||
else:
|
||||
str.write(initialByte(cborSimple.uint8, v.simple))
|
||||
of cborFloat:
|
||||
str.writeCbor(v.float)
|
||||
of cborRaw:
|
||||
str.write(v.raw)
|
||||
elif compiles(writeCborHook(str, v)):
|
||||
writeCborHook(str, v)
|
||||
elif T is SomeUnsignedInt:
|
||||
str.writeInitial(0, v)
|
||||
elif T is SomeSignedInt:
|
||||
if v < 0:
|
||||
# Major type 1
|
||||
str.writeInitial(1, -1 - v)
|
||||
else:
|
||||
# Major type 0
|
||||
str.writeInitial(0, v)
|
||||
elif T is seq[byte]:
|
||||
str.writeInitial(BytesMajor, v.len)
|
||||
if v.len > 0:
|
||||
str.writeData(unsafeAddr v[0], v.len)
|
||||
elif T is openArray[char | uint8 | int8]:
|
||||
str.writeInitial(BytesMajor, v.len)
|
||||
if v.len > 0:
|
||||
str.writeData(unsafeAddr v[0], v.len)
|
||||
elif T is string:
|
||||
str.writeInitial(TextMajor, v.len)
|
||||
str.write(v)
|
||||
elif T is array | seq:
|
||||
str.writeInitial(4, v.len)
|
||||
for e in v.items:
|
||||
writeCbor(str, e)
|
||||
elif T is tuple:
|
||||
str.writeInitial(4, T.tupleLen)
|
||||
for f in v.fields: str.writeCbor(f)
|
||||
elif T is ptr | ref:
|
||||
if system.`==`(v, nil):
|
||||
# Major type 7
|
||||
str.write(Null)
|
||||
else: writeCbor(str, v[])
|
||||
elif T is object:
|
||||
var n: uint
|
||||
for _, _ in v.fieldPairs:
|
||||
inc n
|
||||
str.writeInitial(5, n)
|
||||
for k, f in v.fieldPairs:
|
||||
str.writeCbor(k)
|
||||
str.writeCbor(f)
|
||||
elif T is bool:
|
||||
str.write(initialByte(7, (if v: 21 else: 20)))
|
||||
elif T is SomeFloat:
|
||||
case v.classify
|
||||
of fcNormal, fcSubnormal:
|
||||
let single = v.float32
|
||||
if single.float64 == v.float64:
|
||||
if single.isHalfPrecise:
|
||||
let half = floatHalf(single)
|
||||
str.write(initialByte(7, 25))
|
||||
when system.cpuEndian == bigEndian:
|
||||
str.write(half)
|
||||
else:
|
||||
var be: uint16
|
||||
swapEndian16 be.addr, half.unsafeAddr
|
||||
str.write(be)
|
||||
try:
|
||||
when T is CborNode:
|
||||
if v.tag.isSome:
|
||||
return str.writeCborTag(v.tag.get)
|
||||
case v.kind:
|
||||
of cborUnsigned:
|
||||
return str.writeCbor(v.uint)
|
||||
of cborNegative:
|
||||
return str.writeCbor(v.int)
|
||||
of cborBytes:
|
||||
?str.writeInitial(cborBytes.uint8, v.bytes.len)
|
||||
for b in v.bytes.items:
|
||||
str.write(b)
|
||||
of cborText:
|
||||
?str.writeInitial(cborText.uint8, v.text.len)
|
||||
str.write(v.text)
|
||||
of cborArray:
|
||||
?str.writeInitial(4, v.seq.len)
|
||||
for e in v.seq:
|
||||
?str.writeCbor(e)
|
||||
of cborMap:
|
||||
without isSortedRes =? v.isSorted, error:
|
||||
return failure(error)
|
||||
if not isSortedRes:
|
||||
return failure(newSerdeError("refusing to write unsorted map to stream"))
|
||||
?str.writeInitial(5, v.map.len)
|
||||
for k, f in v.map.pairs:
|
||||
?str.writeCbor(k)
|
||||
?str.writeCbor(f)
|
||||
of cborTag:
|
||||
discard
|
||||
of cborSimple:
|
||||
if v.simple > 31'u or v.simple == 24:
|
||||
str.write(initialByte(cborSimple.uint8, 24))
|
||||
str.write(v.simple)
|
||||
else:
|
||||
str.write initialByte(7, 26)
|
||||
when system.cpuEndian == bigEndian:
|
||||
str.write(single)
|
||||
else:
|
||||
var be: uint32
|
||||
swapEndian32 be.addr, single.unsafeAddr
|
||||
str.write(be)
|
||||
str.write(initialByte(cborSimple.uint8, v.simple))
|
||||
of cborFloat:
|
||||
return str.writeCbor(v.float)
|
||||
of cborRaw:
|
||||
str.write(v.raw)
|
||||
elif compiles(writeCborHook(str, v)):
|
||||
writeCborHook(str, v)
|
||||
elif T is SomeUnsignedInt:
|
||||
?str.writeInitial(0, v)
|
||||
elif T is SomeSignedInt:
|
||||
if v < 0:
|
||||
# Major type 1
|
||||
?str.writeInitial(1, -1 - v)
|
||||
else:
|
||||
str.write initialByte(7, 27)
|
||||
when system.cpuEndian == bigEndian:
|
||||
str.write(v)
|
||||
# Major type 0
|
||||
?str.writeInitial(0, v)
|
||||
elif T is seq[byte]:
|
||||
?str.writeInitial(BytesMajor, v.len)
|
||||
if v.len > 0:
|
||||
str.writeData(unsafeAddr v[0], v.len)
|
||||
elif T is openArray[char | uint8 | int8]:
|
||||
?str.writeInitial(BytesMajor, v.len)
|
||||
if v.len > 0:
|
||||
str.writeData(unsafeAddr v[0], v.len)
|
||||
elif T is string:
|
||||
?str.writeInitial(TextMajor, v.len)
|
||||
str.write(v)
|
||||
elif T is array | seq:
|
||||
?str.writeInitial(4, v.len)
|
||||
for e in v.items:
|
||||
?str.writeCbor(e)
|
||||
elif T is tuple:
|
||||
?str.writeInitial(4, T.tupleLen)
|
||||
for f in v.fields: ?str.writeCbor(f)
|
||||
elif T is ptr | ref:
|
||||
if system.`==`(v, nil):
|
||||
# Major type 7
|
||||
str.write(Null)
|
||||
else: ?str.writeCbor(v[])
|
||||
elif T is object:
|
||||
var n: uint
|
||||
for _, _ in v.fieldPairs:
|
||||
inc n
|
||||
?str.writeInitial(5, n)
|
||||
for k, f in v.fieldPairs:
|
||||
?str.writeCbor(k)
|
||||
?str.writeCbor(f)
|
||||
elif T is bool:
|
||||
str.write(initialByte(7, (if v: 21 else: 20)))
|
||||
elif T is SomeFloat:
|
||||
case v.classify
|
||||
of fcNormal, fcSubnormal:
|
||||
let single = v.float32
|
||||
if single.float64 == v.float64:
|
||||
if single.isHalfPrecise:
|
||||
let half = floatHalf(single)
|
||||
str.write(initialByte(7, 25))
|
||||
when system.cpuEndian == bigEndian:
|
||||
str.write(half)
|
||||
else:
|
||||
var be: uint16
|
||||
swapEndian16 be.addr, half.unsafeAddr
|
||||
str.write(be)
|
||||
else:
|
||||
str.write initialByte(7, 26)
|
||||
when system.cpuEndian == bigEndian:
|
||||
str.write(single)
|
||||
else:
|
||||
var be: uint32
|
||||
swapEndian32 be.addr, single.unsafeAddr
|
||||
str.write(be)
|
||||
else:
|
||||
var be: float64
|
||||
swapEndian64 be.addr, v.unsafeAddr
|
||||
str.write be
|
||||
return
|
||||
of fcZero:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x00)
|
||||
of fcNegZero:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x80)
|
||||
of fcInf:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x7c)
|
||||
of fcNan:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x7e)
|
||||
of fcNegInf:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0xfc)
|
||||
str.write((char)0x00)
|
||||
str.write initialByte(7, 27)
|
||||
when system.cpuEndian == bigEndian:
|
||||
str.write(v)
|
||||
else:
|
||||
var be: float64
|
||||
swapEndian64 be.addr, v.unsafeAddr
|
||||
str.write be
|
||||
|
||||
proc writeCborArray*(str: Stream; args: varargs[CborNode, toCbor]) =
|
||||
return success()
|
||||
of fcZero:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x00)
|
||||
of fcNegZero:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x80)
|
||||
of fcInf:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x7c)
|
||||
of fcNan:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0x7e)
|
||||
of fcNegInf:
|
||||
str.write initialByte(7, 25)
|
||||
str.write((char)0xfc)
|
||||
str.write((char)0x00)
|
||||
success()
|
||||
except IOError as io:
|
||||
return failure(io.msg)
|
||||
except OSError as os:
|
||||
return failure(os.msg)
|
||||
|
||||
proc writeCborArray*(str: Stream; args: varargs[CborNode, toCbor]): ?!void =
|
||||
## Encode to a CBOR array in binary form. This magic doesn't
|
||||
## always work, some arguments may need to be explicitly
|
||||
## converted with ``toCbor`` before passing.
|
||||
str.writeCborArrayLen(args.len)
|
||||
?str.writeCborArrayLen(args.len)
|
||||
for x in args:
|
||||
str.writeCbor(x)
|
||||
?str.writeCbor(x)
|
||||
|
||||
proc encode*[T](v: T): string =
|
||||
proc encode*[T](v: T): ?!string =
|
||||
## Encode an arbitrary value to CBOR binary representation.
|
||||
## A wrapper over ``writeCbor``.
|
||||
let s = newStringStream()
|
||||
s.writeCbor(v)
|
||||
s.data
|
||||
let res = s.writeCbor(v)
|
||||
if res.isFailure:
|
||||
return failure(res.error)
|
||||
success(s.data)
|
||||
|
||||
proc toRaw*(n: CborNode): CborNode =
|
||||
proc toRaw*(n: CborNode): ?!CborNode =
|
||||
## Reduce a CborNode to a string of bytes.
|
||||
if n.kind == cborRaw: n
|
||||
else: CborNode(kind: cborRaw, raw: encode(n))
|
||||
if n.kind == cborRaw:
|
||||
return success(n)
|
||||
else:
|
||||
without res =? encode(n), error:
|
||||
return failure(error)
|
||||
return success(CborNode(kind: cborRaw, raw: res))
|
||||
|
||||
proc isSorted(n: CborNode): bool =
|
||||
proc isSorted(n: CborNode): ?!bool =
|
||||
## Check if the item is sorted correctly.
|
||||
var lastRaw = ""
|
||||
for key in n.map.keys:
|
||||
let thisRaw = key.toRaw.raw
|
||||
without res =? key.toRaw, error:
|
||||
return failure(error.msg)
|
||||
let thisRaw = res.raw
|
||||
if lastRaw != "":
|
||||
if cmp(lastRaw, thisRaw) > 0: return false
|
||||
if cmp(lastRaw, thisRaw) > 0: return success(false)
|
||||
lastRaw = thisRaw
|
||||
true
|
||||
success(true)
|
||||
|
||||
proc sort*(n: var CborNode) =
|
||||
proc sort*(n: var CborNode): ?!void =
|
||||
## Sort a CBOR map object.
|
||||
var tmp = initOrderedTable[CborNode, CborNode](n.map.len.nextPowerOfTwo)
|
||||
for key, val in n.map.mpairs:
|
||||
tmp[key.toRaw] = move(val)
|
||||
sort(tmp) do (x, y: tuple[k: CborNode; v: CborNode]) -> int:
|
||||
result = cmp(x.k.raw, y.k.raw)
|
||||
n.map = move tmp
|
||||
try:
|
||||
var tmp = initOrderedTable[CborNode, CborNode](n.map.len.nextPowerOfTwo)
|
||||
for key, val in n.map.mpairs:
|
||||
without res =? key.toRaw, error:
|
||||
return failure(error)
|
||||
tmp[res] = move(val)
|
||||
sort(tmp) do (x, y: tuple[k: CborNode; v: CborNode]) -> int:
|
||||
result = cmp(x.k.raw, y.k.raw)
|
||||
n.map = move tmp
|
||||
success()
|
||||
except Exception as e:
|
||||
return failure(e.msg)
|
||||
|
||||
proc writeCborHook*(str: Stream; dt: DateTime) =
|
||||
proc writeCborHook*(str: Stream; dt: DateTime): ?!void =
|
||||
## Write a `DateTime` using the tagged string representation
|
||||
## defined in RCF7049 section 2.4.1.
|
||||
writeCborTag(str, 0)
|
||||
writeCbor(str, format(dt, timeFormat))
|
||||
?writeCborTag(str, 0)
|
||||
?writeCbor(str, format(dt, timeFormat))
|
||||
|
||||
proc writeCborHook*(str: Stream; t: Time) =
|
||||
proc writeCborHook*(str: Stream; t: Time): ?!void =
|
||||
## Write a `Time` using the tagged numerical representation
|
||||
## defined in RCF7049 section 2.4.1.
|
||||
writeCborTag(str, 1)
|
||||
writeCbor(str, t.toUnix)
|
||||
?writeCborTag(str, 1)
|
||||
?writeCbor(str, t.toUnix)
|
||||
|
||||
func toCbor*(x: CborNode): CborNode = x
|
||||
func toCbor*(x: CborNode): ?!CborNode = success(x)
|
||||
|
||||
func toCbor*(x: SomeInteger): CborNode =
|
||||
func toCbor*(x: SomeInteger): ?!CborNode =
|
||||
if x > 0:
|
||||
CborNode(kind: cborUnsigned, uint: x.uint64)
|
||||
success(CborNode(kind: cborUnsigned, uint: x.uint64))
|
||||
else:
|
||||
CborNode(kind: cborNegative, int: x.int64)
|
||||
success(CborNode(kind: cborNegative, int: x.int64))
|
||||
|
||||
func toCbor*(x: openArray[byte]): CborNode =
|
||||
CborNode(kind: cborBytes, bytes: @x)
|
||||
func toCbor*(x: openArray[byte]): ?!CborNode =
|
||||
success(CborNode(kind: cborBytes, bytes: @x))
|
||||
|
||||
func toCbor*(x: string): CborNode =
|
||||
CborNode(kind: cborText, text: x)
|
||||
func toCbor*(x: string): ?!CborNode =
|
||||
success(CborNode(kind: cborText, text: x))
|
||||
|
||||
func toCbor*(x: openArray[CborNode]): CborNode =
|
||||
CborNode(kind: cborArray, seq: @x)
|
||||
func toCbor*(x: openArray[CborNode]): ?!CborNode =
|
||||
success(CborNode(kind: cborArray, seq: @x))
|
||||
|
||||
func toCbor*(pairs: openArray[(CborNode, CborNode)]): CborNode =
|
||||
CborNode(kind: cborMap, map: pairs.toOrderedTable)
|
||||
func toCbor*(pairs: openArray[(CborNode, CborNode)]): ?!CborNode =
|
||||
try:
|
||||
return success(CborNode(kind: cborMap, map: pairs.toOrderedTable))
|
||||
except Exception as e:
|
||||
return failure(e.msg)
|
||||
|
||||
func toCbor*(tag: uint64; val: CborNode): CborNode =
|
||||
result = toCbor(val)
|
||||
result.tag = some(tag)
|
||||
func toCbor*(tag: uint64; val: CborNode): ?!CborNode =
|
||||
without res =? toCbor(val), error:
|
||||
return failure(error.msg)
|
||||
var cnode = res
|
||||
cnode.tag = some(tag)
|
||||
return success(cnode)
|
||||
|
||||
func toCbor*(x: bool): CborNode =
|
||||
func toCbor*(x: bool): ?!CborNode =
|
||||
case x
|
||||
of false:
|
||||
CborNode(kind: cborSimple, simple: 20)
|
||||
success(CborNode(kind: cborSimple, simple: 20))
|
||||
of true:
|
||||
CborNode(kind: cborSimple, simple: 21)
|
||||
success(CborNode(kind: cborSimple, simple: 21))
|
||||
|
||||
func toCbor*(x: SomeFloat): CborNode =
|
||||
CborNode(kind: cborFloat, float: x.float64)
|
||||
func toCbor*(x: SomeFloat): ?!CborNode =
|
||||
success(CborNode(kind: cborFloat, float: x.float64))
|
||||
|
||||
func toCbor*(x: pointer): CborNode =
|
||||
func toCbor*(x: pointer): ?!CborNode =
|
||||
## A hack to produce a CBOR null item.
|
||||
assert(x.isNil)
|
||||
CborNode(kind: cborSimple, simple: 22)
|
||||
if not x.isNil:
|
||||
return failure("pointer is not nil")
|
||||
success(CborNode(kind: cborSimple, simple: 22))
|
||||
|
||||
func initCborBytes*[T: char|byte](buf: openArray[T]): CborNode =
|
||||
## Create a CBOR byte string from `buf`.
|
||||
@ -359,11 +399,4 @@ func initCbor*(items: varargs[CborNode, toCbor]): CborNode =
|
||||
## Initialize a CBOR arrary.
|
||||
CborNode(kind: cborArray, seq: @items)
|
||||
|
||||
template initCborOther*(x: untyped): CborNode =
|
||||
## Initialize a ``CborNode`` from a type where ``toCbor`` is not implemented.
|
||||
## This encodes ``x`` to binary using ``writeCbor``, so
|
||||
## ``$(initCborOther(x))`` will incur an encode and decode roundtrip.
|
||||
let s = newStringStream()
|
||||
s.writeCbor(x)
|
||||
CborNode(kind: cborRaw, raw: s.data)
|
||||
|
||||
|
||||
@ -21,11 +21,6 @@ type
|
||||
point: CustomPoint
|
||||
color: CustomColor
|
||||
|
||||
Person = object
|
||||
name: string
|
||||
age: int
|
||||
isActive: bool
|
||||
|
||||
Inner = object
|
||||
s: string
|
||||
nums: seq[int]
|
||||
@ -134,12 +129,22 @@ suite "CBOR deserialization":
|
||||
refInner: refObj
|
||||
)
|
||||
|
||||
# Serialize to CBOR with encode
|
||||
without encodedStr =? encode(original), error:
|
||||
fail()
|
||||
|
||||
# Serialize to CBOR
|
||||
let stream = newStringStream()
|
||||
stream.writeCbor(original)
|
||||
if stream.writeCbor(original).isFailure:
|
||||
fail()
|
||||
|
||||
let cborData = stream.data
|
||||
# Check that both serialized forms are equal
|
||||
check cborData == encodedStr
|
||||
|
||||
# Parse CBOR back to CborNode
|
||||
let parseResult = parseCbor(cborData)
|
||||
|
||||
check parseResult.isSuccess
|
||||
let node = parseResult.tryValue
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user