Skip to main content

azul_core/
id.rs

1//! Node tree data structures and hierarchy management.
2//!
3//! This module provides the core data structures for managing DOM-like tree hierarchies:
4//!
5//! - `NodeId`: Type-safe node identifiers with Option<NodeId> optimization
6//! - `NodeHierarchy`: Parent-child relationships between nodes
7//! - `NodeDataContainer`: Generic storage for node data with efficient indexing
8//!
9//! # Memory Layout
10//!
11//! `NodeId` stores a plain `usize` index internally. For FFI structs that need
12//! `Option<NodeId>`, a manual 1-based encoding is used (0 = None, n > 0 = Some(n-1)).
13//!
14//! # Performance
15//!
16//! - Node lookups are O(1) via direct array indexing
17//! - Parent/child traversal is O(1) via pre-computed indices
18//! - No heap allocations after initial tree construction
19
20use alloc::vec::Vec;
21use core::{
22    ops::{Index, IndexMut},
23    slice::Iter,
24};
25
26pub use self::node_id::NodeId;
27use crate::styled_dom::NodeHierarchyItem;
28
29/// Type alias for depth-first traversal results: (depth, node_id) pairs
30pub type NodeDepths = Vec<(usize, NodeId)>;
31
32// Simple FFI-safe NodeId - just a wrapper around usize
33pub mod node_id {
34
35    use alloc::vec::Vec;
36    use core::{
37        fmt,
38        ops::{Add, AddAssign},
39    };
40
41    /// A type-safe identifier for a node within a DOM tree.
42    ///
43    /// `NodeId` is FFI-safe (`#[repr(C)]`) and stores a **zero-based** index internally.
44    /// Use `NodeId::index()` to get the array index for direct node access.
45    ///
46    /// # Zero-based indexing
47    ///
48    /// - `NodeId::new(0)` → first node (index 0)
49    /// - `NodeId::new(5)` → sixth node (index 5)
50    /// - Use `node_id.index()` to get the array index
51    ///
52    /// # FFI Encoding (for `Option<NodeId>`)
53    ///
54    /// When storing `Option<NodeId>` in FFI structs (like `NodeHierarchyItem`),
55    /// we use a **1-based encoding** to represent None:
56    ///
57    /// - `0` means `None` (no node)
58    /// - `n > 0` means `Some(NodeId(n - 1))`
59    ///
60    /// Use [`NodeId::from_usize`] to decode and [`NodeId::into_raw`] to encode.
61    /// See also: [`crate::styled_dom::NodeHierarchyItemId`] for the FFI wrapper type.
62    ///
63    /// # Warning
64    ///
65    /// **Never manually construct raw usize values for node hierarchy fields!**
66    /// Always use the provided `from_usize`/`into_raw` functions to avoid
67    /// off-by-one errors that can cause index-out-of-bounds panics.
68    ///
69    #[repr(C)]
70    #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
71    pub struct NodeId {
72        // Private field to prevent direct manipulation.
73        // Use NodeId::new() to create, NodeId::index() to read.
74        inner: usize,
75    }
76
77    impl NodeId {
78        /// The zero/first node ID (index 0).
79        pub const ZERO: NodeId = NodeId { inner: 0 };
80
81        /// Creates a new `NodeId` from a zero-based index.
82        #[inline(always)]
83        pub const fn new(value: usize) -> Self {
84            NodeId { inner: value }
85        }
86
87        /// Decodes a raw `usize` to `Option<NodeId>` using 1-based encoding.
88        ///
89        /// This is the inverse of [`NodeId::into_usize`].
90        ///
91        /// - `0` → `None` (no node)
92        /// - `n > 0` → `Some(NodeId(n - 1))`
93        ///
94        /// # Warning
95        ///
96        /// This function is for decoding values stored in FFI structs like
97        /// `NodeHierarchyItem`. Do not use raw usize values directly - always
98        /// decode them first!
99        #[inline]
100        pub const fn from_usize(value: usize) -> Option<Self> {
101            match value {
102                0 => None,
103                i => Some(NodeId { inner: i - 1 }),
104            }
105        }
106
107        /// Encodes `Option<NodeId>` to a raw `usize` for storage in FFI structs.
108        ///
109        /// - `None` → `0`
110        /// - `Some(NodeId(n))` → `n + 1`
111        ///
112        /// The returned value uses **1-based encoding**! A value of `0` means "no node",
113        /// NOT "node at index 0". Use [`NodeId::from_usize`] to decode.
114        ///
115        #[inline]
116        pub const fn into_raw(val: &Option<Self>) -> usize {
117            match val {
118                None => 0,
119                Some(s) => s.inner + 1,
120            }
121        }
122
123        /// Returns the **zero-based** index of this node.
124        ///
125        /// This is the actual array index where the node data is stored.
126        #[inline(always)]
127        pub const fn index(&self) -> usize {
128            self.inner
129        }
130    }
131
132    impl From<usize> for NodeId {
133        fn from(val: usize) -> Self {
134            NodeId::new(val)
135        }
136    }
137
138    impl From<NodeId> for usize {
139        fn from(val: NodeId) -> Self {
140            val.inner
141        }
142    }
143
144    impl Add<usize> for NodeId {
145        type Output = NodeId;
146        #[inline(always)]
147        fn add(self, other: usize) -> NodeId {
148            NodeId::new(self.inner + other)
149        }
150    }
151
152    impl AddAssign<usize> for NodeId {
153        #[inline(always)]
154        fn add_assign(&mut self, other: usize) {
155            *self = *self + other;
156        }
157    }
158
159    impl fmt::Display for NodeId {
160        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161            write!(f, "{}", self.inner)
162        }
163    }
164
165    impl fmt::Debug for NodeId {
166        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167            write!(f, "NodeId({})", self.inner)
168        }
169    }
170}
171
172/// Hierarchical information about a node (stores the indices of the parent / child nodes).
173#[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
174pub struct Node {
175    pub parent: Option<NodeId>,
176    pub previous_sibling: Option<NodeId>,
177    pub next_sibling: Option<NodeId>,
178    pub last_child: Option<NodeId>,
179    // NOTE: first_child can be calculated on the fly:
180    //
181    //   - if last_child is None, first_child is None
182    //   - if last_child is Some, first_child is parent_index + 1
183    //
184    // This makes the "Node" struct take up 4 registers instead of 5
185    //
186    // pub first_child: Option<NodeId>,
187}
188
189impl Node {
190    pub const ROOT: Node = Node {
191        parent: None,
192        previous_sibling: None,
193        next_sibling: None,
194        last_child: None,
195    };
196
197    #[inline]
198    pub const fn has_parent(&self) -> bool {
199        self.parent.is_some()
200    }
201    #[inline]
202    pub const fn has_previous_sibling(&self) -> bool {
203        self.previous_sibling.is_some()
204    }
205    #[inline]
206    pub const fn has_next_sibling(&self) -> bool {
207        self.next_sibling.is_some()
208    }
209    #[inline]
210    pub const fn has_first_child(&self) -> bool {
211        self.last_child.is_some() /* last_child and first_child are always set together */
212    }
213    #[inline]
214    pub const fn has_last_child(&self) -> bool {
215        self.last_child.is_some()
216    }
217
218    #[inline]
219    pub fn get_first_child(&self, current_node_id: NodeId) -> Option<NodeId> {
220        // last_child and first_child are always set together
221        self.last_child.map(|_| current_node_id + 1)
222    }
223}
224
225/// The hierarchy of nodes is stored separately from the actual node content in order
226/// to save on memory, since the hierarchy can be re-used across several DOM trees even
227/// if the content changes.
228#[derive(Debug, Default, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
229pub struct NodeHierarchy {
230    pub internal: Vec<Node>,
231}
232
233impl NodeHierarchy {
234    #[inline(always)]
235    pub const fn new(data: Vec<Node>) -> Self {
236        Self { internal: data }
237    }
238
239    #[inline(always)]
240    pub fn as_ref(&self) -> NodeHierarchyRef<'_> {
241        NodeHierarchyRef {
242            internal: &self.internal[..],
243        }
244    }
245
246}
247
248/// The hierarchy of nodes is stored separately from the actual node content in order
249/// to save on memory, since the hierarchy can be re-used across several DOM trees even
250/// if the content changes.
251#[derive(Debug, PartialEq, Hash, Eq)]
252pub struct NodeHierarchyRef<'a> {
253    pub internal: &'a [Node],
254}
255
256impl<'a> NodeHierarchyRef<'a> {
257    #[inline(always)]
258    pub fn from_slice(data: &'a [Node]) -> NodeHierarchyRef<'a> {
259        NodeHierarchyRef { internal: data }
260    }
261
262    #[inline(always)]
263    pub fn len(&self) -> usize {
264        self.internal.len()
265    }
266
267    #[inline(always)]
268    pub fn get(&self, id: NodeId) -> Option<&Node> {
269        self.internal.get(id.index())
270    }
271
272    #[inline(always)]
273    pub fn linear_iter(&self) -> LinearIterator {
274        LinearIterator {
275            arena_len: self.len(),
276            position: 0,
277        }
278    }
279
280    /// Returns the `(depth, NodeId)` of all parent nodes (i.e. nodes that have a
281    /// `first_child`), in depth sorted order, (i.e. `NodeId(0)` with a depth of 0) is
282    /// the first element.
283    ///
284    /// Runtime: O(n) max
285    pub fn get_parents_sorted_by_depth(&self) -> NodeDepths {
286        let mut non_leaf_nodes = Vec::new();
287        let mut current_children = vec![(0, NodeId::new(0))];
288        let mut next_children = Vec::new();
289        let mut depth = 1_usize;
290
291        loop {
292            for id in &current_children {
293                for child_id in id.1.children(self).filter(|id| self[*id].has_first_child()) {
294                    next_children.push((depth, child_id));
295                }
296            }
297
298            non_leaf_nodes.extend(&mut current_children.drain(..));
299
300            if next_children.is_empty() {
301                break;
302            } else {
303                current_children.extend(&mut next_children.drain(..));
304                depth += 1;
305            }
306        }
307
308        non_leaf_nodes
309    }
310
311}
312
313#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
314pub struct NodeDataContainer<T> {
315    pub internal: Vec<T>,
316}
317
318impl<T> From<Vec<T>> for NodeDataContainer<T> {
319    fn from(v: Vec<T>) -> NodeDataContainer<T> {
320        NodeDataContainer { internal: v }
321    }
322}
323
324#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
325pub struct NodeDataContainerRef<'a, T> {
326    pub internal: &'a [T],
327}
328
329#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
330pub struct NodeDataContainerRefMut<'a, T> {
331    pub internal: &'a mut [T],
332}
333
334impl<T> Default for NodeDataContainer<T> {
335    fn default() -> Self {
336        Self {
337            internal: Vec::new(),
338        }
339    }
340}
341
342impl<'a> Index<NodeId> for NodeHierarchyRef<'a> {
343    type Output = Node;
344
345    #[inline(always)]
346    fn index(&self, node_id: NodeId) -> &Node {
347        &self.internal[node_id.index()]
348    }
349}
350
351impl<T> NodeDataContainer<T> {
352    #[inline(always)]
353    pub const fn new(data: Vec<T>) -> Self {
354        Self { internal: data }
355    }
356
357    #[inline(always)]
358    pub fn is_empty(&self) -> bool {
359        self.internal.is_empty()
360    }
361
362    #[inline(always)]
363    pub fn as_ref(&self) -> NodeDataContainerRef<'_, T> {
364        NodeDataContainerRef {
365            internal: &self.internal[..],
366        }
367    }
368
369    #[inline(always)]
370    pub fn as_ref_mut(&mut self) -> NodeDataContainerRefMut<'_, T> {
371        NodeDataContainerRefMut {
372            internal: &mut self.internal[..],
373        }
374    }
375
376    #[inline(always)]
377    pub fn len(&self) -> usize {
378        self.internal.len()
379    }
380}
381
382impl<'a, T: 'a> NodeDataContainerRefMut<'a, T> {
383    #[inline(always)]
384    pub fn from_slice(data: &'a mut [T]) -> NodeDataContainerRefMut<'a, T> {
385        NodeDataContainerRefMut { internal: data }
386    }
387}
388
389impl<'a, T: 'a> NodeDataContainerRefMut<'a, T> {
390    #[inline(always)]
391    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
392        self.internal.get_mut(id.index())
393    }
394}
395
396impl<'a, T: Send + 'a> NodeDataContainerRef<'a, T> {
397    pub fn transform_nodeid_optional<U: Send, F: Send + Sync>(
398        &self,
399        closure: F,
400    ) -> NodeDataContainer<U>
401    where
402        F: Fn(NodeId) -> Option<U>,
403    {
404        let len = self.len();
405        NodeDataContainer {
406            internal: (0..len)
407                .filter_map(|node_id| closure(NodeId::new(node_id)))
408                .collect::<Vec<U>>(),
409        }
410    }
411}
412
413impl<'a, T: 'a> NodeDataContainerRef<'a, T> {
414    #[inline(always)]
415    pub fn from_slice(data: &'a [T]) -> NodeDataContainerRef<'a, T> {
416        NodeDataContainerRef { internal: data }
417    }
418
419    #[inline(always)]
420    pub fn len(&self) -> usize {
421        self.internal.len()
422    }
423
424    #[inline(always)]
425    pub fn get(&self, id: NodeId) -> Option<&T> {
426        self.internal.get(id.index())
427    }
428
429    #[inline(always)]
430    pub fn iter(&self) -> Iter<T> {
431        self.internal.iter()
432    }
433
434    #[inline(always)]
435    pub fn linear_iter(&self) -> LinearIterator {
436        LinearIterator {
437            arena_len: self.len(),
438            position: 0,
439        }
440    }
441}
442
443impl<'a, T> Index<NodeId> for NodeDataContainerRef<'a, T> {
444    type Output = T;
445
446    #[inline(always)]
447    fn index(&self, node_id: NodeId) -> &T {
448        &self.internal[node_id.index()]
449    }
450}
451
452impl<'a, T> Index<NodeId> for NodeDataContainerRefMut<'a, T> {
453    type Output = T;
454
455    #[inline(always)]
456    fn index(&self, node_id: NodeId) -> &T {
457        &self.internal[node_id.index()]
458    }
459}
460
461impl<'a, T> IndexMut<NodeId> for NodeDataContainerRefMut<'a, T> {
462    #[inline(always)]
463    fn index_mut(&mut self, node_id: NodeId) -> &mut T {
464        &mut self.internal[node_id.index()]
465    }
466}
467
468impl NodeId {
469    /// Return an iterator of references to this node and the siblings before it.
470    ///
471    /// Call `.next().unwrap()` once on the iterator to skip the node itself.
472    #[inline]
473    pub const fn preceding_siblings<'a>(
474        self,
475        node_hierarchy: &'a NodeHierarchyRef<'a>,
476    ) -> PrecedingSiblings<'a> {
477        PrecedingSiblings {
478            node_hierarchy,
479            node: Some(self),
480        }
481    }
482
483    /// Return an iterator of references to this node's children.
484    #[inline]
485    pub fn children<'a>(self, node_hierarchy: &'a NodeHierarchyRef<'a>) -> Children<'a> {
486        Children {
487            node_hierarchy,
488            node: node_hierarchy[self].get_first_child(self),
489        }
490    }
491}
492
493macro_rules! impl_node_iterator {
494    ($name:ident, $next:expr) => {
495        impl<'a> Iterator for $name<'a> {
496            type Item = NodeId;
497
498            fn next(&mut self) -> Option<NodeId> {
499                match self.node.take() {
500                    Some(node) => {
501                        self.node = $next(&self.node_hierarchy[node]);
502                        Some(node)
503                    }
504                    None => None,
505                }
506            }
507        }
508    };
509}
510
511/// An linear iterator, does not respect the DOM in any way,
512/// it just iterates over the nodes like a Vec
513#[derive(Debug, Copy, Clone)]
514pub struct LinearIterator {
515    arena_len: usize,
516    position: usize,
517}
518
519impl Iterator for LinearIterator {
520    type Item = NodeId;
521
522    fn next(&mut self) -> Option<NodeId> {
523        if self.arena_len < 1 || self.position > (self.arena_len - 1) {
524            None
525        } else {
526            let new_id = Some(NodeId::new(self.position));
527            self.position += 1;
528            new_id
529        }
530    }
531}
532
533/// An iterator of references to the siblings before a given node.
534pub struct PrecedingSiblings<'a> {
535    node_hierarchy: &'a NodeHierarchyRef<'a>,
536    node: Option<NodeId>,
537}
538
539impl_node_iterator!(PrecedingSiblings, |node: &Node| node.previous_sibling);
540
541/// Special iterator for using NodeDataContainerRef<AzNode> instead of NodeHierarchy
542pub struct AzChildren<'a> {
543    node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
544    node: Option<NodeId>,
545}
546
547impl<'a> Iterator for AzChildren<'a> {
548    type Item = NodeId;
549
550    fn next(&mut self) -> Option<NodeId> {
551        match self.node.take() {
552            Some(node) => {
553                self.node = self.node_hierarchy[node].next_sibling_id();
554                Some(node)
555            }
556            None => None,
557        }
558    }
559}
560
561/// Special iterator for using NodeDataContainerRef<AzNode> instead of NodeHierarchy
562pub struct AzReverseChildren<'a> {
563    node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
564    node: Option<NodeId>,
565}
566
567impl<'a> Iterator for AzReverseChildren<'a> {
568    type Item = NodeId;
569
570    fn next(&mut self) -> Option<NodeId> {
571        match self.node.take() {
572            Some(node) => {
573                self.node = self.node_hierarchy[node].previous_sibling_id();
574                Some(node)
575            }
576            None => None,
577        }
578    }
579}
580
581impl NodeId {
582    /// Traverse up through the hierarchy until a node matching the predicate is found.
583    ///
584    /// Necessary to resolve the last positioned (= relative)
585    /// element of an absolute node.
586    pub fn get_nearest_matching_parent<'a, F>(
587        self,
588        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
589        predicate: F,
590    ) -> Option<NodeId>
591    where
592        F: Fn(NodeId) -> bool,
593    {
594        let mut current_node = node_hierarchy[self].parent_id()?;
595        loop {
596            match predicate(current_node) {
597                true => {
598                    return Some(current_node);
599                }
600                false => {
601                    current_node = node_hierarchy[current_node].parent_id()?;
602                }
603            }
604        }
605    }
606
607    /// Return the children of this node (necessary for parallel iteration over children)
608    #[inline]
609    pub fn az_children_collect<'a>(
610        self,
611        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
612    ) -> Vec<NodeId> {
613        self.az_children(node_hierarchy).collect()
614    }
615
616    /// Return an iterator of references to this node's children.
617    #[inline]
618    pub fn az_children<'a>(
619        self,
620        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
621    ) -> AzChildren<'a> {
622        AzChildren {
623            node_hierarchy,
624            node: node_hierarchy[self].first_child_id(self),
625        }
626    }
627
628    /// Return an iterator of references to this node's children.
629    #[inline]
630    pub fn az_reverse_children<'a>(
631        self,
632        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
633    ) -> AzReverseChildren<'a> {
634        AzReverseChildren {
635            node_hierarchy,
636            node: node_hierarchy[self].last_child_id(),
637        }
638    }
639}
640
641/// An iterator of references to the children of a given node.
642pub struct Children<'a> {
643    node_hierarchy: &'a NodeHierarchyRef<'a>,
644    node: Option<NodeId>,
645}
646
647impl_node_iterator!(Children, |node: &Node| node.next_sibling);