src/necsus/util/blockstore

Types

BlockIter = object
BlockSpan = object
Every slot a store had handed out at the moment the span was taken, described in raw terms so a caller can walk it without going back through the store for each one. The slots are laid out end to end, so walking is a matter of stepping a pointer
BlockStore[V] = ref BlockStoreObj[V]
Stores a block of packed values
Entry[V] = object
A reserved slot in a BlockStore. Liveness is tracked in a bitmap alongside the values instead of inline, which keeps the stored rows free of bookkeeping

Procs

proc `[]`[V](store: BlockStore[V]; idx: uint): var V
Reads a field
proc commit[V](entry: Entry[V]) {.inline.}
Marks that an entry is ready to be used
proc del[V](store: var BlockStore[V]; idx: uint): V
Deletes a field
proc index[V](entry: Entry[V]): uint {.inline.}
Returns the index of an entry
proc isAlive(span: BlockSpan; idx: uint): bool {.inline, ...raises: [], tags: [],
    forbids: [].}
Whether the slot at an index is currently filled. Reads live state, not the state from when the span was taken
proc isDone(iter: BlockIter): bool {.inline, ...raises: [], tags: [], forbids: [].}
proc isEmpty(span: BlockSpan): bool {.inline, ...raises: [], tags: [], forbids: [].}
Whether this span covers any slots at all
func len[V](blockstore: var BlockStore[V]): Natural
Returns the length of this blockstore
proc newBlockStore[V](size: Natural): BlockStore[V]
Instantiates a new BlockStore
proc next[V](store: var BlockStore[V]; iter: var BlockIter): ptr V {.inline.}
Returns the next value in an iterator
proc reserve[V](blockstore: var BlockStore[V]): Entry[V]
Reserves a slot for a value
proc slots(span: BlockSpan): uint {.inline, ...raises: [], tags: [], forbids: [].}
The number of slots this span covers, filled or not
proc wholeSpan[V](store: var BlockStore[V]): BlockSpan

Returns every slot the store has handed out, filled or not. The count is fixed when the span is taken, so a walk sees the store as it stood when the walk began and values pushed part way through are left for the next one.

Liveness is deliberately not baked in, because values can be deleted midway through a span being walked. It travels with the span so each slot can be confirmed as it is reached

Iterators

iterator addresses[V](span: BlockSpan; kind: typedesc[V]): ptr V

Walks the filled slots in a span, yielding the address of each as a ptr V. V needs to fit in a slot, but does not have to fill it: a caller that only knows how a slot starts can walk it as that, then work out the rest of the slot from there.

Liveness is confirmed as the walk goes rather than trusted from when the span was taken, so a caller is free to delete slots this walk has not reached yet. The bitmap word is shared by sixty four slots, so it stays in cache.

This is named apart from items on purpose: items is late bound inside generic code, which would resolve it against whatever the instantiating module happens to import

iterator items[V](span: BlockSpan; kind: typedesc[V]): var V
Walks the filled slots in a span as the type they were stored as
iterator items[V](store: var BlockStore[V]): var V
Iterate through all values in this BlockStore

Templates

template `[]=`[V](store: BlockStore[V]; idx: uint; newValue: V)
Sets a new value for a key
template push[V](store: var BlockStore[V]; newValue: V): uint
Adds a value and returns an index to it
template set[V](entry: Entry[V]; newValue: V)
Sets a value on an entry
template value[V](entry: Entry[V]): var V
Returns the value of an entry