Skip to main content

index_db/
tree.rs

1//! The [`BPlusTree`] public type: an ordered map laid out as a B+tree.
2
3use alloc::vec::Vec;
4use core::ops::RangeBounds;
5
6use crate::iter::Iter;
7use crate::node::Node;
8use crate::ops;
9use crate::store::{InMemoryStore, NodeId, NodeStore};
10
11/// Smallest fan-out a node may have. With fewer than three children a split
12/// cannot leave both halves non-empty, so the tree could not stay balanced.
13const MIN_ORDER: usize = 3;
14
15/// Default fan-out: up to 64 children per node, so up to 63 keys. A binary
16/// search over a node is then at most six comparisons, and a tree of a million
17/// keys stands four levels tall.
18const DEFAULT_ORDER: usize = 64;
19
20/// An ordered map backed by a B+tree.
21///
22/// Keys are kept in sorted order across a tree of fixed-fan-out nodes. Point
23/// operations — [`get`](BPlusTree::get), [`insert`](BPlusTree::insert),
24/// [`contains_key`](BPlusTree::contains_key) — run in time logarithmic in the
25/// number of entries: each level is one binary search over a node, and the
26/// height grows with the logarithm of the entry count. Beyond point access it
27/// supports ordered iteration and range scans, forward and in reverse.
28///
29/// The tree does not own its nodes directly. It addresses them by id through an
30/// internal node store, and the store the nodes live in is a seam: today they
31/// sit in a heap slab (a fast, single-threaded, in-process ordered map), and the
32/// same algorithm can later run over a page-backed store without change. That
33/// seam is internal — the public surface is just this one type.
34///
35/// `K` must be [`Ord`]; [`insert`](BPlusTree::insert) and
36/// [`remove`](BPlusTree::remove) additionally need [`Clone`], because the tree
37/// copies separator keys between nodes as it splits and rebalances.
38///
39/// # Examples
40///
41/// ```
42/// use index_db::BPlusTree;
43///
44/// let mut index = BPlusTree::new();
45/// index.insert(3_u32, "three");
46/// index.insert(1, "one");
47/// index.insert(2, "two");
48///
49/// assert_eq!(index.get(&2), Some(&"two"));
50/// assert_eq!(index.len(), 3);
51/// ```
52pub struct BPlusTree<K, V> {
53    /// Id of the root node within `store`.
54    root: NodeId,
55    /// Backend the nodes live in.
56    store: InMemoryStore<K, V>,
57    /// Maximum fan-out: the most children an internal node may hold, and one
58    /// more than the most keys any node may hold.
59    order: usize,
60    /// Number of entries in the tree.
61    len: usize,
62}
63
64impl<K, V> BPlusTree<K, V> {
65    /// Create an empty tree with the default node fan-out.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use index_db::BPlusTree;
71    ///
72    /// let index: BPlusTree<u32, &str> = BPlusTree::new();
73    /// assert!(index.is_empty());
74    /// ```
75    #[must_use]
76    pub fn new() -> Self {
77        Self::with_order(DEFAULT_ORDER)
78    }
79
80    /// Create an empty tree with an explicit node fan-out, clamped up to the
81    /// minimum a balanced tree requires. Used by the test suite to force splits
82    /// at small key counts; the public surface fixes the fan-out via [`new`].
83    ///
84    /// [`new`]: BPlusTree::new
85    #[must_use]
86    pub(crate) fn with_order(order: usize) -> Self {
87        let mut store = InMemoryStore::new();
88        let root = store.alloc(Node::empty_leaf());
89        BPlusTree {
90            root,
91            store,
92            order: order.max(MIN_ORDER),
93            len: 0,
94        }
95    }
96}
97
98impl<K: Ord + Clone, V> BPlusTree<K, V> {
99    /// Build a tree in bulk from entries already sorted by key.
100    ///
101    /// When the input is sorted strictly ascending by key, the tree is built
102    /// bottom-up — leaves packed and balanced in one pass — which is much faster
103    /// than inserting one entry at a time. If the input is *not* strictly
104    /// ascending (out of order or with duplicate keys), it falls back to ordinary
105    /// insertion, so the result is always a correct tree; only the fast path
106    /// requires sorted, unique keys. On the fallback path a later duplicate key
107    /// overwrites an earlier one.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use index_db::BPlusTree;
113    ///
114    /// // Sorted input takes the fast bottom-up path.
115    /// let index = BPlusTree::from_sorted((0..1_000_u32).map(|k| (k, k * k)));
116    /// assert_eq!(index.len(), 1_000);
117    /// assert_eq!(index.get(&30), Some(&900));
118    /// assert_eq!(index.get(&999), Some(&998_001));
119    /// ```
120    #[must_use]
121    pub fn from_sorted<I: IntoIterator<Item = (K, V)>>(entries: I) -> Self {
122        Self::from_sorted_with_order(entries, DEFAULT_ORDER)
123    }
124
125    /// [`from_sorted`](Self::from_sorted) with an explicit fan-out, for tests.
126    #[must_use]
127    pub(crate) fn from_sorted_with_order<I>(entries: I, order: usize) -> Self
128    where
129        I: IntoIterator<Item = (K, V)>,
130    {
131        let order = order.max(MIN_ORDER);
132        let entries: Vec<(K, V)> = entries.into_iter().collect();
133        let ascending = entries.windows(2).all(|w| w[0].0 < w[1].0);
134        if ascending {
135            let mut store = InMemoryStore::new();
136            let (root, len) = ops::bulk_load(&mut store, entries, order);
137            BPlusTree {
138                root,
139                store,
140                order,
141                len,
142            }
143        } else {
144            let mut tree = Self::with_order(order);
145            for (key, value) in entries {
146                let _previous = tree.insert(key, value);
147            }
148            tree
149        }
150    }
151}
152
153impl<K, V> BPlusTree<K, V> {
154    /// The number of entries in the tree.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// use index_db::BPlusTree;
160    ///
161    /// let mut index = BPlusTree::new();
162    /// index.insert("k", 1);
163    /// assert_eq!(index.len(), 1);
164    /// ```
165    #[must_use]
166    #[inline]
167    pub fn len(&self) -> usize {
168        self.len
169    }
170
171    /// Whether the tree holds no entries.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use index_db::BPlusTree;
177    ///
178    /// let mut index = BPlusTree::new();
179    /// assert!(index.is_empty());
180    /// index.insert("k", 1);
181    /// assert!(!index.is_empty());
182    /// ```
183    #[must_use]
184    #[inline]
185    pub fn is_empty(&self) -> bool {
186        self.len == 0
187    }
188}
189
190impl<K, V> BPlusTree<K, V> {
191    /// The height of the tree in levels: a tree whose root is a leaf has height
192    /// one, and every level of internal nodes above the leaves adds one more.
193    ///
194    /// Because the tree is balanced, this is the number of nodes touched on any
195    /// root-to-leaf path, and so the cost of a point lookup in node visits.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use index_db::BPlusTree;
201    ///
202    /// let mut index = BPlusTree::new();
203    /// assert_eq!(index.height(), 1); // just the root leaf
204    /// for k in 0..1_000_u32 {
205    ///     index.insert(k, k);
206    /// }
207    /// assert!(index.height() >= 2); // splits have grown the tree taller
208    /// ```
209    #[must_use]
210    pub fn height(&self) -> usize {
211        let mut height = 1;
212        let mut id = self.root;
213        while let Node::Internal(internal) = self.store.get(id) {
214            height += 1;
215            id = internal.children[0];
216        }
217        height
218    }
219
220    /// Remove every entry, returning the tree to its empty state.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use index_db::BPlusTree;
226    ///
227    /// let mut index = BPlusTree::new();
228    /// index.insert(1_u32, "a");
229    /// index.clear();
230    /// assert!(index.is_empty());
231    /// assert_eq!(index.get(&1), None);
232    /// ```
233    pub fn clear(&mut self) {
234        self.root = self.store.reset();
235        self.len = 0;
236    }
237
238    /// An iterator over every entry, in ascending key order.
239    ///
240    /// The iterator is double-ended: call [`rev`](Iterator::rev) for descending
241    /// order, or drive it from both ends.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// use index_db::BPlusTree;
247    ///
248    /// let mut index = BPlusTree::new();
249    /// index.insert(2_u32, "b");
250    /// index.insert(1, "a");
251    /// index.insert(3, "c");
252    ///
253    /// let collected: Vec<_> = index.iter().map(|(&k, &v)| (k, v)).collect();
254    /// assert_eq!(collected, vec![(1, "a"), (2, "b"), (3, "c")]);
255    ///
256    /// // Reverse with `.rev()`.
257    /// let keys: Vec<_> = index.iter().rev().map(|(&k, _)| k).collect();
258    /// assert_eq!(keys, vec![3, 2, 1]);
259    /// ```
260    #[must_use]
261    pub fn iter(&self) -> Iter<'_, K, V> {
262        Iter::full(&self.store, self.root)
263    }
264}
265
266impl<K: Ord, V> BPlusTree<K, V> {
267    /// Look up the value stored under `key`, or `None` if the key is absent.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// use index_db::BPlusTree;
273    ///
274    /// let mut index = BPlusTree::new();
275    /// index.insert(10_u32, "ten");
276    /// assert_eq!(index.get(&10), Some(&"ten"));
277    /// assert_eq!(index.get(&11), None);
278    /// ```
279    #[must_use]
280    #[inline]
281    pub fn get(&self, key: &K) -> Option<&V> {
282        ops::get(&self.store, self.root, key)
283    }
284
285    /// Whether the tree holds an entry for `key`.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// use index_db::BPlusTree;
291    ///
292    /// let mut index = BPlusTree::new();
293    /// index.insert(10_u32, "ten");
294    /// assert!(index.contains_key(&10));
295    /// assert!(!index.contains_key(&11));
296    /// ```
297    #[must_use]
298    #[inline]
299    pub fn contains_key(&self, key: &K) -> bool {
300        self.get(key).is_some()
301    }
302
303    /// An iterator over the entries whose keys fall in `range`, in ascending key
304    /// order.
305    ///
306    /// `range` is any standard range expression — `a..b`, `a..=b`, `..b`, `a..`,
307    /// or `..` — interpreted over the key order. Like [`iter`](Self::iter) the
308    /// result is double-ended, so a range can be walked forward or in reverse
309    /// with [`rev`](Iterator::rev).
310    ///
311    /// # Examples
312    ///
313    /// ```
314    /// use index_db::BPlusTree;
315    ///
316    /// let mut index = BPlusTree::new();
317    /// for k in 0..10_u32 {
318    ///     index.insert(k, k);
319    /// }
320    ///
321    /// // Half-open range [3, 7).
322    /// let keys: Vec<_> = index.range(3..7).map(|(&k, _)| k).collect();
323    /// assert_eq!(keys, vec![3, 4, 5, 6]);
324    ///
325    /// // Inclusive range, walked in reverse.
326    /// let rev: Vec<_> = index.range(2..=4).rev().map(|(&k, _)| k).collect();
327    /// assert_eq!(rev, vec![4, 3, 2]);
328    ///
329    /// // Open-ended range.
330    /// let tail: Vec<_> = index.range(8..).map(|(&k, _)| k).collect();
331    /// assert_eq!(tail, vec![8, 9]);
332    /// ```
333    #[must_use]
334    pub fn range<R: RangeBounds<K>>(&self, range: R) -> Iter<'_, K, V> {
335        Iter::range(
336            &self.store,
337            self.root,
338            range.start_bound(),
339            range.end_bound(),
340        )
341    }
342}
343
344impl<K: Ord + Clone, V> BPlusTree<K, V> {
345    /// Insert `key` with `value`. If the key was already present its previous
346    /// value is replaced and returned; otherwise the entry is added and `None`
347    /// is returned.
348    ///
349    /// # Examples
350    ///
351    /// ```
352    /// use index_db::BPlusTree;
353    ///
354    /// let mut index = BPlusTree::new();
355    /// assert_eq!(index.insert(1_u32, "a"), None);    // new key
356    /// assert_eq!(index.insert(1, "b"), Some("a"));   // replaced
357    /// assert_eq!(index.get(&1), Some(&"b"));
358    /// ```
359    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
360        let (replaced, root) = ops::insert(&mut self.store, self.root, key, value, self.order);
361        self.root = root;
362        if replaced.is_none() {
363            self.len = self.len.saturating_add(1);
364        }
365        replaced
366    }
367
368    /// Remove `key`, returning its value if it was present, or `None` if the
369    /// tree held no such key.
370    ///
371    /// Removing keeps the tree balanced: an under-full node borrows an entry
372    /// from a sibling or merges with one, and when the root is left with a single
373    /// child the tree collapses a level. Every leaf stays at the same depth.
374    ///
375    /// # Examples
376    ///
377    /// ```
378    /// use index_db::BPlusTree;
379    ///
380    /// let mut index = BPlusTree::new();
381    /// index.insert(1_u32, "a");
382    /// index.insert(2, "b");
383    ///
384    /// assert_eq!(index.remove(&1), Some("a")); // returns the removed value
385    /// assert_eq!(index.remove(&1), None);       // already gone
386    /// assert_eq!(index.get(&1), None);
387    /// assert_eq!(index.len(), 1);
388    /// ```
389    pub fn remove(&mut self, key: &K) -> Option<V> {
390        let min_keys = self.min_keys();
391        let (removed, root) = ops::remove(&mut self.store, self.root, key, min_keys);
392        self.root = root;
393        if removed.is_some() {
394            self.len -= 1;
395        }
396        removed
397    }
398
399    /// The minimum number of keys a non-root node must hold: a node is at least
400    /// half full, so two under-full siblings always fit in one node on merge.
401    #[inline]
402    fn min_keys(&self) -> usize {
403        self.order.div_ceil(2) - 1
404    }
405}
406
407impl<K, V> Default for BPlusTree<K, V> {
408    fn default() -> Self {
409        Self::new()
410    }
411}
412
413impl<'a, K, V> IntoIterator for &'a BPlusTree<K, V> {
414    type Item = (&'a K, &'a V);
415    type IntoIter = Iter<'a, K, V>;
416
417    fn into_iter(self) -> Self::IntoIter {
418        self.iter()
419    }
420}
421
422#[cfg(test)]
423#[allow(clippy::unwrap_used, clippy::expect_used, reason = "test assertions")]
424mod tests {
425    use alloc::vec::Vec;
426
427    use proptest::prelude::*;
428
429    use super::*;
430
431    /// Recursively verify the structural invariants of the subtree at `id`,
432    /// returning its `(min_key, max_key, height)` for the parent to check
433    /// separators against. Panics with a description on the first violation.
434    fn check<K: Ord + Clone + core::fmt::Debug, V>(
435        store: &InMemoryStore<K, V>,
436        id: NodeId,
437        order: usize,
438        min_keys: usize,
439        is_root: bool,
440    ) -> (K, K, usize) {
441        match store.get(id) {
442            Node::Leaf(leaf) => {
443                assert!(!leaf.keys.is_empty(), "non-root leaf is empty");
444                assert!(
445                    leaf.keys.len() < order,
446                    "leaf over capacity: {} >= {order}",
447                    leaf.keys.len()
448                );
449                assert!(
450                    is_root || leaf.keys.len() >= min_keys,
451                    "non-root leaf under capacity: {} < {min_keys}",
452                    leaf.keys.len()
453                );
454                assert_eq!(
455                    leaf.keys.len(),
456                    leaf.vals.len(),
457                    "keys/vals length mismatch"
458                );
459                for w in leaf.keys.windows(2) {
460                    assert!(w[0] < w[1], "leaf keys not strictly ascending");
461                }
462                (
463                    leaf.keys[0].clone(),
464                    leaf.keys[leaf.keys.len() - 1].clone(),
465                    1,
466                )
467            }
468            Node::Internal(internal) => {
469                assert!(!internal.keys.is_empty(), "internal node has no separators");
470                assert!(
471                    internal.keys.len() < order,
472                    "internal node over capacity: {} >= {order}",
473                    internal.keys.len()
474                );
475                assert!(
476                    is_root || internal.keys.len() >= min_keys,
477                    "non-root internal node under capacity: {} < {min_keys}",
478                    internal.keys.len()
479                );
480                assert_eq!(
481                    internal.children.len(),
482                    internal.keys.len() + 1,
483                    "child count must be separator count + 1"
484                );
485                for w in internal.keys.windows(2) {
486                    assert!(w[0] < w[1], "separators not strictly ascending");
487                }
488
489                let mut child_height = None;
490                let mut subtree_min = None;
491                let mut last_max: Option<K> = None;
492                for (i, child) in internal.children.iter().enumerate() {
493                    let (cmin, cmax, h) = check(store, *child, order, min_keys, false);
494                    match child_height {
495                        None => child_height = Some(h),
496                        Some(prev) => assert_eq!(prev, h, "subtrees differ in height (unbalanced)"),
497                    }
498                    if subtree_min.is_none() {
499                        subtree_min = Some(cmin.clone());
500                    }
501                    // Routing separator: max(left) < sep <= min(right). After a
502                    // delete the separator may sit strictly below the right min,
503                    // so this is `<=`, not equality.
504                    if i > 0 {
505                        let sep = &internal.keys[i - 1];
506                        assert!(
507                            last_max.as_ref().is_some_and(|m| m < sep),
508                            "left subtree max not below separator"
509                        );
510                        assert!(sep <= &cmin, "separator above right subtree's min key");
511                    }
512                    last_max = Some(cmax);
513                }
514                let height = child_height.map_or(1, |h| h + 1);
515                match (subtree_min, last_max) {
516                    (Some(min), Some(max)) => (min, max, height),
517                    _ => panic!("internal node with no children"),
518                }
519            }
520        }
521    }
522
523    /// `check` entry point: treats the tree root as exempt from minimum occupancy.
524    fn check_tree<K: Ord + Clone + core::fmt::Debug, V>(tree: &BPlusTree<K, V>) {
525        let _bounds = check(&tree.store, tree.root, tree.order, tree.min_keys(), true);
526    }
527
528    /// Collect every entry key left to right across the leaves.
529    fn collect_keys<K: Clone, V>(store: &InMemoryStore<K, V>, id: NodeId, out: &mut Vec<K>) {
530        match store.get(id) {
531            Node::Leaf(leaf) => out.extend(leaf.keys.iter().cloned()),
532            Node::Internal(internal) => {
533                for child in &internal.children {
534                    collect_keys(store, *child, out);
535                }
536            }
537        }
538    }
539
540    #[test]
541    fn test_get_empty_returns_none() {
542        let tree: BPlusTree<u32, u32> = BPlusTree::new();
543        assert_eq!(tree.get(&0), None);
544        assert!(tree.is_empty());
545        assert_eq!(tree.height(), 1);
546    }
547
548    #[test]
549    fn test_insert_duplicate_key_replaces_value() {
550        let mut tree = BPlusTree::new();
551        assert_eq!(tree.insert(1_u32, "a"), None);
552        assert_eq!(tree.insert(1, "b"), Some("a"));
553        assert_eq!(tree.get(&1), Some(&"b"));
554        assert_eq!(tree.len(), 1);
555    }
556
557    #[test]
558    fn test_insert_many_splits_and_stays_balanced() {
559        let mut tree = BPlusTree::with_order(4);
560        for k in 0..256_u32 {
561            assert_eq!(tree.insert(k, k * 10), None);
562        }
563        check_tree(&tree);
564        assert!(
565            tree.height() > 1,
566            "tree should have split into multiple levels"
567        );
568        for k in 0..256_u32 {
569            assert_eq!(tree.get(&k), Some(&(k * 10)));
570        }
571        assert_eq!(tree.get(&256), None);
572    }
573
574    #[test]
575    fn test_insert_reverse_order_keeps_keys_sorted() {
576        let mut tree = BPlusTree::with_order(3);
577        for k in (0..100_u32).rev() {
578            assert_eq!(tree.insert(k, k), None);
579        }
580        let mut keys = Vec::new();
581        collect_keys(&tree.store, tree.root, &mut keys);
582        assert_eq!(keys.len(), 100);
583        assert!(keys.windows(2).all(|w| w[0] < w[1]), "leaf order broken");
584    }
585
586    #[test]
587    fn test_remove_absent_key_returns_none() {
588        let mut tree = BPlusTree::new();
589        assert_eq!(tree.insert(1_u32, "a"), None);
590        assert_eq!(tree.remove(&2), None);
591        assert_eq!(tree.len(), 1);
592    }
593
594    #[test]
595    fn test_remove_present_key_returns_value() {
596        let mut tree = BPlusTree::new();
597        assert_eq!(tree.insert(1_u32, "a"), None);
598        assert_eq!(tree.insert(2, "b"), None);
599        assert_eq!(tree.remove(&1), Some("a"));
600        assert_eq!(tree.get(&1), None);
601        assert_eq!(tree.get(&2), Some(&"b"));
602        assert_eq!(tree.len(), 1);
603    }
604
605    #[test]
606    fn test_remove_all_empties_tree_and_collapses_root() {
607        let mut tree = BPlusTree::with_order(4);
608        for k in 0..200_u32 {
609            let _ = tree.insert(k, k);
610        }
611        assert!(tree.height() > 1);
612        for k in (0..200_u32).step_by(2) {
613            assert_eq!(tree.remove(&k), Some(k));
614        }
615        for k in (1..200_u32).step_by(2) {
616            assert_eq!(tree.remove(&k), Some(k));
617        }
618        assert!(tree.is_empty());
619        assert_eq!(tree.height(), 1, "root should collapse back to a leaf");
620    }
621
622    #[test]
623    fn test_remove_keeps_tree_balanced() {
624        let mut tree = BPlusTree::with_order(3);
625        for k in 0..500_u32 {
626            let _ = tree.insert(k, k);
627        }
628        for k in (0..500_u32).filter(|k| k % 3 == 0) {
629            assert_eq!(tree.remove(&k), Some(k));
630        }
631        check_tree(&tree);
632        for k in 0..500_u32 {
633            assert_eq!(tree.get(&k), if k % 3 == 0 { None } else { Some(&k) });
634        }
635    }
636
637    #[test]
638    fn test_bulk_load_builds_balanced_tree() {
639        for &n in &[0_u32, 1, 2, 5, 63, 64, 65, 1_000] {
640            let tree = BPlusTree::from_sorted_with_order((0..n).map(|k| (k, k * 2)), 5);
641            assert_eq!(tree.len(), n as usize);
642            if n > 0 {
643                check_tree(&tree);
644            }
645            for k in 0..n {
646                assert_eq!(tree.get(&k), Some(&(k * 2)));
647            }
648            let keys: Vec<_> = tree.iter().map(|(&k, _)| k).collect();
649            assert_eq!(keys, (0..n).collect::<Vec<_>>());
650        }
651    }
652
653    #[test]
654    fn test_bulk_load_unsorted_falls_back() {
655        let tree = BPlusTree::from_sorted_with_order([(3_u32, 3), (1, 1), (2, 2), (1, 9)], 4);
656        assert_eq!(tree.len(), 3);
657        assert_eq!(tree.get(&1), Some(&9)); // last write wins on the fallback path
658        let keys: Vec<_> = tree.iter().map(|(&k, _)| k).collect();
659        assert_eq!(keys, vec![1, 2, 3]);
660    }
661
662    #[test]
663    fn test_iter_empty_yields_nothing() {
664        let tree: BPlusTree<u32, u32> = BPlusTree::new();
665        assert_eq!(tree.iter().count(), 0);
666        assert_eq!(tree.iter().next_back(), None);
667        assert_eq!(tree.range(..).count(), 0);
668    }
669
670    #[test]
671    fn test_iter_forward_and_reverse() {
672        let mut tree = BPlusTree::with_order(4);
673        for k in 0..50_u32 {
674            let _ = tree.insert(k, k * 10);
675        }
676        let fwd: Vec<_> = tree.iter().map(|(&k, &v)| (k, v)).collect();
677        let expected: Vec<_> = (0..50_u32).map(|k| (k, k * 10)).collect();
678        assert_eq!(fwd, expected);
679
680        let rev: Vec<_> = tree.iter().rev().map(|(&k, _)| k).collect();
681        let expected_rev: Vec<_> = (0..50_u32).rev().collect();
682        assert_eq!(rev, expected_rev);
683    }
684
685    #[test]
686    fn test_iter_from_both_ends_meets_in_middle() {
687        let mut tree = BPlusTree::with_order(3);
688        for k in 0..9_u32 {
689            let _ = tree.insert(k, k);
690        }
691        let mut it = tree.iter();
692        let mut seq = Vec::new();
693        let mut take_front = true;
694        loop {
695            let item = if take_front {
696                it.next()
697            } else {
698                it.next_back()
699            };
700            match item {
701                Some((&k, _)) => seq.push(k),
702                None => break,
703            }
704            take_front = !take_front;
705        }
706        assert_eq!(seq, vec![0, 8, 1, 7, 2, 6, 3, 5, 4]);
707    }
708
709    #[test]
710    fn test_range_bounds() {
711        let mut tree = BPlusTree::with_order(4);
712        for k in 0..20_u32 {
713            let _ = tree.insert(k, k);
714        }
715        let collect = |it: Iter<'_, u32, u32>| it.map(|(&k, _)| k).collect::<Vec<_>>();
716        assert_eq!(collect(tree.range(5..10)), vec![5, 6, 7, 8, 9]);
717        assert_eq!(collect(tree.range(5..=10)), vec![5, 6, 7, 8, 9, 10]);
718        assert_eq!(collect(tree.range(..3)), vec![0, 1, 2]);
719        assert_eq!(collect(tree.range(17..)), vec![17, 18, 19]);
720        assert_eq!(collect(tree.range(100..200)), Vec::<u32>::new());
721        let mut sparse = BPlusTree::with_order(3);
722        for k in [0_u32, 10, 20, 30, 40] {
723            let _ = sparse.insert(k, k);
724        }
725        assert_eq!(collect(sparse.range(5..35)), vec![10, 20, 30]);
726    }
727
728    /// Drive an adversarial workload at a small order across pathological
729    /// orderings, checking the structural invariants after every delete and the
730    /// contents against a reference set at the end.
731    fn adversarial_workload(order: usize, inserts: &[u32], deletes: &[u32]) {
732        use std::collections::BTreeSet;
733
734        let mut tree = BPlusTree::with_order(order);
735        let mut reference = BTreeSet::new();
736        for &k in inserts {
737            let _ = tree.insert(k, k);
738            let _ = reference.insert(k);
739        }
740        check_tree(&tree);
741        for &k in deletes {
742            let in_tree = tree.remove(&k).is_some();
743            let in_ref = reference.remove(&k);
744            assert_eq!(in_tree, in_ref, "remove({k}) disagreed with reference");
745            if !tree.is_empty() {
746                check_tree(&tree);
747            }
748        }
749        let keys: Vec<u32> = tree.iter().map(|(&k, _)| k).collect();
750        let expected: Vec<u32> = reference.iter().copied().collect();
751        assert_eq!(keys, expected);
752    }
753
754    #[test]
755    fn test_adversarial_ascending_insert_descending_delete() {
756        for &order in &[3_usize, 4, 5, 7, 16] {
757            let inserts: Vec<u32> = (0..400).collect();
758            let deletes: Vec<u32> = (0..400).rev().collect();
759            adversarial_workload(order, &inserts, &deletes);
760        }
761    }
762
763    #[test]
764    fn test_adversarial_descending_insert_ascending_delete() {
765        for &order in &[3_usize, 4, 5, 7, 16] {
766            let inserts: Vec<u32> = (0..400).rev().collect();
767            let deletes: Vec<u32> = (0..400).collect();
768            adversarial_workload(order, &inserts, &deletes);
769        }
770    }
771
772    #[test]
773    fn test_adversarial_zigzag_insert_middle_out_delete() {
774        for &order in &[3_usize, 4, 6] {
775            // Insert from both ends toward the middle.
776            let mut inserts = Vec::new();
777            let (mut lo, mut hi) = (0_u32, 399_u32);
778            while lo <= hi {
779                inserts.push(lo);
780                if lo != hi {
781                    inserts.push(hi);
782                }
783                lo += 1;
784                hi = hi.wrapping_sub(1);
785            }
786            // Delete from the middle outward.
787            let mut deletes = Vec::new();
788            let (mut left, mut right) = (199_i32, 200_i32);
789            while left >= 0 || right < 400 {
790                if left >= 0 {
791                    deletes.push(left as u32);
792                    left -= 1;
793                }
794                if right < 400 {
795                    deletes.push(right as u32);
796                    right += 1;
797                }
798            }
799            adversarial_workload(order, &inserts, &deletes);
800        }
801    }
802
803    #[test]
804    fn test_adversarial_clustered_keys() {
805        // Many keys packed into a narrow band, so most splits happen in one
806        // subtree before the tree balances out.
807        let inserts: Vec<u32> = (1_000_000..1_000_500).collect();
808        let deletes: Vec<u32> = (1_000_000..1_000_500).step_by(3).collect();
809        adversarial_workload(3, &inserts, &deletes);
810    }
811
812    #[test]
813    fn test_adversarial_repeated_overwrite_then_clear() {
814        let mut tree = BPlusTree::with_order(4);
815        for round in 0..50_u32 {
816            for k in 0..100_u32 {
817                let _ = tree.insert(k, round * 100 + k);
818            }
819        }
820        assert_eq!(tree.len(), 100);
821        for k in 0..100_u32 {
822            assert_eq!(tree.get(&k), Some(&(49 * 100 + k)));
823        }
824        check_tree(&tree);
825        tree.clear();
826        assert!(tree.is_empty());
827        assert_eq!(tree.height(), 1);
828    }
829
830    proptest! {
831        /// Adversarial generator: small order, a wide key band relative to the
832        /// op count so splits and merges churn, every op checked against the
833        /// reference and the structural invariants.
834        #[test]
835        fn prop_adversarial_small_order(
836            order in 3_usize..6,
837            ops in prop::collection::vec((any::<bool>(), 0_u32..60), 0..800),
838        ) {
839            use std::collections::BTreeMap;
840
841            let mut tree = BPlusTree::with_order(order);
842            let mut reference = BTreeMap::new();
843            for (is_insert, k) in ops {
844                if is_insert {
845                    prop_assert_eq!(tree.insert(k, k), reference.insert(k, k));
846                } else {
847                    prop_assert_eq!(tree.remove(&k), reference.remove(&k));
848                }
849                prop_assert_eq!(tree.len(), reference.len());
850                if !tree.is_empty() {
851                    check_tree(&tree);
852                }
853            }
854            let keys: Vec<u32> = tree.iter().map(|(&k, _)| k).collect();
855            let expected: Vec<u32> = reference.keys().copied().collect();
856            prop_assert_eq!(keys, expected);
857        }
858        #[test]
859        fn prop_matches_reference_map(
860            order in 3_usize..8,
861            ops in prop::collection::vec((0_u32..200, 0_u32..1_000_000), 0..400),
862        ) {
863            use std::collections::BTreeMap;
864
865            let mut tree = BPlusTree::with_order(order);
866            let mut reference = BTreeMap::new();
867            for (k, v) in ops {
868                prop_assert_eq!(tree.insert(k, v), reference.insert(k, v));
869            }
870
871            prop_assert_eq!(tree.len(), reference.len());
872            if !tree.is_empty() {
873                check_tree(&tree);
874            }
875            for (k, v) in &reference {
876                prop_assert_eq!(tree.get(k), Some(v));
877            }
878            for k in 0_u32..200 {
879                if !reference.contains_key(&k) {
880                    prop_assert_eq!(tree.get(&k), None);
881                }
882            }
883            let mut keys = Vec::new();
884            collect_keys(&tree.store, tree.root, &mut keys);
885            let expected: Vec<u32> = reference.keys().copied().collect();
886            prop_assert_eq!(keys, expected);
887        }
888
889        #[test]
890        fn prop_iter_and_range_match_reference(
891            order in 3_usize..8,
892            keys in prop::collection::vec(0_u32..200, 0..300),
893            lo in 0_u32..200,
894            hi in 0_u32..200,
895        ) {
896            use std::collections::BTreeMap;
897
898            let mut tree = BPlusTree::with_order(order);
899            let mut reference = BTreeMap::new();
900            for k in keys {
901                let _ = tree.insert(k, k.wrapping_mul(7));
902                let _ = reference.insert(k, k.wrapping_mul(7));
903            }
904
905            let tree_fwd: Vec<_> = tree.iter().map(|(&k, &v)| (k, v)).collect();
906            let ref_fwd: Vec<_> = reference.iter().map(|(&k, &v)| (k, v)).collect();
907            prop_assert_eq!(&tree_fwd, &ref_fwd);
908
909            let tree_rev: Vec<_> = tree.iter().rev().map(|(&k, &v)| (k, v)).collect();
910            let ref_rev: Vec<_> = reference.iter().rev().map(|(&k, &v)| (k, v)).collect();
911            prop_assert_eq!(tree_rev, ref_rev);
912
913            let (lo, hi) = (lo.min(hi), lo.max(hi));
914            let tree_range: Vec<_> = tree.range(lo..hi).map(|(&k, _)| k).collect();
915            let ref_range: Vec<_> = reference.range(lo..hi).map(|(&k, _)| k).collect();
916            prop_assert_eq!(tree_range, ref_range);
917
918            let tree_incl: Vec<_> = tree.range(lo..=hi).rev().map(|(&k, _)| k).collect();
919            let ref_incl: Vec<_> = reference.range(lo..=hi).rev().map(|(&k, _)| k).collect();
920            prop_assert_eq!(tree_incl, ref_incl);
921        }
922
923        #[test]
924        fn prop_insert_remove_matches_reference(
925            order in 3_usize..8,
926            ops in prop::collection::vec((any::<bool>(), 0_u32..150), 0..600),
927        ) {
928            use std::collections::BTreeMap;
929
930            let mut tree = BPlusTree::with_order(order);
931            let mut reference = BTreeMap::new();
932            for (is_insert, k) in ops {
933                if is_insert {
934                    prop_assert_eq!(tree.insert(k, k), reference.insert(k, k));
935                } else {
936                    prop_assert_eq!(tree.remove(&k), reference.remove(&k));
937                }
938                prop_assert_eq!(tree.len(), reference.len());
939                if !tree.is_empty() {
940                    check_tree(&tree);
941                }
942            }
943
944            let mut keys = Vec::new();
945            collect_keys(&tree.store, tree.root, &mut keys);
946            let expected: Vec<u32> = reference.keys().copied().collect();
947            prop_assert_eq!(keys, expected);
948        }
949
950        #[test]
951        fn prop_bulk_load_matches_inserts(
952            order in 3_usize..8,
953            keys in prop::collection::btree_set(0_u32..1_000, 0..400),
954        ) {
955            let sorted: Vec<(u32, u32)> = keys.iter().map(|&k| (k, k)).collect();
956            let bulk = BPlusTree::from_sorted_with_order(sorted.iter().copied(), order);
957            prop_assert_eq!(bulk.len(), keys.len());
958            if !bulk.is_empty() {
959                check_tree(&bulk);
960            }
961            let bulk_keys: Vec<_> = bulk.iter().map(|(&k, _)| k).collect();
962            let expected: Vec<u32> = keys.iter().copied().collect();
963            prop_assert_eq!(bulk_keys, expected);
964        }
965    }
966}