Skip to main content

spg_storage/
persistent_btree.rs

1// v4.40 — workspace `doc-markdown` flags `B-tree`, `BTreeMap`, `Arc::clone`
2// in prose contexts even when surrounding identifiers are already
3// backticked; the lint is fine in source code but too noisy here.
4// `many-single-char-names` flags the K / V / k / v / i naming the rest of
5// the workspace already uses for map-shaped types.
6#![allow(
7    clippy::doc_markdown,
8    clippy::many_single_char_names,
9    clippy::type_complexity
10)]
11
12//! Persistent (structural-sharing) B-tree map — the v4.40 building block for
13//! migrating `Table::indices` off `alloc::collections::BTreeMap`.
14//!
15//! `PersistentBTreeMap<K, V>` is a path-copy CoW B-tree (`ORDER = 8`,
16//! `MAX_ENTRIES = 7`, `MIN_ENTRIES = 3`). Every mutating operation produces a
17//! new handle that shares interior nodes with the old handle via `Arc`.
18//! `Clone` is `O(1)`; `insert` and `get` are `O(log₈ N)`; a CoW path touches
19//! only the spine to the affected node.
20//!
21//! Same hard rules as `persistent::PersistentVec`:
22//! - `no_std` compatible (`alloc::sync::Arc`, `alloc::vec::Vec`).
23//! - Zero `unsafe`.
24//! - Zero external deps.
25//!
26//! Layout (traditional B-tree, *not* B+ tree — entries live at every level,
27//! including internal nodes; descending hits a value if and only if the key
28//! sits along the spine):
29//!
30//!   enum BNode<K, V> {
31//!       Leaf { entries: Vec<(K, V)> },                    // entries.len() ∈ [1, MAX_ENTRIES]
32//!       Internal {
33//!           entries: Vec<(K, V)>,                          // entries.len() ∈ [1, MAX_ENTRIES]
34//!           children: Vec<Arc<BNode<K, V>>>,              // children.len() == entries.len() + 1
35//!       },
36//!   }
37//!
38//! Invariants (debug-checked in `#[cfg(test)]` only):
39//! - Every internal node satisfies `children.len() == entries.len() + 1`.
40//! - Entries inside any single node are sorted strictly ascending by `K`.
41//! - The root may have fewer than `MIN_ENTRIES`; every other node has ≥
42//!   `MIN_ENTRIES`.
43
44use alloc::sync::Arc;
45use alloc::vec::Vec;
46use core::ops::Bound;
47
48/// B-tree order (max children per internal node). Picked at the small end of
49/// the conventional 8–16 range to keep per-CoW node-clone cost low — the
50/// path-copy hits one node per level, and each cloned node carries up to
51/// `MAX_ENTRIES` of `(K, V)`.
52const ORDER: usize = 8;
53const MAX_ENTRIES: usize = ORDER - 1; // 7
54const MAX_CHILDREN: usize = ORDER; // 8
55
56#[derive(Debug)]
57enum BNode<K, V> {
58    Leaf {
59        entries: Vec<(K, V)>,
60    },
61    Internal {
62        entries: Vec<(K, V)>,
63        children: Vec<Arc<BNode<K, V>>>,
64    },
65}
66
67// Manual `Clone` impl so the bound only applies when `Arc::make_mut`
68// (the v4.40.1 transient path) actually needs it. The non-mutating
69// `get` / `iter` paths stay generic over any `K`, `V`.
70impl<K: Clone, V: Clone> Clone for BNode<K, V> {
71    fn clone(&self) -> Self {
72        match self {
73            Self::Leaf { entries } => Self::Leaf {
74                entries: entries.clone(),
75            },
76            Self::Internal { entries, children } => Self::Internal {
77                entries: entries.clone(),
78                children: children.clone(),
79            },
80        }
81    }
82}
83
84/// A persistent ordered map. `Clone` is `O(1)`; `insert` returns a new handle
85/// that shares unaffected subtrees with the old via `Arc::clone`.
86#[derive(Debug)]
87pub struct PersistentBTreeMap<K, V> {
88    root: Arc<BNode<K, V>>,
89    len: usize,
90}
91
92impl<K, V> Default for PersistentBTreeMap<K, V> {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98impl<K, V> Clone for PersistentBTreeMap<K, V> {
99    /// O(1) — `Arc` bump on the root. The whole reason this type exists in
100    /// v4.40 is to make `Table::indices: Vec<Index>` cheap to clone once
101    /// the inner `BTreeMap` is replaced.
102    fn clone(&self) -> Self {
103        Self {
104            root: self.root.clone(),
105            len: self.len,
106        }
107    }
108}
109
110impl<K: PartialEq, V: PartialEq> PartialEq for PersistentBTreeMap<K, V>
111where
112    K: Ord,
113{
114    fn eq(&self, other: &Self) -> bool {
115        self.len == other.len && self.iter().eq(other.iter())
116    }
117}
118
119impl<K: Eq + Ord, V: Eq> Eq for PersistentBTreeMap<K, V> {}
120
121impl<K, V> PersistentBTreeMap<K, V> {
122    /// Empty map. Builds one empty `Leaf` root; subsequent inserts grow
123    /// the trie outward when overflowing `MAX_ENTRIES`.
124    #[must_use]
125    pub fn new() -> Self {
126        Self {
127            root: Arc::new(BNode::Leaf {
128                entries: Vec::new(),
129            }),
130            len: 0,
131        }
132    }
133
134    #[must_use]
135    pub const fn len(&self) -> usize {
136        self.len
137    }
138
139    #[must_use]
140    pub const fn is_empty(&self) -> bool {
141        self.len == 0
142    }
143}
144
145/// v7.37.43 (INSUBQ B-3) — internal-node search outcome: either the
146/// key matched an entry directly, or the search bisected and the
147/// caller must descend into `children[i]`.
148enum FoundOrDescend {
149    Found(usize),
150    Descend(usize),
151}
152
153/// v7.37.43 (INSUBQ B-3) — linear search a leaf's entries for `key`.
154/// `entries.len() ≤ MAX_ENTRIES = 7`; linear-scan beats binary_search
155/// at this size on modern branch predictors. Returns the entry index
156/// when present.
157#[inline]
158fn linear_find_entry<K: Ord, V>(entries: &[(K, V)], key: &K) -> Option<usize> {
159    for (i, (k, _)) in entries.iter().enumerate() {
160        match k.cmp(key) {
161            core::cmp::Ordering::Equal => return Some(i),
162            core::cmp::Ordering::Greater => return None,
163            core::cmp::Ordering::Less => continue,
164        }
165    }
166    None
167}
168
169/// v7.37.43 (INSUBQ B-3) — linear search an internal node's entries
170/// for `key`. Returns Found(idx) if key matches an entry, otherwise
171/// Descend(idx) with the child slot to follow.
172#[inline]
173fn linear_position_internal<K: Ord, V>(entries: &[(K, V)], key: &K) -> FoundOrDescend {
174    for (i, (k, _)) in entries.iter().enumerate() {
175        match k.cmp(key) {
176            core::cmp::Ordering::Equal => return FoundOrDescend::Found(i),
177            core::cmp::Ordering::Greater => return FoundOrDescend::Descend(i),
178            core::cmp::Ordering::Less => continue,
179        }
180    }
181    FoundOrDescend::Descend(entries.len())
182}
183
184impl<K: Ord, V> PersistentBTreeMap<K, V> {
185    /// `O(log₈ N)`. Per-node search at each level; on hit returns the
186    /// value, on miss descends into the child between adjacent entries.
187    ///
188    /// v7.37.43 (INSUBQ B-3) — every node holds ≤ `MAX_ENTRIES = 7`
189    /// `(K, V)` pairs, so the inner search is over at most 7 elements.
190    /// `binary_search_by` issues a data-dependent branch per probe;
191    /// for n ≤ 7 a straight linear scan with a single ordering compare
192    /// per element wins on modern branch predictors and has tighter
193    /// codegen (no early-exit on found-vs-bisect handling). Probing a
194    /// 100k-entry index on 1k keys (the INSUBQ shape) cuts ~30-60 ns
195    /// per descent × ~5 levels × 1k keys ≈ 150-300 µs off the loop.
196    pub fn get(&self, key: &K) -> Option<&V> {
197        let mut node: &Arc<BNode<K, V>> = &self.root;
198        loop {
199            match &**node {
200                BNode::Leaf { entries } => {
201                    return linear_find_entry(entries, key).map(|i| &entries[i].1);
202                }
203                BNode::Internal { entries, children } => {
204                    match linear_position_internal(entries, key) {
205                        FoundOrDescend::Found(i) => return Some(&entries[i].1),
206                        FoundOrDescend::Descend(i) => {
207                            node = &children[i];
208                        }
209                    }
210                }
211            }
212        }
213    }
214
215    /// v7.39 (round 215) — the entry with the largest key STRICTLY less than
216    /// `key` (the in-order predecessor of `key`), or `None` when every key is
217    /// ≥ `key`. `O(log₈ N)`: descend once toward `key`; at each internal node
218    /// the entries left of the descent slot are all < `key`, and the rightmost
219    /// of them is the best candidate at that level — but the child we descend
220    /// into holds keys strictly between it and `key`, so a deeper hit always
221    /// overrides. The building block for the range-exclusion overlap probe
222    /// (find the existing range whose lower bound sits just below a candidate).
223    pub fn predecessor(&self, key: &K) -> Option<(&K, &V)> {
224        let mut node: &Arc<BNode<K, V>> = &self.root;
225        let mut best: Option<(&K, &V)> = None;
226        loop {
227            match &**node {
228                BNode::Leaf { entries } => {
229                    let i = entries.partition_point(|e| &e.0 < key);
230                    if i > 0 {
231                        let (k, v) = &entries[i - 1];
232                        best = Some((k, v));
233                    }
234                    return best;
235                }
236                BNode::Internal { entries, children } => {
237                    let i = entries.partition_point(|e| &e.0 < key);
238                    if i > 0 {
239                        let (k, v) = &entries[i - 1];
240                        best = Some((k, v));
241                    }
242                    node = &children[i];
243                }
244            }
245        }
246    }
247
248    /// In-order key-then-value iterator. Used by `PartialEq` and any caller
249    /// that needs to walk the whole map (e.g. catalog deserialization).
250    /// v7.39 (round 170) — bulk-build from PRE-SORTED entries, bottom-up.
251    /// The per-row `insert_mut` path pays a path-copy allocation per
252    /// element (~300ns each), which made every `rebuild_indices` pass
253    /// O(n) allocations per index (the dominant cost of a VACUUM on an
254    /// indexed table). This builds leaves in ~ORDER-sized runs and
255    /// levels them up with the run separators as the internal entries —
256    /// zero path copies. Groups are cut evenly so no node is left
257    /// pathologically underfull for the later insert/remove rebalance.
258    ///
259    /// Debug builds assert the input is strictly sorted by `K`.
260    #[must_use]
261    pub fn from_sorted(entries: Vec<(K, V)>) -> Self
262    where
263        K: Ord + Clone,
264        V: Clone,
265    {
266        #[cfg(debug_assertions)]
267        for w in entries.windows(2) {
268            debug_assert!(w[0].0 < w[1].0, "from_sorted requires strictly sorted keys");
269        }
270        let len = entries.len();
271        if len <= MAX_ENTRIES {
272            return Self {
273                root: Arc::new(BNode::Leaf { entries }),
274                len,
275            };
276        }
277        // Level 0 — cut into leaf runs with one separator entry between
278        // consecutive leaves. Choose the number of leaves so every leaf
279        // gets between ceil(MAX_ENTRIES/2) and MAX_ENTRIES entries.
280        let mut nodes: Vec<Arc<BNode<K, V>>> = Vec::new();
281        let mut seps: Vec<(K, V)> = Vec::new();
282        {
283            // n items into g groups of ≤ MAX_ENTRIES with g-1 separators:
284            // g = ceil((n + 1) / (MAX_ENTRIES + 1)).
285            let g = (len + 1).div_ceil(MAX_ENTRIES + 1);
286            let mut it = entries.into_iter();
287            let mut remaining = len;
288            for gi in 0..g {
289                let groups_left = g - gi;
290                // Evenly split what's left (minus the separators still owed).
291                let seps_left = groups_left - 1;
292                let take = (remaining - seps_left).div_ceil(groups_left);
293                let leaf: Vec<(K, V)> = (&mut it).take(take).collect();
294                remaining -= leaf.len();
295                nodes.push(Arc::new(BNode::Leaf { entries: leaf }));
296                if gi + 1 < g {
297                    let sep = it.next().expect("separator exists");
298                    remaining -= 1;
299                    seps.push(sep);
300                }
301            }
302        }
303        // Level up until one root remains: group ≤ MAX_CHILDREN children
304        // with the intra-group separators as the internal entries; the
305        // inter-group separators bubble to the next level.
306        while nodes.len() > 1 {
307            let g = nodes.len().div_ceil(MAX_CHILDREN);
308            let per = nodes.len().div_ceil(g);
309            let mut up_nodes: Vec<Arc<BNode<K, V>>> = Vec::with_capacity(g);
310            let mut up_seps: Vec<(K, V)> = Vec::with_capacity(g - 1);
311            let mut node_it = nodes.into_iter();
312            let mut sep_it = seps.into_iter();
313            let mut children: Vec<Arc<BNode<K, V>>> = Vec::with_capacity(per);
314            let mut inner: Vec<(K, V)> = Vec::with_capacity(per - 1);
315            loop {
316                match node_it.next() {
317                    Some(n) => {
318                        if !children.is_empty() {
319                            // The separator BEFORE this child: intra-group
320                            // if the group isn't full, else it bubbles up.
321                            let sep = sep_it.next().expect("separator per boundary");
322                            if children.len() < per {
323                                inner.push(sep);
324                            } else {
325                                up_nodes.push(Arc::new(BNode::Internal {
326                                    entries: core::mem::take(&mut inner),
327                                    children: core::mem::take(&mut children),
328                                }));
329                                up_seps.push(sep);
330                            }
331                        }
332                        children.push(n);
333                    }
334                    None => {
335                        up_nodes.push(Arc::new(BNode::Internal {
336                            entries: inner,
337                            children,
338                        }));
339                        break;
340                    }
341                }
342            }
343            nodes = up_nodes;
344            seps = up_seps;
345        }
346        Self {
347            root: nodes.pop().expect("one root"),
348            len,
349        }
350    }
351
352    pub fn iter(&self) -> Iter<'_, K, V> {
353        let mut stack: Vec<(&Arc<BNode<K, V>>, usize)> = Vec::with_capacity(8);
354        stack.push((&self.root, 0));
355        Iter { stack }
356    }
357
358    /// v7.34.4 — descending-order iterator. Mirrors `iter()` but the
359    /// per-node walk visits child-then-entry pairs right-to-left.
360    /// Used by the ORDER BY `<indexed col>` DESC + LIMIT N executor
361    /// path to walk only the first N matches off the rightmost leaf
362    /// instead of materialising every row + partial-sorting.
363    pub fn iter_rev(&self) -> IterRev<'_, K, V> {
364        let mut stack: Vec<(&Arc<BNode<K, V>>, usize)> = Vec::with_capacity(8);
365        stack.push((&self.root, 1));
366        IterRev { stack }
367    }
368
369    /// v7.38 (perf, index range scan) — in-order iterator over the entries
370    /// whose keys fall in `(lo, hi)` (each end honoured per `core::ops::Bound`).
371    /// Descends to `lo` in `O(log₈ N)` (skipping the subtrees entirely below
372    /// it) by building the same `(node, child_index)` cursor stack `iter()`
373    /// uses, positioned at the first key ≥/> `lo`; then walks forward and stops
374    /// at the first key past `hi`. `O(log N + k)` for `k` hits — the building
375    /// block for `Index::lookup_range` (BETWEEN / `>` / `<` seeks).
376    pub fn range<'a>(&'a self, lo: Bound<&K>, hi: Bound<&K>) -> RangeIter<'a, K, V>
377    where
378        K: Clone,
379    {
380        let mut stack: Vec<(&'a Arc<BNode<K, V>>, usize)> = Vec::with_capacity(8);
381        let mut node = &self.root;
382        loop {
383            match &**node {
384                BNode::Leaf { entries } => {
385                    // Leaf frame: begin emitting at the first in-range entry.
386                    stack.push((node, lower_index(entries, lo)));
387                    break;
388                }
389                BNode::Internal { entries, children } => {
390                    let i = lower_index(entries, lo);
391                    // children[i] may hold keys ≥ lo and < entries[i]; descend
392                    // into it, and set this frame to resume by emitting
393                    // entries[i] once that subtree is exhausted (phase-1 slot i
394                    // → idx = 2*i + 1, matching `Iter::next`'s frame encoding).
395                    stack.push((node, 2 * i + 1));
396                    node = &children[i];
397                }
398            }
399        }
400        let (hi_key, hi_incl) = match hi {
401            Bound::Unbounded => (None, false),
402            Bound::Included(k) => (Some(k.clone()), true),
403            Bound::Excluded(k) => (Some(k.clone()), false),
404        };
405        RangeIter {
406            inner: Iter { stack },
407            hi_key,
408            hi_incl,
409            done: false,
410        }
411    }
412}
413
414/// First entry index whose key is ≥ (Included) / > (Excluded) `lo`; 0 for
415/// Unbounded. Linear — a node holds ≤ `MAX_ENTRIES` = 7 entries.
416fn lower_index<K: Ord, V>(entries: &[(K, V)], lo: Bound<&K>) -> usize {
417    match lo {
418        Bound::Unbounded => 0,
419        Bound::Included(k) => entries.partition_point(|e| &e.0 < k),
420        Bound::Excluded(k) => entries.partition_point(|e| &e.0 <= k),
421    }
422}
423
424impl<K: Ord + Clone, V: Clone> PersistentBTreeMap<K, V> {
425    /// `O(log₈ N)`. Path-copy insert; replaces if `key` exists, otherwise
426    /// inserts and grows by 1. Returns `(new_map, previous_value)`.
427    #[must_use]
428    pub fn insert(&self, key: K, value: V) -> (Self, Option<V>) {
429        let (new_left, split, prev_v) = insert_helper(&self.root, key, value);
430        let new_root = if let Some((right, median)) = split {
431            Arc::new(BNode::Internal {
432                entries: alloc::vec![median],
433                children: alloc::vec![new_left, right],
434            })
435        } else {
436            new_left
437        };
438        let new_len = if prev_v.is_none() {
439            self.len + 1
440        } else {
441            self.len
442        };
443        (
444            Self {
445                root: new_root,
446                len: new_len,
447            },
448            prev_v,
449        )
450    }
451
452    /// `O(log₈ N)` transient insert. v4.40.1 perf path: walks
453    /// `Arc::make_mut` down the spine — when the spine `Arc`s are uniquely
454    /// owned (the common case in `Table::insert` outside a TX wrap), every
455    /// touched node mutates in place at roughly `std::BTreeMap::insert`
456    /// cost. When a cloned handle is outstanding (e.g. a Catalog snapshot
457    /// inside a TX wrap), `Arc::make_mut` path-copies just the affected
458    /// node and the snapshot stays untouched. Either way, callers see the
459    /// same end state as the immutable `insert` followed by reassignment.
460    /// r1019 — `get` / `get_mut` addressed by a BORROWED form of the key.
461    ///
462    /// The GIN maps are keyed by `String`, and their maintenance now holds
463    /// trigrams as `[u8; 3]` on the stack. Without this, every lookup would
464    /// have to allocate a `String` just to be allowed to ask — which is the
465    /// allocation r1019 exists to remove. `map.get_mut_by(trigram_str(&t))`
466    /// asks with a `&str` and allocates only when a genuinely new key has to
467    /// be inserted.
468    ///
469    /// Same descent as [`Self::get`] / [`Self::get_mut`], same copy-on-write
470    /// discipline; `K: Borrow<Q>` is what guarantees the two orderings agree.
471    pub fn get_by<Q>(&self, key: &Q) -> Option<&V>
472    where
473        K: core::borrow::Borrow<Q>,
474        Q: Ord + ?Sized,
475    {
476        let mut node: &Arc<BNode<K, V>> = &self.root;
477        loop {
478            match &**node {
479                BNode::Leaf { entries } => {
480                    return linear_find_entry_by(entries, key).map(|i| &entries[i].1);
481                }
482                BNode::Internal { entries, children } => {
483                    match linear_position_internal_by(entries, key) {
484                        FoundOrDescend::Found(i) => return Some(&entries[i].1),
485                        FoundOrDescend::Descend(i) => node = &children[i],
486                    }
487                }
488            }
489        }
490    }
491
492    /// See [`Self::get_by`]. Walks `Arc::make_mut`, like `get_mut`.
493    pub fn get_mut_by<Q>(&mut self, key: &Q) -> Option<&mut V>
494    where
495        K: core::borrow::Borrow<Q> + Clone,
496        V: Clone,
497        Q: Ord + ?Sized,
498    {
499        get_mut_by_helper(&mut self.root, key)
500    }
501
502    /// r1018 — `O(log₈ N)` mutable borrow of an existing value, under the
503    /// same copy-on-write discipline as [`Self::insert_mut`]: uniquely-owned
504    /// spine nodes mutate in place, an outstanding snapshot path-copies only
505    /// the spine it touches.
506    ///
507    /// The map is the posting-list store for every GIN index kind, whose
508    /// maintenance had no way to APPEND to a list. It read the list out,
509    /// cloned it, pushed one locator and inserted the clone back — so a
510    /// trigram already present in k rows cost a k-element copy to record the
511    /// (k+1)-th, and a text column's common trigrams are present in nearly
512    /// every row. Measured on mailrs's schema (2026-08-13): four trigram GIN
513    /// indexes over message text took 93 % of a 14,000-row load, superlinearly
514    /// — 43.6 s with them, 2.9 s without.
515    ///
516    /// Returns `None` when the key is absent; the caller inserts a fresh
517    /// single-element list in that case, which is the only path that needs to
518    /// grow the tree.
519    pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
520        get_mut_helper(&mut self.root, key)
521    }
522
523    pub fn insert_mut(&mut self, key: K, value: V) -> Option<V> {
524        let (split, prev_v) = insert_transient_helper(&mut self.root, key, value);
525        if let Some((right, median)) = split {
526            // Root overflow: wrap the old root + new right sibling under a
527            // fresh top-level Internal carrying the median entry. We need
528            // to take ownership of self.root to move it into `children`,
529            // so swap in a placeholder Leaf and then overwrite with the
530            // real new root below.
531            let old_root = core::mem::replace(
532                &mut self.root,
533                Arc::new(BNode::Leaf {
534                    entries: Vec::new(),
535                }),
536            );
537            self.root = Arc::new(BNode::Internal {
538                entries: alloc::vec![median],
539                children: alloc::vec![old_root, right],
540            });
541        }
542        if prev_v.is_none() {
543            self.len += 1;
544        }
545        prev_v
546    }
547
548    /// `O(log₈ N)` transient remove. Returns the value that was stored
549    /// under `key`, or `None` when the key was absent (the map is then
550    /// untouched).
551    ///
552    /// v7.39 (round 465) — the map had no removal at all: `new / get /
553    /// predecessor / from_sorted / iter / iter_rev / range / insert /
554    /// insert_mut`. That is why dropping a single index entry meant
555    /// rebuilding the whole map from the rows, and why one autovacuum tick
556    /// costs 11 ms on a 50k-row table with one secondary index — five
557    /// times the INSERT it exists to protect, all of it under the engine
558    /// write lock. Round 464 measured a filtered rebuild and it lost to
559    /// the existing from-the-rows rebuild, because iterating a
560    /// structurally-shared tree is pointer chasing while the rebuild is a
561    /// linear scan plus one sort. Removal is the operation that was
562    /// missing; with it, reclaiming k rows touches k spines instead of
563    /// rebuilding n entries.
564    ///
565    /// Walks `Arc::make_mut` down the spine like `insert_mut`, so a
566    /// uniquely-owned tree mutates in place and an outstanding snapshot
567    /// (a Catalog clone inside a TX wrap) path-copies only the spine.
568    pub fn remove_mut(&mut self, key: &K) -> Option<V> {
569        let removed = remove_transient_helper(&mut self.root, key)?;
570        // The root is the one node allowed to underflow, but an internal
571        // root emptied of entries has exactly one child and must be
572        // replaced by it or the tree grows a permanently useless level.
573        let collapse = match self.root.as_ref() {
574            BNode::Internal { entries, children } if entries.is_empty() => {
575                debug_assert_eq!(children.len(), 1);
576                children.first().cloned()
577            }
578            _ => None,
579        };
580        if let Some(only) = collapse {
581            self.root = only;
582        }
583        self.len -= 1;
584        Some(removed)
585    }
586
587    /// Immutable removal, for symmetry with [`Self::insert`]. Returns
588    /// `(new_map, previous_value)`; the receiver is untouched.
589    #[must_use]
590    pub fn remove(&self, key: &K) -> (Self, Option<V>) {
591        let mut next = self.clone();
592        let prev = next.remove_mut(key);
593        (next, prev)
594    }
595}
596
597/// Minimum entries in any node but the root. A node that drops below this
598/// borrows from a sibling, or merges with one.
599const MIN_ENTRIES: usize = ORDER / 2 - 1; // 3
600
601impl<K, V> BNode<K, V> {
602    fn entry_count(&self) -> usize {
603        match self {
604            BNode::Leaf { entries } | BNode::Internal { entries, .. } => entries.len(),
605        }
606    }
607}
608
609/// Transient remove worker. Returns the removed value, or `None` when the
610/// key is not in this subtree (in which case nothing was modified).
611fn remove_transient_helper<K: Ord + Clone, V: Clone>(
612    node: &mut Arc<BNode<K, V>>,
613    key: &K,
614) -> Option<V> {
615    // Probe before `make_mut`: a miss must not path-copy the spine.
616    let (found, idx) = match node.as_ref() {
617        BNode::Leaf { entries } | BNode::Internal { entries, .. } => {
618            match entries.binary_search_by(|(ek, _)| ek.cmp(key)) {
619                Ok(i) => (true, i),
620                Err(i) => (false, i),
621            }
622        }
623    };
624    if !found && matches!(node.as_ref(), BNode::Leaf { .. }) {
625        return None;
626    }
627    let inner = Arc::make_mut(node);
628    match inner {
629        BNode::Leaf { entries } => Some(entries.remove(idx).1),
630        BNode::Internal { entries, children } => {
631            if found {
632                // Standard B-tree interior delete: swap in the in-order
633                // predecessor, which always lives in a leaf, then repair
634                // the subtree it came out of.
635                let pred = remove_max(&mut children[idx]);
636                let old = core::mem::replace(&mut entries[idx], pred);
637                fix_child(entries, children, idx);
638                Some(old.1)
639            } else {
640                let removed = remove_transient_helper(&mut children[idx], key)?;
641                fix_child(entries, children, idx);
642                Some(removed)
643            }
644        }
645    }
646}
647
648/// Detach the largest entry of this subtree. The subtree must be non-empty;
649/// callers only reach it from an internal node whose children are populated.
650fn remove_max<K: Ord + Clone, V: Clone>(node: &mut Arc<BNode<K, V>>) -> (K, V) {
651    let inner = Arc::make_mut(node);
652    match inner {
653        BNode::Leaf { entries } => entries.pop().expect("a B-tree leaf is never empty"),
654        BNode::Internal { entries, children } => {
655            let last = children.len() - 1;
656            let kv = remove_max(&mut children[last]);
657            fix_child(entries, children, last);
658            kv
659        }
660    }
661}
662
663/// Restore `children[i]`'s minimum occupancy by borrowing from a sibling, or
664/// merging with one when neither sibling can spare an entry.
665fn fix_child<K: Ord + Clone, V: Clone>(
666    entries: &mut Vec<(K, V)>,
667    children: &mut Vec<Arc<BNode<K, V>>>,
668    i: usize,
669) {
670    if children[i].entry_count() >= MIN_ENTRIES {
671        return;
672    }
673    if i > 0 && children[i - 1].entry_count() > MIN_ENTRIES {
674        rotate_from_left(entries, children, i);
675    } else if i + 1 < children.len() && children[i + 1].entry_count() > MIN_ENTRIES {
676        rotate_from_right(entries, children, i);
677    } else if i > 0 {
678        merge_children(entries, children, i - 1);
679    } else {
680        merge_children(entries, children, i);
681    }
682}
683
684/// Move the left sibling's largest entry up into the separator slot and the
685/// old separator down into `children[i]`'s front.
686fn rotate_from_left<K: Ord + Clone, V: Clone>(
687    entries: &mut [(K, V)],
688    children: &mut [Arc<BNode<K, V>>],
689    i: usize,
690) {
691    let (moved_entry, moved_child) = match Arc::make_mut(&mut children[i - 1]) {
692        BNode::Leaf { entries: le } => (le.pop().expect("sibling has entries to spare"), None),
693        BNode::Internal {
694            entries: le,
695            children: lc,
696        } => (
697            le.pop().expect("sibling has entries to spare"),
698            Some(
699                lc.pop()
700                    .expect("internal node has entries.len()+1 children"),
701            ),
702        ),
703    };
704    let separator = core::mem::replace(&mut entries[i - 1], moved_entry);
705    match Arc::make_mut(&mut children[i]) {
706        BNode::Leaf { entries: ce } => {
707            debug_assert!(moved_child.is_none());
708            ce.insert(0, separator);
709        }
710        BNode::Internal {
711            entries: ce,
712            children: cc,
713        } => {
714            ce.insert(0, separator);
715            cc.insert(
716                0,
717                moved_child.expect("sibling of an internal node is internal"),
718            );
719        }
720    }
721}
722
723/// Mirror of [`rotate_from_left`] using the right sibling.
724fn rotate_from_right<K: Ord + Clone, V: Clone>(
725    entries: &mut [(K, V)],
726    children: &mut [Arc<BNode<K, V>>],
727    i: usize,
728) {
729    let (moved_entry, moved_child) = match Arc::make_mut(&mut children[i + 1]) {
730        BNode::Leaf { entries: re } => (re.remove(0), None),
731        BNode::Internal {
732            entries: re,
733            children: rc,
734        } => (re.remove(0), Some(rc.remove(0))),
735    };
736    let separator = core::mem::replace(&mut entries[i], moved_entry);
737    match Arc::make_mut(&mut children[i]) {
738        BNode::Leaf { entries: ce } => {
739            debug_assert!(moved_child.is_none());
740            ce.push(separator);
741        }
742        BNode::Internal {
743            entries: ce,
744            children: cc,
745        } => {
746            ce.push(separator);
747            cc.push(moved_child.expect("sibling of an internal node is internal"));
748        }
749    }
750}
751
752/// Fold `children[sep + 1]` and the separator entry into `children[sep]`.
753/// Both children are at minimum occupancy, so the result fits.
754fn merge_children<K: Ord + Clone, V: Clone>(
755    entries: &mut Vec<(K, V)>,
756    children: &mut Vec<Arc<BNode<K, V>>>,
757    sep: usize,
758) {
759    let separator = entries.remove(sep);
760    let right = children.remove(sep + 1);
761    let right = Arc::try_unwrap(right).unwrap_or_else(|shared| (*shared).clone());
762    match (Arc::make_mut(&mut children[sep]), right) {
763        (BNode::Leaf { entries: le }, BNode::Leaf { entries: re }) => {
764            le.push(separator);
765            le.extend(re);
766        }
767        (
768            BNode::Internal {
769                entries: le,
770                children: lc,
771            },
772            BNode::Internal {
773                entries: re,
774                children: rc,
775            },
776        ) => {
777            le.push(separator);
778            le.extend(re);
779            lc.extend(rc);
780        }
781        // Siblings are always at the same depth, so the mixed cases are
782        // unreachable; putting the separator back keeps the map a valid
783        // (if larger) tree rather than losing an entry.
784        (BNode::Leaf { entries: le }, BNode::Internal { .. })
785        | (BNode::Internal { entries: le, .. }, BNode::Leaf { .. }) => {
786            debug_assert!(false, "B-tree siblings must be at the same depth");
787            le.push(separator);
788        }
789    }
790}
791
792/// r1019 — the `Borrow`-generic twins of `linear_find_entry` /
793/// `linear_position_internal`, and of `get_mut_helper`. Identical searches;
794/// the key is compared through `Borrow` so a `&str` can address a `String`.
795fn linear_find_entry_by<K, V, Q>(entries: &[(K, V)], key: &Q) -> Option<usize>
796where
797    K: core::borrow::Borrow<Q>,
798    Q: Ord + ?Sized,
799{
800    for (i, (k, _)) in entries.iter().enumerate() {
801        match k.borrow().cmp(key) {
802            core::cmp::Ordering::Equal => return Some(i),
803            core::cmp::Ordering::Greater => return None,
804            core::cmp::Ordering::Less => continue,
805        }
806    }
807    None
808}
809
810fn linear_position_internal_by<K, V, Q>(entries: &[(K, V)], key: &Q) -> FoundOrDescend
811where
812    K: core::borrow::Borrow<Q>,
813    Q: Ord + ?Sized,
814{
815    for (i, (k, _)) in entries.iter().enumerate() {
816        match k.borrow().cmp(key) {
817            core::cmp::Ordering::Equal => return FoundOrDescend::Found(i),
818            core::cmp::Ordering::Greater => return FoundOrDescend::Descend(i),
819            core::cmp::Ordering::Less => continue,
820        }
821    }
822    FoundOrDescend::Descend(entries.len())
823}
824
825fn get_mut_by_helper<'a, K, V, Q>(node: &'a mut Arc<BNode<K, V>>, key: &Q) -> Option<&'a mut V>
826where
827    K: core::borrow::Borrow<Q> + Clone,
828    V: Clone,
829    Q: Ord + ?Sized,
830{
831    match Arc::make_mut(node) {
832        BNode::Leaf { entries } => {
833            let i = linear_find_entry_by(entries, key)?;
834            Some(&mut entries[i].1)
835        }
836        BNode::Internal { entries, children } => match linear_position_internal_by(entries, key) {
837            FoundOrDescend::Found(i) => Some(&mut entries[i].1),
838            FoundOrDescend::Descend(i) => get_mut_by_helper(&mut children[i], key),
839        },
840    }
841}
842
843/// r1018 — the descent behind [`PersistentBTreeMap::get_mut`]. Mirrors
844/// [`PersistentBTreeMap::get`]'s search exactly; the only difference is that
845/// it walks `Arc::make_mut` so the borrow it hands back is unique.
846fn get_mut_helper<'a, K: Ord + Clone, V: Clone>(
847    node: &'a mut Arc<BNode<K, V>>,
848    key: &K,
849) -> Option<&'a mut V> {
850    match Arc::make_mut(node) {
851        BNode::Leaf { entries } => {
852            let i = linear_find_entry(entries, key)?;
853            Some(&mut entries[i].1)
854        }
855        BNode::Internal { entries, children } => match linear_position_internal(entries, key) {
856            FoundOrDescend::Found(i) => Some(&mut entries[i].1),
857            FoundOrDescend::Descend(i) => get_mut_helper(&mut children[i], key),
858        },
859    }
860}
861
862/// Transient insert worker — walks `Arc::make_mut` down the spine so each
863/// uniquely-owned node mutates in place. Splits still allocate fresh
864/// `Arc<BNode>` for the new right sibling (those are genuinely new nodes,
865/// not CoW copies).
866fn insert_transient_helper<K: Ord + Clone, V: Clone>(
867    node: &mut Arc<BNode<K, V>>,
868    k: K,
869    v: V,
870) -> (Option<(Arc<BNode<K, V>>, (K, V))>, Option<V>) {
871    let inner = Arc::make_mut(node);
872    match inner {
873        BNode::Leaf { entries } => {
874            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
875            let prev_v = match pos {
876                Ok(idx) => Some(core::mem::replace(&mut entries[idx].1, v)),
877                Err(idx) => {
878                    entries.insert(idx, (k, v));
879                    None
880                }
881            };
882            if entries.len() <= MAX_ENTRIES {
883                return (None, prev_v);
884            }
885            // Overflow: split (same arithmetic as the immutable path).
886            let mid = entries.len() / 2;
887            let right_entries = entries.split_off(mid + 1);
888            let median = entries.pop().expect("mid was in-bounds");
889            let right = Arc::new(BNode::Leaf {
890                entries: right_entries,
891            });
892            (Some((right, median)), prev_v)
893        }
894        BNode::Internal { entries, children } => {
895            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
896            match pos {
897                Ok(idx) => {
898                    let prev_v = core::mem::replace(&mut entries[idx].1, v);
899                    (None, Some(prev_v))
900                }
901                Err(idx) => {
902                    let (split, prev_v) = insert_transient_helper(&mut children[idx], k, v);
903                    if let Some((right_sibling, median)) = split {
904                        entries.insert(idx, median);
905                        children.insert(idx + 1, right_sibling);
906                    }
907                    if children.len() <= MAX_CHILDREN {
908                        return (None, prev_v);
909                    }
910                    let mid = entries.len() / 2;
911                    let right_entries = entries.split_off(mid + 1);
912                    let median = entries.pop().expect("mid was in-bounds");
913                    let right_children = children.split_off(mid + 1);
914                    let right = Arc::new(BNode::Internal {
915                        entries: right_entries,
916                        children: right_children,
917                    });
918                    (Some((right, median)), prev_v)
919                }
920            }
921        }
922    }
923}
924
925/// Recursive insert worker. Returns `(new_left_node, optional_split, prev_v)`
926/// where `optional_split = Some((right_sibling, median_entry))` when this
927/// node overflowed; the caller bubbles the split up.
928fn insert_helper<K: Ord + Clone, V: Clone>(
929    node: &Arc<BNode<K, V>>,
930    k: K,
931    v: V,
932) -> (
933    Arc<BNode<K, V>>,
934    Option<(Arc<BNode<K, V>>, (K, V))>,
935    Option<V>,
936) {
937    match &**node {
938        BNode::Leaf { entries } => {
939            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
940            let mut new_entries = entries.clone();
941            let prev_v = match pos {
942                Ok(idx) => Some(core::mem::replace(&mut new_entries[idx].1, v)),
943                Err(idx) => {
944                    new_entries.insert(idx, (k, v));
945                    None
946                }
947            };
948            if new_entries.len() <= MAX_ENTRIES {
949                return (
950                    Arc::new(BNode::Leaf {
951                        entries: new_entries,
952                    }),
953                    None,
954                    prev_v,
955                );
956            }
957            // Overflow: split. With MAX_ENTRIES = 7 the overflowed leaf holds
958            // 8 entries → 4 left + median + 3 right (or vice versa). Each
959            // half retains ≥ MIN_ENTRIES = 3.
960            let mid = new_entries.len() / 2; // 4
961            let right_entries = new_entries.split_off(mid + 1);
962            let median = new_entries.pop().expect("mid was in-bounds");
963            let left = Arc::new(BNode::Leaf {
964                entries: new_entries,
965            });
966            let right = Arc::new(BNode::Leaf {
967                entries: right_entries,
968            });
969            (left, Some((right, median)), prev_v)
970        }
971        BNode::Internal { entries, children } => {
972            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
973            match pos {
974                Ok(idx) => {
975                    // Key already lives on this internal node — replace V.
976                    let mut new_entries = entries.clone();
977                    let prev_v = core::mem::replace(&mut new_entries[idx].1, v);
978                    (
979                        Arc::new(BNode::Internal {
980                            entries: new_entries,
981                            children: children.clone(),
982                        }),
983                        None,
984                        Some(prev_v),
985                    )
986                }
987                Err(idx) => {
988                    // Descend into children[idx]; bubble up any split.
989                    let (new_child, split, prev_v) = insert_helper(&children[idx], k, v);
990                    let mut new_entries = entries.clone();
991                    let mut new_children = children.clone();
992                    new_children[idx] = new_child;
993                    if let Some((right_sibling, median)) = split {
994                        new_entries.insert(idx, median);
995                        new_children.insert(idx + 1, right_sibling);
996                    }
997                    if new_children.len() <= MAX_CHILDREN {
998                        return (
999                            Arc::new(BNode::Internal {
1000                                entries: new_entries,
1001                                children: new_children,
1002                            }),
1003                            None,
1004                            prev_v,
1005                        );
1006                    }
1007                    // Internal overflow: 8 entries + 9 children → split into
1008                    // 4-entry + 5-child left, median goes up, 3-entry +
1009                    // 4-child right. Both halves keep ≥ MIN_ENTRIES = 3 and
1010                    // ≥ MIN_CHILDREN = 4.
1011                    let mid = new_entries.len() / 2; // 4
1012                    let right_entries = new_entries.split_off(mid + 1);
1013                    let median = new_entries.pop().expect("mid was in-bounds");
1014                    let right_children = new_children.split_off(mid + 1);
1015                    let left = Arc::new(BNode::Internal {
1016                        entries: new_entries,
1017                        children: new_children,
1018                    });
1019                    let right = Arc::new(BNode::Internal {
1020                        entries: right_entries,
1021                        children: right_children,
1022                    });
1023                    (left, Some((right, median)), prev_v)
1024                }
1025            }
1026        }
1027    }
1028}
1029
1030/// In-order `(K, V)` iterator. Uses an explicit stack with `(node, child_index)`
1031/// frames so we don't need to copy entries into a flat buffer.
1032#[derive(Debug)]
1033pub struct Iter<'a, K, V> {
1034    stack: Vec<(&'a Arc<BNode<K, V>>, usize)>,
1035}
1036
1037impl<'a, K, V> Iterator for Iter<'a, K, V> {
1038    type Item = (&'a K, &'a V);
1039    fn next(&mut self) -> Option<(&'a K, &'a V)> {
1040        loop {
1041            let (node, idx) = *self.stack.last()?;
1042            match &**node {
1043                BNode::Leaf { entries } => {
1044                    if idx < entries.len() {
1045                        let (k, v) = &entries[idx];
1046                        self.stack.last_mut().unwrap().1 = idx + 1;
1047                        return Some((k, v));
1048                    }
1049                    self.stack.pop();
1050                }
1051                BNode::Internal { entries, children } => {
1052                    // Frame layout: child_index `idx` means "we still need
1053                    // to descend into children[idx / 2]" (even) or "emit
1054                    // entries[idx / 2]" (odd). Encoding two phases per
1055                    // entry slot.
1056                    let phase = idx & 1;
1057                    let slot = idx >> 1;
1058                    if phase == 0 {
1059                        // Descend into children[slot] if it exists.
1060                        if slot < children.len() {
1061                            self.stack.last_mut().unwrap().1 = idx + 1;
1062                            self.stack.push((&children[slot], 0));
1063                            continue;
1064                        }
1065                        self.stack.pop();
1066                    } else {
1067                        // Emit entries[slot] if it exists.
1068                        if slot < entries.len() {
1069                            let (k, v) = &entries[slot];
1070                            self.stack.last_mut().unwrap().1 = idx + 1;
1071                            return Some((k, v));
1072                        }
1073                        self.stack.pop();
1074                    }
1075                }
1076            }
1077        }
1078    }
1079}
1080
1081/// v7.38 — bounded-above wrapper over [`Iter`], produced by
1082/// [`PersistentBTreeMap::range`]. `Iter` already starts at `lo` (the seek
1083/// stack); this stops emission at the first key past `hi`.
1084#[derive(Debug)]
1085pub struct RangeIter<'a, K, V> {
1086    inner: Iter<'a, K, V>,
1087    hi_key: Option<K>,
1088    hi_incl: bool,
1089    done: bool,
1090}
1091
1092impl<'a, K: Ord, V> Iterator for RangeIter<'a, K, V> {
1093    type Item = (&'a K, &'a V);
1094    fn next(&mut self) -> Option<(&'a K, &'a V)> {
1095        if self.done {
1096            return None;
1097        }
1098        let (k, v) = self.inner.next()?;
1099        if let Some(h) = &self.hi_key {
1100            let past = if self.hi_incl { k > h } else { k >= h };
1101            if past {
1102                self.done = true;
1103                return None;
1104            }
1105        }
1106        Some((k, v))
1107    }
1108}
1109
1110/// v7.34.4 — descending-order `(K, V)` iterator. Mirrors `Iter` but each
1111/// node's child-then-entry walk runs right-to-left so the first yielded
1112/// pair is the maximum key in the map. Used by the ORDER BY `<indexed
1113/// col>` DESC + LIMIT N executor path to walk only the first N matches
1114/// off the rightmost leaf instead of materialising every row + partial-
1115/// sorting; the existing forward `Iter` stays untouched so unrelated
1116/// callers (catalog deserialisation, PartialEq) are unaffected.
1117#[derive(Debug)]
1118pub struct IterRev<'a, K, V> {
1119    // (node, next_pos) where next_pos counts the remaining reverse
1120    // step within the node, starting at 1. A pos > step_count means
1121    // the node is exhausted (pop). For a Leaf with E entries the
1122    // step count is E (emit entries right-to-left). For an Internal
1123    // node with E entries / E+1 children the step count is 2E+1: odd
1124    // positions descend into a child, even positions emit an entry,
1125    // both walking right-to-left.
1126    stack: Vec<(&'a Arc<BNode<K, V>>, usize)>,
1127}
1128
1129impl<'a, K, V> Iterator for IterRev<'a, K, V> {
1130    type Item = (&'a K, &'a V);
1131    fn next(&mut self) -> Option<(&'a K, &'a V)> {
1132        loop {
1133            let (node, pos) = *self.stack.last()?;
1134            match &**node {
1135                BNode::Leaf { entries } => {
1136                    if pos <= entries.len() {
1137                        let i = entries.len() - pos;
1138                        self.stack.last_mut().unwrap().1 = pos + 1;
1139                        let (k, v) = &entries[i];
1140                        return Some((k, v));
1141                    }
1142                    self.stack.pop();
1143                }
1144                BNode::Internal { entries, children } => {
1145                    let n_steps = 2 * entries.len() + 1;
1146                    if pos <= n_steps {
1147                        self.stack.last_mut().unwrap().1 = pos + 1;
1148                        if pos % 2 == 1 {
1149                            // Odd: descend into `children[E - (pos-1)/2]`.
1150                            let child_idx = entries.len() - (pos - 1) / 2;
1151                            self.stack.push((&children[child_idx], 1));
1152                            continue;
1153                        }
1154                        // Even: emit `entries[E - pos/2]`.
1155                        let entry_idx = entries.len() - pos / 2;
1156                        let (k, v) = &entries[entry_idx];
1157                        return Some((k, v));
1158                    }
1159                    self.stack.pop();
1160                }
1161            }
1162        }
1163    }
1164}
1165
1166impl<'a, K: Ord, V> IntoIterator for &'a PersistentBTreeMap<K, V> {
1167    type Item = (&'a K, &'a V);
1168    type IntoIter = Iter<'a, K, V>;
1169    fn into_iter(self) -> Self::IntoIter {
1170        self.iter()
1171    }
1172}
1173
1174#[cfg(test)]
1175#[allow(
1176    clippy::cast_possible_truncation,
1177    clippy::cast_possible_wrap,
1178    clippy::cast_sign_loss,
1179    clippy::cast_lossless,
1180    clippy::needless_range_loop,
1181    clippy::items_after_statements,
1182    clippy::manual_range_patterns,
1183    clippy::unreadable_literal,
1184    clippy::similar_names
1185)]
1186mod tests {
1187    use super::*;
1188    use alloc::collections::BTreeMap;
1189    use alloc::vec;
1190
1191    // ---- round 465: structural checks for removal ----
1192
1193    /// Depth of every leaf, or `None` when they disagree. Also asserts each
1194    /// node's local shape: entry count in range, entries strictly ascending,
1195    /// and `children.len() == entries.len() + 1`.
1196    fn check_node<K: Ord + core::fmt::Debug, V>(
1197        node: &BNode<K, V>,
1198        is_root: bool,
1199        depth: usize,
1200    ) -> usize {
1201        let entries = match node {
1202            BNode::Leaf { entries } | BNode::Internal { entries, .. } => entries,
1203        };
1204        assert!(
1205            entries.len() <= MAX_ENTRIES,
1206            "node overfull: {} entries",
1207            entries.len()
1208        );
1209        if !is_root {
1210            assert!(
1211                entries.len() >= MIN_ENTRIES,
1212                "non-root node underfull: {} entries",
1213                entries.len()
1214            );
1215        }
1216        for w in entries.windows(2) {
1217            assert!(w[0].0 < w[1].0, "entries out of order inside a node");
1218        }
1219        match node {
1220            BNode::Leaf { .. } => depth,
1221            BNode::Internal { entries, children } => {
1222                assert_eq!(
1223                    children.len(),
1224                    entries.len() + 1,
1225                    "internal node must have entries.len()+1 children"
1226                );
1227                let mut seen: Option<usize> = None;
1228                for c in children {
1229                    let d = check_node(c, false, depth + 1);
1230                    match seen {
1231                        None => seen = Some(d),
1232                        Some(prev) => assert_eq!(prev, d, "leaves at different depths"),
1233                    }
1234                }
1235                seen.expect("internal node has children")
1236            }
1237        }
1238    }
1239
1240    fn check_map<K: Ord + Clone + core::fmt::Debug, V: Clone>(m: &PersistentBTreeMap<K, V>) {
1241        check_node(m.root.as_ref(), true, 0);
1242        // In-order traversal must be globally sorted, and `len` must match.
1243        let keys: Vec<&K> = m.iter().map(|(k, _)| k).collect();
1244        for w in keys.windows(2) {
1245            assert!(w[0] < w[1], "iteration order is not globally sorted");
1246        }
1247        assert_eq!(keys.len(), m.len(), "len disagrees with iteration");
1248    }
1249
1250    /// Deterministic sequence — no rand dependency, and a failure is
1251    /// reproducible from the seed alone.
1252    fn lcg(state: &mut u64) -> u64 {
1253        *state = state
1254            .wrapping_mul(6_364_136_223_846_793_005)
1255            .wrapping_add(1);
1256        *state >> 33
1257    }
1258
1259    #[test]
1260    fn round465_remove_absent_key_is_a_no_op() {
1261        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1262        for i in 0..50_i64 {
1263            pb.insert_mut(i * 2, i);
1264        }
1265        let before = pb.len();
1266        assert_eq!(pb.remove_mut(&7), None);
1267        assert_eq!(pb.remove_mut(&-1), None);
1268        assert_eq!(pb.remove_mut(&1000), None);
1269        assert_eq!(pb.len(), before);
1270        check_map(&pb);
1271    }
1272
1273    #[test]
1274    fn round465_remove_every_key_empties_the_map() {
1275        for n in [1_i64, 7, 8, 9, 100, 500] {
1276            let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1277            for i in 0..n {
1278                pb.insert_mut(i, i * 3);
1279            }
1280            for i in 0..n {
1281                assert_eq!(pb.remove_mut(&i), Some(i * 3), "n={n} i={i}");
1282                assert_eq!(pb.len() as i64, n - i - 1);
1283                check_map(&pb);
1284                // Everything not yet removed must still be reachable.
1285                for j in (i + 1)..n {
1286                    assert_eq!(
1287                        pb.get(&j),
1288                        Some(&(j * 3)),
1289                        "n={n} lost {j} after removing {i}"
1290                    );
1291                }
1292            }
1293            assert!(pb.is_empty());
1294        }
1295    }
1296
1297    #[test]
1298    fn round465_remove_in_reverse_order_empties_the_map() {
1299        // Descending removal drives the merge path from the other side —
1300        // rotate_from_left instead of rotate_from_right.
1301        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1302        for i in 0..300_i64 {
1303            pb.insert_mut(i, i);
1304        }
1305        for i in (0..300_i64).rev() {
1306            assert_eq!(pb.remove_mut(&i), Some(i));
1307            check_map(&pb);
1308        }
1309        assert!(pb.is_empty());
1310    }
1311
1312    #[test]
1313    fn round465_matches_btreemap_under_mixed_traffic() {
1314        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1315        let mut model: BTreeMap<i64, i64> = BTreeMap::new();
1316        let mut seed = 0x5eed_1234_u64;
1317        for step in 0..4000 {
1318            let k = (lcg(&mut seed) % 300) as i64;
1319            if lcg(&mut seed) % 3 == 0 {
1320                assert_eq!(
1321                    pb.remove_mut(&k),
1322                    model.remove(&k),
1323                    "step {step} remove {k}"
1324                );
1325            } else {
1326                let v = (lcg(&mut seed) % 1000) as i64;
1327                assert_eq!(
1328                    pb.insert_mut(k, v),
1329                    model.insert(k, v),
1330                    "step {step} insert {k}"
1331                );
1332            }
1333            assert_eq!(pb.len(), model.len(), "step {step}");
1334            if step % 97 == 0 {
1335                check_map(&pb);
1336                let got: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1337                let want: Vec<(i64, i64)> = model.iter().map(|(k, v)| (*k, *v)).collect();
1338                assert_eq!(got, want, "step {step}");
1339            }
1340        }
1341        check_map(&pb);
1342        let got: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1343        let want: Vec<(i64, i64)> = model.iter().map(|(k, v)| (*k, *v)).collect();
1344        assert_eq!(got, want);
1345    }
1346
1347    #[test]
1348    fn round465_remove_leaves_a_shared_snapshot_untouched() {
1349        // The whole point of the persistent structure: a clone taken before
1350        // the removal must still see the removed key.
1351        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1352        for i in 0..200_i64 {
1353            pb.insert_mut(i, i * 5);
1354        }
1355        let snapshot = pb.clone();
1356        for i in 0..100_i64 {
1357            pb.remove_mut(&(i * 2));
1358        }
1359        check_map(&pb);
1360        check_map(&snapshot);
1361        assert_eq!(snapshot.len(), 200);
1362        for i in 0..200_i64 {
1363            assert_eq!(snapshot.get(&i), Some(&(i * 5)), "snapshot lost {i}");
1364        }
1365        for i in 0..100_i64 {
1366            assert_eq!(pb.get(&(i * 2)), None);
1367            assert_eq!(pb.get(&(i * 2 + 1)), Some(&(i * 10 + 5)));
1368        }
1369    }
1370
1371    #[test]
1372    fn round465_immutable_remove_does_not_touch_the_receiver() {
1373        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1374        for i in 0..60_i64 {
1375            pb.insert_mut(i, i);
1376        }
1377        let (next, prev) = pb.remove(&30);
1378        assert_eq!(prev, Some(30));
1379        assert_eq!(pb.get(&30), Some(&30), "receiver must be untouched");
1380        assert_eq!(next.get(&30), None);
1381        assert_eq!(pb.len(), 60);
1382        assert_eq!(next.len(), 59);
1383        check_map(&pb);
1384        check_map(&next);
1385    }
1386
1387    #[test]
1388    fn round465_range_and_predecessor_still_work_after_removals() {
1389        // Removal rewires spines; the ordered readers must follow.
1390        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1391        for i in 0..400_i64 {
1392            pb.insert_mut(i, i);
1393        }
1394        for i in 0..400_i64 {
1395            if i % 3 == 0 {
1396                pb.remove_mut(&i);
1397            }
1398        }
1399        check_map(&pb);
1400        let in_range: Vec<i64> = pb
1401            .range(Bound::Included(&100), Bound::Excluded(&120))
1402            .map(|(k, _)| *k)
1403            .collect();
1404        let want: Vec<i64> = (100..120).filter(|i| i % 3 != 0).collect();
1405        assert_eq!(in_range, want);
1406        // 99 is a multiple of 3 and was removed, so 100's predecessor is 98.
1407        assert_eq!(pb.predecessor(&100).map(|(k, _)| *k), Some(98));
1408        let rev: Vec<i64> = pb.iter_rev().map(|(k, _)| *k).take(3).collect();
1409        assert_eq!(rev, vec![398, 397, 395]);
1410    }
1411
1412    #[test]
1413    fn empty_map_is_empty() {
1414        let pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1415        assert_eq!(pb.len(), 0);
1416        assert!(pb.is_empty());
1417        assert!(pb.get(&42).is_none());
1418    }
1419
1420    #[test]
1421    fn insert_single_into_empty_works() {
1422        let (pb, prev) = PersistentBTreeMap::<i64, i64>::new().insert(1, 100);
1423        assert_eq!(prev, None);
1424        assert_eq!(pb.len(), 1);
1425        assert_eq!(pb.get(&1), Some(&100));
1426        assert_eq!(pb.get(&2), None);
1427    }
1428
1429    #[test]
1430    fn insert_replace_returns_prev_keeps_len() {
1431        let (pb, p1) = PersistentBTreeMap::<i64, i64>::new().insert(7, 10);
1432        assert_eq!(p1, None);
1433        let (pb, p2) = pb.insert(7, 99);
1434        assert_eq!(p2, Some(10));
1435        assert_eq!(pb.len(), 1);
1436        assert_eq!(pb.get(&7), Some(&99));
1437    }
1438
1439    #[test]
1440    fn insert_crosses_leaf_split_boundary() {
1441        // 8 inserts cause the first leaf to split.
1442        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1443        for i in 0..20_i64 {
1444            pb = pb.insert(i, i * 7).0;
1445        }
1446        for i in 0..20_i64 {
1447            assert_eq!(pb.get(&i), Some(&(i * 7)));
1448        }
1449        assert!(pb.get(&20).is_none());
1450        assert_eq!(pb.len(), 20);
1451    }
1452
1453    #[test]
1454    fn insert_grows_through_multiple_internal_splits() {
1455        // 200 inserts force the trie depth to grow more than once.
1456        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1457        for i in 0..200_i64 {
1458            pb = pb.insert(i, i * 11).0;
1459        }
1460        for i in 0..200_i64 {
1461            assert_eq!(pb.get(&i), Some(&(i * 11)));
1462        }
1463        assert_eq!(pb.len(), 200);
1464    }
1465
1466    #[test]
1467    fn clone_then_insert_preserves_original() {
1468        let mut a: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1469        for i in 0..100_i64 {
1470            a = a.insert(i, i).0;
1471        }
1472        let b = a.clone();
1473        let (b, _) = b.insert(999, 999);
1474        assert_eq!(a.len(), 100);
1475        assert!(a.get(&999).is_none());
1476        assert_eq!(b.len(), 101);
1477        assert_eq!(b.get(&999), Some(&999));
1478        for i in 0..100_i64 {
1479            assert_eq!(a.get(&i), Some(&i), "A drift at {i}");
1480            assert_eq!(b.get(&i), Some(&i), "B drift at {i}");
1481        }
1482    }
1483
1484    #[test]
1485    fn iter_yields_sorted_order() {
1486        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1487        // Insert in shuffled order; iter must still come out sorted.
1488        for &k in &[7_i64, 3, 11, 1, 9, 5, 14, 2, 8, 12, 4, 6, 10, 13] {
1489            pb = pb.insert(k, k * 2).0;
1490        }
1491        let collected: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1492        let expected: Vec<(i64, i64)> = (1..=14).map(|k| (k, k * 2)).collect();
1493        assert_eq!(collected, expected);
1494    }
1495
1496    #[test]
1497    fn iter_handles_taller_tree() {
1498        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1499        for i in 0..500_i64 {
1500            pb = pb.insert(i, i).0;
1501        }
1502        let collected: Vec<i64> = pb.iter().map(|(k, _)| *k).collect();
1503        let expected: Vec<i64> = (0..500).collect();
1504        assert_eq!(collected, expected);
1505    }
1506
1507    #[test]
1508    fn range_basic_bounds() {
1509        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1510        for i in 0..100_i64 {
1511            pb = pb.insert(i, i * 10).0;
1512        }
1513        let keys = |lo: Bound<&i64>, hi: Bound<&i64>| -> Vec<i64> {
1514            pb.range(lo, hi).map(|(k, _)| *k).collect()
1515        };
1516        assert_eq!(
1517            keys(Bound::Included(&20), Bound::Included(&24)),
1518            vec![20, 21, 22, 23, 24]
1519        );
1520        assert_eq!(
1521            keys(Bound::Excluded(&20), Bound::Excluded(&24)),
1522            vec![21, 22, 23]
1523        );
1524        assert_eq!(keys(Bound::Unbounded, Bound::Excluded(&3)), vec![0, 1, 2]);
1525        assert_eq!(
1526            keys(Bound::Included(&97), Bound::Unbounded),
1527            vec![97, 98, 99]
1528        );
1529        assert!(keys(Bound::Included(&50), Bound::Included(&49)).is_empty());
1530        // Out-of-range bounds clamp to the data.
1531        assert_eq!(
1532            keys(Bound::Included(&-5), Bound::Included(&2)),
1533            vec![0, 1, 2]
1534        );
1535        assert_eq!(
1536            keys(Bound::Included(&200), Bound::Unbounded),
1537            Vec::<i64>::new()
1538        );
1539    }
1540
1541    /// Fuzz `range` against `BTreeMap::range` across random data + random
1542    /// bounds (inclusive / exclusive / unbounded on each end) — the perf
1543    /// index range scan rides on this, and it's a stone (max blast radius),
1544    /// so the range walk must match the std oracle exactly.
1545    #[test]
1546    fn fuzz_range_against_btreemap() {
1547        let mut rng = Splitmix::new(0x5EED_1234_u64);
1548        const KEY_RANGE: i64 = 512;
1549        // A few tree sizes so we exercise leaf-only, shallow, and deep trees.
1550        for &n_inserts in &[0usize, 5, 40, 300, 2000] {
1551            let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1552            let mut oracle: BTreeMap<i64, i64> = BTreeMap::new();
1553            for _ in 0..n_inserts {
1554                let key = (rng.next() as i64).rem_euclid(KEY_RANGE);
1555                let val = rng.next() as i64;
1556                pb = pb.insert(key, val).0;
1557                oracle.insert(key, val);
1558            }
1559            for _ in 0..2000 {
1560                let a = (rng.next() as i64).rem_euclid(KEY_RANGE + 40) - 20;
1561                let b = (rng.next() as i64).rem_euclid(KEY_RANGE + 40) - 20;
1562                let (lo_raw, hi_raw) = if a <= b { (a, b) } else { (b, a) };
1563                let mk = |raw: i64, sel: u64| -> Bound<i64> {
1564                    match sel % 3 {
1565                        0 => Bound::Included(raw),
1566                        1 => Bound::Excluded(raw),
1567                        _ => Bound::Unbounded,
1568                    }
1569                };
1570                let lo = mk(lo_raw, rng.next());
1571                let hi = mk(hi_raw, rng.next());
1572                // `BTreeMap::range` panics on `Excluded(x)..Excluded(x)`; our
1573                // `range` yields empty there. Skip that one case for the oracle.
1574                if lo_raw == hi_raw
1575                    && matches!(lo, Bound::Excluded(_))
1576                    && matches!(hi, Bound::Excluded(_))
1577                {
1578                    continue;
1579                }
1580                let lo_ref = match &lo {
1581                    Bound::Included(k) => Bound::Included(k),
1582                    Bound::Excluded(k) => Bound::Excluded(k),
1583                    Bound::Unbounded => Bound::Unbounded,
1584                };
1585                let hi_ref = match &hi {
1586                    Bound::Included(k) => Bound::Included(k),
1587                    Bound::Excluded(k) => Bound::Excluded(k),
1588                    Bound::Unbounded => Bound::Unbounded,
1589                };
1590                let got: Vec<(i64, i64)> =
1591                    pb.range(lo_ref, hi_ref).map(|(k, v)| (*k, *v)).collect();
1592                let want: Vec<(i64, i64)> = oracle.range((lo, hi)).map(|(k, v)| (*k, *v)).collect();
1593                assert_eq!(
1594                    got, want,
1595                    "range drift n={n_inserts} lo={lo_raw:?} hi={hi_raw:?}"
1596                );
1597            }
1598        }
1599    }
1600
1601    /// Fuzz `predecessor` against a `BTreeMap` oracle (largest key < probe)
1602    /// across leaf-only, shallow, and deep trees.
1603    #[test]
1604    fn fuzz_predecessor_against_btreemap() {
1605        let mut rng = Splitmix::new(0x9E37_79B9_7F4A_7C15_u64);
1606        const KEY_RANGE: i64 = 512;
1607        for &n_inserts in &[0usize, 5, 40, 300, 2000] {
1608            let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1609            let mut oracle: BTreeMap<i64, i64> = BTreeMap::new();
1610            for _ in 0..n_inserts {
1611                let key = (rng.next() as i64).rem_euclid(KEY_RANGE);
1612                let val = rng.next() as i64;
1613                pb = pb.insert(key, val).0;
1614                oracle.insert(key, val);
1615            }
1616            for _ in 0..2000 {
1617                let probe = (rng.next() as i64).rem_euclid(KEY_RANGE + 40) - 20;
1618                let got = pb.predecessor(&probe).map(|(k, v)| (*k, *v));
1619                let want = oracle.range(..probe).next_back().map(|(k, v)| (*k, *v));
1620                assert_eq!(got, want, "predecessor drift n={n_inserts} probe={probe}");
1621            }
1622        }
1623    }
1624
1625    #[test]
1626    fn iter_rev_yields_descending() {
1627        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1628        for &k in &[7_i64, 3, 11, 1, 9, 5, 14, 2, 8, 12, 4, 6, 10, 13] {
1629            pb = pb.insert(k, k * 2).0;
1630        }
1631        let collected: Vec<(i64, i64)> = pb.iter_rev().map(|(k, v)| (*k, *v)).collect();
1632        let expected: Vec<(i64, i64)> = (1..=14).rev().map(|k| (k, k * 2)).collect();
1633        assert_eq!(collected, expected);
1634    }
1635
1636    #[test]
1637    fn iter_rev_handles_taller_tree() {
1638        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1639        for i in 0..500_i64 {
1640            pb = pb.insert(i, i).0;
1641        }
1642        let collected: Vec<i64> = pb.iter_rev().map(|(k, _)| *k).collect();
1643        let expected: Vec<i64> = (0..500).rev().collect();
1644        assert_eq!(collected, expected);
1645    }
1646
1647    #[test]
1648    fn iter_rev_empty_map_returns_nothing() {
1649        let pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1650        assert_eq!(pb.iter_rev().count(), 0);
1651    }
1652
1653    #[test]
1654    fn iter_rev_lazy_stops_at_take() {
1655        // Critical for the ORDER BY DESC + LIMIT N executor path: only
1656        // the first N entries are touched, not the full N-entry walk.
1657        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1658        for i in 0..10000_i64 {
1659            pb = pb.insert(i, i).0;
1660        }
1661        let top5: Vec<i64> = pb.iter_rev().take(5).map(|(k, _)| *k).collect();
1662        assert_eq!(top5, vec![9999, 9998, 9997, 9996, 9995]);
1663    }
1664
1665    /// SplitMix-style PRNG so the fuzz oracle is reproducible.
1666    struct Splitmix(u64);
1667    impl Splitmix {
1668        fn new(seed: u64) -> Self {
1669            Self(seed)
1670        }
1671        fn next(&mut self) -> u64 {
1672            self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
1673            let mut x = self.0;
1674            x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
1675            x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
1676            x ^ (x >> 31)
1677        }
1678    }
1679
1680    /// 100K-step random `insert` / `get` fuzz against `std::BTreeMap`.
1681    /// Validates split/merge/replace semantics across the full tree depth.
1682    #[test]
1683    fn fuzz_oracle_against_std_btreemap() {
1684        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1685        let mut oracle: BTreeMap<i64, i64> = BTreeMap::new();
1686        let mut rng = Splitmix::new(0xC0FFEE_u64);
1687        const STEPS: usize = 100_000;
1688        // Use a bounded key range so we hit replaces, not just inserts.
1689        const KEY_RANGE: i64 = 4096;
1690        for step in 0..STEPS {
1691            let op = rng.next() % 3; // 0/1: insert, 2: get-check
1692            let key = (rng.next() as i64) % KEY_RANGE;
1693            match op {
1694                0 | 1 => {
1695                    let val = rng.next() as i64;
1696                    let (new_pb, prev_pb) = pb.insert(key, val);
1697                    let prev_oracle = oracle.insert(key, val);
1698                    assert_eq!(prev_pb, prev_oracle, "prev drift @ step {step}, key {key}");
1699                    pb = new_pb;
1700                    assert_eq!(pb.len(), oracle.len(), "len drift @ step {step}");
1701                }
1702                2 => {
1703                    let pb_v = pb.get(&key).copied();
1704                    let oracle_v = oracle.get(&key).copied();
1705                    assert_eq!(pb_v, oracle_v, "get drift @ step {step}, key {key}");
1706                }
1707                _ => unreachable!(),
1708            }
1709        }
1710        // Final sweep: every key in the oracle must match.
1711        for (k, v) in &oracle {
1712            assert_eq!(pb.get(k), Some(v), "final drift at key {k}");
1713        }
1714        // And iter must produce the same sorted sequence.
1715        let pb_collected: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1716        let oracle_collected: Vec<(i64, i64)> = oracle.iter().map(|(k, v)| (*k, *v)).collect();
1717        assert_eq!(pb_collected, oracle_collected);
1718    }
1719
1720    /// Clone-isolation: branch A → B and C, mutate independently, verify
1721    /// each handle reads back its own state without leaking into others.
1722    #[test]
1723    fn fuzz_oracle_clone_isolation() {
1724        let mut a: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1725        let mut oracle_a: BTreeMap<i64, i64> = BTreeMap::new();
1726        let mut rng = Splitmix::new(0xDECAFBAD_u64);
1727        for _ in 0..1_000 {
1728            let k = (rng.next() as i64) % 1000;
1729            let v = rng.next() as i64;
1730            a = a.insert(k, v).0;
1731            oracle_a.insert(k, v);
1732        }
1733        // Branch.
1734        let mut b = a.clone();
1735        let mut oracle_b = oracle_a.clone();
1736        let mut c = a.clone();
1737        let mut oracle_c = oracle_a.clone();
1738        for _ in 0..500 {
1739            let k = (rng.next() as i64) % 2000;
1740            let v = rng.next() as i64;
1741            b = b.insert(k, v).0;
1742            oracle_b.insert(k, v);
1743        }
1744        for _ in 0..300 {
1745            let k = (rng.next() as i64) % 500;
1746            let v = rng.next() as i64;
1747            c = c.insert(k, v).0;
1748            oracle_c.insert(k, v);
1749        }
1750        for (k, v) in &oracle_a {
1751            assert_eq!(a.get(k), Some(v), "A drift at {k}");
1752        }
1753        for (k, v) in &oracle_b {
1754            assert_eq!(b.get(k), Some(v), "B drift at {k}");
1755        }
1756        for (k, v) in &oracle_c {
1757            assert_eq!(c.get(k), Some(v), "C drift at {k}");
1758        }
1759    }
1760
1761    #[test]
1762    fn partial_eq_compares_by_elements() {
1763        let mut a: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1764        let mut b: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1765        // Build the same end-state via different insertion orders → tree
1766        // shapes likely differ, but PartialEq compares by iter().
1767        for &k in &[5_i64, 2, 8, 1, 7, 3, 6, 4] {
1768            a = a.insert(k, k * 10).0;
1769        }
1770        for &k in &[1_i64, 2, 3, 4, 5, 6, 7, 8] {
1771            b = b.insert(k, k * 10).0;
1772        }
1773        assert_eq!(a, b);
1774        let (a, _) = a.insert(9, 90);
1775        assert_ne!(a, b);
1776    }
1777}