270 lines
9.1 KiB
Nim
270 lines
9.1 KiB
Nim
# nimbus-eth1
|
|
# Copyright (c) 2023-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.
|
|
|
|
{.push raises: [].}
|
|
|
|
import
|
|
std/[algorithm, sequtils, sets, tables],
|
|
eth/common,
|
|
results,
|
|
./aristo_desc
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
func dup(sTab: Table[VertexID,VertexRef]): Table[VertexID,VertexRef] =
|
|
## Explicit dup for `VertexRef` values
|
|
for (k,v) in sTab.pairs:
|
|
result[k] = v.dup
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public getters: lazy value lookup for read only versions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
func dirty*(db: AristoDbRef): HashSet[VertexID] =
|
|
db.top.final.dirty
|
|
|
|
func pPrf*(db: AristoDbRef): HashSet[VertexID] =
|
|
db.top.final.pPrf
|
|
|
|
func vGen*(db: AristoDbRef): seq[VertexID] =
|
|
db.top.final.vGen
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public getters/helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
func nLayersVtx*(db: AristoDbRef): int =
|
|
## Number of vertex ID/vertex entries on the cache layers. This is an upper
|
|
## bound for the number of effective vertex ID mappings held on the cache
|
|
## layers as there might be duplicate entries for the same vertex ID on
|
|
## different layers.
|
|
##
|
|
db.stack.mapIt(it.delta.sTab.len).foldl(a + b, db.top.delta.sTab.len)
|
|
|
|
func nLayersKey*(db: AristoDbRef): int =
|
|
## Number of vertex ID/key entries on the cache layers. This is an upper
|
|
## bound for the number of effective vertex ID mappingss held on the cache
|
|
## layers as there might be duplicate entries for the same vertex ID on
|
|
## different layers.
|
|
##
|
|
db.stack.mapIt(it.delta.kMap.len).foldl(a + b, db.top.delta.kMap.len)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions: getter variants
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc layersGetVtx*(db: AristoDbRef; vid: VertexID): Result[VertexRef,void] =
|
|
## Find a vertex on the cache layers. An `ok()` result might contain a
|
|
## `nil` vertex if it is stored on the cache that way.
|
|
##
|
|
if db.top.delta.sTab.hasKey vid:
|
|
return ok(db.top.delta.sTab.getOrVoid vid)
|
|
|
|
for w in db.stack.reversed:
|
|
if w.delta.sTab.hasKey vid:
|
|
return ok(w.delta.sTab.getOrVoid vid)
|
|
|
|
err()
|
|
|
|
proc layersGetVtxOrVoid*(db: AristoDbRef; vid: VertexID): VertexRef =
|
|
## Simplified version of `layersGetVtx()`
|
|
db.layersGetVtx(vid).valueOr: VertexRef(nil)
|
|
|
|
|
|
proc layersGetKey*(db: AristoDbRef; vid: VertexID): Result[HashKey,void] =
|
|
## Find a hash key on the cache layers. An `ok()` result might contain a void
|
|
## hash key if it is stored on the cache that way.
|
|
##
|
|
if db.top.delta.kMap.hasKey vid:
|
|
# This is ok regardless of the `dirty` flag. If this vertex has become
|
|
# dirty, there is an empty `kMap[]` entry on this layer.
|
|
return ok(db.top.delta.kMap.getOrVoid vid)
|
|
|
|
for w in db.stack.reversed:
|
|
if w.delta.kMap.hasKey vid:
|
|
# Same reasoning as above regarding the `dirty` flag.
|
|
return ok(w.delta.kMap.getOrVoid vid)
|
|
|
|
err()
|
|
|
|
proc layersGetKeyOrVoid*(db: AristoDbRef; vid: VertexID): HashKey =
|
|
## Simplified version of `layersGetkey()`
|
|
db.layersGetKey(vid).valueOr: VOID_HASH_KEY
|
|
|
|
|
|
proc layerGetProofKeyOrVoid*(db: AristoDbRef; vid: VertexID): HashKey =
|
|
## Get the hash key of a proof node if it was registered as such.
|
|
if vid in db.top.final.pPrf:
|
|
db.top.delta.kMap.getOrVoid vid
|
|
else:
|
|
VOID_HASH_KEY
|
|
|
|
proc layerGetProofVidOrVoid*(db: AristoDbRef; key: HashKey): VertexID =
|
|
## Reverse look up for a registered proof node or a link key for such a
|
|
## node. The vertex for a returned vertex ID might not exist if the
|
|
## argument `key` refers to a link key of a registered proof node.
|
|
db.top.final.fRpp.getOrVoid key
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions: setter variants
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc layersPutVtx*(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
vid: VertexID;
|
|
vtx: VertexRef;
|
|
) =
|
|
## Store a (potentally empty) vertex on the top layer
|
|
db.top.delta.sTab[vid] = vtx
|
|
db.top.final.dirty.incl root
|
|
|
|
proc layersResVtx*(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
vid: VertexID;
|
|
) =
|
|
## Shortcut for `db.layersPutVtx(vid, VertexRef(nil))`. It is sort of the
|
|
## equivalent of a delete function.
|
|
db.layersPutVtx(root, vid, VertexRef(nil))
|
|
|
|
|
|
proc layersPutKey*(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
vid: VertexID;
|
|
key: HashKey;
|
|
) =
|
|
## Store a (potentally void) hash key on the top layer
|
|
db.top.delta.kMap[vid] = key
|
|
db.top.final.dirty.incl root # Modified top cache layers => hashify
|
|
|
|
|
|
proc layersResKey*(db: AristoDbRef; root: VertexID; vid: VertexID) =
|
|
## Shortcut for `db.layersPutKey(vid, VOID_HASH_KEY)`. It is sort of the
|
|
## equivalent of a delete function.
|
|
db.layersPutKey(root, vid, VOID_HASH_KEY)
|
|
|
|
|
|
proc layersPutProof*(db: AristoDbRef; vid: VertexID; key: HashKey) =
|
|
## Register a link key of a proof node.
|
|
let lKey = db.layersGetKeyOrVoid vid
|
|
if not lKey.isValid or lKey != key:
|
|
db.top.delta.kMap[vid] = key
|
|
db.top.final.fRpp[key] = vid
|
|
|
|
proc layersPutProof*(
|
|
db: AristoDbRef;
|
|
vid: VertexID;
|
|
key: HashKey;
|
|
vtx: VertexRef;
|
|
) =
|
|
## Register a full proof node (not only a link key.)
|
|
let lVtx = db.layersGetVtxOrVoid vid
|
|
if not lVtx.isValid or lVtx != vtx:
|
|
db.top.delta.sTab[vid] = vtx
|
|
db.top.final.pPrf.incl vid
|
|
db.layersPutProof(vid, key)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc layersMergeOnto*(src: LayerRef; trg: var LayerObj) =
|
|
## Merges the argument `src` into the argument `trg` and returns `trg`. For
|
|
## the result layer, the `txUid` value set to `0`.
|
|
##
|
|
trg.final = src.final
|
|
trg.txUid = 0
|
|
|
|
for (vid,vtx) in src.delta.sTab.pairs:
|
|
trg.delta.sTab[vid] = vtx
|
|
for (vid,key) in src.delta.kMap.pairs:
|
|
trg.delta.kMap[vid] = key
|
|
|
|
|
|
func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
|
|
## Provide a collapsed copy of layers up to a particular transaction level.
|
|
## If the `level` argument is too large, the maximum transaction level is
|
|
## returned. For the result layer, the `txUid` value set to `0`.
|
|
##
|
|
let layers = if db.stack.len <= level: db.stack & @[db.top]
|
|
else: db.stack[0 .. level]
|
|
|
|
# Set up initial layer (bottom layer)
|
|
result = LayerRef(
|
|
final: layers[^1].final.dup, # Pre-merged/final values
|
|
delta: LayerDeltaRef(
|
|
sTab: layers[0].delta.sTab.dup, # explicit dup for ref values
|
|
kMap: layers[0].delta.kMap))
|
|
|
|
# Consecutively merge other layers on top
|
|
for n in 1 ..< layers.len:
|
|
for (vid,vtx) in layers[n].delta.sTab.pairs:
|
|
result.delta.sTab[vid] = vtx
|
|
for (vid,key) in layers[n].delta.kMap.pairs:
|
|
result.delta.kMap[vid] = key
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public iterators
|
|
# ------------------------------------------------------------------------------
|
|
|
|
iterator layersWalkVtx*(
|
|
db: AristoDbRef;
|
|
seen: var HashSet[VertexID];
|
|
): tuple[vid: VertexID, vtx: VertexRef] =
|
|
## Walk over all `(VertexID,VertexRef)` pairs on the cache layers. Note that
|
|
## entries are unsorted.
|
|
##
|
|
## The argument `seen` collects a set of all visited vertex IDs including
|
|
## the one with a zero vertex which are othewise skipped by the iterator.
|
|
## The `seen` argument must not be modified while the iterator is active.
|
|
##
|
|
for (vid,vtx) in db.top.delta.sTab.pairs:
|
|
yield (vid,vtx)
|
|
seen.incl vid
|
|
|
|
for w in db.stack.reversed:
|
|
for (vid,vtx) in w.delta.sTab.pairs:
|
|
if vid notin seen:
|
|
yield (vid,vtx)
|
|
seen.incl vid
|
|
|
|
iterator layersWalkVtx*(
|
|
db: AristoDbRef;
|
|
): tuple[vid: VertexID, vtx: VertexRef] =
|
|
## Variant of `layersWalkVtx()`.
|
|
var seen: HashSet[VertexID]
|
|
for (vid,vtx) in db.layersWalkVtx seen:
|
|
yield (vid,vtx)
|
|
|
|
|
|
iterator layersWalkKey*(
|
|
db: AristoDbRef;
|
|
): tuple[vid: VertexID, key: HashKey] =
|
|
## Walk over all `(VertexID,HashKey)` pairs on the cache layers. Note that
|
|
## entries are unsorted.
|
|
var seen: HashSet[VertexID]
|
|
for (vid,key) in db.top.delta.kMap.pairs:
|
|
yield (vid,key)
|
|
seen.incl vid
|
|
|
|
for w in db.stack.reversed:
|
|
for (vid,key) in w.delta.kMap.pairs:
|
|
if vid notin seen:
|
|
yield (vid,key)
|
|
seen.incl vid
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|