168 lines
3.9 KiB
Nim

# toml-serialization
# Copyright (c) 2020 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
# * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strutils,
../types,
serialization/object_serialization,
faststreams/outputs
type
CodecState* = enum
TopLevel
InsideRecord
ExpectValue
ArrayOfTable
func cmpNimIdent(a, b: string): bool =
if a.len == 0 and b.len == 0:
return true
if a.len == 0 or b.len == 0:
return false
if a[0] != b[0]:
return false
var i = 1
var j = 1
while true:
while i < a.len and a[i] == '_': inc i
while j < b.len and b[j] == '_': inc j
var aa = if i < a.len: toLowerAscii(a[i]) else: '\0'
var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
let res = ord(aa) - ord(bb)
if res != 0: return false
# the characters are identical:
if i >= a.len:
# both cursors at the end:
if j >= b.len: return true
# not yet at the end of 'b':
return false
elif j >= b.len:
return true
inc i
inc j
func compare*(a, b: openArray[string], tomlCase: TomlCase): bool =
case tomlCase
of TomlCaseSensitive:
result = a == b
of TomlCaseInsensitive:
if a.len != b.len: return false
for i, x in a:
if cmpIgnoreCase(x, b[i]) != 0:
return false
result = true
of TomlCaseNim:
if a.len != b.len: return false
for i, x in a:
if not cmpNimIdent(x, b[i]):
return false
result = true
func compare*(a, b: string, tomlCase: TomlCase): bool =
case tomlCase
of TomlCaseSensitive: a == b
of TomlCaseInsensitive: cmpIgnoreCase(a, b) == 0
of TomlCaseNim: cmpNimIdent(a, b)
proc findFieldReader*(fieldsTable: FieldReadersTable,
fieldName: string,
expectedFieldPos: var int, tomlCase: TomlCase): auto =
for i in expectedFieldPos ..< fieldsTable.len:
if compare(fieldsTable[i].fieldName, fieldName, tomlCase):
expectedFieldPos = i + 1
return fieldsTable[i].reader
for i in 0 ..< expectedFieldPos:
if compare(fieldsTable[i].fieldName, fieldName, tomlCase):
return fieldsTable[i].reader
return nil
const
digits = block:
var z = ""
for i in 0..99:
if i < 10: z.add '0'
z.add $i
z
maxLen = ($BiggestInt.high).len
proc writeInt*(s: OutputStream, x: BiggestInt, len: Positive) =
var
num: array[maxLen, char]
pos = num.len
numWritten = 0
template prepend(c: char) =
if numWritten < len:
dec pos
num[pos] = c
inc numWritten
var val = x
while val > 99:
let idx = (val mod 100) * 2
val = val div 100
prepend digits[idx + 1]
prepend digits[idx]
if val < 10:
prepend char(ord('0') + val)
else:
let idx = val * 2
prepend digits[idx + 1]
prepend digits[idx]
for _ in numWritten ..< len:
prepend '0'
write s, num.toOpenArray(pos, static(num.len - 1))
proc toHex*(s: OutputStream, x: BiggestInt, len: Positive) =
const
hexChars = "0123456789ABCDEF"
maxDigits = sizeof(x) * 2
var
hex: array[maxDigits, char]
pos = hex.len
n = x
template prepend(c: char) =
dec pos
hex[pos] = c
for _ in 0 ..< len:
prepend(hexChars[int(n and 0xF)])
n = n shr 4
write s, hex.toOpenArray(pos, static(hex.len - 1))
proc emptyTable*(): TomlValueRef =
TomlValueRef(
kind: TomlKind.Table,
tableVal: TomlTableRef.new
)
template uTypeIsRecord*(_: typed): bool =
false
template uTypeIsRecord*[T](_: type seq[T]): bool =
when T is (object or tuple):
true
else:
false
template uTypeIsRecord*[N, T](_: type array[N, T]): bool =
when T is (object or tuple):
true
else:
false