nimbus-eth1/nimbus/evm/code_bytes.nim

95 lines
2.9 KiB
Nim

# Nimbus
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
stew/byteutils,
stew/assign2,
results,
./interpreter/op_codes
export results
type CodeBytesRef* = ref object
## Code buffer that caches invalid jump positions used for verifying jump
## destinations - `bytes` is immutable once instances is created while
## `invalidPositions` will be built up on demand
bytes: seq[byte]
invalidPositions: seq[byte] # bit seq of invalid jump positions
processed: int
persisted*: bool ## This code stream has been persisted to the database
template bitpos(pos: int): (int, byte) =
(pos shr 3, 1'u8 shl (pos and 0x07))
func init*(
T: type CodeBytesRef, bytes: sink seq[byte], persisted = false
): CodeBytesRef =
let ip = newSeq[byte]((bytes.len + 7) div 8)
CodeBytesRef(bytes: move(bytes), invalidPositions: ip, persisted: persisted)
func init*(
T: type CodeBytesRef, bytes: openArray[byte], persisted = false
): CodeBytesRef =
CodeBytesRef.init(@bytes, persisted = persisted)
func init*(T: type CodeBytesRef, bytes: openArray[char]): CodeBytesRef =
CodeBytesRef.init(bytes.toOpenArrayByte(0, bytes.high()))
func fromHex*(T: type CodeBytesRef, hex: string): Opt[CodeBytesRef] =
try:
Opt.some(CodeBytesRef.init(hexToSeqByte(hex)))
except ValueError:
Opt.none(CodeBytesRef)
func invalidPosition(c: CodeBytesRef, pos: int): bool =
let (bpos, bbit) = bitpos(pos)
(c.invalidPositions[bpos] and bbit) > 0
func bytes*(c: CodeBytesRef): lent seq[byte] =
c[].bytes
func len*(c: CodeBytesRef): int =
len(c.bytes)
func isValidOpcode*(c: CodeBytesRef, position: int): bool =
if position >= len(c):
false
elif c.invalidPosition(position):
false
elif position <= c.processed:
true
else:
var i = c.processed
while i <= position:
var opcode = Op(c.bytes[i])
if opcode >= Op.Push1 and opcode <= Op.Push32:
var leftBound = (i + 1)
var rightBound = min(leftBound + (opcode.int - 95), c.bytes.len)
for z in leftBound ..< rightBound:
let (bpos, bbit) = bitpos(z)
c.invalidPositions[bpos] = c.invalidPositions[bpos] or bbit
i = rightBound
else:
i += 1
c.processed = i - 1
not c.invalidPosition(position)
func `==`*(a: CodeBytesRef, b: openArray[byte]): bool =
a.bytes == b
func hasPrefix*(a: CodeBytesRef, b: openArray[byte]): bool =
if b.len > a.bytes.len:
return false
for i in 0..<b.len:
if a.bytes[i] != b[i]:
return false
true
func slice*[N: static[int]](a: CodeBytesRef, b, c: int): array[N, byte] =
assign(result, a.bytes.toOpenArray(b, c))