# Persistent Map — Hash Array Mapped Trie (HAMT) # Bara Lang-style 32-way branching with structural sharing # Takes pre-computed hashes and equality functions from callers import hashes const PMAP_SHIFT* = 5 PMAP_BRANCH* = 32 PMAP_MASK* = 31 PMAP_MAX_SHIFT* = 25 # 6 levels (25,20,15,10,5,0) = 30 bits of hash type PMapNode*[K, V] = ref object children*: seq[PMapNode[K, V]] keys*: seq[K] vals*: seq[V] hashes*: seq[Hash] # stored hashes for leaf nodes, parallel to keys PersistentMap*[K, V] = object root*: PMapNode[K, V] count*: int proc newPMapLeaf*[K, V](keys: seq[K] = @[], vals: seq[V] = @[], hashes: seq[Hash] = @[]): PMapNode[K, V] = PMapNode[K, V](keys: keys, vals: vals, hashes: hashes) proc newPMapInternal*[K, V](): PMapNode[K, V] = PMapNode[K, V](children: @[]) proc pmapCopyNode*[K, V](n: PMapNode[K, V]): PMapNode[K, V] = if n.isNil: return nil if n.children.len > 0: result = newPMapInternal[K, V]() result.children = n.children else: result = newPMapLeaf[K, V](n.keys, n.vals, n.hashes) proc newPersistentMap*[K, V](): PersistentMap[K, V] = PersistentMap[K, V](root: nil, count: 0) proc pmapGet*[K, V](m: PersistentMap[K, V], key: K, default: V, kh: Hash, eq: proc(a, b: K): bool): V = if m.root.isNil: return default var node: PMapNode[K, V] = m.root var shift = PMAP_MAX_SHIFT var h = kh while shift >= 0: if node.children.len > 0: let idx = (h shr shift) and PMAP_MASK if idx >= node.children.len or node.children[idx].isNil: return default node = node.children[idx] shift -= PMAP_SHIFT continue for i in 0..= 0: if node.children.len > 0: let idx = (h shr shift) and PMAP_MASK if idx >= node.children.len or node.children[idx].isNil: return false node = node.children[idx] shift -= PMAP_SHIFT continue for i in 0.. 0: result = pmapCopyNode(node) if idx >= result.children.len: let oldLen = result.children.len result.children.setLen(idx + 1) for i in oldLen..= result.children.len: let oldLen = result.children.len result.children.setLen(eidx + 1) for i in oldLen.. result.children.len: let oldLen = result.children.len result.children.setLen(ns) for i in oldLen.. 0: result = pmapCopyNode(node) if idx < result.children.len and not result.children[idx].isNil: result.children[idx] = doDissoc(result.children[idx], shift - PMAP_SHIFT) return # Leaf node result = newPMapLeaf[K, V]() for i in 0.. 0: let node = stack.pop() if node.isNil: continue if node.children.len > 0: for i in countdown(node.children.len - 1, 0): if not node.children[i].isNil: stack.add(node.children[i]) else: result.add(node.keys) proc pmapVals*[K, V](m: PersistentMap[K, V]): seq[V] = result = @[] if m.root.isNil: return var stack: seq[PMapNode[K, V]] = @[m.root] while stack.len > 0: let node = stack.pop() if node.isNil: continue if node.children.len > 0: for i in countdown(node.children.len - 1, 0): if not node.children[i].isNil: stack.add(node.children[i]) else: result.add(node.vals) proc pmapEntries*[K, V](m: PersistentMap[K, V]): seq[(K, V)] = result = @[] if m.root.isNil: return var stack: seq[PMapNode[K, V]] = @[m.root] while stack.len > 0: let node = stack.pop() if node.isNil: continue if node.children.len > 0: for i in countdown(node.children.len - 1, 0): if not node.children[i].isNil: stack.add(node.children[i]) else: for i in 0.. 0: let node = stack.pop() if node.isNil: continue if node.children.len > 0: for i in countdown(node.children.len - 1, 0): if not node.children[i].isNil: stack.add(node.children[i]) else: for i in 0..