trailtree/lib.rs
1//! # TrailTree
2//!
3//! `TrailTree` is a hierarchical search tree structure designed for efficiently searching and traversing paths using layer-scoped, unique keys.
4//!
5//! ## Features
6//! - **Path-based Operations**: Provides efficient insertion and retrieval of paths using subtrees.
7//! - **Generic Keys and Data**: Supports keys `K: Clone + Ord` and associated data `T`.
8//! - **Persistent Identification**: Operations return stable `usize` identifiers (except where explicitly stated as a side effect).
9//!
10//! ## Implementation
11//! `TrailTree` is implemented as a recursive, tertiary, AVL search tree—such that the Equal child of each element is the root of its subtree—stored on
12//! top of an object pool.
13//!
14//! ### Time
15//! Efficiencies are expressed in terms of fundamental operations—complex operations will need to be multiplied by path length or number of operations.
16//! The term `n` refers to the number of elements in the local tree, while `m` refers to the number of elements in the complete subtree.
17//!
18//! - Insert—binary search + rebalance, `O(log n)`
19//! - Delete—subtree deletion + rebalance, `O(m + log n)`
20//! - Relocate—binary search + rebalance (x2), `O(log n)`
21//! - Key Search—binary search, `O(log n)`
22//! - Enumeration—exhaustive iteration, `O(n)`
23//! - Sibling key traversal—subtree traversal, `O(log n)`
24//! - Upward traversal—`O(1)`
25//! - Access—`O(1)`
26//!
27//! ### Memory
28//! `TrailTree` trades some memory overhead for improved time efficiency, resulting in nonoptimal memory usage.
29//! Update operations may incur memory spikes due to the use of an underlying `Vec`.
30//!
31//! ### Caching
32//! While the structure uses continuous memory, the positions of related elements are not guaranteed to be cache efficient.
33//!
34//! ## Example
35//! ```
36//! use trailtree::TrailTree;
37//!
38//! let mut tree = TrailTree::<u32, ()>::new();
39//!
40//! // Insert a path into the tree
41//! let leaf = tree.insert(&[10, 12, 2]);
42//!
43//! // Retrieve the path associated with the leaf
44//! assert_eq!(&tree.path(leaf), &[10, 12, 2]);
45//! ```
46//!
47//! ## When to Use
48//! `TrailTree` is best suited for workloads where paths are queried incrementally, subtrees are traversed frequently, and element identity must remain stable across mutations.
49//!
50//! - File systems
51//! - Namespaces
52//! - Type systems
53//! - Organizational charts
54//!
55
56#![cfg_attr(not(feature = "std"), no_std)]
57#![allow(dead_code)]
58extern crate alloc;
59
60use alloc::vec::Vec;
61
62#[cfg(test)]
63mod tests;
64
65mod node; use node::{Node, NodeRel};
66mod cell; use cell::Cell;
67mod implement;
68
69/// A hierarchical search tree structure for efficiently searching paths.
70/// Operates over locally-unique, searchable keys `K` with associated data `T`.
71///
72/// # Examples
73///
74/// ```
75/// use trailtree::TrailTree;
76///
77/// let mut con = TrailTree::<u32,()>::new();
78///
79/// let leaf = con.insert(&[10, 12, 2]);
80///
81/// assert_eq!(&con.path(leaf), &[10, 12, 2]);
82/// ```
83pub struct TrailTree<K,T>
84where K: Clone + Ord
85{
86 pub(crate) cells:Vec<Cell<K,T>>,
87 pub(crate) root:usize,
88 pub(crate) next:usize,
89}
90
91impl<K,T> TrailTree<K,T>
92where K: Clone + Ord
93{
94 /// Constructs a new, empty structure.
95 ///
96 /// ```
97 /// use trailtree::TrailTree;
98 ///
99 /// let mut con = TrailTree::<u32,()>::new();
100 /// assert_eq!(con.capacity(), 0);
101 /// ```
102 pub const fn new() -> Self
103 {
104 Self {
105 cells:Vec::new(),
106 root:usize::MAX,
107 next:0,
108 }
109 }
110
111 /// Constructs a new structure with the requested capacity of preallocated cells.
112 ///
113 /// ```
114 /// use trailtree::TrailTree;
115 ///
116 /// let mut con = TrailTree::<u32,()>::with_capacity(10);
117 ///
118 /// assert!(con.capacity() >= 10);
119 /// ```
120 pub fn with_capacity(capacity:usize) -> Self
121 {
122 Self {
123 cells:Vec::with_capacity(capacity),
124 root:usize::MAX,
125 next:0,
126 }
127 }
128}
129
130#[cfg(feature = "std")]
131impl<K,T> std::fmt::Debug for TrailTree<K,T>
132where K: Clone + Ord + std::fmt::Debug
133{
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
135 {
136 fn id_or(id:usize) -> String
137 {
138 if id != usize::MAX {
139 id.to_string()
140 } else {
141 "-".to_string()
142 }
143 }
144
145 let mut out = String::new();
146
147 let mut stack: Vec<_> = alloc::vec::Vec::new();
148 stack.push((self.root, 0));
149
150 while let Some((id, depth)) = stack.pop() {
151 if id != usize::MAX {
152 if let Some(node) = self.node(id) {
153 out.push_str(&format!("{}#{} {:?} [{}:{}] [{}, {}, {}]\n", ".".repeat(depth),
154 id,
155 node.key,
156 id_or(node.rel.root),
157 id_or(node.rel.parent),
158 id_or(node.rel.child[0]),
159 id_or(node.rel.child[1]),
160 id_or(node.rel.child[2]),
161 ));
162
163 stack.push((node.rel.child[2], depth + 1));
164 stack.push((node.rel.child[0], depth + 1));
165 stack.push((node.rel.child[1], depth + 1));
166 } else {
167 out.push_str(&format!("{}invalid\n", " ".repeat(depth)));
168 }
169 }
170 }
171
172 write!(f, "{}", out)
173 }
174}