Skip to main content

subms_adaptive_radix_tree/
lib.rs

1//! Adaptive Radix Tree (ART) over byte-string keys - Leis et al., 2013.
2//!
3//! Two ideas carry the structure:
4//!
5//! * **Adaptive nodes.** Child storage grows with fan-out: `Node4` (up to 4
6//!   children, linear scan), `Node16` (up to 16), `Node48` (a 256-entry byte
7//!   index into 48 slots), `Node256` (direct 256-way). A node promotes to the
8//!   next size when it fills and demotes when `compaction` shrinks it, so a
9//!   sparse node never pays for 256 pointers and a dense one never pays a scan.
10//! * **Path compression.** A run of single-child bytes collapses into one
11//!   node's `prefix`, so a long shared key stem costs one node, not one per
12//!   byte. On a diverging insert the node splits at the first mismatch.
13//!
14//! ```
15//! use subms_adaptive_radix_tree::Art;
16//! let mut t: Art<i32> = Art::new();
17//! t.insert(b"alice", 1);
18//! t.insert(b"alicia", 2);   // shares "ali", splits at the 4th byte
19//! assert_eq!(t.get(b"alice").copied(), Some(1));
20//! assert_eq!(t.get(b"missing"), None);
21//! ```
22//!
23//! Full writeup, design notes and measured benchmarks:
24//! <https://www.submillisecond.com/cookbook/recipes/subms-adaptive-radix-tree>
25
26pub struct Art<V> {
27    root: Node<V>,
28    len: usize,
29}
30
31pub(crate) struct Node<V> {
32    /// Path-compressed bytes shared by every key under this node, matched before
33    /// the branching byte selects a child.
34    pub(crate) prefix: Vec<u8>,
35    /// Value of the key that terminates at this node (after `prefix`), if any.
36    pub(crate) value: Option<V>,
37    pub(crate) children: Children<V>,
38}
39
40/// Which adaptive node layout a `Children` currently uses. Exposed for the
41/// `metrics` feature's node-type distribution.
42#[allow(dead_code)]
43#[derive(Copy, Clone, Debug, PartialEq, Eq)]
44pub(crate) enum NodeKind {
45    Node4,
46    Node16,
47    Node48,
48    Node256,
49}
50
51pub(crate) enum Children<V> {
52    Node4 {
53        keys: [u8; 4],
54        child: [Option<Box<Node<V>>>; 4],
55        count: u8,
56    },
57    Node16 {
58        keys: [u8; 16],
59        // Boxed so a Node16 does not bloat every `Children` to its size - each
60        // node kind should cost roughly its own capacity, ART's memory point.
61        child: Box<[Option<Box<Node<V>>>; 16]>,
62        count: u8,
63    },
64    Node48 {
65        /// `index[b] == 0` means absent; otherwise the child is `child[index[b] - 1]`.
66        index: Box<[u8; 256]>,
67        child: Box<[Option<Box<Node<V>>>; 48]>,
68        count: u8,
69    },
70    Node256 {
71        child: Box<[Option<Box<Node<V>>>; 256]>,
72        count: u16,
73    },
74}
75
76impl<V> Default for Art<V> {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82impl<V> Art<V> {
83    pub fn new() -> Self {
84        Self {
85            root: Node::inner(Vec::new()),
86            len: 0,
87        }
88    }
89
90    pub fn len(&self) -> usize {
91        self.len
92    }
93    pub fn is_empty(&self) -> bool {
94        self.len == 0
95    }
96
97    /// Insert or replace. Returns the prior value if the key was already present.
98    pub fn insert(&mut self, key: &[u8], value: V) -> Option<V> {
99        let (prior, added) = insert_rec(&mut self.root, key, value);
100        if added {
101            self.len += 1;
102        }
103        prior
104    }
105
106    pub fn get(&self, key: &[u8]) -> Option<&V> {
107        let mut node = &self.root;
108        let mut depth = 0usize;
109        loop {
110            let p = node.prefix.len();
111            if key.len() < depth + p || key[depth..depth + p] != node.prefix[..] {
112                return None;
113            }
114            depth += p;
115            if depth == key.len() {
116                return node.value.as_ref();
117            }
118            node = node.children.get(key[depth])?;
119            depth += 1;
120        }
121    }
122
123    // Accessors below are used only by the opt-in feature modules (serialize /
124    // range-scan / concurrent-reads / metrics / compaction); a feature-free
125    // build compiles none of them, so `allow(dead_code)` keeps it warning-free.
126    #[allow(dead_code)]
127    pub(crate) fn root(&self) -> &Node<V> {
128        &self.root
129    }
130
131    #[allow(dead_code)]
132    pub(crate) fn root_mut(&mut self) -> &mut Node<V> {
133        &mut self.root
134    }
135
136    #[allow(dead_code)]
137    pub(crate) fn set_len(&mut self, len: usize) {
138        self.len = len;
139    }
140
141    /// Remove the value at `key`, leaving the (now valueless) path in place - run
142    /// `compaction` to reclaim it. Returns the removed value if any.
143    #[allow(dead_code)]
144    pub(crate) fn delete_value(&mut self, key: &[u8]) -> Option<V> {
145        let node = walk_mut(&mut self.root, key)?;
146        let prior = node.value.take();
147        if prior.is_some() {
148            self.len -= 1;
149        }
150        prior
151    }
152}
153
154impl<V> Node<V> {
155    pub(crate) fn inner(prefix: Vec<u8>) -> Self {
156        Self {
157            prefix,
158            value: None,
159            children: Children::new(),
160        }
161    }
162
163    fn leaf(prefix: Vec<u8>, value: V) -> Self {
164        Self {
165            prefix,
166            value: Some(value),
167            children: Children::new(),
168        }
169    }
170}
171
172fn common_prefix_len(a: &[u8], b: &[u8]) -> usize {
173    let n = a.len().min(b.len());
174    let mut i = 0;
175    while i < n && a[i] == b[i] {
176        i += 1;
177    }
178    i
179}
180
181/// `key` is the portion of the search key remaining at `node`. Returns
182/// `(prior_value, added_new_key)`.
183fn insert_rec<V>(node: &mut Node<V>, key: &[u8], value: V) -> (Option<V>, bool) {
184    let common = common_prefix_len(&node.prefix, key);
185    if common < node.prefix.len() {
186        // The node's prefix diverges from the key: split at `common`.
187        split_node(node, key, value, common);
188        return (None, true);
189    }
190    // Whole prefix matched - consume it.
191    let key = &key[node.prefix.len()..];
192    if key.is_empty() {
193        let prior = node.value.replace(value);
194        let added = prior.is_none();
195        return (prior, added);
196    }
197    let b = key[0];
198    let rest = &key[1..];
199    if node.children.get(b).is_some() {
200        let child = node.children.get_mut(b).unwrap();
201        insert_rec(child, rest, value)
202    } else {
203        node.children
204            .insert(b, Box::new(Node::leaf(rest.to_vec(), value)));
205        (None, true)
206    }
207}
208
209/// Split `node` at prefix position `common` (which is `< node.prefix.len()`):
210/// a fresh parent takes `prefix[..common]`, the old node drops to a child under
211/// byte `prefix[common]` with `prefix[common + 1..]`, and the new key branches
212/// beside it (or terminates in the parent).
213fn split_node<V>(node: &mut Node<V>, key: &[u8], value: V, common: usize) {
214    let mut old = std::mem::replace(node, Node::inner(Vec::new()));
215    let old_prefix = std::mem::take(&mut old.prefix);
216    let parent_prefix = old_prefix[..common].to_vec();
217    let old_edge = old_prefix[common];
218    old.prefix = old_prefix[common + 1..].to_vec();
219
220    node.prefix = parent_prefix;
221    node.children.insert(old_edge, Box::new(old));
222
223    let krest = &key[common..];
224    if krest.is_empty() {
225        node.value = Some(value);
226    } else {
227        node.children
228            .insert(krest[0], Box::new(Node::leaf(krest[1..].to_vec(), value)));
229    }
230}
231
232#[allow(dead_code)] // reached only via delete_value (feature-only)
233fn walk_mut<'a, V>(node: &'a mut Node<V>, key: &[u8]) -> Option<&'a mut Node<V>> {
234    let mut cur = node;
235    let mut depth = 0usize;
236    loop {
237        let p = cur.prefix.len();
238        if key.len() < depth + p || key[depth..depth + p] != cur.prefix[..] {
239            return None;
240        }
241        depth += p;
242        if depth == key.len() {
243            return Some(cur);
244        }
245        let b = key[depth];
246        cur = cur.children.get_mut(b)?;
247        depth += 1;
248    }
249}
250
251impl<V> Children<V> {
252    pub(crate) fn new() -> Self {
253        Children::Node4 {
254            keys: [0u8; 4],
255            child: [const { None }; 4],
256            count: 0,
257        }
258    }
259
260    #[allow(dead_code)]
261    pub(crate) fn kind(&self) -> NodeKind {
262        match self {
263            Children::Node4 { .. } => NodeKind::Node4,
264            Children::Node16 { .. } => NodeKind::Node16,
265            Children::Node48 { .. } => NodeKind::Node48,
266            Children::Node256 { .. } => NodeKind::Node256,
267        }
268    }
269
270    #[allow(dead_code)]
271    pub(crate) fn len(&self) -> usize {
272        match self {
273            Children::Node4 { count, .. } | Children::Node16 { count, .. } => *count as usize,
274            Children::Node48 { count, .. } => *count as usize,
275            Children::Node256 { count, .. } => *count as usize,
276        }
277    }
278
279    #[allow(dead_code)]
280    pub(crate) fn is_empty(&self) -> bool {
281        self.len() == 0
282    }
283
284    pub(crate) fn get(&self, byte: u8) -> Option<&Node<V>> {
285        match self {
286            Children::Node4 {
287                keys, child, count, ..
288            } => {
289                for i in 0..(*count as usize) {
290                    if keys[i] == byte {
291                        return child[i].as_deref();
292                    }
293                }
294                None
295            }
296            Children::Node16 {
297                keys, child, count, ..
298            } => {
299                for i in 0..(*count as usize) {
300                    if keys[i] == byte {
301                        return child[i].as_deref();
302                    }
303                }
304                None
305            }
306            Children::Node48 { index, child, .. } => {
307                let slot = index[byte as usize];
308                if slot == 0 {
309                    None
310                } else {
311                    child[(slot - 1) as usize].as_deref()
312                }
313            }
314            Children::Node256 { child, .. } => child[byte as usize].as_deref(),
315        }
316    }
317
318    pub(crate) fn get_mut(&mut self, byte: u8) -> Option<&mut Node<V>> {
319        match self {
320            Children::Node4 {
321                keys, child, count, ..
322            } => {
323                for i in 0..(*count as usize) {
324                    if keys[i] == byte {
325                        return child[i].as_deref_mut();
326                    }
327                }
328                None
329            }
330            Children::Node16 {
331                keys, child, count, ..
332            } => {
333                for i in 0..(*count as usize) {
334                    if keys[i] == byte {
335                        return child[i].as_deref_mut();
336                    }
337                }
338                None
339            }
340            Children::Node48 { index, child, .. } => {
341                let slot = index[byte as usize];
342                if slot == 0 {
343                    None
344                } else {
345                    child[(slot - 1) as usize].as_deref_mut()
346                }
347            }
348            Children::Node256 { child, .. } => child[byte as usize].as_deref_mut(),
349        }
350    }
351
352    /// Insert a child under `byte` (which must not already be present), growing
353    /// to the next node size first if this one is full.
354    pub(crate) fn insert(&mut self, byte: u8, node: Box<Node<V>>) {
355        if self.is_full() {
356            self.grow();
357        }
358        match self {
359            Children::Node4 {
360                keys, child, count, ..
361            } => {
362                let i = *count as usize;
363                keys[i] = byte;
364                child[i] = Some(node);
365                *count += 1;
366            }
367            Children::Node16 {
368                keys, child, count, ..
369            } => {
370                let i = *count as usize;
371                keys[i] = byte;
372                child[i] = Some(node);
373                *count += 1;
374            }
375            Children::Node48 {
376                index,
377                child,
378                count,
379            } => {
380                let i = *count as usize;
381                child[i] = Some(node);
382                index[byte as usize] = (i + 1) as u8;
383                *count += 1;
384            }
385            Children::Node256 { child, count } => {
386                child[byte as usize] = Some(node);
387                *count += 1;
388            }
389        }
390    }
391
392    fn is_full(&self) -> bool {
393        match self {
394            Children::Node4 { count, .. } => *count == 4,
395            Children::Node16 { count, .. } => *count == 16,
396            Children::Node48 { count, .. } => *count == 48,
397            Children::Node256 { .. } => false,
398        }
399    }
400
401    fn grow(&mut self) {
402        match self {
403            Children::Node4 {
404                keys, child, count, ..
405            } => {
406                let mut nkeys = [0u8; 16];
407                let mut nchild: [Option<Box<Node<V>>>; 16] = [const { None }; 16];
408                for i in 0..(*count as usize) {
409                    nkeys[i] = keys[i];
410                    nchild[i] = child[i].take();
411                }
412                *self = Children::Node16 {
413                    keys: nkeys,
414                    child: Box::new(nchild),
415                    count: *count,
416                };
417            }
418            Children::Node16 {
419                keys, child, count, ..
420            } => {
421                let mut index = [0u8; 256];
422                let mut nchild: [Option<Box<Node<V>>>; 48] = [const { None }; 48];
423                for i in 0..(*count as usize) {
424                    nchild[i] = child[i].take();
425                    index[keys[i] as usize] = (i + 1) as u8;
426                }
427                *self = Children::Node48 {
428                    index: Box::new(index),
429                    child: Box::new(nchild),
430                    count: *count,
431                };
432            }
433            Children::Node48 {
434                index,
435                child,
436                count,
437            } => {
438                let mut nchild: [Option<Box<Node<V>>>; 256] = [const { None }; 256];
439                for b in 0..256usize {
440                    let slot = index[b];
441                    if slot != 0 {
442                        nchild[b] = child[(slot - 1) as usize].take();
443                    }
444                }
445                *self = Children::Node256 {
446                    child: Box::new(nchild),
447                    count: *count as u16,
448                };
449            }
450            Children::Node256 { .. } => {}
451        }
452    }
453
454    /// `(byte, child)` pairs in ascending byte order. Used by the feature modules
455    /// that reconstruct keys or serialize the tree.
456    #[allow(dead_code)]
457    pub(crate) fn sorted_pairs(&self) -> Vec<(u8, &Node<V>)> {
458        let mut out: Vec<(u8, &Node<V>)> = match self {
459            Children::Node4 {
460                keys, child, count, ..
461            } => (0..(*count as usize))
462                .filter_map(|i| child[i].as_deref().map(|c| (keys[i], c)))
463                .collect(),
464            Children::Node16 {
465                keys, child, count, ..
466            } => (0..(*count as usize))
467                .filter_map(|i| child[i].as_deref().map(|c| (keys[i], c)))
468                .collect(),
469            Children::Node48 { index, child, .. } => (0..256usize)
470                .filter_map(|b| {
471                    let slot = index[b];
472                    if slot == 0 {
473                        None
474                    } else {
475                        child[(slot - 1) as usize].as_deref().map(|c| (b as u8, c))
476                    }
477                })
478                .collect(),
479            Children::Node256 { child, .. } => (0..256usize)
480                .filter_map(|b| child[b].as_deref().map(|c| (b as u8, c)))
481                .collect(),
482        };
483        out.sort_by_key(|(b, _)| *b);
484        out
485    }
486
487    /// Mutable `(byte, child)` pairs, unordered. Used by `compaction` to walk and
488    /// rewrite the subtree.
489    #[allow(dead_code)]
490    pub(crate) fn each_child_mut(&mut self, mut f: impl FnMut(&mut Node<V>)) {
491        match self {
492            Children::Node4 { child, count, .. } => {
493                for slot in child.iter_mut().take(*count as usize) {
494                    if let Some(c) = slot.as_deref_mut() {
495                        f(c);
496                    }
497                }
498            }
499            Children::Node16 { child, count, .. } => {
500                for slot in child.iter_mut().take(*count as usize) {
501                    if let Some(c) = slot.as_deref_mut() {
502                        f(c);
503                    }
504                }
505            }
506            Children::Node48 { child, .. } => {
507                for slot in child.iter_mut() {
508                    if let Some(c) = slot.as_deref_mut() {
509                        f(c);
510                    }
511                }
512            }
513            Children::Node256 { child, .. } => {
514                for slot in child.iter_mut() {
515                    if let Some(c) = slot.as_deref_mut() {
516                        f(c);
517                    }
518                }
519            }
520        }
521    }
522
523    /// Remove the child under `byte`, if present, returning it. Does not demote
524    /// the node size - `compaction` decides when to shrink.
525    #[allow(dead_code)]
526    pub(crate) fn remove(&mut self, byte: u8) -> Option<Box<Node<V>>> {
527        match self {
528            Children::Node4 {
529                keys, child, count, ..
530            } => {
531                let n = *count as usize;
532                for i in 0..n {
533                    if keys[i] == byte {
534                        let removed = child[i].take();
535                        keys[i] = keys[n - 1];
536                        child[i] = child[n - 1].take();
537                        keys[n - 1] = 0;
538                        *count -= 1;
539                        return removed;
540                    }
541                }
542                None
543            }
544            Children::Node16 {
545                keys, child, count, ..
546            } => {
547                let n = *count as usize;
548                for i in 0..n {
549                    if keys[i] == byte {
550                        let removed = child[i].take();
551                        // Compact the arrays: move the last entry into the hole.
552                        keys[i] = keys[n - 1];
553                        child[i] = child[n - 1].take();
554                        keys[n - 1] = 0;
555                        *count -= 1;
556                        return removed;
557                    }
558                }
559                None
560            }
561            Children::Node48 {
562                index,
563                child,
564                count,
565            } => {
566                let slot = index[byte as usize];
567                if slot == 0 {
568                    return None;
569                }
570                let removed = child[(slot - 1) as usize].take();
571                index[byte as usize] = 0;
572                *count -= 1;
573                removed
574            }
575            Children::Node256 { child, count } => {
576                let removed = child[byte as usize].take();
577                if removed.is_some() {
578                    *count -= 1;
579                }
580                removed
581            }
582        }
583    }
584
585    /// Drain every `(byte, child)` out, leaving an empty `Node4`. Used by
586    /// `compaction` to rebuild a node at a smaller size (re-inserting auto-grows
587    /// to the minimal layout for the occupancy).
588    #[allow(dead_code)]
589    pub(crate) fn take_all(&mut self) -> Vec<(u8, Box<Node<V>>)> {
590        let taken = std::mem::replace(self, Children::new());
591        match taken {
592            Children::Node4 {
593                keys,
594                mut child,
595                count,
596            } => (0..count as usize)
597                .filter_map(|i| child[i].take().map(|c| (keys[i], c)))
598                .collect(),
599            Children::Node16 {
600                keys,
601                mut child,
602                count,
603            } => (0..count as usize)
604                .filter_map(|i| child[i].take().map(|c| (keys[i], c)))
605                .collect(),
606            Children::Node48 {
607                index, mut child, ..
608            } => (0..256usize)
609                .filter_map(|b| {
610                    let slot = index[b];
611                    if slot == 0 {
612                        None
613                    } else {
614                        child[(slot - 1) as usize].take().map(|c| (b as u8, c))
615                    }
616                })
617                .collect(),
618            Children::Node256 { mut child, .. } => (0..256usize)
619                .filter_map(|b| child[b].take().map(|c| (b as u8, c)))
620                .collect(),
621        }
622    }
623
624    /// Insert-or-get the child under `byte`, creating an empty inner node if
625    /// absent. Used by `serialize` while rebuilding a tree from bytes.
626    #[allow(dead_code)]
627    pub(crate) fn get_or_insert_for_load(&mut self, byte: u8) -> &mut Node<V> {
628        if self.get(byte).is_none() {
629            self.insert(byte, Box::new(Node::inner(Vec::new())));
630        }
631        self.get_mut(byte).unwrap()
632    }
633}
634
635#[cfg(feature = "harness")]
636pub mod recipe;
637
638// Opt-in feature catalog. Each submodule is gated by its own Cargo feature;
639// `cargo add subms-adaptive-radix-tree` alone keeps the base zero-dep + std-only.
640#[cfg(any(
641    feature = "serialize",
642    feature = "range-scan",
643    feature = "concurrent-reads",
644    feature = "metrics",
645    feature = "compaction",
646))]
647pub mod features;
648
649#[cfg(feature = "compaction")]
650pub use features::compaction::{compact, delete};
651#[cfg(feature = "concurrent-reads")]
652pub use features::concurrent_reads::ArtSnapshot;
653#[cfg(feature = "metrics")]
654pub use features::metrics::{ArtMetrics, MeasuredArt, NodeTypeCounts};
655#[cfg(feature = "range-scan")]
656pub use features::range_scan::{Bound, range};
657#[cfg(feature = "serialize")]
658pub use features::serialize::{ArtCodec, parse, write_to};
659
660#[cfg(test)]
661#[path = "art_tests.rs"]
662mod art_tests;
663#[cfg(test)]
664#[path = "sample_app_tests.rs"]
665mod sample_app_tests;