Expand description
Persistent (structural-sharing) vector — the v4.38 building block for the
v4.39 cheap-Catalog::clone migration.
PersistentVec<T> is a Bitmapped Vector Trie (Clojure persistent vector
shape): 32-way branching trie with a tail buffer at the open end. Every
mutating operation produces a new handle that shares interior nodes with
the old handle via Arc. Clone is O(1); push and get are
O(log₃₂ N); a CoW path touches only the spine of the affected leaf.
Hard rules (do not relax in later milestones):
no_stdcompatible (alloc::sync::Arc,alloc::vec::Vec).- Zero
unsafe. Workspace lintunsafe_code = "deny"stays in force here. - Zero external deps. Pure std +
alloc.
Layout:
root: Arc<Node<T>>— the persistent trie.Node::Internal(Vec<Arc<Node>>)for non-leaf levels,Node::Leaf(Vec<T>)for the bottom.tail: Arc<Vec<T>>— the open-end buffer (≤ 32 elements). Lives outside the trie sopushto a non-full tail avoids walking the spine.len: usize— total element count (trie_size + tail.len()).shift: u32— distance from the root to the leaf level, in bits, in multiples ofSHIFT. An empty PV hasshift = SHIFTand an empty root so the first incorporate doesn’t have to special-case the root type.
Invariants (debug-asserted in hot paths):
tail.len() ≤ BRANCH.- When
tail.len() == BRANCHwe incorporate it into the trie before the next push (so post-condition istail.len() < BRANCH, except briefly in the middle ofpush). shiftis always a multiple ofSHIFTand ≥SHIFT.trie_size = len - tail.len()always fits in1 << (shift + SHIFT).
Structs§
- Iter
- Sequential
&Titerator. v4.38 implementation isget(i)-driven — simple and correct, but O(N log N) over the whole vector. Profile in v4.39 / v4.40 and upgrade if it shows up in flamegraphs. - Persistent
Vec - A persistent vector with structural sharing.
Cloneis O(1) (bumps the rootArc);pushis amortised O(log₃₂ N) and only allocates fresh nodes along the spine from the root to the affected leaf. - RunCursor
- v7.39 (round 567) — see
PersistentVec::run_cursor.