2019-02-05 12:01:10 +00:00
|
|
|
type
|
2020-04-20 18:14:39 +00:00
|
|
|
NibblesSeq* = object
|
|
|
|
bytes: seq[byte]
|
2019-02-05 12:01:10 +00:00
|
|
|
ibegin, iend: int
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc initNibbleRange*(bytes: openArray[byte]): NibblesSeq =
|
|
|
|
result.bytes = @bytes
|
2019-02-05 12:01:10 +00:00
|
|
|
result.ibegin = 0
|
|
|
|
result.iend = bytes.len * 2
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc `{}`(r: NibblesSeq, pos: int): byte {.inline.} =
|
2019-02-05 12:01:10 +00:00
|
|
|
## This is a helper for a more raw access to the nibbles.
|
|
|
|
## It works with absolute positions.
|
2023-06-03 18:47:55 +00:00
|
|
|
if pos > r.iend: raise newException(RangeDefect, "index out of range")
|
2019-02-05 12:01:10 +00:00
|
|
|
return if (pos and 1) != 0: (r.bytes[pos div 2] and 0xf)
|
|
|
|
else: (r.bytes[pos div 2] shr 4)
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
template `[]`*(r: NibblesSeq, i: int): byte = r{r.ibegin + i}
|
2019-02-05 12:01:10 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc len*(r: NibblesSeq): int =
|
2019-02-05 12:01:10 +00:00
|
|
|
r.iend - r.ibegin
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc `==`*(lhs, rhs: NibblesSeq): bool =
|
2019-02-05 12:01:10 +00:00
|
|
|
if lhs.len == rhs.len:
|
|
|
|
for i in 0 ..< lhs.len:
|
|
|
|
if lhs[i] != rhs[i]:
|
|
|
|
return false
|
|
|
|
return true
|
|
|
|
else:
|
|
|
|
return false
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc `$`*(r: NibblesSeq): string =
|
2019-02-05 12:01:10 +00:00
|
|
|
result = newStringOfCap(100)
|
|
|
|
for i in r.ibegin ..< r.iend:
|
|
|
|
let n = int r{i}
|
|
|
|
let c = if n > 9: char(ord('a') + n - 10)
|
|
|
|
else: char(ord('0') + n)
|
|
|
|
result.add c
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc slice*(r: NibblesSeq, ibegin: int, iend = -1): NibblesSeq =
|
2019-02-05 12:01:10 +00:00
|
|
|
result.bytes = r.bytes
|
|
|
|
result.ibegin = r.ibegin + ibegin
|
|
|
|
let e = if iend < 0: r.iend + iend + 1
|
|
|
|
else: r.ibegin + iend
|
2019-03-13 22:15:26 +00:00
|
|
|
doAssert ibegin >= 0 and e <= result.bytes.len * 2
|
2019-02-05 12:01:10 +00:00
|
|
|
result.iend = e
|
|
|
|
|
|
|
|
template writeFirstByte(nibbleCountExpr) {.dirty.} =
|
|
|
|
let nibbleCount = nibbleCountExpr
|
|
|
|
var oddnessFlag = (nibbleCount and 1) != 0
|
|
|
|
newSeq(result, (nibbleCount div 2) + 1)
|
|
|
|
result[0] = byte((int(isLeaf) * 2 + int(oddnessFlag)) shl 4)
|
|
|
|
var writeHead = 0
|
|
|
|
|
|
|
|
template writeNibbles(r) {.dirty.} =
|
|
|
|
for i in r.ibegin ..< r.iend:
|
|
|
|
let nextNibble = r{i}
|
|
|
|
if oddnessFlag:
|
|
|
|
result[writeHead] = result[writeHead] or nextNibble
|
|
|
|
else:
|
|
|
|
inc writeHead
|
|
|
|
result[writeHead] = nextNibble shl 4
|
|
|
|
oddnessFlag = not oddnessFlag
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc hexPrefixEncode*(r: NibblesSeq, isLeaf = false): seq[byte] =
|
2019-02-05 12:01:10 +00:00
|
|
|
writeFirstByte(r.len)
|
|
|
|
writeNibbles(r)
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc hexPrefixEncode*(r1, r2: NibblesSeq, isLeaf = false): seq[byte] =
|
2019-02-05 12:01:10 +00:00
|
|
|
writeFirstByte(r1.len + r2.len)
|
|
|
|
writeNibbles(r1)
|
|
|
|
writeNibbles(r2)
|
|
|
|
|
|
|
|
proc hexPrefixEncodeByte*(val: byte, isLeaf = false): byte =
|
2019-03-13 22:15:26 +00:00
|
|
|
doAssert val < 16
|
2019-02-05 12:01:10 +00:00
|
|
|
result = (((byte(isLeaf) * 2) + 1) shl 4) or val
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc sharedPrefixLen*(lhs, rhs: NibblesSeq): int =
|
2019-02-05 12:01:10 +00:00
|
|
|
result = 0
|
|
|
|
while result < lhs.len and result < rhs.len:
|
|
|
|
if lhs[result] != rhs[result]: break
|
|
|
|
inc result
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc startsWith*(lhs, rhs: NibblesSeq): bool =
|
2019-02-05 12:01:10 +00:00
|
|
|
sharedPrefixLen(lhs, rhs) == rhs.len
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc hexPrefixDecode*(r: openArray[byte]): tuple[isLeaf: bool, nibbles: NibblesSeq] =
|
2019-02-05 12:01:10 +00:00
|
|
|
result.nibbles = initNibbleRange(r)
|
|
|
|
if r.len > 0:
|
|
|
|
result.isLeaf = (r[0] and 0x20) != 0
|
|
|
|
let hasOddLen = (r[0] and 0x10) != 0
|
|
|
|
result.nibbles.ibegin = 2 - int(hasOddLen)
|
|
|
|
else:
|
|
|
|
result.isLeaf = false
|
|
|
|
|
|
|
|
template putNibble(bytes, x: untyped) =
|
|
|
|
if odd:
|
|
|
|
bytes[pos] = (bytes[pos] and 0xF0) or x
|
|
|
|
inc pos
|
|
|
|
else:
|
|
|
|
bytes[pos] = x shl 4
|
|
|
|
|
|
|
|
template putNibbles(bytes, src: untyped) =
|
|
|
|
for i in 0 ..< src.len:
|
|
|
|
bytes.putNibble(src[i])
|
|
|
|
odd = not odd
|
|
|
|
|
|
|
|
template calcNeededBytes(len: int): int =
|
|
|
|
(len shr 1) + (len and 1)
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc `&`*(a, b: NibblesSeq): NibblesSeq =
|
2019-02-05 12:01:10 +00:00
|
|
|
let
|
|
|
|
len = a.len + b.len
|
|
|
|
bytesNeeded = calcNeededBytes(len)
|
|
|
|
|
|
|
|
var
|
|
|
|
bytes = newSeq[byte](bytesNeeded)
|
|
|
|
odd = false
|
|
|
|
pos = 0
|
|
|
|
|
|
|
|
bytes.putNibbles(a)
|
|
|
|
bytes.putNibbles(b)
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
result = initNibbleRange(bytes)
|
2019-02-05 12:01:10 +00:00
|
|
|
result.iend = len
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc cloneAndReserveNibble*(a: NibblesSeq): NibblesSeq =
|
2019-02-05 12:01:10 +00:00
|
|
|
let
|
|
|
|
len = a.len + 1
|
|
|
|
bytesNeeded = calcNeededBytes(len)
|
|
|
|
|
|
|
|
var
|
|
|
|
bytes = newSeq[byte](bytesNeeded)
|
|
|
|
odd = false
|
|
|
|
pos = 0
|
|
|
|
|
|
|
|
bytes.putNibbles(a)
|
2020-04-20 18:14:39 +00:00
|
|
|
result = initNibbleRange(bytes)
|
2019-02-05 12:01:10 +00:00
|
|
|
result.iend = len
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc replaceLastNibble*(a: var NibblesSeq, b: byte) =
|
2019-02-05 12:01:10 +00:00
|
|
|
var
|
|
|
|
odd = (a.len and 1) == 0
|
|
|
|
pos = (a.len shr 1) - odd.int
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
putNibble(a.bytes, b)
|
2019-02-05 12:01:10 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc getBytes*(a: NibblesSeq): seq[byte] =
|
2019-02-05 12:01:10 +00:00
|
|
|
a.bytes
|