Skip to main content

inkferro_core/dom/
arena.rs

1//! Arena: externally-indexed `Vec<Option<Node>>`.
2//!
3//! See module docs (`dom/mod.rs`) for the id-storage rationale.
4
5use super::node::Node;
6
7/// Slab storage for DOM nodes, indexed by externally-supplied `u32` ids.
8///
9/// `Vec<Option<Node>>` is grown on demand; the allocator trusts that the
10/// JS reconciler uses dense sequential ids.  An adversarial gap id just
11/// over-allocates — acceptable here.
12#[derive(Debug, Default)]
13pub struct Arena {
14    slots: Vec<Option<Node>>,
15}
16
17impl Arena {
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    /// Insert `node` at `id`, growing the backing vec if necessary.
23    /// If the slot is already occupied the call is a no-op (React allocates
24    /// each id exactly once; a double-create indicates a protocol error).
25    pub(crate) fn insert(&mut self, id: u32, node: Node) {
26        let idx = id as usize;
27        if idx >= self.slots.len() {
28            self.slots.resize_with(idx + 1, || None);
29        }
30        if self.slots[idx].is_none() {
31            self.slots[idx] = Some(node);
32        }
33    }
34
35    /// Drop the slot for `id`.  Does NOT recurse into children — see module
36    /// docs for the Free-cascade rationale.
37    pub(crate) fn free(&mut self, id: u32) {
38        if let Some(slot) = self.slots.get_mut(id as usize) {
39            *slot = None;
40        }
41    }
42
43    pub fn get(&self, id: u32) -> Option<&Node> {
44        self.slots.get(id as usize)?.as_ref()
45    }
46
47    pub(crate) fn get_mut(&mut self, id: u32) -> Option<&mut Node> {
48        self.slots.get_mut(id as usize)?.as_mut()
49    }
50
51    /// Return the number of live (non-None) slots.
52    #[cfg(test)]
53    pub fn live_count(&self) -> usize {
54        self.slots.iter().filter(|s| s.is_some()).count()
55    }
56}