mirror of https://github.com/status-im/nim-eth.git
166 lines
4.3 KiB
Nim
166 lines
4.3 KiB
Nim
import
|
|
trie_defs
|
|
|
|
type
|
|
NibblesRange* = object
|
|
bytes: ByteRange
|
|
ibegin, iend: int
|
|
|
|
proc initNibbleRange*(bytes: ByteRange): NibblesRange =
|
|
result.bytes = bytes
|
|
result.ibegin = 0
|
|
result.iend = bytes.len * 2
|
|
|
|
# can't be a const: https://github.com/status-im/nim-eth/issues/6
|
|
# we can't initialise it here, but since it's already zeroed memory, we don't need to
|
|
var zeroNibblesRange* {.threadvar.}: NibblesRange
|
|
|
|
proc `{}`(r: NibblesRange, pos: int): byte {.inline.} =
|
|
## This is a helper for a more raw access to the nibbles.
|
|
## It works with absolute positions.
|
|
if pos > r.iend: raise newException(RangeError, "index out of range")
|
|
return if (pos and 1) != 0: (r.bytes[pos div 2] and 0xf)
|
|
else: (r.bytes[pos div 2] shr 4)
|
|
|
|
template `[]`*(r: NibblesRange, i: int): byte = r{r.ibegin + i}
|
|
|
|
proc len*(r: NibblesRange): int =
|
|
r.iend - r.ibegin
|
|
|
|
proc `==`*(lhs, rhs: NibblesRange): bool =
|
|
if lhs.len == rhs.len:
|
|
for i in 0 ..< lhs.len:
|
|
if lhs[i] != rhs[i]:
|
|
return false
|
|
return true
|
|
else:
|
|
return false
|
|
|
|
proc `$`*(r: NibblesRange): string =
|
|
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
|
|
|
|
proc slice*(r: NibblesRange, ibegin: int, iend = -1): NibblesRange =
|
|
result.bytes = r.bytes
|
|
result.ibegin = r.ibegin + ibegin
|
|
let e = if iend < 0: r.iend + iend + 1
|
|
else: r.ibegin + iend
|
|
doAssert ibegin >= 0 and e <= result.bytes.len * 2
|
|
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
|
|
|
|
proc hexPrefixEncode*(r: NibblesRange, isLeaf = false): Bytes =
|
|
writeFirstByte(r.len)
|
|
writeNibbles(r)
|
|
|
|
proc hexPrefixEncode*(r1, r2: NibblesRange, isLeaf = false): Bytes =
|
|
writeFirstByte(r1.len + r2.len)
|
|
writeNibbles(r1)
|
|
writeNibbles(r2)
|
|
|
|
proc hexPrefixEncodeByte*(val: byte, isLeaf = false): byte =
|
|
doAssert val < 16
|
|
result = (((byte(isLeaf) * 2) + 1) shl 4) or val
|
|
|
|
proc sharedPrefixLen*(lhs, rhs: NibblesRange): int =
|
|
result = 0
|
|
while result < lhs.len and result < rhs.len:
|
|
if lhs[result] != rhs[result]: break
|
|
inc result
|
|
|
|
proc startsWith*(lhs, rhs: NibblesRange): bool =
|
|
sharedPrefixLen(lhs, rhs) == rhs.len
|
|
|
|
proc hexPrefixDecode*(r: ByteRange): tuple[isLeaf: bool, nibbles: NibblesRange] =
|
|
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)
|
|
|
|
proc `&`*(a, b: NibblesRange): NibblesRange =
|
|
let
|
|
len = a.len + b.len
|
|
bytesNeeded = calcNeededBytes(len)
|
|
|
|
var
|
|
bytes = newSeq[byte](bytesNeeded)
|
|
odd = false
|
|
pos = 0
|
|
|
|
bytes.putNibbles(a)
|
|
bytes.putNibbles(b)
|
|
|
|
result = initNibbleRange(bytes.toRange)
|
|
result.iend = len
|
|
|
|
proc cloneAndReserveNibble*(a: NibblesRange): NibblesRange =
|
|
let
|
|
len = a.len + 1
|
|
bytesNeeded = calcNeededBytes(len)
|
|
|
|
var
|
|
bytes = newSeq[byte](bytesNeeded)
|
|
odd = false
|
|
pos = 0
|
|
|
|
bytes.putNibbles(a)
|
|
result = initNibbleRange(bytes.toRange)
|
|
result.iend = len
|
|
|
|
proc replaceLastNibble*(a: var NibblesRange, b: byte) =
|
|
var
|
|
odd = (a.len and 1) == 0
|
|
pos = (a.len shr 1) - odd.int
|
|
|
|
putNibble(MutRange[byte](a.bytes), b)
|
|
|
|
proc getBytes*(a: NibblesRange): ByteRange =
|
|
a.bytes
|
|
|
|
when false:
|
|
proc keyOf(r: ByteRange): NibblesRange =
|
|
let firstIdx = if r.len == 0: 0
|
|
elif (r[0] and 0x10) != 0: 1
|
|
else: 2
|
|
|
|
return initNibbleRange(s).slice(firstIdx)
|
|
|