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## Generations & Free Lists
12This structure uses generations on its safe handles to mitigate dangling access & use-after-free errors, dead references via ABA problem mitigation, and handle forgery. This is something that both naive arena-backed structures and raw pointer structures are vulnerable to. Reference counting prevents dangling access and dead references, and makes handle forgery impossible.
13
14This structure implements a free list to mitigate leaks. When nodes are removed, the backing slot takes on a `None` value, which can bloat the list unless those nodes are re-filled. However, this means that two node references could point to the same memory slot. As a result, the structure increments generational count by usage, guaranteeing that only the correct generational combination can access the memory slot.
15
16## Drawbacks
17This 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.
18
19# Example
20
21```rust
22
23```
24
25*/
26
27#[derive(Copy, Debug, PartialEq)]
28pub struct Position {
29    ptr: usize,
30    generation: usize,
31}
32impl Position {
33    pub fn new(ptr: usize, generation: usize) -> Position {
34        Position { ptr, generation }
35    }
36
37    fn _get(&self) -> usize {
38        self.ptr
39    }
40}
41// SAFETY: Cloning a Position produces another handle to the same arena
42// entry. ABA prevention is enforced by generation validation in the
43// arena; handles merely carry the generation that was current when issued.
44// This is effectively the same thing as a default #[derive(Clone)].
45impl Clone for Position {
46    fn clone(&self) -> Self {
47        *self // Because the type derives Copy
48        //Position {
49        //    ptr: self.ptr,
50        //    generation: self.generation,
51        //}
52    }
53}
54
55// TODO: The children field might optionally use smallvec or tinyvec
56// or similar for more efficient stack storage which theoretically
57// reduces pointer chasing
58#[derive(Debug)]
59struct Node<T> {
60    parent: Option<Position>,
61    children: Vec<Position>,
62    data: Option<T>,
63    generation: usize,
64}
65impl<T> Node<T> {
66    fn _get_parent(&self) -> Option<&Position> {
67        self.parent.as_ref()
68    }
69    fn _get_children(&self) -> &Vec<Position> {
70        &self.children
71    }
72}
73
74use std::cell::{Ref, RefCell};
75
76#[derive(Debug)]
77pub struct GenTree<T> {
78    // RefCell moves structural mutation borrow checks from compile-time to runtime
79    arena: RefCell<Vec<Node<T>>>,
80    size: RefCell<usize>,
81    root: Position,
82    free_list: RefCell<Vec<usize>>,
83}
84impl<T> Default for GenTree<T> {
85    fn default() -> Self {
86        Self::new()
87    }
88}
89impl<T> GenTree<T> {
90    pub fn new() -> Self {
91        let arena = vec![Node {
92            parent: None,
93            children: Vec::new(),
94            data: None,
95            generation: 0,
96        }];
97
98        GenTree {
99            arena: RefCell::new(arena),
100            size: RefCell::new(0),
101            root: Position::new(0, 0),
102            free_list: RefCell::new(Vec::new()),
103        }
104    }
105
106    pub fn new_with_capacity(cap: usize) -> Self {
107        let arena = vec![Node {
108            parent: None,
109            children: Vec::with_capacity(cap),
110            data: None,
111            generation: 0,
112        }];
113
114        GenTree {
115            arena: RefCell::new(arena),
116            size: RefCell::new(0),
117            root: Position::new(0, 0),
118            free_list: RefCell::new(Vec::new()),
119        }
120    }
121
122    pub fn root(&self) -> &Position {
123        &self.root
124    }
125
126    pub fn size(&self) -> usize {
127        *self.size.borrow() // Dumb
128    }
129
130    pub fn num_children(&self, pos: &Position) -> usize {
131        self.arena.borrow()[pos.ptr].children.len()
132    }
133
134    pub fn is_some(&self, pos: &Position) -> bool {
135        self.arena.borrow()[pos.ptr].data.is_some()
136    }
137
138    pub fn is_empty(&self) -> bool {
139        //self.arena.borrow()[0].data.is_none()
140        self.size.borrow().eq(&0)
141    }
142
143    pub fn mut_root(&self, data: T) {
144        self.arena.borrow_mut()[0].data = Some(data);
145    }
146
147    /// The number of levels from a given node to the root
148    pub fn depth(&self, pos: &Position) -> usize {
149        match self.parent(pos) {
150            Some(parent) => 1 + self.depth(&parent),
151            None => 0, // root
152        }
153    }
154
155    // The number of levels from a given node to its tallest descendant,
156    // or the distance between a given position and its furthest leaf
157    //pub fn height(&self, pos: &Position) -> usize {
158    //    self.children(pos)
159    //    .map(|child| self.height(&child))
160    //    .max()
161    //    .map_or(0, |h| h + 1)
162    //}
163
164    fn is_token_valid(&self, position: &Position) -> bool {
165        let arena = self.arena.borrow();
166        if position.ptr >= arena.len() {
167            return false;
168        }
169        let node = &arena[position.ptr];
170        node.generation == position.generation && node.data.is_some()
171    }
172
173    pub fn is_none(&self, position: &Position) -> bool {
174        !self.is_token_valid(position)
175    }
176
177    /// Yields a dynamic reference guard to the parent token inside the arena.
178    pub fn parent<'a>(&'a self, position: &Position) -> Option<Ref<'a, Position>> {
179        if !self.is_token_valid(position) {
180            return None;
181        }
182
183        let arena = self.arena.borrow();
184        if arena[position.ptr].parent.is_some() {
185            Some(Ref::map(arena, |a| {
186                a[position.ptr].parent.as_ref().unwrap()
187            }))
188        } else {
189            None
190        }
191    }
192
193    pub fn children(&self, position: &Position) -> Ref<'_, Vec<Position>> {
194        assert!(self.is_token_valid(position), "Target handle is dead!"); // ????
195        Ref::map(self.arena.borrow(), |arena| &arena[position.ptr].children)
196    }
197
198    pub fn get_data<'a>(&'a self, position: &Position) -> Option<Ref<'a, T>> {
199        if !self.is_token_valid(position) {
200            return None;
201        }
202        Some(Ref::map(self.arena.borrow(), |arena| {
203            arena[position.ptr].data.as_ref().unwrap()
204        }))
205    }
206
207    pub fn add_child(&mut self, parent_pos: &Position, data: T) -> Position {
208        assert!(
209            parent_pos.ptr == 0 || self.is_token_valid(parent_pos),
210            "Target parent handle is dead!"
211        );
212
213        let mut arena = self.arena.borrow_mut();
214        let mut free_list = self.free_list.borrow_mut();
215
216        let (index, next_gen) = if let Some(reuse_idx) = free_list.pop() {
217            arena[reuse_idx].generation += 1;
218            let gen = arena[reuse_idx].generation;
219
220            arena[reuse_idx] = Node {
221                // Duplicate internally via component destructuring, never by copying the token object
222                parent: Some(Position::new(parent_pos.ptr, parent_pos.generation)),
223                children: Vec::new(),
224                data: Some(data),
225                generation: gen,
226            };
227            (reuse_idx, gen)
228        } else {
229            let new_idx = arena.len();
230            arena.push(Node {
231                parent: Some(Position::new(parent_pos.ptr, parent_pos.generation)),
232                children: Vec::new(),
233                data: Some(data),
234                generation: 0,
235            });
236            (new_idx, 0)
237        };
238
239        // Increment the list's size!
240        *self.size.get_mut() += 1;
241
242        arena[parent_pos.ptr]
243            .children
244            .push(Position::new(index, next_gen));
245        Position::new(index, next_gen)
246    }
247
248    /// Explicitly consumes the token, destroying it from the caller's frame permanently.
249    pub fn remove(&self, position: Position) -> Option<T> {
250        if !self.is_token_valid(&position) {
251            return None;
252        }
253
254        let mut arena = self.arena.borrow_mut();
255        let data = arena[position.ptr].data.take();
256        let parent_pos = arena[position.ptr].parent.take();
257
258        self.free_list.borrow_mut().push(position.ptr);
259
260        if let Some(parent) = parent_pos {
261            arena[parent.ptr].children.retain(|p| p.ptr != position.ptr);
262
263            let orphans = std::mem::take(&mut arena[position.ptr].children);
264            for child in orphans {
265                arena[child.ptr].parent = Some(Position::new(parent.ptr, parent.generation));
266                arena[parent.ptr].children.push(child);
267            }
268        }
269        data
270    }
271}
272
273#[cfg(test)]
274mod tests {
275
276    #[test]
277    /// TODO: actually test the structure's members!
278    fn atomic() {
279        use super::GenTree;
280        use crate::hierarchies::arena_gentree_builder::Heading;
281
282        let mut tree = GenTree::new();
283        // Instantiated tree automatically has a single, empty root node
284        // with a size of zero
285        assert_eq!(tree.size(), 0);
286        assert!(tree.is_empty());
287        let root = tree.root().clone();
288
289        let mut cursor = tree.add_child(
290            &root,
291            Heading {
292                level: 2,
293                title: "Landlocked".to_string(),
294            },
295        );
296        assert_eq!(tree.size(), 1);
297        assert!(!tree.is_empty());
298
299        cursor = tree.add_child(
300            &cursor,
301            Heading {
302                level: 3,
303                title: "Switzerland".to_string(),
304            },
305        );
306        cursor = tree.add_child(
307            &cursor,
308            Heading {
309                level: 4,
310                title: "Geneva".to_string(),
311            },
312        );
313        cursor = tree.add_child(
314            &cursor,
315            Heading {
316                level: 5,
317                title: "Old Town".to_string(),
318            },
319        );
320        //cursor = tree.parent(&cursor).expect(""); // Geneva
321        //cursor = tree.parent(&cursor).expect(""); // Switzerland
322        tree.add_child(
323            &cursor,
324            Heading {
325                level: 3,
326                title: "Botswana".to_string(),
327            },
328        );
329        assert_eq!(tree.size(), 5);
330
331        eprintln!("{tree:#?}");
332        //panic!("MANUAL TEST FAILURE");
333    }
334
335    #[test]
336    fn dangle() {
337        use crate::hierarchies::arena_gentree_builder::{construct, Heading};
338
339        use super::GenTree;
340        let one = vec![
341            Heading {
342                level: 1,
343                title: "Landlocked".to_string(),
344            },
345            Heading {
346                level: 2,
347                title: "Switzerland".to_string(),
348            },
349        ];
350        let two = vec![
351            Heading {
352                level: 1,
353                title: "Bolivia".to_string(),
354            },
355            Heading {
356                level: 2,
357                title: "Zimbabwe".to_string(),
358            },
359        ];
360
361        // Creates a tree, Position, and CursorMut
362        let outer_tree: GenTree<Heading> = construct(0, one);
363        //let outer_tree: GenTree<Heading> = construct_from(one);
364        let mut _pos = outer_tree.root();
365
366        {
367            let inner_tree: GenTree<Heading> = construct(0, two);
368            //let inner_tree: GenTree<Heading> = construct_from(two);
369            _pos = inner_tree.root();
370        } // inner_tree dropped here
371
372        // No UB (not possible) because inner_tree and _pos is already dropped
373        //let _oopsie = outer_tree.get_data(_pos);
374    }
375
376    use super::*;
377
378    #[test]
379    // Irrelevant because Position is move-only
380    fn test_aba_slot_recycling_isolation() {}
381
382    #[test]
383    fn test_parent_child_severance_on_remove() {}
384
385    #[test]
386    fn test_arena_churn_and_size_accounting() {}
387
388    #[test]
389    fn test_structural_queries_on_stale_positions() {}
390
391    #[test]
392    #[allow(unused)]
393    fn test_recycling_preserves_live_nodes() {
394        let mut tree = GenTree::new();
395        tree.mut_root("Root".to_string());
396
397        let root = tree.root(); // Immutable borrow
398
399        //let victim = tree.add_child(root, "Victim".to_string()); // Mutable borrow
400        //let survivor = tree.add_child(root, "Survivor".to_string()); // Mutable borrow
401
402        //tree.remove(victim);
403
404        //let replacement = tree.add_child(root, "Replacement".to_string()); // Mutable borrow
405
406        //assert_eq!(*tree.get_data(&survivor).unwrap(), "Survivor");
407        //assert_eq!(*tree.get_data(&replacement).unwrap(), "Replacement");
408    }
409}