Skip to main content

embed_btree/
large.rs

1use crate::CACHE_LINE_SIZE;
2use crate::bptree::{
3    self, BTree, entry::*, helper::PathBuffer, inter::*, leaf::*, node::*, tree::BTreeInner, *,
4};
5use alloc::alloc::{Layout, alloc, dealloc, handle_alloc_error, realloc};
6use core::fmt::{self, Debug};
7use core::mem::{align_of, size_of};
8use core::num::NonZeroUsize;
9
10pub type BTreeMap<K, V> = bptree::BTree<K, V, TreeInfo>;
11pub use bptree::cursor::Cursor;
12pub use bptree::iter::{Iter, IterMut, Keys, Range, RangeMut, Values, ValuesMut};
13pub type IntoIter<K, V> = bptree::iter::IntoIter<K, V, TreeInfo>;
14pub type Entry<'a, K, V> = bptree::entry::Entry<'a, K, V, TreeInfo>;
15pub type OccupiedEntry<'a, K, V> = bptree::entry::OccupiedEntry<'a, K, V, TreeInfo>;
16pub type VacantEntry<'a, K, V> = bptree::entry::VacantEntry<'a, K, V, TreeInfo>;
17
18/// Compact heap-allocated metadata for `BTreeMap`.
19///
20/// Holds `leaf_count`, `inter_count`, and a growable path-cache stack in one
21/// contiguous allocation, decoupled from the `BTreeMap` struct itself.
22///
23/// # Heap memory layout
24///
25/// `[TreeInfoHeader | (InterNode<K,V>, u32) × cap]`
26///
27/// Initial buffer = one `CACHE_LINE_SIZE` block; grows by one block per overflow.
28///
29/// # Stack memory layout
30///
31/// TreeInfo store either point, or ((entry_idx) << 8) | EMPTY_FLAG.
32///
33/// Because in our scenario iteration with `Entry` is hot path, to reduce moving cost, we pack
34/// entry_idx inside TreeInfo stack or Heap (`TreeInfoHeader`)
35///
36/// NOTE: we tried enum u8 and NonNull, but rustc does not pack it to usize.
37///
38/// Using NonZeroUsize will optimize `Option<BTreeMap>`
39pub struct TreeInfo(NonZeroUsize);
40
41const EMPTY_FLAG: usize = 1;
42
43unsafe impl Send for TreeInfo {}
44unsafe impl Sync for TreeInfo {}
45
46impl Default for TreeInfo {
47    #[inline]
48    fn default() -> Self {
49        Self(unsafe { NonZeroUsize::new_unchecked(EMPTY_FLAG) })
50    }
51}
52
53// for log
54impl Debug for TreeInfo {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        write!(f, "TreeInfo")
57    }
58}
59
60/// Header stored at the start of the `TreeInfo` heap buffer.
61///
62/// Immediately followed by `[(InterNode<K,V>, u8); cap]` items.
63#[repr(C)]
64struct TreeInfoHeader {
65    leaf_count: usize,
66    inter_count: u32,
67    /// Capacity of items can be stored except root
68    cap: u8,
69    entry_idx: u8,
70    /// len=1 means root is pushed (as root idx),
71    /// len-1 is the items stored in the heap.
72    len: u8,
73    /// left < 0, or right > 0, or center = 0
74    buffer_pos: i8,
75}
76
77impl TreeInfoHeader {
78    #[inline]
79    fn clear(&mut self) {
80        self.entry_idx = 0;
81        self.len = 0;
82        self.buffer_pos = 0;
83    }
84}
85
86impl Stats for TreeInfo {}
87
88impl StatsPriv for TreeInfo {
89    type EntryInner<'a, K, V>
90        = TreeInfoEntry<'a, K, V>
91    where
92        K: Key + 'a,
93        V: Value + 'a;
94
95    type PathBufferRef<'a> = &'a mut Self;
96
97    type PathBuffer = Self;
98
99    type BufferStack = ();
100
101    #[inline]
102    fn get_cache<'a, K>(&'a mut self, _stack: &mut (), root: Option<InterNode<K>>) -> &'a mut Self {
103        if let Some(root) = root.as_ref() {
104            self.ensure_cap(root.height());
105        } else {
106            self.clear_cache();
107        }
108        self
109    }
110
111    #[inline]
112    fn take_cache<K>(mut self, _root: Option<InterNode<K>>) -> Self {
113        // take_cache will not increase height, it's always enough
114        self.clear_cache();
115        self
116    }
117
118    #[cfg(test)]
119    fn init_count(&mut self, leaf_count: usize, inter_count: u32) {
120        if let Some(header) = self.header_mut() {
121            header.leaf_count = leaf_count;
122            header.inter_count = inter_count;
123        } else {
124            self._alloc(leaf_count, inter_count, 0);
125        }
126    }
127
128    #[inline]
129    fn search_entry<'a, K: Key, V: Value>(
130        tree: &'a mut BTree<K, V, Self>, key: K,
131    ) -> Entry<'a, K, V> {
132        if let Some(root) = tree.inner.get_root() {
133            let mut is_seq = true;
134            let leaf = match root {
135                Node::Inter(inter) => {
136                    let mut stack = ();
137                    let mut cache = tree.stats.get_cache(&mut stack, Some(inter.clone()));
138                    inter.find_leaf_with_cache_smart::<K, V, _>(&mut cache, &key, &mut is_seq)
139                }
140                Node::Leaf(leaf) => {
141                    tree.stats.clear_cache();
142                    leaf
143                }
144            };
145            let (idx, is_equal) = leaf.search_smart(&key, is_seq);
146            let mut inner = TreeInfoEntry { tree };
147            inner.set_idx(idx);
148            if is_equal {
149                Entry::Occupied(OccupiedEntry { inner, leaf })
150            } else {
151                Entry::Vacant(VacantEntry { inner, key, leaf: Some(leaf) })
152            }
153        } else {
154            let inner = TreeInfoEntry { tree };
155            // we have entry_idx = 0 default
156            Entry::Vacant(VacantEntry { inner, key, leaf: None })
157        }
158    }
159
160    /// seek first or last entry
161    #[inline]
162    fn seek_entry<'a, K: Key, V: Value, const FIRST: bool>(
163        tree: &'a mut BTree<K, V, Self>,
164    ) -> Option<OccupiedEntry<'a, K, V>> {
165        if let Some(root) = tree.inner.get_root() {
166            let leaf = match root {
167                Node::Inter(inter) => {
168                    let mut stack = ();
169                    let mut cache = tree.stats.get_cache(&mut stack, Some(inter.clone()));
170                    if FIRST {
171                        inter.find_first_leaf_with_cache::<V, _>(&mut cache)
172                    } else {
173                        inter.find_last_leaf_with_cache::<V, _>(&mut cache)
174                    }
175                }
176                Node::Leaf(leaf) => {
177                    // make sure buffer_pos is clear
178                    tree.stats.clear_cache();
179                    leaf
180                }
181            };
182            let count = leaf.key_count();
183            if count > 0 {
184                let mut inner = TreeInfoEntry { tree };
185                if FIRST {
186                    inner.set_idx(0);
187                } else {
188                    inner.set_idx(count - 1);
189                };
190                return Some(OccupiedEntry { inner, leaf });
191            }
192        }
193        None
194    }
195
196    #[cfg(test)]
197    fn assert_leaf_count(&self, leaf_count: usize) {
198        assert_eq!(self.leaf_count(), leaf_count);
199    }
200
201    #[cfg(test)]
202    fn assert_inter_count(&self, inter_count: usize) {
203        assert_eq!(self.inter_count() as usize, inter_count);
204    }
205}
206
207pub(crate) struct TreeInfoEntry<'a, K, V>
208where
209    K: Key,
210    V: Value,
211{
212    tree: &'a mut BTree<K, V, TreeInfo>,
213}
214
215impl<'a, K: Key, V: Value> EntryInner<K, V> for TreeInfoEntry<'a, K, V> {
216    #[inline(always)]
217    fn get_cache(&mut self) -> impl PathBuffer {
218        &mut self.tree.stats
219    }
220
221    #[cfg(test)]
222    #[inline(always)]
223    fn get_tree(&self) -> &BTreeInner<K, V> {
224        &self.tree.inner
225    }
226
227    #[inline(always)]
228    fn get_tree_cache(&mut self) -> (&mut BTreeInner<K, V>, impl PathBuffer) {
229        let (tree, stats) = (&mut self.tree.inner, &mut self.tree.stats);
230        (tree, stats)
231    }
232
233    #[inline(always)]
234    fn set_idx(&mut self, idx: u8) {
235        self.tree.stats.set_entry_idx(idx);
236    }
237
238    #[inline(always)]
239    fn get_idx(&self) -> u8 {
240        self.tree.stats.get_entry_idx()
241    }
242}
243
244impl TreeInfo {
245    const ITEM_SIZE: usize = size_of::<(*mut u8, u8)>();
246
247    // Offset at which items start (header size rounded up to item alignment).
248    const ITEMS_OFFSET: usize = {
249        let hs = size_of::<TreeInfoHeader>();
250        let ia = align_of::<(*mut u8, u8)>();
251        let offset = (hs + ia - 1) & !(ia - 1);
252        if offset != hs {
253            panic!("TreeInfoHeader is not aligned");
254        }
255        offset
256    };
257
258    /// Reset the stack without freeing the buffer.
259    #[inline]
260    fn clear_cache(&mut self) {
261        if self.0.get() & EMPTY_FLAG == 0 {
262            let header = self.header_mut_unwrap();
263            header.len = 0;
264            header.buffer_pos = 0;
265            header.entry_idx = 0;
266        } else {
267            *self = Self::default();
268        }
269    }
270
271    #[inline]
272    fn set_entry_idx(&mut self, entry_idx: u8) {
273        if self.0.get() & EMPTY_FLAG == 0 {
274            self.header_mut_unwrap().entry_idx = entry_idx;
275        } else {
276            self.0 = unsafe { NonZeroUsize::new_unchecked((entry_idx as usize) << 8 | EMPTY_FLAG) };
277        }
278    }
279
280    /// height is the root.height (tree height - 1)
281    #[inline]
282    fn ensure_cap(&mut self, height: u8) {
283        assert!(height < u8::MAX);
284        match self.header_mut_or() {
285            Ok(header) => {
286                if height <= header.cap {
287                    header.clear();
288                } else {
289                    let old_cap = header.cap;
290                    // grow one CACHE_LINE_SIZE each time
291                    let old_size = Self::buf_size_from_cap(old_cap);
292                    // because the tree only insert with PathBuffer, it should grow one height at a time
293                    let new_size = old_size + CACHE_LINE_SIZE;
294                    let new_cap = Self::cal_cap(new_size);
295                    let old_layout = Self::get_layout(old_size);
296                    unsafe {
297                        let p = realloc(self.0.get() as *mut u8, old_layout, new_size);
298                        if !p.is_null() {
299                            crate::trace_log!("grow pathbuf cap {old_cap}->{new_cap}");
300                            let header = p as *mut TreeInfoHeader;
301                            (*header).cap = new_cap;
302                            (*header).clear();
303                            self.0 = NonZeroUsize::new_unchecked(p as usize);
304                        } else {
305                            handle_alloc_error(Self::get_layout(new_size));
306                        }
307                    }
308                }
309            }
310            Err(entry_idx) => {
311                if height > 0 {
312                    // assume current height = 1 and previously root=leaf
313                    self._alloc(2, 1, entry_idx);
314                }
315            }
316        }
317    }
318
319    #[inline]
320    fn leaf_count(&self) -> usize {
321        if let Some(header) = self.header() { header.leaf_count } else { 1 }
322    }
323
324    #[inline(always)]
325    fn inter_count(&self) -> u32 {
326        if let Some(header) = self.header() { header.inter_count } else { 0 }
327    }
328
329    #[inline(always)]
330    const fn cal_cap(buf_size: usize) -> u8 {
331        ((buf_size - Self::ITEMS_OFFSET) / Self::ITEM_SIZE) as u8
332    }
333
334    #[inline(always)]
335    const fn buf_size_from_cap(cap: u8) -> usize {
336        let size = cap as usize * Self::ITEM_SIZE + Self::ITEMS_OFFSET;
337        #[cfg(debug_assertions)]
338        {
339            if !size.is_multiple_of(CACHE_LINE_SIZE) {
340                panic!("not aligned");
341            }
342        }
343        size
344    }
345
346    #[inline(always)]
347    const fn get_layout(buf_size: usize) -> Layout {
348        let align = align_of::<usize>();
349        #[cfg(debug_assertions)]
350        {
351            if align_of::<(*mut u8, u8)>() != align {
352                panic!("TreeInfoHeader is not aligned");
353            }
354        }
355        unsafe { Layout::from_size_align_unchecked(buf_size, align) }
356    }
357
358    #[inline]
359    fn _alloc(&mut self, leaf_count: usize, inter_count: u32, entry_idx: u8) {
360        debug_assert!(self.header().is_none());
361        unsafe {
362            let layout = Self::get_layout(CACHE_LINE_SIZE);
363            let cap = Self::cal_cap(CACHE_LINE_SIZE);
364            let p = alloc(layout);
365            if !p.is_null() {
366                crate::trace_log!("alloc pathbuf cap {cap}");
367                let header = p as *mut TreeInfoHeader;
368                header.write(TreeInfoHeader {
369                    leaf_count,
370                    inter_count,
371                    entry_idx,
372                    cap,
373                    len: 0,
374                    buffer_pos: 0,
375                });
376                self.0 = NonZeroUsize::new_unchecked(p as usize);
377            } else {
378                handle_alloc_error(layout);
379            }
380        }
381    }
382
383    #[inline]
384    fn header(&self) -> Option<&TreeInfoHeader> {
385        let this = self.0.get();
386        if this & EMPTY_FLAG == 0 {
387            let p = this as *mut TreeInfoHeader;
388            Some(unsafe { &*p })
389        } else {
390            None
391        }
392    }
393
394    #[inline]
395    fn get_entry_idx(&self) -> u8 {
396        let this = self.0.get();
397        if this & EMPTY_FLAG == 0 {
398            let p = this as *mut TreeInfoHeader;
399            unsafe { (*p).entry_idx }
400        } else {
401            (self.0.get() >> 8) as u8
402        }
403    }
404
405    #[inline]
406    fn header_mut(&mut self) -> Option<&mut TreeInfoHeader> {
407        let this = self.0.get();
408        if this & EMPTY_FLAG == 0 {
409            let p = this as *mut TreeInfoHeader;
410            Some(unsafe { &mut *p })
411        } else {
412            None
413        }
414    }
415
416    #[inline]
417    fn header_mut_or(&mut self) -> Result<&mut TreeInfoHeader, u8> {
418        let this = self.0.get();
419        if this & EMPTY_FLAG == 0 {
420            let p = this as *mut TreeInfoHeader;
421            Ok(unsafe { &mut *p })
422        } else {
423            Err((self.0.get() >> 8) as u8)
424        }
425    }
426
427    #[inline]
428    fn header_ptr_unwrap(&mut self) -> *mut TreeInfoHeader {
429        let this = self.0.get();
430        debug_assert_eq!(this & EMPTY_FLAG, 0);
431        this as *mut TreeInfoHeader
432    }
433
434    #[inline]
435    fn header_mut_unwrap(&mut self) -> &mut TreeInfoHeader {
436        unsafe { &mut *self.header_ptr_unwrap() }
437    }
438
439    #[inline]
440    unsafe fn item_ptr(&self, idx: u8) -> *mut (NodeBase, u8) {
441        debug_assert_eq!(self.0.get() & EMPTY_FLAG, 0);
442        unsafe {
443            (self.0.get() as *mut u8).add(Self::ITEMS_OFFSET + idx as usize * Self::ITEM_SIZE)
444                as *mut _
445        }
446    }
447}
448
449impl Drop for TreeInfo {
450    #[inline]
451    fn drop(&mut self) {
452        if self.0.get() & EMPTY_FLAG == 0 {
453            let p = self.0.get() as *mut TreeInfoHeader;
454            unsafe {
455                let size = Self::buf_size_from_cap((*p).cap);
456                dealloc(p as *mut u8, Self::get_layout(size));
457            }
458        }
459    }
460}
461
462impl PathBuffer for TreeInfo {
463    // --- stats method begins ---
464
465    #[inline(always)]
466    fn inc_leaf_count(&mut self) {
467        match self.header_mut_or() {
468            Ok(header) => {
469                header.leaf_count += 1;
470                crate::trace_log!("inc_leaf_count {}", header.leaf_count);
471            }
472            Err(entry_idx) => {
473                self._alloc(2, 1, entry_idx);
474            }
475        }
476    }
477
478    #[inline(always)]
479    fn dec_leaf_count(&mut self) {
480        self.header_mut_unwrap().leaf_count -= 1;
481    }
482
483    /// # Safety
484    ///
485    /// on (height=1) root creation, caller should not call this function (inter_count init within
486    /// inc_leaf_count)
487    #[inline(always)]
488    fn inc_inter_count(&mut self) {
489        match self.header_mut() {
490            Some(header) => {
491                header.inter_count += 1;
492                crate::trace_log!("inc inter_count {}", header.inter_count);
493            }
494            None => unreachable!(),
495        }
496    }
497
498    #[inline(always)]
499    fn dec_inter_count(&mut self) {
500        self.header_mut_unwrap().inter_count -= 1;
501        crate::trace_log!("dec inter_count {}", self.header_mut_unwrap().inter_count);
502    }
503
504    // --- stats method ends ---
505
506    /// The count of current level items contains by the PathBuffer
507    fn buffer_len(&self) -> u8 {
508        if let Some(header) = self.header() { header.len } else { 0 }
509    }
510
511    /// The delta position of current entry to the PathBuffer.
512    /// < 0 for left, > 0 for right, ==0 for center
513    #[inline]
514    fn buffer_pos(&self) -> i8 {
515        if let Some(header) = self.header() { header.buffer_pos } else { 0 }
516    }
517
518    #[inline]
519    fn _set_pos(&mut self, buffer_pos: i8) {
520        if let Some(header) = self.header_mut() {
521            header.buffer_pos = buffer_pos;
522        }
523    }
524
525    /// Push one entry onto the cache stack, growing the buffer if needed.
526    #[inline]
527    fn _push(&mut self, inter: NodeBase, idx: u8) {
528        let header = self.header_mut_unwrap();
529        let wi = header.len;
530        header.len = wi + 1;
531        debug_assert!(wi < header.cap, "{wi} {}", header.cap);
532        unsafe { self.item_ptr(wi).write((inter, idx)) };
533    }
534
535    #[inline]
536    unsafe fn _get_unchecked(&self, idx: u8) -> (NodeBase, u8) {
537        unsafe {
538            let p = &*self.item_ptr(idx);
539            (p.0.clone(), p.1)
540        }
541    }
542
543    /// Pop the top entry from the cache stack.
544    #[inline]
545    fn _pop(&mut self) -> Option<(NodeBase, u8)> {
546        let header = self.header_mut()?;
547        let mut wi = header.len;
548        if wi > 0 {
549            wi -= 1;
550            header.len = wi;
551            unsafe { Some(self.item_ptr(wi).read()) }
552        } else {
553            None
554        }
555    }
556
557    #[inline]
558    fn _last(&self) -> Option<(NodeBase, u8)> {
559        let header = self.header()?;
560        let mut wi = header.len;
561        if wi > 0 {
562            wi -= 1;
563            unsafe { Some(self.item_ptr(wi).read()) }
564        } else {
565            None
566        }
567    }
568}
569
570impl PathBuffer for &mut TreeInfo {
571    // --- stats method begins ---
572    // methods should belong to Stats, but we put here due to borrow checker issues
573
574    #[inline]
575    fn inc_leaf_count(&mut self) {
576        PathBuffer::inc_leaf_count(*self)
577    }
578
579    #[inline]
580    fn dec_leaf_count(&mut self) {
581        PathBuffer::dec_leaf_count(*self)
582    }
583
584    #[inline]
585    fn inc_inter_count(&mut self) {
586        PathBuffer::inc_inter_count(*self)
587    }
588
589    #[inline]
590    fn dec_inter_count(&mut self) {
591        PathBuffer::dec_inter_count(*self)
592    }
593
594    // --- stats method ends ---
595
596    /// The count of current level items contains by the PathBuffer
597    #[inline]
598    fn buffer_len(&self) -> u8 {
599        PathBuffer::buffer_len(*self)
600    }
601
602    #[inline]
603    fn buffer_pos(&self) -> i8 {
604        PathBuffer::buffer_pos(*self)
605    }
606
607    #[inline]
608    fn _set_pos(&mut self, buffer_pos: i8) {
609        PathBuffer::_set_pos(*self, buffer_pos);
610    }
611
612    /// Push one entry onto the cache stack, growing the buffer if needed.
613    #[inline]
614    fn _push(&mut self, inter: NodeBase, idx: u8) {
615        PathBuffer::_push(*self, inter, idx)
616    }
617
618    /// Pop the top entry from the cache stack.
619    #[inline]
620    fn _pop(&mut self) -> Option<(NodeBase, u8)> {
621        PathBuffer::_pop(*self)
622    }
623
624    #[inline]
625    unsafe fn _get_unchecked(&self, idx: u8) -> (NodeBase, u8) {
626        unsafe { PathBuffer::_get_unchecked(*self, idx) }
627    }
628
629    /// Peek at the top of the stack (equivalent to `Various::last`).
630    #[inline]
631    fn _last(&self) -> Option<(NodeBase, u8)> {
632        PathBuffer::_last(*self)
633    }
634}
635
636impl<K: Key, V: Value> BTree<K, V, TreeInfo> {
637    /// Return the number of leaf nodes
638    #[inline(always)]
639    pub fn leaf_count(&self) -> usize {
640        if self.inner.root.is_some() { self.stats.leaf_count() } else { 0 }
641    }
642
643    /// Return the number of inter nodes
644    #[inline(always)]
645    pub fn inter_count(&self) -> usize {
646        self.stats.inter_count() as usize
647    }
648
649    #[inline]
650    pub fn memory_used(&self) -> usize {
651        (self.leaf_count() + self.inter_count()) * NODE_SIZE
652    }
653
654    /// Return the average fill ratio of leaf nodes
655    ///
656    /// The range is [0.0, 100]
657    #[inline]
658    pub fn get_fill_ratio(&self) -> f32 {
659        if self.inner.len == 0 {
660            0.0
661        } else {
662            let cap = LeafNode::<K, V>::cap() as usize * self.leaf_count();
663            self.inner.len as f32 / cap as f32 * 100.0
664        }
665    }
666}
667
668#[cfg(test)]
669mod tests {
670
671    use super::*;
672    use crate::bptree::tests::*;
673    use captains_log::logfn;
674    use rstest::*;
675    use std::vec::Vec;
676
677    #[logfn]
678    #[rstest]
679    fn test_tree_info_path_buffer(setup_log: ()) {
680        fn test_push_pop(info: &mut TreeInfo, height: u8) {
681            let mut nodes: Vec<InterNode<u32>> = Vec::new();
682            let root = unsafe { InterNode::<u32>::alloc(height) };
683            let mut _temp = ();
684            let cache = info.get_cache(&mut _temp, Some(root.clone()));
685            cache.push_path(root.clone(), height);
686            nodes.push(root);
687            for i in 1..height {
688                let node = unsafe { InterNode::<u32>::alloc(height - i) };
689                cache.push_path(node.clone(), height - i);
690                nodes.push(node);
691            }
692            let mut _nodes = Vec::new();
693            let mut i = 0;
694            while let Some((node, idx)) = cache.pop_path() {
695                i += 1;
696                assert_eq!(idx, i);
697                _nodes.insert(0, node);
698            }
699            assert_eq!(i, height);
700            assert_eq!(nodes, _nodes);
701            for node in nodes {
702                node.dealloc::<false>();
703            }
704        }
705        {
706            let mut info = TreeInfo::default();
707            assert!(info.header().is_none());
708            let inner0 = info.0.get();
709            assert_eq!(inner0 & EMPTY_FLAG, EMPTY_FLAG);
710            info.ensure_cap(3);
711            let inner1 = info.0.get();
712            assert_eq!(inner1 & EMPTY_FLAG, 0);
713            assert_eq!(info.header().unwrap().cap, 3);
714            info.ensure_cap(3);
715            assert_eq!(info.0.get(), inner1); // not changed
716            test_push_pop(&mut info, 3);
717
718            assert_eq!(info.header().unwrap().cap, 3);
719            info.ensure_cap(7);
720            assert!(info.0.get() != inner1); // re-alloc
721            assert_eq!(info.header().unwrap().cap, 7);
722
723            test_push_pop(&mut info, 7);
724        }
725    }
726}