dsa_rust/hierarchies/
arena_gentree.rs

1/*! A safe, indexed, n-ary tree implementation
2
3# About
4This module using arena-like backing primarily as an easy way to provide safe node referencing to mutable tree structures in a way that avoids complex lifetime management, overly-restrictive API design, and the runtime overhead of reference counting. 
5
6Compromises over the link-based tree include being less spatially efficient as the arena's growth algorithm logically shifts "pointers" to Nodes in the arena.
7
8# Design
9The implementation stores all `Node` values in a flat `Vec`-backed arena. For small trees (fewer than ~100 nodes), it is marginally slower than the `Rc<RefCell>`-based design due to fixed arena management overhead. However, for larger trees (starting around 1,000–10,000 nodes), it improves construction speed by roughly 20–25%, primarily from reduced heap allocations and better cache locality.
10
11## Drawbacks
12This implementation includes several critical compromises over a more traditional link-based approach. This Vec-backed design is intended to provide a more ergonomic design over reference counted alternatives with interior mutability, as well as raw pointer-based designs with complex lifetime management and restrictive APIs. Unfortunately, this Vec-backed design also comes with its own compromises. The Vec-backed design is far less spatially efficient due to the structure's growth. As the tree gets mutated, it also potentially loses cache locality.
13
14# Example
15
16```rust
17
18```
19
20*/
21
22#[derive(Debug, PartialEq)]
23pub struct Position {
24    ptr: usize,
25    generation: usize,
26}
27impl Position {
28    pub fn new(ptr: usize, generation: usize) -> Position {
29        Position { 
30            ptr,
31            generation,
32        }
33    }
34
35    fn _get(&self) -> usize {
36        self.ptr
37    }
38}
39// SAFETY: Cloning a Position produces another handle to the same arena 
40// entry. ABA prevention is enforced by generation validation in the 
41// arena; handles merely carry the generation that was current when issued.
42// This is effectively the same thing as a default #[derive(Clone)].
43impl Clone for Position {
44    fn clone(&self) -> Self {
45        Position { 
46            ptr: self.ptr,
47            generation: self.generation,
48        }
49    }
50}
51
52// TODO: The children field might optionally use smallvec or tinyvec 
53// or similar for more efficient stack storage which theoretically
54// reduces pointer chasing
55#[derive(Debug)]
56struct Node<T> {
57    parent: Option<Position>,
58    children: Vec<Position>,
59    data: Option<T>,
60    generation: usize,
61}
62impl<T> Node<T> {
63    fn _get_parent(&self) -> Option<&Position> {
64        self.parent.as_ref()
65    }
66    fn _get_children(&self) -> &Vec<Position> {
67        &self.children
68    }
69}
70
71use std::cell::{RefCell, Ref};
72
73#[derive(Debug)]
74pub struct GenTree<T> {
75    // RefCell moves structural mutation borrow checks from compile-time to runtime
76    arena: RefCell<Vec<Node<T>>>,
77    size: RefCell<usize>,
78    root: Position,
79    free_list: RefCell<Vec<usize>>,
80}
81impl<T> Default for GenTree<T> {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86impl<T> GenTree<T> {
87    pub fn new() -> Self {
88        let arena = vec!(Node {
89            parent: None,
90            children: Vec::new(),
91            data: None,
92            generation: 0,
93        });
94
95        GenTree {
96            arena: RefCell::new(arena),
97            size: RefCell::new(0),
98            root: Position::new(0, 0),
99            free_list: RefCell::new(Vec::new()),
100        }
101    }
102
103    pub fn root(&self) -> &Position {
104        &self.root
105    }
106
107    pub fn size(&self) -> usize {
108        *self.size.borrow() // Dumb
109    }
110
111    pub fn num_children(&self, pos: &Position) -> usize {
112        self.arena.borrow()[pos.ptr].children.len()
113    }
114
115    pub fn is_some(&self, pos: &Position) -> bool {
116        self.arena.borrow()[pos.ptr].data.is_some()
117    }
118
119    pub fn is_empty(&self) -> bool {
120        self.arena.borrow()[0].data.is_none()
121    }
122
123    pub fn mut_root(&self, data: T) {
124        self.arena.borrow_mut()[0].data = Some(data);
125    }
126
127    /// The number of levels from a given node to the root
128    pub fn depth(&self, pos: &Position) -> usize { 
129        match self.parent(pos) {
130            Some(parent) => 1 + self.depth(&parent),
131            None => 0, // root
132        }
133    }
134
135    // The number of levels from a given node to its tallest descendant,
136    // or the distance between a given position and its furthest leaf
137    //pub fn height(&self, pos: &Position) -> usize { 
138    //    self.children(pos)
139    //    .map(|child| self.height(&child))
140    //    .max()
141    //    .map_or(0, |h| h + 1)
142    //}
143
144    fn is_token_valid(&self, position: &Position) -> bool {
145        let arena = self.arena.borrow();
146        if position.ptr >= arena.len() {
147            return false;
148        }
149        let node = &arena[position.ptr];
150        node.generation == position.generation && node.data.is_some()
151    }
152
153    pub fn is_none(&self, position: &Position) -> bool {
154        !self.is_token_valid(position)
155    }
156
157    /// Yields a dynamic reference guard to the parent token inside the arena.
158    pub fn parent<'a>(&'a self, position: &Position) -> Option<Ref<'a, Position>> {
159        if !self.is_token_valid(position) { return None; }
160        
161        let arena = self.arena.borrow();
162        if arena[position.ptr].parent.is_some() {
163            Some(Ref::map(arena, |a| a[position.ptr].parent.as_ref().unwrap()))
164        } else {
165            None
166        }
167    }
168
169    pub fn children(&self, position: &Position) -> Ref<'_, Vec<Position>> {
170        assert!(self.is_token_valid(position), "Target handle is dead!");
171        Ref::map(self.arena.borrow(), |arena| &arena[position.ptr].children)
172    }
173
174    pub fn get_data<'a>(&'a self, position: &Position) -> Option<Ref<'a, T>> {
175        if !self.is_token_valid(position) { return None; }
176        Some(Ref::map(self.arena.borrow(), |arena| {
177            arena[position.ptr].data.as_ref().unwrap()
178        }))
179    }
180
181    pub fn add_child(&self, parent_pos: &Position, data: T) -> Position {
182        assert!(parent_pos.ptr == 0 || self.is_token_valid(parent_pos), "Target parent handle is dead!");
183
184        let mut arena = self.arena.borrow_mut();
185        let mut free_list = self.free_list.borrow_mut();
186
187        let (index, next_gen) = if let Some(reuse_idx) = free_list.pop() {
188            arena[reuse_idx].generation += 1;
189            let gen = arena[reuse_idx].generation;
190            
191            arena[reuse_idx] = Node {
192                // Duplicate internally via component destructuring, never by copying the token object
193                parent: Some(Position::new(parent_pos.ptr, parent_pos.generation)),
194                children: Vec::new(),
195                data: Some(data),
196                generation: gen,
197            };
198            (reuse_idx, gen)
199        } else {
200            let new_idx = arena.len();
201            arena.push(Node {
202                parent: Some(Position::new(parent_pos.ptr, parent_pos.generation)),
203                children: Vec::new(),
204                data: Some(data),
205                generation: 0,
206            });
207            (new_idx, 0)
208        };
209
210        arena[parent_pos.ptr].children.push(Position::new(index, next_gen));
211        Position::new(index, next_gen)
212    }
213
214    /// Explicitly consumes the token, destroying it from the caller's frame permanently.
215    pub fn remove(&self, position: Position) -> Option<T> {
216        if !self.is_token_valid(&position) { return None; }
217
218        let mut arena = self.arena.borrow_mut();
219        let data = arena[position.ptr].data.take();
220        let parent_pos = arena[position.ptr].parent.take();
221        
222        self.free_list.borrow_mut().push(position.ptr);
223
224        if let Some(parent) = parent_pos {
225            arena[parent.ptr].children.retain(|p| p.ptr != position.ptr);
226            
227            let orphans = std::mem::take(&mut arena[position.ptr].children);
228            for child in orphans {
229                arena[child.ptr].parent = Some(Position::new(parent.ptr, parent.generation));
230                arena[parent.ptr].children.push(child);
231            }
232        }
233        data
234    }
235}
236
237#[cfg(test)]
238mod tests {
239
240    #[test]
241    /// TODO: actually test the structure's members!
242    fn atomic() {
243        use super::GenTree;
244        use crate::hierarchies::arena_gentree_builder::Heading;
245
246        let tree = GenTree::new(); 
247        // Instantiated tree automatically has a single, empty root node
248        // with a size of zero
249        assert_eq!(tree.size(), 0); 
250        assert!(tree.is_empty());
251        let root = tree.root().clone();
252        let mut cursor = tree.add_child(
253            &root,
254            Heading {
255                level: 2,
256                title: "Landlocked".to_string(),
257            },
258        );
259        assert_eq!(tree.size(), 1);
260        assert!(!tree.is_empty());
261
262        cursor = tree.add_child(
263            &cursor,
264            Heading {
265                level: 3,
266                title: "Switzerland".to_string(),
267            },
268        );
269        cursor = tree.add_child(
270            &cursor,
271            Heading {
272                level: 4,
273                title: "Geneva".to_string(),
274            },
275        );
276        cursor = tree.add_child(
277            &cursor,
278            Heading {
279                level: 5,
280                title: "Old Town".to_string(),
281            },
282        );
283        //cursor = tree.parent(&cursor).expect(""); // Geneva
284        //cursor = tree.parent(&cursor).expect(""); // Switzerland
285        tree.add_child(
286            &cursor,
287            Heading {
288                level: 3,
289                title: "Botswana".to_string(),
290            },
291        );
292        assert_eq!(tree.size(), 6);
293
294        eprintln!("{tree:#?}");
295        //panic!("MANUAL TEST FAILURE");
296    }
297
298    #[test]
299    fn dangle() {
300        use crate::hierarchies::arena_gentree_builder::{construct, Heading};
301
302        use super::GenTree;
303        let one = vec![
304            Heading {
305                level: 1,
306                title: "Landlocked".to_string(),
307            },
308            Heading {
309                level: 2,
310                title: "Switzerland".to_string(),
311            },
312        ];
313        let two = vec![
314            Heading {
315                level: 1,
316                title: "Bolivia".to_string(),
317            },
318            Heading {
319                level: 2,
320                title: "Zimbabwe".to_string(),
321            },
322        ];
323
324        // Creates a tree, Position, and CursorMut
325        let outer_tree: GenTree<Heading> = construct(0, one);
326        //let outer_tree: GenTree<Heading> = construct_from(one);
327        let mut _pos = outer_tree.root();
328
329        {
330            let inner_tree: GenTree<Heading> = construct(0, two);
331            //let inner_tree: GenTree<Heading> = construct_from(two);
332            _pos = inner_tree.root();
333        } // inner_tree dropped here
334
335        // No UB (not possible) because inner_tree and _pos is already dropped
336        //let _oopsie = outer_tree.get_data(_pos);
337    }
338
339    use super::*;
340
341    #[test]
342    // Irrelevant because Position is move-only
343    fn test_aba_slot_recycling_isolation() {} 
344
345   #[test]
346    fn test_parent_child_severance_on_remove() {} 
347
348    #[test]
349    fn test_arena_churn_and_size_accounting() {}
350
351    #[test]
352    fn test_structural_queries_on_stale_positions() {}
353
354    #[test]
355fn test_recycling_preserves_live_nodes() {
356    let tree = GenTree::new();
357    tree.mut_root("Root".to_string());
358
359    let root = tree.root();
360
361    let victim = tree.add_child(root, "Victim".to_string());
362    let survivor = tree.add_child(root, "Survivor".to_string());
363
364    tree.remove(victim);
365
366    let replacement = tree.add_child(root, "Replacement".to_string());
367
368    assert_eq!(*tree.get_data(&survivor).unwrap(), "Survivor");
369    assert_eq!(*tree.get_data(&replacement).unwrap(), "Replacement");
370}
371}