mirror of https://github.com/status-im/nim-eth.git
130 lines
3.4 KiB
Nim
130 lines
3.4 KiB
Nim
import
|
|
stew/bitops2
|
|
|
|
type
|
|
TrieBitSeq* = object
|
|
## Bit sequence as used in ethereum tries
|
|
data: seq[byte]
|
|
start: int
|
|
mLen: int ## Length in bits
|
|
|
|
template `@`(s, idx: untyped): untyped =
|
|
(when idx is BackwardsIndex: s.len - int(idx) else: int(idx))
|
|
|
|
proc bits*(a: seq[byte], start, len: int): TrieBitSeq =
|
|
doAssert start <= len
|
|
doAssert len <= 8 * a.len
|
|
TrieBitSeq(data: a, start: start, mLen: len)
|
|
|
|
template bits*(a: seq[byte]): TrieBitSeq =
|
|
bits(a, 0, a.len * 8)
|
|
|
|
template bits*(a: seq[byte], len: int): TrieBitSeq =
|
|
bits(a, 0, len)
|
|
|
|
template bits*(a: openArray[byte], start, len: int): TrieBitSeq =
|
|
bits(@a, start, len)
|
|
|
|
template bits*(a: openArray[byte]): TrieBitSeq =
|
|
bits(@a, 0, a.len * 8)
|
|
|
|
template bits*(a: openArray[byte], len: int): TrieBitSeq =
|
|
bits(@a, 0, len)
|
|
|
|
template bits*(x: TrieBitSeq): TrieBitSeq = x
|
|
|
|
proc len*(r: TrieBitSeq): int = r.mLen
|
|
|
|
iterator enumerateBits(x: TrieBitSeq): (int, bool) =
|
|
var p = x.start
|
|
var i = 0
|
|
let e = x.len
|
|
while i != e:
|
|
yield (i, getBitBE(x.data, p))
|
|
inc p
|
|
inc i
|
|
|
|
iterator items*(x: TrieBitSeq): bool =
|
|
for _, v in enumerateBits(x): yield v
|
|
|
|
iterator pairs*(x: TrieBitSeq): (int, bool) =
|
|
for i, v in enumerateBits(x): yield (i, v)
|
|
|
|
proc `[]`*(x: TrieBitSeq, idx: int): bool =
|
|
doAssert idx < x.len
|
|
let p = x.start + idx
|
|
result = getBitBE(x.data, p)
|
|
|
|
proc sliceNormalized(x: TrieBitSeq, ibegin, iend: int): TrieBitSeq =
|
|
doAssert ibegin >= 0 and
|
|
ibegin < x.len and
|
|
iend < x.len and
|
|
iend + 1 >= ibegin # the +1 here allows the result to be
|
|
# an empty range
|
|
|
|
result.data = x.data
|
|
result.start = x.start + ibegin
|
|
result.mLen = iend - ibegin + 1
|
|
|
|
proc `[]`*(r: TrieBitSeq, s: HSlice): TrieBitSeq =
|
|
sliceNormalized(r, r @ s.a, r @ s.b)
|
|
|
|
proc `==`*(a, b: TrieBitSeq): bool =
|
|
if a.len != b.len: return false
|
|
for i in 0 ..< a.len:
|
|
if a[i] != b[i]: return false
|
|
true
|
|
|
|
proc `[]=`*(r: var TrieBitSeq, idx: Natural, val: bool) =
|
|
doAssert idx < r.len
|
|
let absIdx = r.start + idx
|
|
changeBitBE(r.data, absIdx, val)
|
|
|
|
proc pushFront*(x: var TrieBitSeq, val: bool) =
|
|
doAssert x.start > 0
|
|
dec x.start
|
|
x[0] = val
|
|
inc x.mLen
|
|
|
|
template neededBytes(nBits: int): int =
|
|
(nBits shr 3) + ord((nBits and 0b111) != 0)
|
|
|
|
static:
|
|
doAssert neededBytes(2) == 1
|
|
doAssert neededBytes(8) == 1
|
|
doAssert neededBytes(9) == 2
|
|
|
|
proc `&`*(a, b: TrieBitSeq): TrieBitSeq =
|
|
let totalLen = a.len + b.len
|
|
|
|
var bytes = newSeq[byte](totalLen.neededBytes)
|
|
result = bits(bytes, 0, totalLen)
|
|
|
|
for i in 0 ..< a.len: result.data.changeBitBE(i, a[i])
|
|
for i in 0 ..< b.len: result.data.changeBitBE(i + a.len, b[i])
|
|
|
|
proc `$`*(r: TrieBitSeq): string =
|
|
result = newStringOfCap(r.len)
|
|
for bit in r:
|
|
result.add(if bit: '1' else: '0')
|
|
|
|
proc fromBits*(T: type, r: TrieBitSeq, offset, num: Natural): T =
|
|
doAssert(num <= sizeof(T) * 8)
|
|
# XXX: Nim has a bug that a typedesc parameter cannot be used
|
|
# in a type coercion, so we must define an alias here:
|
|
type TT = T
|
|
for i in 0 ..< num:
|
|
result = (result shl 1) or TT(r[offset + i])
|
|
|
|
proc parse*(T: type TrieBitSeq, s: string): TrieBitSeq =
|
|
var bytes = newSeq[byte](s.len.neededBytes)
|
|
for i, c in s:
|
|
case c
|
|
of '0': discard
|
|
of '1': setBitBE(bytes, i)
|
|
else: doAssert false
|
|
result = bits(bytes, 0, s.len)
|
|
|
|
proc toBytes*(r: TrieBitSeq): seq[byte] =
|
|
r.data[(r.start div 8)..<((r.mLen - r.start + 7) div 8)]
|