You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The implementation is based on a [Hash Array Mapped Trie
(HAMT)](https://en.wikipedia.org/wiki/Hash_array_mapped_trie)
following [Bagwell
(2000)](http://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf).
A HAMT uses a fixed branching factor (commonly 32) together with each
node being sparse.
In order to search for an entry we take the hash of the key and chunk it
up into blocks,
with a branching factor of 32 each block is 5 bits. We use those 5 bits
to calculate the
index inside the node and use a bitmap within the node to keep track if
an element is
already set. This makes search a `log(32, n)` operation.
Persistency is implemented by path-copying. When we insert/delete a
value into the HAMT
we copy each node along the path into a new HAMT, all other nodes are
shared with
the previous HAMT.
A noteable implementation choice is that I didn't add a (resizeable)
root table.
Normally this root table is dense and uses the first `t` bits to
calculate an index
within. This makes large HAMT a bit cheaper since the root-table
effectivly folds
multiple lookup steps into one. It does hurt persistent use-cases since
path-copying
means that we also copy the root node/table.
Importantly the HAMT itself is not immutable/persistent, the use of it
as part of the
`PersistentDict` is. Direct mutation of the underlying data breaks the
persistentcy
invariants. One could use the HAMT to implement a non-persistent
dictionary (or
other datastructures).
As an interesting side-note we could use a related data-structure
[Ctrie](http://lamp.epfl.ch/~prokopec/ctries-snapshot.pdf)
to implement a concurrent lock-free dictionary. Ctrie also support
`O(1)` snapshotting
so we could replace the HAMT used here with a Ctrie.
0 commit comments