Skip to main content

denise_ui/
arena.rs

1//! The generational arena the tree lives in.
2//!
3//! A `Vec` of slots and a free list threaded through the empty ones. A key is a
4//! slot's index and the generation the slot had when the key was issued; taking
5//! a value out bumps the generation, so every key to it goes stale at once and
6//! the next tenant of the slot is somebody no old key can name.
7//!
8//! This used to be the `slotmap` crate, and it was the last crate between the
9//! widget tree and a dependency list of nothing. The tree asks six things of
10//! its storage — put, take, look, look mutably, ask whether, count — which is
11//! not enough to be worth somebody else's build script.
12
13use alloc::vec::Vec;
14use core::num::NonZeroU32;
15use core::ops::{Index, IndexMut};
16
17/// Identifies a node for exactly as long as that node exists.
18///
19/// The generation in the key is the point: an application that keeps an id
20/// after removing the node gets `None` back, not somebody else's widget. That
21/// is also why the tree stores ids rather than references — parent-linked
22/// component graphs are what forced `Rc<RefCell<_>>` on CoreCanvas, and this
23/// is the replacement.
24#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct NodeId {
26    index: u32,
27    /// Never zero, so that `Option<NodeId>` is no bigger than a `NodeId` — the
28    /// tree holds a great many of those.
29    generation: NonZeroU32,
30}
31
32impl NodeId {
33    /// An id no arena ever issues: the index is one past the last a slot can have.
34    const NONE: Self = Self {
35        index: u32::MAX,
36        generation: NonZeroU32::MIN,
37    };
38
39    /// The key as a plain `u64`, for carrying across the C ABI: the generation
40    /// in the high half and the slot, counted from one, in the low half. Never
41    /// `0`, which is what the C side uses for "no node".
42    #[inline]
43    pub fn as_ffi(self) -> u64 {
44        (u64::from(self.generation.get()) << 32) | u64::from(self.index.wrapping_add(1))
45    }
46
47    /// Rebuilds a key from [`NodeId::as_ffi`]. A value that never came from there
48    /// simply fails to resolve.
49    #[inline]
50    pub fn from_ffi(value: u64) -> Self {
51        // Both halves are truncations on purpose: that is how the two were packed.
52        let (slot, generation) = (value as u32, (value >> 32) as u32);
53        match (slot.checked_sub(1), NonZeroU32::new(generation)) {
54            (Some(index), Some(generation)) => Self { index, generation },
55            _ => Self::NONE,
56        }
57    }
58}
59
60impl Default for NodeId {
61    /// An id that resolves to nothing, in any tree.
62    fn default() -> Self {
63        Self::NONE
64    }
65}
66
67impl core::fmt::Debug for NodeId {
68    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69        write!(f, "NodeId({}v{})", self.index, self.generation)
70    }
71}
72
73enum Entry<T> {
74    Occupied(T),
75    /// The next slot on the free list, if this is not the last of it.
76    Free(Option<u32>),
77}
78
79struct Slot<T> {
80    /// The generation of the key that names what is here now, or, when nothing
81    /// is, of the key the next value put here will get.
82    generation: NonZeroU32,
83    entry: Entry<T>,
84}
85
86pub(crate) struct Arena<T> {
87    slots: Vec<Slot<T>>,
88    free: Option<u32>,
89    len: usize,
90}
91
92impl<T> Arena<T> {
93    pub(crate) const fn new() -> Self {
94        Self {
95            slots: Vec::new(),
96            free: None,
97            len: 0,
98        }
99    }
100
101    pub(crate) fn len(&self) -> usize {
102        self.len
103    }
104
105    pub(crate) fn insert(&mut self, value: T) -> NodeId {
106        self.len += 1;
107        if let Some(index) = self.free {
108            let slot = &mut self.slots[index as usize];
109            let Entry::Free(next) = slot.entry else {
110                unreachable!("the free list only threads free slots");
111            };
112            self.free = next;
113            slot.entry = Entry::Occupied(value);
114            return NodeId {
115                index,
116                generation: slot.generation,
117            };
118        }
119        // `u32::MAX` itself is `NodeId::NONE`'s, so the last slot is the one before.
120        let index = u32::try_from(self.slots.len())
121            .ok()
122            .filter(|&index| index < u32::MAX)
123            .expect("a tree of four thousand million nodes");
124        let generation = NonZeroU32::MIN;
125        self.slots.push(Slot {
126            generation,
127            entry: Entry::Occupied(value),
128        });
129        NodeId { index, generation }
130    }
131
132    pub(crate) fn remove(&mut self, id: NodeId) -> Option<T> {
133        let slot = self.slots.get_mut(id.index as usize)?;
134        if slot.generation != id.generation || matches!(slot.entry, Entry::Free(_)) {
135            return None;
136        }
137        // A slot that has run out of generations is retired rather than wrapped
138        // round to ones it has already handed out: it stays empty and off the
139        // free list, and costs a few bytes. Wrapping is the one way a stale id
140        // could come to name a live node.
141        let entry = match slot.generation.checked_add(1) {
142            Some(next) => {
143                slot.generation = next;
144                Entry::Free(self.free.replace(id.index))
145            }
146            None => Entry::Free(None),
147        };
148        self.len -= 1;
149        match core::mem::replace(&mut slot.entry, entry) {
150            Entry::Occupied(value) => Some(value),
151            Entry::Free(_) => unreachable!("checked above"),
152        }
153    }
154
155    pub(crate) fn get(&self, id: NodeId) -> Option<&T> {
156        match self.slots.get(id.index as usize) {
157            Some(Slot {
158                generation,
159                entry: Entry::Occupied(value),
160            }) if *generation == id.generation => Some(value),
161            _ => None,
162        }
163    }
164
165    pub(crate) fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
166        match self.slots.get_mut(id.index as usize) {
167            Some(Slot {
168                generation,
169                entry: Entry::Occupied(value),
170            }) if *generation == id.generation => Some(value),
171            _ => None,
172        }
173    }
174
175    pub(crate) fn contains_key(&self, id: NodeId) -> bool {
176        self.get(id).is_some()
177    }
178}
179
180impl<T> Index<NodeId> for Arena<T> {
181    type Output = T;
182
183    fn index(&self, id: NodeId) -> &T {
184        self.get(id).expect("a node that is no longer in the tree")
185    }
186}
187
188impl<T> IndexMut<NodeId> for Arena<T> {
189    fn index_mut(&mut self, id: NodeId) -> &mut T {
190        self.get_mut(id)
191            .expect("a node that is no longer in the tree")
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn ffi_round_trip_preserves_identity() {
201        let mut arena = Arena::new();
202        let a = arena.insert(1);
203        let b = arena.insert(2);
204        assert_eq!(NodeId::from_ffi(a.as_ffi()), a);
205        assert_eq!(NodeId::from_ffi(b.as_ffi()), b);
206        assert_ne!(a.as_ffi(), b.as_ffi());
207    }
208
209    #[test]
210    fn the_first_id_is_the_one_c_callers_have_always_seen() {
211        // Generation one, slot one. Nobody should depend on it, and somebody does.
212        let mut arena = Arena::new();
213        assert_eq!(arena.insert(()).as_ffi(), 0x0000_0001_0000_0001);
214    }
215
216    #[test]
217    fn no_id_crosses_the_abi_as_zero_and_zero_names_nothing() {
218        let mut arena = Arena::new();
219        let first = arena.insert(1);
220        assert_ne!(first.as_ffi(), 0);
221        assert_eq!(arena.get(NodeId::from_ffi(0)), None);
222        assert_eq!(arena.get(NodeId::default()), None);
223    }
224
225    #[test]
226    fn nonsense_from_the_other_side_of_the_abi_resolves_to_nothing() {
227        let mut arena = Arena::new();
228        arena.insert(1);
229        for value in [
230            1,
231            1 << 32,
232            u64::MAX,
233            0xdead_beef_0000_0001,
234            0x0000_0001_0000_0009,
235        ] {
236            assert_eq!(arena.get(NodeId::from_ffi(value)), None, "{value:#x}");
237        }
238    }
239
240    #[test]
241    fn a_stale_id_does_not_resolve_to_the_next_node() {
242        let mut arena = Arena::new();
243        let a = arena.insert(1);
244        assert_eq!(arena.remove(a), Some(1));
245        let b = arena.insert(2);
246        assert_ne!(a, b);
247        assert_eq!(arena.get(a), None);
248        assert_eq!(arena.get_mut(a), None);
249        assert!(!arena.contains_key(a));
250        assert_eq!(arena.remove(a), None, "and cannot take the new tenant out");
251        assert_eq!(arena.get(b), Some(&2));
252    }
253
254    #[test]
255    fn a_vacated_slot_is_used_again_before_the_arena_grows() {
256        let mut arena = Arena::new();
257        let ids: Vec<NodeId> = (0..4).map(|n| arena.insert(n)).collect();
258        arena.remove(ids[1]);
259        arena.remove(ids[2]);
260        assert_eq!(arena.len(), 2);
261        let (c, d, e) = (arena.insert(10), arena.insert(11), arena.insert(12));
262        assert_eq!(arena.slots.len(), 5, "two reused, one new");
263        assert_eq!(arena.len(), 5);
264        assert_eq!((arena[c], arena[d], arena[e]), (10, 11, 12));
265        assert_eq!((arena[ids[0]], arena[ids[3]]), (0, 3));
266    }
267
268    #[test]
269    fn removing_twice_takes_nothing_the_second_time() {
270        let mut arena = Arena::new();
271        let a = arena.insert(1);
272        let b = arena.insert(2);
273        assert_eq!(arena.remove(a), Some(1));
274        assert_eq!(arena.remove(a), None);
275        assert_eq!(arena.len(), 1);
276        // The free list still holds the slot once, not twice.
277        let c = arena.insert(3);
278        let d = arena.insert(4);
279        assert_eq!((arena[b], arena[c], arena[d]), (2, 3, 4));
280    }
281
282    #[test]
283    fn a_slot_out_of_generations_is_retired_not_wrapped() {
284        let mut arena = Arena::new();
285        let first = arena.insert(1);
286        arena.remove(first);
287        arena.slots[0].generation = NonZeroU32::MAX;
288        let last = arena.insert(2);
289        assert_eq!(last.generation, NonZeroU32::MAX);
290        assert_eq!(arena.remove(last), Some(2));
291        assert_eq!(arena.get(last), None);
292        assert_eq!(arena.len(), 0);
293
294        let next = arena.insert(3);
295        assert_eq!(next.index, 1, "the worn-out slot is not handed out again");
296        assert_eq!(arena.get(first), None);
297        assert_eq!(arena.get(last), None);
298    }
299
300    #[test]
301    fn an_optional_id_is_no_bigger_than_an_id() {
302        assert_eq!(size_of::<Option<NodeId>>(), size_of::<NodeId>());
303        assert_eq!(size_of::<NodeId>(), 8);
304    }
305
306    #[test]
307    #[should_panic(expected = "no longer in the tree")]
308    fn indexing_with_a_stale_id_panics() {
309        let mut arena = Arena::new();
310        let a = arena.insert(1);
311        arena.remove(a);
312        let _ = arena[a];
313    }
314}