113 lines
3.2 KiB
Nim
113 lines
3.2 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[endians, streams]
|
|
import bigints
|
|
import ./values
|
|
|
|
proc writeVarint(s: Stream; n: Natural) =
|
|
var n = n
|
|
while n > 0x7f:
|
|
s.write(uint8 n or 0x80)
|
|
n = n shr 7
|
|
s.write(uint8 n and 0x7f)
|
|
|
|
proc write*(str: Stream; pr: Value) =
|
|
## Write the binary-encoding of a Preserves value to a stream.
|
|
if pr.embedded: str.write(0x86'u8)
|
|
case pr.kind:
|
|
of pkBoolean:
|
|
case pr.bool
|
|
of false: str.write(0x80'u8)
|
|
of true: str.write(0x81'u8)
|
|
of pkFloat:
|
|
str.write("\x87\x08")
|
|
when system.cpuEndian == bigEndian:
|
|
str.write(pr.double)
|
|
else:
|
|
var be: float64
|
|
swapEndian64(be.addr, pr.float.unsafeAddr)
|
|
str.write(be)
|
|
of pkRegister:
|
|
if pr.register == 0: str.write("\xb0\x00")
|
|
else:
|
|
const bufLen = sizeof(int)
|
|
var buf: array[bufLen, byte]
|
|
when bufLen == 4: bigEndian32(addr buf[0], addr pr.register)
|
|
elif bufLen == 8: bigEndian64(addr buf[0], addr pr.register)
|
|
else: {.error: "int size " & $bufLen & " not supported here".}
|
|
if buf[0] != 0x00 and buf[0] != 0xff:
|
|
str.write(cast[string](buf)) # dumbass hex conversion
|
|
else:
|
|
var start = 0
|
|
while start < buf.high and buf[0] == buf[succ start]: inc start
|
|
if start < buf.high and (buf[succ start] and 0x80) == (buf[0] and 0x80): inc start
|
|
str.write('\xb0')
|
|
str.write(uint8(bufLen - start))
|
|
str.write(cast[string](buf[start..<bufLen]))
|
|
of pkBigInt:
|
|
if pr.bigint.isZero: str.write("\xb0\x00")
|
|
elif pr.bigint.isNegative:
|
|
var buf = pr.bigint.succ.toBytes(bigEndian)
|
|
for i, b in buf: buf[i] = not b
|
|
str.write('\xb0')
|
|
if (buf[0] and 0x80) != 0x80:
|
|
str.writeVarint(buf.len.succ)
|
|
str.write('\xff')
|
|
else:
|
|
str.writeVarint(buf.len)
|
|
str.write(cast[string](buf))
|
|
else:
|
|
var buf = pr.bigint.toBytes(bigEndian)
|
|
str.write('\xb0')
|
|
if (buf[0] and 0x80) != 0:
|
|
str.writeVarint(buf.len.succ)
|
|
str.write('\x00')
|
|
else:
|
|
str.writeVarint(buf.len)
|
|
str.write(cast[string](buf))
|
|
of pkString:
|
|
str.write(0xb1'u8)
|
|
str.writeVarint(pr.string.len)
|
|
str.write(pr.string)
|
|
of pkByteString:
|
|
str.write(0xb2'u8)
|
|
str.writeVarint(pr.bytes.len)
|
|
str.write(cast[string](pr.bytes))
|
|
of pkSymbol:
|
|
str.write(0xb3'u8)
|
|
str.writeVarint(pr.symbol.len)
|
|
str.write(string pr.symbol)
|
|
of pkRecord:
|
|
assert(pr.record.len > 0)
|
|
str.write(0xb4'u8)
|
|
str.write(pr.record[pr.record.high])
|
|
for i in 0..<pr.record.high:
|
|
str.write(pr.record[i])
|
|
str.write(0x84'u8)
|
|
of pkSequence:
|
|
str.write(0xb5'u8)
|
|
for e in pr.sequence:
|
|
str.write(e)
|
|
str.write(0x84'u8)
|
|
of pkSet:
|
|
str.write(0xb6'u8)
|
|
for val in pr.set.items:
|
|
str.write(val)
|
|
str.write(0x84'u8)
|
|
of pkDictionary:
|
|
str.write(0xb7'u8)
|
|
for (key, value) in pr.dict.items:
|
|
str.write(key)
|
|
str.write(value)
|
|
str.write(0x84'u8)
|
|
of pkEmbedded:
|
|
# str.write(0x86'u8)
|
|
raise newException(ValueError, "cannot encode an embedded object")
|
|
|
|
proc encode*(pr: Value): seq[byte] =
|
|
## Return the binary-encoding of a Preserves value.
|
|
let s = newStringStream()
|
|
s.write pr
|
|
result = cast[seq[byte]](move s.data)
|