Skip to main content

subetha_cxc/
shared_btree_map.rs

1//! `SharedBTreeMap` - cross-process MMF B-tree ordered map.
2//!
3//! A cross-process MMF ordered key/value map with a cache-friendly layout:
4//! each node packs up to `B` sorted keys (fanout `B + 1`), so a lookup
5//! touches ~`log_{B+1}(N)` nodes. The per-node binary search reads a
6//! contiguous key array (prefetcher-friendly) rather than chasing scattered
7//! single-cache-line nodes, which is what wins once the map far exceeds L3
8//! and lookups go to RAM. It is the substrate's ordered-map primitive.
9//!
10//! Minimum degree `T = 8` => up to `B = 2T - 1 = 15` keys per node, `2T = 16`
11//! children. Insert uses CLRS proactive top-down splitting (full children
12//! are split before descent), so it is single-pass and never overflows.
13//!
14//! Storage: self-contained MMF `[BTreeHeader | BTreeNode array]` with bump
15//! allocation. Concurrency model: single-writer for `insert` / `remove`
16//! (serialise externally); reads are consistent
17//! against a quiescent tree (build-then-query), which is what the cold
18//! benchmark exercises.
19
20use std::fs::{File, OpenOptions};
21use std::mem::size_of;
22use std::path::Path;
23use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
24
25use memmap2::{MmapMut, MmapOptions};
26
27/// Minimum degree.
28pub const T: usize = 8;
29/// Max keys per node.
30pub const B: usize = 2 * T - 1; // 15
31/// Sentinel "no node".
32pub const NIL: u32 = u32::MAX;
33
34pub const BTREE_MAGIC: u64 = 0x4254_5245_454D_4150; // "BTREEMAP"
35
36#[repr(C, align(64))]
37pub struct BTreeHeader {
38    pub magic: u64,
39    pub root: AtomicU32,
40    pub node_count: AtomicU32,
41    pub capacity: u64,
42    pub len: AtomicU64,
43    /// Head of the single-writer free list (NIL = empty); merged/removed
44    /// nodes are recycled here so deletes reclaim slots.
45    pub free_head: AtomicU32,
46    _pad0: u32,
47    /// Global seqlock. A writer makes it odd for the duration of a
48    /// structural mutation (insert/remove) and even after; readers retry
49    /// the whole search if it changes or is odd, so concurrent reads never
50    /// observe a torn tree. Single-writer, multi-reader.
51    pub version: AtomicU64,
52    _pad: [u8; 16],
53}
54
55const _: () = {
56    assert!(size_of::<BTreeHeader>() == 64);
57};
58
59#[repr(C)]
60pub struct BTreeNode<K: Copy + Ord + Default + 'static, V: Copy + Default + 'static> {
61    pub count: u16,
62    pub is_leaf: u8,
63    _pad: [u8; 5],
64    pub keys: [K; B],
65    pub children: [u32; B + 1],
66    pub values: [V; B],
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum BTreeError {
71    Full,
72    LayoutMismatch,
73    InvalidConfig,
74    IoError(std::io::ErrorKind),
75}
76
77impl From<std::io::Error> for BTreeError {
78    fn from(e: std::io::Error) -> Self {
79        Self::IoError(e.kind())
80    }
81}
82
83pub fn btree_file_size<K, V>(capacity: usize) -> usize
84where
85    K: Copy + Ord + Default + 'static,
86    V: Copy + Default + 'static,
87{
88    size_of::<BTreeHeader>() + capacity * size_of::<BTreeNode<K, V>>()
89}
90
91pub struct SharedBTreeMap<K: Copy + Ord + Default + 'static, V: Copy + Default + 'static> {
92    _file: File,
93    mmap: MmapMut,
94    raw_ptr: *mut u8,
95    capacity: usize,
96    _phantom: std::marker::PhantomData<(K, V)>,
97    header_sidecar: subetha_core::HandshakeHeader,
98    ring_sidecar: Box<subetha_core::ObservationRing>,
99}
100
101unsafe impl<K: Copy + Ord + Default + Send + 'static, V: Copy + Default + Send + 'static> Send
102    for SharedBTreeMap<K, V>
103{
104}
105unsafe impl<K: Copy + Ord + Default + Sync + 'static, V: Copy + Default + Sync + 'static> Sync
106    for SharedBTreeMap<K, V>
107{
108}
109
110impl<K: Copy + Ord + Default + Send + Sync + 'static, V: Copy + Default + Send + Sync + 'static>
111    subetha_sidecar::AdaptiveInstance for SharedBTreeMap<K, V>
112{
113    fn header(&self) -> &subetha_core::HandshakeHeader { &self.header_sidecar }
114    fn ring(&self) -> &subetha_core::ObservationRing { &self.ring_sidecar }
115    fn make_policy(&self) -> Box<dyn subetha_sidecar::Policy> {
116        Box::new(subetha_sidecar::NoMigrationPolicy)
117    }
118}
119
120impl<K: Copy + Ord + Default + 'static, V: Copy + Default + 'static> SharedBTreeMap<K, V> {
121    /// Obtain the map at `path`, initializing an empty one if the path
122    /// does not yet exist and attaching to it if it does. Attaching
123    /// leaves the live tree in place; a region built with a different
124    /// capacity is a `LayoutMismatch`. [`reset`](Self::reset)
125    /// reinitializes.
126    pub fn create(path: impl AsRef<Path>, capacity: usize) -> Result<Self, BTreeError> {
127        if capacity < 1 {
128            return Err(BTreeError::InvalidConfig);
129        }
130        let (file, mmap) = crate::mmf_attach::create_or_attach(
131            path.as_ref(),
132            btree_file_size::<K, V>(capacity),
133            |ptr| unsafe { Self::init_region(ptr, capacity) },
134            |ptr| unsafe { (*(ptr as *const BTreeHeader)).magic == BTREE_MAGIC },
135        )?;
136        Self::from_region(file, mmap, capacity)
137    }
138
139    /// Truncate the map at `path` and initialize an empty one,
140    /// discarding the tree live peers share. For a caller that knows it
141    /// owns the path.
142    pub fn reset(path: impl AsRef<Path>, capacity: usize) -> Result<Self, BTreeError> {
143        if capacity < 1 {
144            return Err(BTreeError::InvalidConfig);
145        }
146        let (file, mmap) = crate::mmf_attach::reset(
147            path.as_ref(),
148            btree_file_size::<K, V>(capacity),
149            |ptr| unsafe { Self::init_region(ptr, capacity) },
150        )?;
151        Self::from_region(file, mmap, capacity)
152    }
153
154    /// Lay out an empty tree: capacity and the NIL root and free head
155    /// first, magic last, because attachers spin on it.
156    ///
157    /// # Safety
158    /// `ptr` addresses at least `btree_file_size::<K, V>(capacity)`
159    /// writable zeroed bytes.
160    unsafe fn init_region(ptr: *mut u8, capacity: usize) {
161        let hdr = ptr as *mut BTreeHeader;
162        unsafe {
163            (*hdr).capacity = capacity as u64;
164            std::ptr::write(&raw mut (*hdr).root, AtomicU32::new(NIL));
165            std::ptr::write(&raw mut (*hdr).free_head, AtomicU32::new(NIL));
166            std::ptr::write_volatile(&raw mut (*hdr).magic, BTREE_MAGIC);
167        }
168    }
169
170    /// Wrap an initialized region, refusing one built with a different
171    /// capacity.
172    fn from_region(file: File, mut mmap: MmapMut, capacity: usize) -> Result<Self, BTreeError> {
173        let hdr = unsafe { &*(mmap.as_ptr() as *const BTreeHeader) };
174        if hdr.magic != BTREE_MAGIC || hdr.capacity != capacity as u64 {
175            return Err(BTreeError::LayoutMismatch);
176        }
177        let raw_ptr = mmap.as_mut_ptr();
178        Ok(Self {
179            _file: file, mmap, raw_ptr, capacity,
180            _phantom: std::marker::PhantomData,
181            header_sidecar: subetha_core::HandshakeHeader::new(),
182            ring_sidecar: Box::new(subetha_core::ObservationRing::new()),
183        })
184    }
185
186    pub fn open(path: impl AsRef<Path>, expected_capacity: usize) -> Result<Self, BTreeError> {
187        let total = btree_file_size::<K, V>(expected_capacity);
188        let file = OpenOptions::new().read(true).write(true).open(path.as_ref())?;
189        if file.metadata()?.len() < total as u64 {
190            return Err(BTreeError::LayoutMismatch);
191        }
192        let mmap = unsafe { MmapOptions::new().len(total).map_mut(&file)? };
193        Self::from_region(file, mmap, expected_capacity)
194    }
195
196    #[inline]
197    fn header(&self) -> &BTreeHeader {
198        unsafe { &*(self.raw_ptr as *const BTreeHeader) }
199    }
200
201    #[inline]
202    pub fn len(&self) -> usize {
203        self.header().len.load(Ordering::Acquire) as usize
204    }
205
206    #[inline]
207    pub fn is_empty(&self) -> bool {
208        self.len() == 0
209    }
210
211    #[inline]
212    pub fn capacity(&self) -> usize {
213        self.capacity
214    }
215
216    /// Number of nodes bump-allocated so far (resident-memory witness:
217    /// `node_count() * size_of::<BTreeNode>()`).
218    #[inline]
219    pub fn node_count(&self) -> usize {
220        self.header().node_count.load(Ordering::Acquire) as usize
221    }
222
223    #[inline]
224    fn node(&self, idx: u32) -> *mut BTreeNode<K, V> {
225        let off = size_of::<BTreeHeader>() + idx as usize * size_of::<BTreeNode<K, V>>();
226        unsafe { self.raw_ptr.add(off) as *mut BTreeNode<K, V> }
227    }
228
229    /// Allocate a node: recycle from the free list, else bump-allocate.
230    /// Single-writer, so the free list needs no ABA protection.
231    fn alloc_node(&self, is_leaf: bool) -> Result<u32, BTreeError> {
232        let h = self.header();
233        let free = h.free_head.load(Ordering::Acquire);
234        let idx = if free != NIL {
235            let next = unsafe { (*self.node(free)).children[0] };
236            h.free_head.store(next, Ordering::Release);
237            free
238        } else {
239            let idx = h.node_count.fetch_add(1, Ordering::AcqRel);
240            if idx as usize >= self.capacity {
241                h.node_count.fetch_sub(1, Ordering::AcqRel);
242                return Err(BTreeError::Full);
243            }
244            idx
245        };
246        let n = self.node(idx);
247        unsafe {
248            (*n).count = 0;
249            (*n).is_leaf = is_leaf as u8;
250            (*n).children = [NIL; B + 1];
251        }
252        Ok(idx)
253    }
254
255    /// Return a node to the free list (linked via `children[0]`).
256    fn free_node(&self, idx: u32) {
257        let h = self.header();
258        let head = h.free_head.load(Ordering::Acquire);
259        unsafe { (*self.node(idx)).children[0] = head; }
260        h.free_head.store(idx, Ordering::Release);
261    }
262
263    /// Enter / leave a structural mutation. The global version is odd while
264    /// a write is in flight; readers retry the whole search if they observe
265    /// an odd or changed version (seqlock). Single-writer.
266    #[inline]
267    fn begin_write(&self) {
268        self.header().version.fetch_add(1, Ordering::AcqRel);
269    }
270    #[inline]
271    fn end_write(&self) {
272        self.header().version.fetch_add(1, Ordering::Release);
273    }
274
275    /// Binary search `key` within node `idx`. Returns `(pos, found)`: when
276    /// found, `keys[pos] == key`; otherwise `pos` is the child slot / leaf
277    /// insertion position. `count` is clamped to `B` so a torn read under a
278    /// concurrent writer can never index past the fixed-size arrays.
279    #[inline]
280    fn search(&self, idx: u32, key: &K) -> (usize, bool) {
281        let n = self.node(idx);
282        let count = (unsafe { (*n).count } as usize).min(B);
283        let (mut lo, mut hi) = (0usize, count);
284        while lo < hi {
285            let mid = (lo + hi) / 2;
286            let mk = unsafe { (*n).keys[mid] };
287            match mk.cmp(key) {
288                std::cmp::Ordering::Less => lo = mid + 1,
289                std::cmp::Ordering::Greater => hi = mid,
290                std::cmp::Ordering::Equal => return (mid, true),
291            }
292        }
293        (lo, false)
294    }
295
296    /// Look up `key`. Lock-free read against a quiescent tree.
297    pub fn get(&self, key: &K) -> Option<V> {
298        let r = self.get_inner(key);
299        self.ring_sidecar.push_op(
300            crate::sidecar_ops::ordered::OP_GET,
301            if r.is_none() { 2 } else { 0 },
302        );
303        r
304    }
305
306    fn get_inner(&self, key: &K) -> Option<V> {
307        let h = self.header();
308        let cap = self.capacity as u32;
309        loop {
310            let v1 = h.version.load(Ordering::Acquire);
311            if v1 & 1 != 0 {
312                std::hint::spin_loop();
313                continue; // a writer is mid-mutation
314            }
315            // Descend. Every node index is bounds-guarded so a torn read
316            // (a concurrent split moving keys) cannot deref out of range;
317            // `search` clamps `count`. If anything looks inconsistent we
318            // simply finish and let the version re-check force a retry.
319            let mut idx = h.root.load(Ordering::Acquire);
320            let mut result: Option<V> = None;
321            let mut torn = false;
322            while idx != NIL {
323                if idx >= cap {
324                    torn = true;
325                    break;
326                }
327                let (pos, found) = self.search(idx, key);
328                let n = self.node(idx);
329                if found {
330                    result = Some(unsafe { (*n).values[pos] });
331                    break;
332                }
333                if unsafe { (*n).is_leaf } != 0 {
334                    break;
335                }
336                idx = unsafe { (*n).children[pos] };
337            }
338            let v2 = h.version.load(Ordering::Acquire);
339            if v1 == v2 && !torn {
340                return result;
341            }
342            std::hint::spin_loop();
343        }
344    }
345
346    /// True if `key` is present.
347    pub fn contains_key(&self, key: &K) -> bool {
348        self.get_inner(key).is_some()
349    }
350
351    /// Insert / update. Single-writer (serialise externally). Returns the
352    /// previous value if `key` was present.
353    pub fn insert(&self, key: K, value: V) -> Result<Option<V>, BTreeError> {
354        self.begin_write();
355        let r = self.insert_inner(key, value);
356        self.end_write();
357        self.ring_sidecar.push_op(
358            crate::sidecar_ops::ordered::OP_INSERT,
359            if r.is_err() { 1 } else { 0 },
360        );
361        r
362    }
363
364    fn insert_inner(&self, key: K, value: V) -> Result<Option<V>, BTreeError> {
365        let root = self.header().root.load(Ordering::Acquire);
366        if root == NIL {
367            let r = self.alloc_node(true)?;
368            let n = self.node(r);
369            unsafe {
370                (*n).keys[0] = key;
371                (*n).values[0] = value;
372                (*n).count = 1;
373            }
374            self.header().root.store(r, Ordering::Release);
375            self.header().len.fetch_add(1, Ordering::AcqRel);
376            return Ok(None);
377        }
378        // Grow height if the root is full.
379        let root = if unsafe { (*self.node(root)).count as usize } == B {
380            let new_root = self.alloc_node(false)?;
381            unsafe {
382                (*self.node(new_root)).children[0] = root;
383                (*self.node(new_root)).count = 0;
384            }
385            self.split_child(new_root, 0)?;
386            self.header().root.store(new_root, Ordering::Release);
387            new_root
388        } else {
389            root
390        };
391        self.insert_nonfull(root, key, value)
392    }
393
394    /// Insert into a guaranteed-non-full subtree rooted at `idx`.
395    fn insert_nonfull(&self, mut idx: u32, key: K, value: V) -> Result<Option<V>, BTreeError> {
396        loop {
397            let (pos, found) = self.search(idx, &key);
398            let n = self.node(idx);
399            if found {
400                let old = unsafe { (*n).values[pos] };
401                unsafe { (*n).values[pos] = value; }
402                return Ok(Some(old));
403            }
404            if unsafe { (*n).is_leaf } != 0 {
405                let count = unsafe { (*n).count as usize };
406                unsafe {
407                    let mut j = count;
408                    while j > pos {
409                        (*n).keys[j] = (*n).keys[j - 1];
410                        (*n).values[j] = (*n).values[j - 1];
411                        j -= 1;
412                    }
413                    (*n).keys[pos] = key;
414                    (*n).values[pos] = value;
415                    (*n).count = (count + 1) as u16;
416                }
417                self.header().len.fetch_add(1, Ordering::AcqRel);
418                return Ok(None);
419            }
420            let child = unsafe { (*n).children[pos] };
421            if unsafe { (*self.node(child)).count as usize } == B {
422                self.split_child(idx, pos)?;
423                // After split, the promoted median sits at keys[pos].
424                let n = self.node(idx);
425                let med = unsafe { (*n).keys[pos] };
426                match key.cmp(&med) {
427                    std::cmp::Ordering::Equal => {
428                        let old = unsafe { (*n).values[pos] };
429                        unsafe { (*n).values[pos] = value; }
430                        return Ok(Some(old));
431                    }
432                    std::cmp::Ordering::Greater => idx = unsafe { (*n).children[pos + 1] },
433                    std::cmp::Ordering::Less => idx = unsafe { (*n).children[pos] },
434                }
435            } else {
436                idx = child;
437            }
438        }
439    }
440
441    /// Split the full child at `parent.children[i]` into two, promoting the
442    /// median (key+value) into `parent` at position `i`.
443    fn split_child(&self, parent: u32, i: usize) -> Result<(), BTreeError> {
444        let full = unsafe { (*self.node(parent)).children[i] };
445        let is_leaf = unsafe { (*self.node(full)).is_leaf };
446        let right = self.alloc_node(is_leaf != 0)?;
447
448        let fp = self.node(full);
449        let rp = self.node(right);
450        unsafe {
451            // right gets the upper T-1 keys/values.
452            for j in 0..(T - 1) {
453                (*rp).keys[j] = (*fp).keys[T + j];
454                (*rp).values[j] = (*fp).values[T + j];
455            }
456            if is_leaf == 0 {
457                for j in 0..T {
458                    (*rp).children[j] = (*fp).children[T + j];
459                }
460            }
461            (*rp).count = (T - 1) as u16;
462            (*rp).is_leaf = is_leaf;
463        }
464        let median_key = unsafe { (*fp).keys[T - 1] };
465        let median_value = unsafe { (*fp).values[T - 1] };
466        // Left keeps the lower T-1 keys. Publish the new count last so a
467        // reader never sees the promoted/duplicated entries on the left.
468        unsafe { (*fp).count = (T - 1) as u16; }
469
470        let pp = self.node(parent);
471        unsafe {
472            let pc = (*pp).count as usize;
473            let mut j = pc;
474            while j > i {
475                (*pp).children[j + 1] = (*pp).children[j];
476                j -= 1;
477            }
478            (*pp).children[i + 1] = right;
479            let mut j = pc;
480            while j > i {
481                (*pp).keys[j] = (*pp).keys[j - 1];
482                (*pp).values[j] = (*pp).values[j - 1];
483                j -= 1;
484            }
485            (*pp).keys[i] = median_key;
486            (*pp).values[i] = median_value;
487            (*pp).count = (pc + 1) as u16;
488        }
489        Ok(())
490    }
491
492    /// Remove `key`, returning its previous value if present. Single-writer
493    /// (serialise externally, as with insert).
494    pub fn remove(&self, key: &K) -> Result<Option<V>, BTreeError> {
495        self.begin_write();
496        let r = self.remove_inner(key);
497        self.end_write();
498        self.ring_sidecar.push_op(
499            crate::sidecar_ops::ordered::OP_REMOVE,
500            if r.is_none() { 2 } else { 0 },
501        );
502        Ok(r)
503    }
504
505    fn remove_inner(&self, key: &K) -> Option<V> {
506        let root = self.header().root.load(Ordering::Acquire);
507        if root == NIL {
508            return None;
509        }
510        let removed = self.delete_from(root, key);
511        // Shrink height if the root emptied.
512        let rn = self.node(root);
513        if unsafe { (*rn).count } == 0 {
514            if unsafe { (*rn).is_leaf } != 0 {
515                self.header().root.store(NIL, Ordering::Release);
516            } else {
517                let new_root = unsafe { (*rn).children[0] };
518                self.header().root.store(new_root, Ordering::Release);
519            }
520            self.free_node(root);
521        }
522        if removed.is_some() {
523            self.header().len.fetch_sub(1, Ordering::AcqRel);
524        }
525        removed
526    }
527
528    /// Delete `key` from the subtree at `idx` (guaranteed >= T keys, or the
529    /// root). CLRS deletion: from a leaf directly; from an internal node by
530    /// replacing with the in-order predecessor/successor, or merging.
531    fn delete_from(&self, idx: u32, key: &K) -> Option<V> {
532        let (pos, found) = self.search(idx, key);
533        let n = self.node(idx);
534        let is_leaf = unsafe { (*n).is_leaf } != 0;
535        if found {
536            let old = unsafe { (*n).values[pos] };
537            if is_leaf {
538                let count = unsafe { (*n).count as usize };
539                unsafe {
540                    for j in pos..count - 1 {
541                        (*n).keys[j] = (*n).keys[j + 1];
542                        (*n).values[j] = (*n).values[j + 1];
543                    }
544                    (*n).count = (count - 1) as u16;
545                }
546            } else {
547                let left = unsafe { (*n).children[pos] };
548                let right = unsafe { (*n).children[pos + 1] };
549                if unsafe { (*self.node(left)).count as usize } >= T {
550                    let (pk, pv) = self.max_pair(left);
551                    unsafe {
552                        (*n).keys[pos] = pk;
553                        (*n).values[pos] = pv;
554                    }
555                    self.delete_from(left, &pk);
556                } else if unsafe { (*self.node(right)).count as usize } >= T {
557                    let (sk, sv) = self.min_pair(right);
558                    unsafe {
559                        (*n).keys[pos] = sk;
560                        (*n).values[pos] = sv;
561                    }
562                    self.delete_from(right, &sk);
563                } else {
564                    self.merge_at(idx, pos);
565                    self.delete_from(left, key);
566                }
567            }
568            return Some(old);
569        }
570        if is_leaf {
571            return None;
572        }
573        let child = self.ensure_min_degree(idx, pos);
574        self.delete_from(child, key)
575    }
576
577    /// Ensure `parent.children[i]` has >= T keys before descending, by
578    /// borrowing from a sibling or merging. Returns the index to descend.
579    fn ensure_min_degree(&self, parent: u32, i: usize) -> u32 {
580        let p = self.node(parent);
581        let child = unsafe { (*p).children[i] };
582        if unsafe { (*self.node(child)).count as usize } >= T {
583            return child;
584        }
585        let pcount = unsafe { (*p).count as usize };
586        if i > 0 {
587            let left = unsafe { (*p).children[i - 1] };
588            if unsafe { (*self.node(left)).count as usize } >= T {
589                self.borrow_from_left(parent, i);
590                return child;
591            }
592        }
593        if i < pcount {
594            let right = unsafe { (*p).children[i + 1] };
595            if unsafe { (*self.node(right)).count as usize } >= T {
596                self.borrow_from_right(parent, i);
597                return child;
598            }
599        }
600        if i < pcount {
601            self.merge_at(parent, i);
602            unsafe { (*self.node(parent)).children[i] }
603        } else {
604            self.merge_at(parent, i - 1);
605            unsafe { (*self.node(parent)).children[i - 1] }
606        }
607    }
608
609    fn borrow_from_left(&self, parent: u32, i: usize) {
610        let p = self.node(parent);
611        let child = unsafe { (*p).children[i] };
612        let left = unsafe { (*p).children[i - 1] };
613        let c = self.node(child);
614        let l = self.node(left);
615        unsafe {
616            let cc = (*c).count as usize;
617            let internal = (*c).is_leaf == 0;
618            let mut j = cc;
619            while j > 0 {
620                (*c).keys[j] = (*c).keys[j - 1];
621                (*c).values[j] = (*c).values[j - 1];
622                j -= 1;
623            }
624            if internal {
625                let mut j = cc + 1;
626                while j > 0 {
627                    (*c).children[j] = (*c).children[j - 1];
628                    j -= 1;
629                }
630            }
631            (*c).keys[0] = (*p).keys[i - 1];
632            (*c).values[0] = (*p).values[i - 1];
633            let lc = (*l).count as usize;
634            if internal {
635                (*c).children[0] = (*l).children[lc];
636            }
637            (*p).keys[i - 1] = (*l).keys[lc - 1];
638            (*p).values[i - 1] = (*l).values[lc - 1];
639            (*l).count = (lc - 1) as u16;
640            (*c).count = (cc + 1) as u16;
641        }
642    }
643
644    fn borrow_from_right(&self, parent: u32, i: usize) {
645        let p = self.node(parent);
646        let child = unsafe { (*p).children[i] };
647        let right = unsafe { (*p).children[i + 1] };
648        let c = self.node(child);
649        let r = self.node(right);
650        unsafe {
651            let cc = (*c).count as usize;
652            let internal = (*c).is_leaf == 0;
653            (*c).keys[cc] = (*p).keys[i];
654            (*c).values[cc] = (*p).values[i];
655            if internal {
656                (*c).children[cc + 1] = (*r).children[0];
657            }
658            (*p).keys[i] = (*r).keys[0];
659            (*p).values[i] = (*r).values[0];
660            let rc = (*r).count as usize;
661            for j in 0..rc - 1 {
662                (*r).keys[j] = (*r).keys[j + 1];
663                (*r).values[j] = (*r).values[j + 1];
664            }
665            if internal {
666                for j in 0..rc {
667                    (*r).children[j] = (*r).children[j + 1];
668                }
669            }
670            (*r).count = (rc - 1) as u16;
671            (*c).count = (cc + 1) as u16;
672        }
673    }
674
675    /// Merge `children[i]` + separator `keys[i]` + `children[i+1]` into
676    /// `children[i]`, freeing the right node and dropping the separator.
677    fn merge_at(&self, parent: u32, i: usize) {
678        let p = self.node(parent);
679        let left = unsafe { (*p).children[i] };
680        let right = unsafe { (*p).children[i + 1] };
681        let l = self.node(left);
682        let r = self.node(right);
683        unsafe {
684            let lc = (*l).count as usize;
685            let internal = (*l).is_leaf == 0;
686            (*l).keys[lc] = (*p).keys[i];
687            (*l).values[lc] = (*p).values[i];
688            let rc = (*r).count as usize;
689            for j in 0..rc {
690                (*l).keys[lc + 1 + j] = (*r).keys[j];
691                (*l).values[lc + 1 + j] = (*r).values[j];
692            }
693            if internal {
694                for j in 0..=rc {
695                    (*l).children[lc + 1 + j] = (*r).children[j];
696                }
697            }
698            (*l).count = (lc + 1 + rc) as u16;
699            let pc = (*p).count as usize;
700            for j in i..pc - 1 {
701                (*p).keys[j] = (*p).keys[j + 1];
702                (*p).values[j] = (*p).values[j + 1];
703            }
704            for j in i + 1..pc {
705                (*p).children[j] = (*p).children[j + 1];
706            }
707            (*p).count = (pc - 1) as u16;
708        }
709        self.free_node(right);
710    }
711
712    fn max_pair(&self, mut idx: u32) -> (K, V) {
713        loop {
714            let n = self.node(idx);
715            let count = unsafe { (*n).count as usize };
716            if unsafe { (*n).is_leaf } != 0 {
717                return unsafe { ((*n).keys[count - 1], (*n).values[count - 1]) };
718            }
719            idx = unsafe { (*n).children[count] };
720        }
721    }
722
723    fn min_pair(&self, mut idx: u32) -> (K, V) {
724        loop {
725            let n = self.node(idx);
726            if unsafe { (*n).is_leaf } != 0 {
727                return unsafe { ((*n).keys[0], (*n).values[0]) };
728            }
729            idx = unsafe { (*n).children[0] };
730        }
731    }
732
733    /// Smallest (key, value) in the map, or `None` if empty.
734    pub fn first(&self) -> Option<(K, V)> {
735        let root = self.header().root.load(Ordering::Acquire);
736        let r = if root == NIL { None } else { Some(self.min_pair(root)) };
737        self.ring_sidecar.push_op(
738            crate::sidecar_ops::ordered::OP_GET,
739            if r.is_none() { 2 } else { 0 },
740        );
741        r
742    }
743
744    /// Collect all (K, V) in ascending key order (validation / iteration).
745    pub fn iter_ascending(&self) -> Vec<(K, V)> {
746        let mut out = Vec::with_capacity(self.len());
747        let root = self.header().root.load(Ordering::Acquire);
748        if root != NIL {
749            self.walk(root, &mut out);
750        }
751        out
752    }
753
754    fn walk(&self, idx: u32, out: &mut Vec<(K, V)>) {
755        let n = self.node(idx);
756        let count = unsafe { (*n).count as usize };
757        let is_leaf = unsafe { (*n).is_leaf } != 0;
758        for i in 0..count {
759            if !is_leaf {
760                let c = unsafe { (*n).children[i] };
761                self.walk(c, out);
762            }
763            out.push(unsafe { ((*n).keys[i], (*n).values[i]) });
764        }
765        if !is_leaf {
766            let c = unsafe { (*n).children[count] };
767            self.walk(c, out);
768        }
769    }
770
771    pub fn flush(&self) -> Result<(), BTreeError> {
772        self.mmap.flush()?;
773        Ok(())
774    }
775}
776
777#[cfg(test)]
778mod tests {
779    use super::*;
780
781    fn tmp(name: &str) -> std::path::PathBuf {
782        let mut p = std::env::temp_dir();
783        p.push(format!("btree_{name}_{}.bin", std::process::id()));
784        p
785    }
786
787    /// A second create attaches with the live tree in place; reset is
788    /// what strips it.
789    #[test]
790    fn second_create_attaches_and_keeps_the_tree() {
791        let p = tmp("attach");
792        std::fs::remove_file(&p).ok();
793        let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
794        m.insert(7, 777).unwrap();
795
796        let m2: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
797        assert_eq!(m2.get(&7), Some(777), "attach lost a live entry");
798        assert!(matches!(
799            SharedBTreeMap::<u64, u64>::create(&p, 32),
800            Err(BTreeError::LayoutMismatch),
801        ));
802
803        // Windows refuses to truncate a mapped file, so every handle goes
804        // before the reset.
805        drop(m);
806        drop(m2);
807        let fresh: SharedBTreeMap<u64, u64> = SharedBTreeMap::reset(&p, 64).unwrap();
808        assert_eq!(fresh.get(&7), None, "reset kept an entry");
809        assert_eq!(fresh.len(), 0);
810        drop(fresh);
811        std::fs::remove_file(&p).ok();
812    }
813
814    #[test]
815    fn insert_get_round_trip() {
816        let p = tmp("rt");
817        let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
818        for i in 0..100u64 {
819            assert_eq!(m.insert(i, i * 10).unwrap(), None);
820        }
821        for i in 0..100u64 {
822            assert_eq!(m.get(&i), Some(i * 10));
823        }
824        assert_eq!(m.get(&1000), None);
825        assert_eq!(m.len(), 100);
826        std::fs::remove_file(&p).ok();
827    }
828
829    #[test]
830    fn duplicate_insert_updates_and_returns_previous() {
831        let p = tmp("dup");
832        let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
833        assert_eq!(m.insert(5, 50).unwrap(), None);
834        assert_eq!(m.insert(5, 55).unwrap(), Some(50));
835        assert_eq!(m.get(&5), Some(55));
836        assert_eq!(m.len(), 1);
837        std::fs::remove_file(&p).ok();
838    }
839
840    #[test]
841    fn many_inserts_stay_sorted_and_findable() {
842        let p = tmp("many");
843        let n = 5000u64;
844        let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 4096).unwrap();
845        // Pseudo-random insertion order.
846        let mut x = 0x1234_5678u64;
847        let mut inserted = Vec::new();
848        for _ in 0..n {
849            x ^= x << 13; x ^= x >> 7; x ^= x << 17;
850            let k = x % 1_000_000;
851            if m.insert(k, k.wrapping_mul(3)).unwrap().is_none() {
852                inserted.push(k);
853            }
854        }
855        for &k in &inserted {
856            assert_eq!(m.get(&k), Some(k.wrapping_mul(3)), "missing {k}");
857        }
858        // Ascending order holds across all splits.
859        let asc = m.iter_ascending();
860        for w in asc.windows(2) {
861            assert!(w[0].0 < w[1].0, "order broken: {} >= {}", w[0].0, w[1].0);
862        }
863        assert_eq!(asc.len(), inserted.len());
864        std::fs::remove_file(&p).ok();
865    }
866
867    #[test]
868    fn cross_handle_visibility() {
869        let p = tmp("xhandle");
870        let a: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
871        for i in 0..50u64 { a.insert(i, i).unwrap(); }
872        let b: SharedBTreeMap<u64, u64> = SharedBTreeMap::open(&p, 64).unwrap();
873        for i in 0..50u64 { assert_eq!(b.get(&i), Some(i)); }
874        std::fs::remove_file(&p).ok();
875    }
876
877    #[test]
878    fn random_ops_match_std_btreemap() {
879        use std::collections::BTreeMap;
880        let p = tmp("oracle");
881        let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 16384).unwrap();
882        let mut oracle: BTreeMap<u64, u64> = BTreeMap::new();
883        let mut x = 0xdead_beef_1234_5678u64;
884        let mut rng = || {
885            x ^= x << 13; x ^= x >> 7; x ^= x << 17; x
886        };
887        for _ in 0..80_000 {
888            let k = rng() % 3000;
889            match rng() % 3 {
890                0 | 1 => {
891                    let v = rng();
892                    assert_eq!(m.insert(k, v).unwrap(), oracle.insert(k, v), "insert {k}");
893                }
894                _ => {
895                    assert_eq!(m.remove(&k).unwrap(), oracle.remove(&k), "remove {k}");
896                }
897            }
898            assert_eq!(m.len(), oracle.len(), "len after op on {k}");
899        }
900        for (k, v) in &oracle {
901            assert_eq!(m.get(k), Some(*v), "final get {k}");
902        }
903        let asc = m.iter_ascending();
904        let oref: Vec<(u64, u64)> = oracle.iter().map(|(k, v)| (*k, *v)).collect();
905        assert_eq!(asc, oref, "iteration order / contents mismatch");
906        std::fs::remove_file(&p).ok();
907    }
908
909    #[test]
910    fn first_returns_smallest() {
911        let p = tmp("first");
912        let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 1024).unwrap();
913        assert_eq!(m.first(), None);
914        for i in (0..500u64).rev() {
915            m.insert(i, i * 7).unwrap();
916        }
917        assert_eq!(m.first(), Some((0, 0)));
918        m.remove(&0).unwrap();
919        assert_eq!(m.first(), Some((1, 7)));
920        std::fs::remove_file(&p).ok();
921    }
922
923    #[test]
924    fn concurrent_readers_during_inserts() {
925        use std::sync::Arc;
926        use std::sync::atomic::AtomicBool;
927        let p = tmp("concurrent");
928        let m = Arc::new(SharedBTreeMap::<u64, u64>::create(&p, 16384).unwrap());
929        for i in 0..1000u64 {
930            m.insert(i, i.wrapping_mul(2)).unwrap();
931        }
932        let stop = Arc::new(AtomicBool::new(false));
933        let readers: Vec<_> = (0..4)
934            .map(|_| {
935                let m = m.clone();
936                let stop = stop.clone();
937                std::thread::spawn(move || {
938                    while !stop.load(Ordering::Relaxed) {
939                        for i in 0..1000u64 {
940                            // Keys 0..1000 are never removed; a reader must
941                            // see the exact value or (transiently) retry to
942                            // it - never a torn / garbage value.
943                            if let Some(v) = m.get(&i) {
944                                assert_eq!(v, i.wrapping_mul(2), "torn read at {i}");
945                            }
946                        }
947                    }
948                })
949            })
950            .collect();
951        for i in 1000..6000u64 {
952            m.insert(i, i.wrapping_mul(2)).unwrap();
953        }
954        stop.store(true, Ordering::Relaxed);
955        for r in readers {
956            r.join().unwrap();
957        }
958        std::fs::remove_file(&p).ok();
959    }
960}