133 lines
4.3 KiB
Nim
133 lines
4.3 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[endians, options, streams, strutils]
|
|
import bigints
|
|
import ./values
|
|
|
|
proc readVarint(s: Stream): uint =
|
|
var
|
|
shift = 0
|
|
c = uint s.readUint8
|
|
while (c and 0x80) == 0x80:
|
|
result = result or ((c and 0x7f) shl shift)
|
|
inc(shift, 7)
|
|
c = uint s.readUint8
|
|
result = result or (c shl shift)
|
|
|
|
proc decodePreserves*(s: Stream): Value =
|
|
## Decode a Preserves value from a binary-encoded stream.
|
|
if s.atEnd: raise newException(IOError, "End of Preserves stream")
|
|
const endMarker = 0x84
|
|
let tag = s.readUint8()
|
|
case tag
|
|
of 0x80: result = Value(kind: pkBoolean, bool: false)
|
|
of 0x81: result = Value(kind: pkBoolean, bool: true)
|
|
of 0x85:
|
|
discard decodePreserves(s)
|
|
result = decodePreserves(s)
|
|
of 0x86:
|
|
result = decodePreserves(s)
|
|
result.embedded = true
|
|
of 0x87:
|
|
result = Value(kind: pkFloat)
|
|
var N: int
|
|
let n = int s.readUint8()
|
|
case n
|
|
of 4:
|
|
var
|
|
buf: uint32
|
|
float: float32
|
|
N = s.readData(addr buf, sizeof(buf))
|
|
bigEndian32(addr float, addr buf)
|
|
result.float = BiggestFloat float
|
|
of 8:
|
|
var buf: uint64
|
|
N = s.readData(addr buf, sizeof(buf))
|
|
bigEndian64(addr result.float, addr buf)
|
|
else:
|
|
raise newException(IOError, "unhandled IEEE754 value of " & $n & " bytes")
|
|
if N != n: raise newException(IOError, "short read")
|
|
of 0xb0:
|
|
var n = int s.readVarint()
|
|
if n <= sizeof(int):
|
|
result = Value(kind: pkRegister)
|
|
if n > 0:
|
|
var
|
|
buf: array[sizeof(int), byte]
|
|
off = buf.len - n
|
|
if s.readData(addr buf[off], n) != n:
|
|
raise newException(IOError, "short read")
|
|
if off > 0:
|
|
var fill: uint8 = if (buf[off] and 0x80) == 0x80'u8: 0xff else: 0x00'u8
|
|
for i in 0..<off: buf[i] = fill
|
|
when buf.len == 4:
|
|
bigEndian32(addr result.register, addr buf[0])
|
|
elif buf.len == 8:
|
|
bigEndian64(addr result.register, addr buf[0])
|
|
else: {.error: "int size " & $buf.len & " not supported here".}
|
|
else:
|
|
result = Value(kind: pkBigInt)
|
|
var buf = newSeq[byte](n)
|
|
if s.readData(addr buf[0], buf.len) != n:
|
|
raise newException(IOError, "short read")
|
|
if (buf[0] and 0x80) == 0x80:
|
|
for i, b in buf: buf[i] = not b
|
|
result.bigint.fromBytes(buf, bigEndian)
|
|
result.bigint = -(result.bigint.succ)
|
|
else:
|
|
result.bigint.fromBytes(buf, bigEndian)
|
|
of 0xb1:
|
|
result = Value(kind: pkString, string: newString(s.readVarint()))
|
|
if result.string.len > 0:
|
|
if s.readData(addr result.string[0], result.string.len) != result.string.len:
|
|
raise newException(IOError, "short read")
|
|
of 0xb2:
|
|
var data = newSeq[byte](s.readVarint())
|
|
if data.len > 0:
|
|
let n = s.readData(addr data[0], data.len)
|
|
if n != data.len:
|
|
raise newException(IOError, "short read")
|
|
result = Value(kind: pkByteString, bytes: data)
|
|
of 0xb3:
|
|
var data = newString(s.readVarint())
|
|
if data.len > 0:
|
|
let n = s.readData(addr data[0], data.len)
|
|
if n != data.len:
|
|
raise newException(IOError, "short read")
|
|
result = Value(kind: pkSymbol, symbol: Symbol data)
|
|
of 0xb4:
|
|
result = Value(kind: pkRecord)
|
|
var label = decodePreserves(s)
|
|
while s.peekUint8() != endMarker:
|
|
result.record.add decodePreserves(s)
|
|
result.record.add(move label)
|
|
discard s.readUint8()
|
|
of 0xb5:
|
|
result = Value(kind: pkSequence)
|
|
while s.peekUint8() != endMarker:
|
|
result.sequence.add decodePreserves(s)
|
|
discard s.readUint8()
|
|
of 0xb6:
|
|
result = Value(kind: pkSet)
|
|
while s.peekUint8() != endMarker:
|
|
incl(result, decodePreserves(s))
|
|
discard s.readUint8()
|
|
of 0xb7:
|
|
result = Value(kind: pkDictionary)
|
|
while s.peekUint8() != endMarker:
|
|
result[decodePreserves(s)] = decodePreserves(s)
|
|
discard s.readUint8()
|
|
of endMarker:
|
|
raise newException(ValueError, "invalid Preserves stream")
|
|
else:
|
|
raise newException(ValueError, "invalid Preserves tag byte 0x" & tag.toHex(2))
|
|
|
|
proc decodePreserves*(s: string): Value =
|
|
## Decode a string of binary-encoded Preserves.
|
|
decodePreserves(s.newStringStream)
|
|
|
|
proc decodePreserves*(s: seq[byte]): Value =
|
|
## Decode a byte-string of binary-encoded Preserves.
|
|
decodePreserves(cast[string](s))
|