mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-27 04:26:07 +00:00
747177df38
When running the import, currently blocks are loaded in batches into a large `seq` then passed to the importer as such. In reality, blocks are still processed one by one, so the batching does not offer any performance advantage. It does however require that the client wastes memory, up to several GB, on the block sequence while they're waiting to be processed. This PR introduces a persister that accepts blocks one by one and at the same time removes a number of redundant / unnecessary copies, assignments and resets that were slowing down the import process in general.
100 lines
2.8 KiB
Nim
100 lines
2.8 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 chronicles, eth/common, stew/byteutils, ./interpreter/op_codes, ./code_bytes
|
|
|
|
export code_bytes
|
|
|
|
type CodeStream* = object
|
|
code: CodeBytesRef
|
|
pc*: int
|
|
|
|
func init*(T: type CodeStream, code: CodeBytesRef): T =
|
|
T(code: code)
|
|
|
|
func init*(T: type CodeStream, code: sink seq[byte]): T =
|
|
T(code: CodeBytesRef.init(move(code)))
|
|
|
|
func init*(T: type CodeStream, code: openArray[byte]): T =
|
|
T(code: CodeBytesRef.init(code))
|
|
|
|
func init*(T: type CodeStream, code: openArray[char]): T =
|
|
T(code: CodeBytesRef.init(code))
|
|
|
|
template read*(c: var CodeStream, size: int): openArray[byte] =
|
|
let
|
|
pos = c.pc
|
|
last = pos + size
|
|
|
|
if last <= c.bytes.len:
|
|
c.pc = last
|
|
c.code.bytes.toOpenArray(pos, last - 1)
|
|
else:
|
|
c.pc = c.bytes.len
|
|
c.code.bytes.toOpenArray(pos, c.bytes.high)
|
|
|
|
func readVmWord*(c: var CodeStream, n: static int): UInt256 {.inline, noinit.} =
|
|
## Reads `n` bytes from the code stream and pads
|
|
## the remaining bytes with zeros.
|
|
UInt256.fromBytesBE(c.read(n))
|
|
|
|
func len*(c: CodeStream): int =
|
|
len(c.code)
|
|
|
|
template next*(c: var CodeStream): Op =
|
|
# Retrieve the next opcode (or stop) - this is a hot spot in the interpreter
|
|
# and must be kept small for performance
|
|
let
|
|
# uint: range checked manually -> benefit from smaller codegen
|
|
pc = uint(c.pc)
|
|
bytes {.cursor.} = c.code.bytes
|
|
if pc < uint(bytes.len):
|
|
let op = Op(bytes[pc])
|
|
c.pc = cast[int](pc + 1)
|
|
op
|
|
else:
|
|
Op.Stop
|
|
|
|
iterator items*(c: var CodeStream): Op =
|
|
var nextOpcode = c.next()
|
|
while nextOpcode != Op.Stop:
|
|
yield nextOpcode
|
|
nextOpcode = c.next()
|
|
|
|
func `[]`*(c: CodeStream, offset: int): Op =
|
|
Op(c.code.bytes[offset])
|
|
|
|
func peek*(c: var CodeStream): Op =
|
|
if c.pc < c.code.bytes.len:
|
|
Op(c.code.bytes[c.pc])
|
|
else:
|
|
Op.Stop
|
|
|
|
func updatePc*(c: var CodeStream, value: int) =
|
|
c.pc = min(value, len(c))
|
|
|
|
func isValidOpcode*(c: CodeStream, position: int): bool =
|
|
c.code.isValidOpcode(position)
|
|
|
|
func bytes*(c: CodeStream): lent seq[byte] =
|
|
c.code.bytes()
|
|
|
|
func atEnd*(c: CodeStream): bool =
|
|
c.pc >= c.code.bytes.len
|
|
|
|
proc decompile*(original: CodeStream): seq[(int, Op, string)] =
|
|
# behave as https://etherscan.io/opcode-tool
|
|
var c = CodeStream.init(original.bytes)
|
|
while not c.atEnd:
|
|
var op = c.next
|
|
if op >= Push1 and op <= Push32:
|
|
result.add((c.pc - 1, op, "0x" & c.read(op.int - 95).toHex))
|
|
elif op != Op.Stop:
|
|
result.add((c.pc - 1, op, ""))
|
|
else:
|
|
result.add((-1, Op.Stop, ""))
|