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

            
20
use alloc::vec::Vec;
21
use core::{
22
    ops::{Index, IndexMut},
23
    slice::Iter,
24
};
25

            
26
pub use self::node_id::NodeId;
27
use crate::styled_dom::NodeHierarchyItem;
28

            
29
/// Type alias for depth-first traversal results: (depth, `node_id`) pairs
30
pub type NodeDepths = Vec<(usize, NodeId)>;
31

            
32
// Simple FFI-safe NodeId - just a wrapper around usize
33
pub 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: Self = Self { inner: 0 };
80

            
81
        /// Creates a new `NodeId` from a zero-based index.
82
        #[inline]
83
24691692
        #[must_use] pub const fn new(value: usize) -> Self {
84
24691692
            Self { inner: value }
85
24691692
        }
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
124928393
        #[must_use] pub const fn from_usize(value: usize) -> Option<Self> {
101
124928393
            match value {
102
7479365
                0 => None,
103
117449028
                i => Some(Self { inner: i - 1 }),
104
            }
105
124928393
        }
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
4326457
        #[must_use] pub const fn into_raw(val: &Option<Self>) -> usize {
117
4326457
            match val {
118
1036637
                None => 0,
119
3289820
                Some(s) => s.inner + 1,
120
            }
121
4326457
        }
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]
127
289000924
        #[must_use] pub const fn index(&self) -> usize {
128
289000924
            self.inner
129
289000924
        }
130
    }
131

            
132
    impl From<usize> for NodeId {
133
3
        fn from(val: usize) -> Self {
134
3
            Self::new(val)
135
3
        }
136
    }
137

            
138
    impl From<NodeId> for usize {
139
3
        fn from(val: NodeId) -> Self {
140
3
            val.inner
141
3
        }
142
    }
143

            
144
    impl Add<usize> for NodeId {
145
        type Output = Self;
146
        /// AUDIT: saturating add. A raw `self.inner + other` could overflow
147
        /// (debug panic / release wrap to a bogus small index that then aliases
148
        /// a real node). `NodeId` indices are bounded by the arena length, so a
149
        /// saturation to `usize::MAX` is an obviously-invalid index that fails
150
        /// loudly at the next bounds-checked access rather than silently aliasing.
151
        #[inline]
152
5549867
        fn add(self, other: usize) -> Self {
153
5549867
            Self::new(self.inner.saturating_add(other))
154
5549867
        }
155
    }
156

            
157
    impl AddAssign<usize> for NodeId {
158
        /// AUDIT: saturating add — see [`Add`] impl above.
159
        #[inline]
160
1
        fn add_assign(&mut self, other: usize) {
161
1
            *self = *self + other;
162
1
        }
163
    }
164

            
165
    impl fmt::Display for NodeId {
166
40
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167
40
            write!(f, "{}", self.inner)
168
40
        }
169
    }
170

            
171
    impl fmt::Debug for NodeId {
172
2007271
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173
2007271
            write!(f, "NodeId({})", self.inner)
174
2007271
        }
175
    }
176
}
177

            
178
/// Hierarchical information about a node (stores the indices of the parent / child nodes).
179
#[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
180
pub struct Node {
181
    pub parent: Option<NodeId>,
182
    pub previous_sibling: Option<NodeId>,
183
    pub next_sibling: Option<NodeId>,
184
    pub last_child: Option<NodeId>,
185
    // NOTE: first_child can be calculated on the fly:
186
    //
187
    //   - if last_child is None, first_child is None
188
    //   - if last_child is Some, first_child is parent_index + 1
189
    //
190
    // This makes the "Node" struct take up 4 registers instead of 5
191
    //
192
    // pub first_child: Option<NodeId>,
193
}
194

            
195
impl Node {
196
    pub const ROOT: Self = Self {
197
        parent: None,
198
        previous_sibling: None,
199
        next_sibling: None,
200
        last_child: None,
201
    };
202

            
203
    #[inline]
204
3
    #[must_use] pub const fn has_parent(&self) -> bool {
205
3
        self.parent.is_some()
206
3
    }
207
    #[inline]
208
3
    #[must_use] pub const fn has_previous_sibling(&self) -> bool {
209
3
        self.previous_sibling.is_some()
210
3
    }
211
    #[inline]
212
3
    #[must_use] pub const fn has_next_sibling(&self) -> bool {
213
3
        self.next_sibling.is_some()
214
3
    }
215
    #[inline]
216
690219
    #[must_use] pub const fn has_first_child(&self) -> bool {
217
690219
        self.last_child.is_some() /* last_child and first_child are always set together */
218
690219
    }
219
    #[inline]
220
6
    #[must_use] pub const fn has_last_child(&self) -> bool {
221
6
        self.last_child.is_some()
222
6
    }
223

            
224
    #[inline]
225
716887
    #[must_use] pub fn get_first_child(&self, current_node_id: NodeId) -> Option<NodeId> {
226
        // last_child and first_child are always set together
227
716887
        self.last_child.map(|_| current_node_id + 1)
228
716887
    }
229
}
230

            
231
/// The hierarchy of nodes is stored separately from the actual node content in order
232
/// to save on memory, since the hierarchy can be re-used across several DOM trees even
233
/// if the content changes.
234
#[derive(Debug, Default, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
235
pub struct NodeHierarchy {
236
    pub internal: Vec<Node>,
237
}
238

            
239
impl NodeHierarchy {
240
    #[inline]
241
67
    #[must_use] pub const fn new(data: Vec<Node>) -> Self {
242
67
        Self { internal: data }
243
67
    }
244

            
245
    #[inline]
246
139930
    #[must_use] pub fn as_ref(&self) -> NodeHierarchyRef<'_> {
247
139930
        NodeHierarchyRef {
248
139930
            internal: &self.internal[..],
249
139930
        }
250
139930
    }
251

            
252
}
253

            
254
/// The hierarchy of nodes is stored separately from the actual node content in order
255
/// to save on memory, since the hierarchy can be re-used across several DOM trees even
256
/// if the content changes.
257
#[derive(Debug, PartialEq, Hash, Eq)]
258
pub struct NodeHierarchyRef<'a> {
259
    pub internal: &'a [Node],
260
}
261

            
262
impl<'a> NodeHierarchyRef<'a> {
263
    #[inline]
264
64
    #[must_use] pub const fn from_slice(data: &'a [Node]) -> Self {
265
64
        NodeHierarchyRef { internal: data }
266
64
    }
267

            
268
    #[inline]
269
71413
    #[must_use] pub const fn len(&self) -> usize {
270
71413
        self.internal.len()
271
71413
    }
272

            
273
    #[inline]
274
35812
    #[must_use] pub const fn is_empty(&self) -> bool {
275
35812
        self.internal.is_empty()
276
35812
    }
277

            
278
    #[inline]
279
5201
    #[must_use] pub fn get(&self, id: NodeId) -> Option<&Node> {
280
5201
        self.internal.get(id.index())
281
5201
    }
282

            
283
    #[inline]
284
89
    #[must_use] pub const fn linear_iter(&self) -> LinearIterator {
285
89
        LinearIterator {
286
89
            arena_len: self.len(),
287
89
            position: 0,
288
89
        }
289
89
    }
290

            
291
    /// Returns the `(depth, NodeId)` of all parent nodes (i.e. nodes that have a
292
    /// `first_child`), in depth sorted order, (i.e. `NodeId(0)` with a depth of 0) is
293
    /// the first element.
294
    ///
295
    /// Runtime: O(n) max
296
    // the `.drain(..)` calls intentionally empty current/next_children to REUSE
297
    // their allocations across the BFS levels; `into_iter()` would move them.
298
    #[allow(clippy::iter_with_drain)]
299
35804
    #[must_use] pub fn get_parents_sorted_by_depth(&self) -> NodeDepths {
300
        // AUDIT: an empty hierarchy has no root node — indexing `internal[0]`
301
        // (via `self[root]` below) would panic. Bail out early.
302
35804
        if self.is_empty() {
303
68
            return Vec::new();
304
35736
        }
305

            
306
35736
        let root = NodeId::new(0);
307
35736
        let mut non_leaf_nodes = Vec::new();
308

            
309
        // AUDIT: a childless root (e.g. a single-node DOM) is a LEAF, not a
310
        // parent. The old code seeded `current_children` with the root and
311
        // unconditionally pushed it into `non_leaf_nodes`, mislabeling it as a
312
        // parent. Only descend (and only emit the root) when it actually has a
313
        // first child.
314
35736
        if !self[root].has_first_child() {
315
3407
            return non_leaf_nodes;
316
32329
        }
317

            
318
32329
        let mut current_children = vec![(0, root)];
319
32329
        let mut next_children = Vec::new();
320
32329
        let mut depth = 1_usize;
321

            
322
        loop {
323
449356
            for id in &current_children {
324
654474
                for child_id in id.1.children(self).filter(|id| self[*id].has_first_child()) {
325
326111
                    next_children.push((depth, child_id));
326
326111
                }
327
            }
328

            
329
90916
            non_leaf_nodes.extend(&mut current_children.drain(..));
330

            
331
90916
            if next_children.is_empty() {
332
32329
                break;
333
58587
            }
334
58587
            current_children.extend(&mut next_children.drain(..));
335
58587
            depth += 1;
336
        }
337

            
338
32329
        non_leaf_nodes
339
35804
    }
340

            
341
}
342

            
343
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
344
pub struct NodeDataContainer<T> {
345
    pub internal: Vec<T>,
346
}
347

            
348
impl<T> From<Vec<T>> for NodeDataContainer<T> {
349
1
    fn from(v: Vec<T>) -> Self {
350
1
        Self { internal: v }
351
1
    }
352
}
353

            
354
#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
355
pub struct NodeDataContainerRef<'a, T> {
356
    pub internal: &'a [T],
357
}
358

            
359
#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
360
pub struct NodeDataContainerRefMut<'a, T> {
361
    pub internal: &'a mut [T],
362
}
363

            
364
impl<T> Default for NodeDataContainer<T> {
365
1
    fn default() -> Self {
366
1
        Self {
367
1
            internal: Vec::new(),
368
1
        }
369
1
    }
370
}
371

            
372
impl Index<NodeId> for NodeHierarchyRef<'_> {
373
    type Output = Node;
374

            
375
    #[inline]
376
10922728
    fn index(&self, node_id: NodeId) -> &Node {
377
10922728
        &self.internal[node_id.index()]
378
10922728
    }
379
}
380

            
381
impl<T> NodeDataContainer<T> {
382
    #[inline]
383
11
    #[must_use] pub const fn new(data: Vec<T>) -> Self {
384
11
        Self { internal: data }
385
11
    }
386

            
387
    #[inline]
388
6
    #[must_use] pub const fn is_empty(&self) -> bool {
389
6
        self.internal.is_empty()
390
6
    }
391

            
392
    #[inline]
393
298309
    #[must_use] pub fn as_ref(&self) -> NodeDataContainerRef<'_, T> {
394
298309
        NodeDataContainerRef {
395
298309
            internal: &self.internal[..],
396
298309
        }
397
298309
    }
398

            
399
    #[inline]
400
1
    pub fn as_ref_mut(&mut self) -> NodeDataContainerRefMut<'_, T> {
401
1
        NodeDataContainerRefMut {
402
1
            internal: &mut self.internal[..],
403
1
        }
404
1
    }
405

            
406
    #[inline]
407
35435
    #[must_use] pub const fn len(&self) -> usize {
408
35435
        self.internal.len()
409
35435
    }
410
}
411

            
412
impl<'a, T: 'a> NodeDataContainerRefMut<'a, T> {
413
    #[inline]
414
2
    pub const fn from_slice(data: &'a mut [T]) -> Self {
415
2
        NodeDataContainerRefMut { internal: data }
416
2
    }
417
}
418

            
419
impl<'a, T: 'a> NodeDataContainerRefMut<'a, T> {
420
    #[inline]
421
4
    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
422
4
        self.internal.get_mut(id.index())
423
4
    }
424
}
425

            
426
impl<'a, T: Send + 'a> NodeDataContainerRef<'a, T> {
427
13429
    pub fn transform_nodeid_optional<U: Send, F>(
428
13429
        &self,
429
13429
        closure: F,
430
13429
    ) -> NodeDataContainer<U>
431
13429
    where
432
13429
        F: Send + Sync + Fn(NodeId) -> Option<U>,
433
    {
434
13429
        let len = self.len();
435
        NodeDataContainer {
436
13429
            internal: (0..len)
437
216164
                .filter_map(|node_id| closure(NodeId::new(node_id)))
438
13429
                .collect::<Vec<U>>(),
439
        }
440
13429
    }
441
}
442

            
443
impl<'a, T> IntoIterator for &NodeDataContainerRef<'a, T> {
444
    type Item = &'a T;
445
    type IntoIter = Iter<'a, T>;
446
    #[inline]
447
1
    fn into_iter(self) -> Self::IntoIter {
448
1
        self.internal.iter()
449
1
    }
450
}
451

            
452
impl<'a, T: 'a> NodeDataContainerRef<'a, T> {
453
    #[inline]
454
125
    pub const fn from_slice(data: &'a [T]) -> Self {
455
125
        NodeDataContainerRef { internal: data }
456
125
    }
457

            
458
    #[inline]
459
2083642
    #[must_use] pub const fn len(&self) -> usize {
460
2083642
        self.internal.len()
461
2083642
    }
462

            
463
    #[inline]
464
6
    #[must_use] pub const fn is_empty(&self) -> bool {
465
6
        self.internal.is_empty()
466
6
    }
467

            
468
    #[inline]
469
27192016
    #[must_use] pub fn get(&self, id: NodeId) -> Option<&T> {
470
27192016
        self.internal.get(id.index())
471
27192016
    }
472

            
473
    #[inline]
474
20
    pub fn iter(&self) -> Iter<'_, T> {
475
20
        self.internal.iter()
476
20
    }
477

            
478
    #[inline]
479
18
    #[must_use] pub const fn linear_iter(&self) -> LinearIterator {
480
18
        LinearIterator {
481
18
            arena_len: self.len(),
482
18
            position: 0,
483
18
        }
484
18
    }
485
}
486

            
487
impl<T> Index<NodeId> for NodeDataContainerRef<'_, T> {
488
    type Output = T;
489

            
490
    #[inline]
491
71965117
    fn index(&self, node_id: NodeId) -> &T {
492
71965117
        &self.internal[node_id.index()]
493
71965117
    }
494
}
495

            
496
impl<T> Index<NodeId> for NodeDataContainerRefMut<'_, T> {
497
    type Output = T;
498

            
499
    #[inline]
500
163
    fn index(&self, node_id: NodeId) -> &T {
501
163
        &self.internal[node_id.index()]
502
163
    }
503
}
504

            
505
impl<T> IndexMut<NodeId> for NodeDataContainerRefMut<'_, T> {
506
    #[inline]
507
1463
    fn index_mut(&mut self, node_id: NodeId) -> &mut T {
508
1463
        &mut self.internal[node_id.index()]
509
1463
    }
510
}
511

            
512
impl NodeId {
513
    /// Return an iterator of references to this node and the siblings before it.
514
    ///
515
    /// Call `.next().unwrap()` once on the iterator to skip the node itself.
516
    #[inline]
517
358440
    #[must_use] pub const fn preceding_siblings<'a>(
518
358440
        self,
519
358440
        node_hierarchy: &'a NodeHierarchyRef<'a>,
520
358440
    ) -> PrecedingSiblings<'a> {
521
358440
        PrecedingSiblings {
522
358440
            node_hierarchy,
523
358440
            node: Some(self),
524
358440
        }
525
358440
    }
526

            
527
    /// Return an iterator of references to this node's children.
528
    #[inline]
529
716882
    #[must_use] pub fn children<'a>(self, node_hierarchy: &'a NodeHierarchyRef<'a>) -> Children<'a> {
530
716882
        Children {
531
716882
            node_hierarchy,
532
716882
            node: node_hierarchy[self].get_first_child(self),
533
716882
        }
534
716882
    }
535
}
536

            
537
macro_rules! impl_node_iterator {
538
    ($name:ident, $next:expr) => {
539
        impl Iterator for $name<'_> {
540
            type Item = NodeId;
541

            
542
9751812
            fn next(&mut self) -> Option<NodeId> {
543
9751812
                match self.node.take() {
544
8676492
                    Some(node) => {
545
8676492
                        self.node = $next(&self.node_hierarchy[node]);
546
8676492
                        Some(node)
547
                    }
548
1075320
                    None => None,
549
                }
550
9751812
            }
551
        }
552
    };
553
}
554

            
555
/// An linear iterator, does not respect the DOM in any way,
556
/// it just iterates over the nodes like a Vec
557
#[derive(Debug, Clone)]
558
pub struct LinearIterator {
559
    arena_len: usize,
560
    position: usize,
561
}
562

            
563
impl Iterator for LinearIterator {
564
    type Item = NodeId;
565

            
566
696
    fn next(&mut self) -> Option<NodeId> {
567
696
        if self.arena_len < 1 || self.position > (self.arena_len - 1) {
568
108
            None
569
        } else {
570
588
            let new_id = Some(NodeId::new(self.position));
571
588
            self.position += 1;
572
588
            new_id
573
        }
574
696
    }
575
}
576

            
577
/// An iterator of references to the siblings before a given node.
578
#[derive(Debug)]
579
pub struct PrecedingSiblings<'a> {
580
    node_hierarchy: &'a NodeHierarchyRef<'a>,
581
    node: Option<NodeId>,
582
}
583

            
584
impl_node_iterator!(PrecedingSiblings, |node: &Node| node.previous_sibling);
585

            
586
/// Special iterator for using `NodeDataContainerRef`<AzNode> instead of `NodeHierarchy`
587
#[derive(Debug)]
588
pub struct AzChildren<'a> {
589
    node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
590
    node: Option<NodeId>,
591
}
592

            
593
impl Iterator for AzChildren<'_> {
594
    type Item = NodeId;
595

            
596
1247508
    fn next(&mut self) -> Option<NodeId> {
597
1247508
        match self.node.take() {
598
668770
            Some(node) => {
599
668770
                self.node = self.node_hierarchy[node].next_sibling_id();
600
668770
                Some(node)
601
            }
602
578738
            None => None,
603
        }
604
1247508
    }
605
}
606

            
607
/// Special iterator for using `NodeDataContainerRef`<AzNode> instead of `NodeHierarchy`
608
#[derive(Debug)]
609
pub struct AzReverseChildren<'a> {
610
    node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
611
    node: Option<NodeId>,
612
}
613

            
614
impl Iterator for AzReverseChildren<'_> {
615
    type Item = NodeId;
616

            
617
4
    fn next(&mut self) -> Option<NodeId> {
618
4
        match self.node.take() {
619
2
            Some(node) => {
620
2
                self.node = self.node_hierarchy[node].previous_sibling_id();
621
2
                Some(node)
622
            }
623
2
            None => None,
624
        }
625
4
    }
626
}
627

            
628
impl NodeId {
629
    /// Traverse up through the hierarchy until a node matching the predicate is found.
630
    ///
631
    /// Necessary to resolve the last positioned (= relative)
632
    /// element of an absolute node.
633
11
    pub fn get_nearest_matching_parent<'a, F>(
634
11
        self,
635
11
        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
636
11
        predicate: F,
637
11
    ) -> Option<Self>
638
11
    where
639
11
        F: Fn(Self) -> bool,
640
    {
641
        // AUDIT: guard against (a) an out-of-bounds `self` and (b) a cycle in a
642
        // corrupt hierarchy (a `parent_id` that points back down into a
643
        // descendant). Use checked `get` and cap the walk at the node count —
644
        // a valid parent chain can never be longer than the number of nodes.
645
11
        let node_count = node_hierarchy.internal.len();
646
11
        let mut current_node = node_hierarchy.internal.get(self.index())?.parent_id()?;
647
8
        for _ in 0..node_count {
648
14
            if predicate(current_node) {
649
4
                return Some(current_node);
650
10
            }
651
10
            current_node = node_hierarchy.internal.get(current_node.index())?.parent_id()?;
652
        }
653
2
        None
654
11
    }
655

            
656
    /// Return the children of this node (necessary for parallel iteration over children)
657
    #[inline]
658
2
    #[must_use] pub fn az_children_collect<'a>(
659
2
        self,
660
2
        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
661
2
    ) -> Vec<Self> {
662
2
        self.az_children(node_hierarchy).collect()
663
2
    }
664

            
665
    /// Return an iterator of references to this node's children.
666
    #[inline]
667
632793
    #[must_use] pub fn az_children<'a>(
668
632793
        self,
669
632793
        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
670
632793
    ) -> AzChildren<'a> {
671
632793
        AzChildren {
672
632793
            node_hierarchy,
673
632793
            node: node_hierarchy[self].first_child_id(self),
674
632793
        }
675
632793
    }
676

            
677
    /// Return an iterator of references to this node's children.
678
    #[inline]
679
2
    #[must_use] pub fn az_reverse_children<'a>(
680
2
        self,
681
2
        node_hierarchy: &'a NodeDataContainerRef<'a, NodeHierarchyItem>,
682
2
    ) -> AzReverseChildren<'a> {
683
2
        AzReverseChildren {
684
2
            node_hierarchy,
685
2
            node: node_hierarchy[self].last_child_id(),
686
2
        }
687
2
    }
688
}
689

            
690
/// An iterator of references to the children of a given node.
691
#[derive(Debug)]
692
pub struct Children<'a> {
693
    node_hierarchy: &'a NodeHierarchyRef<'a>,
694
    node: Option<NodeId>,
695
}
696

            
697
impl_node_iterator!(Children, |node: &Node| node.next_sibling);
698

            
699
#[cfg(test)]
700
mod audit_tests {
701
    use super::*;
702
    use crate::styled_dom::NodeHierarchyItem;
703

            
704
    #[test]
705
1
    fn parents_by_depth_empty_hierarchy() {
706
1
        let h = NodeHierarchy::new(Vec::new());
707
1
        assert!(h.as_ref().get_parents_sorted_by_depth().is_empty());
708
1
    }
709

            
710
    #[test]
711
1
    fn parents_by_depth_single_childless_root() {
712
        // A single-node DOM: the root is a LEAF, not a parent.
713
1
        let h = NodeHierarchy::new(vec![Node::ROOT]);
714
1
        assert!(h.as_ref().get_parents_sorted_by_depth().is_empty());
715
1
    }
716

            
717
    #[test]
718
1
    fn parents_by_depth_root_with_child() {
719
1
        let root = Node {
720
1
            parent: None,
721
1
            previous_sibling: None,
722
1
            next_sibling: None,
723
1
            last_child: Some(NodeId::new(1)),
724
1
        };
725
1
        let child = Node {
726
1
            parent: Some(NodeId::new(0)),
727
1
            ..Node::ROOT
728
1
        };
729
1
        let h = NodeHierarchy::new(vec![root, child]);
730
1
        let parents = h.as_ref().get_parents_sorted_by_depth();
731
1
        assert_eq!(parents, vec![(0, NodeId::new(0))]);
732
1
    }
733

            
734
6
    fn item(parent: Option<usize>) -> NodeHierarchyItem {
735
        NodeHierarchyItem {
736
6
            parent: parent.map_or(0, |p| p + 1),
737
            previous_sibling: 0,
738
            next_sibling: 0,
739
            last_child: 0,
740
        }
741
6
    }
742

            
743
    #[test]
744
1
    fn nearest_matching_parent_cycle_terminates() {
745
        // node1.parent = 2, node2.parent = 1 — cyclic, must not hang.
746
1
        let items = vec![item(None), item(Some(2)), item(Some(1))];
747
1
        let cont = NodeDataContainerRef { internal: &items };
748
1
        let r = NodeId::new(1).get_nearest_matching_parent(&cont, |_| false);
749
1
        assert_eq!(r, None);
750
1
    }
751

            
752
    #[test]
753
1
    fn nearest_matching_parent_finds_match() {
754
        // 0 <- 1 <- 2 ; from 2, find the root (index 0).
755
1
        let items = vec![item(None), item(Some(0)), item(Some(1))];
756
1
        let cont = NodeDataContainerRef { internal: &items };
757
2
        let r = NodeId::new(2).get_nearest_matching_parent(&cont, |n| n == NodeId::new(0));
758
1
        assert_eq!(r, Some(NodeId::new(0)));
759
1
    }
760

            
761
    #[test]
762
1
    fn node_id_add_saturates() {
763
1
        assert_eq!(NodeId::new(5) + 3, NodeId::new(8));
764
1
        assert_eq!(NodeId::new(usize::MAX) + 1, NodeId::new(usize::MAX));
765
1
        let mut n = NodeId::new(usize::MAX);
766
1
        n += 10;
767
1
        assert_eq!(n, NodeId::new(usize::MAX));
768
1
    }
769
}
770

            
771
#[cfg(test)]
772
#[allow(clippy::pedantic, clippy::nursery)]
773
mod autotest_generated {
774
    use core::sync::atomic::{AtomicUsize, Ordering};
775

            
776
    use super::*;
777
    use crate::styled_dom::NodeHierarchyItem;
778

            
779
    // ---------------------------------------------------------------------
780
    // helpers
781
    // ---------------------------------------------------------------------
782

            
783
    /// A well-formed 5-node tree (children are always contiguous after the
784
    /// parent, as the `first_child = parent + 1` design requires):
785
    ///
786
    /// ```text
787
    /// 0 ── 1 ── 2
788
    ///  │    └── 3
789
    ///  └── 4
790
    /// ```
791
    fn tree_5() -> Vec<Node> {
792
        vec![
793
            Node {
794
                parent: None,
795
                previous_sibling: None,
796
                next_sibling: None,
797
                last_child: Some(NodeId::new(4)),
798
            },
799
            Node {
800
                parent: Some(NodeId::new(0)),
801
                previous_sibling: None,
802
                next_sibling: Some(NodeId::new(4)),
803
                last_child: Some(NodeId::new(3)),
804
            },
805
            Node {
806
                parent: Some(NodeId::new(1)),
807
                previous_sibling: None,
808
                next_sibling: Some(NodeId::new(3)),
809
                last_child: None,
810
            },
811
            Node {
812
                parent: Some(NodeId::new(1)),
813
                previous_sibling: Some(NodeId::new(2)),
814
                next_sibling: None,
815
                last_child: None,
816
            },
817
            Node {
818
                parent: Some(NodeId::new(0)),
819
                previous_sibling: Some(NodeId::new(1)),
820
                next_sibling: None,
821
                last_child: None,
822
            },
823
        ]
824
    }
825

            
826
    fn items(nodes: &[Node]) -> Vec<NodeHierarchyItem> {
827
        nodes.iter().copied().map(NodeHierarchyItem::from).collect()
828
    }
829

            
830
    // ---------------------------------------------------------------------
831
    // NodeId::new / index / ZERO  (constructor + getter)
832
    // ---------------------------------------------------------------------
833

            
834
    #[test]
835
    fn node_id_new_roundtrips_index_at_boundaries() {
836
        for v in [0_usize, 1, 2, 42, usize::MAX - 1, usize::MAX] {
837
            assert_eq!(NodeId::new(v).index(), v, "index() must echo new()");
838
        }
839
    }
840

            
841
    #[test]
842
    fn node_id_zero_matches_new_zero() {
843
        assert_eq!(NodeId::ZERO, NodeId::new(0));
844
        assert_eq!(NodeId::ZERO.index(), 0);
845
    }
846

            
847
    #[test]
848
    fn node_id_usize_conversions_are_lossless() {
849
        for v in [0_usize, 7, usize::MAX] {
850
            let id: NodeId = v.into();
851
            let back: usize = id.into();
852
            assert_eq!(back, v);
853
        }
854
    }
855

            
856
    #[test]
857
    fn node_id_ordering_follows_index() {
858
        assert!(NodeId::new(0) < NodeId::new(1));
859
        assert!(NodeId::new(1) < NodeId::new(usize::MAX));
860
        assert_eq!(NodeId::new(3), NodeId::new(3));
861
    }
862

            
863
    // ---------------------------------------------------------------------
864
    // NodeId::from_usize / into_raw  (1-based FFI encoding round-trip)
865
    // ---------------------------------------------------------------------
866

            
867
    #[test]
868
    fn from_usize_zero_is_none_and_shifts_by_one() {
869
        assert_eq!(NodeId::from_usize(0), None);
870
        assert_eq!(NodeId::from_usize(1), Some(NodeId::new(0)));
871
        assert_eq!(NodeId::from_usize(2), Some(NodeId::new(1)));
872
        // usize::MAX must NOT overflow the `i - 1` decode.
873
        assert_eq!(
874
            NodeId::from_usize(usize::MAX),
875
            Some(NodeId::new(usize::MAX - 1))
876
        );
877
    }
878

            
879
    #[test]
880
    fn into_raw_encodes_none_as_zero() {
881
        assert_eq!(NodeId::into_raw(&None), 0);
882
        assert_eq!(NodeId::into_raw(&Some(NodeId::new(0))), 1);
883
        assert_eq!(NodeId::into_raw(&Some(NodeId::new(41))), 42);
884
        // Largest index that survives the +1 encode without overflowing.
885
        assert_eq!(NodeId::into_raw(&Some(NodeId::new(usize::MAX - 1))), usize::MAX);
886
    }
887

            
888
    #[test]
889
    fn encode_decode_roundtrip_is_identity() {
890
        // decode(encode(x)) == x
891
        for x in [
892
            None,
893
            Some(NodeId::new(0)),
894
            Some(NodeId::new(1)),
895
            Some(NodeId::new(9_999)),
896
            Some(NodeId::new(usize::MAX - 1)),
897
        ] {
898
            assert_eq!(NodeId::from_usize(NodeId::into_raw(&x)), x, "decode(encode({x:?}))");
899
        }
900
        // encode(decode(n)) == n
901
        for n in [0_usize, 1, 2, 12_345, usize::MAX] {
902
            assert_eq!(NodeId::into_raw(&NodeId::from_usize(n)), n, "encode(decode({n}))");
903
        }
904
    }
905

            
906
    /// BOUNDARY: `NodeId::new(usize::MAX)` is the one index that cannot be
907
    /// encoded — `into_raw` computes `inner + 1`, which overflows. Unlike the
908
    /// `Add`/`AddAssign` impls (which were deliberately made saturating), this
909
    /// add is unchecked: debug builds panic, release builds wrap to `0`, i.e.
910
    /// the `None` encoding. Either way the node is lost; assert that it never
911
    /// silently produces some *other* valid-looking node id.
912
    #[cfg(feature = "std")]
913
    #[test]
914
    fn into_raw_at_usize_max_never_yields_a_bogus_node() {
915
        let encoded = std::panic::catch_unwind(|| NodeId::into_raw(&Some(NodeId::new(usize::MAX))));
916
        match encoded {
917
            // debug: overflow check fires — loud failure, acceptable.
918
            Err(_) => {}
919
            // release: wraps to 0 == the "no node" encoding.
920
            Ok(raw) => {
921
                assert_eq!(raw, 0, "wrapped encode must not alias a real node id");
922
                assert_eq!(NodeId::from_usize(raw), None);
923
            }
924
        }
925
    }
926

            
927
    // ---------------------------------------------------------------------
928
    // NodeId Display / Debug  (serializer)
929
    // ---------------------------------------------------------------------
930

            
931
    #[test]
932
    fn node_id_display_and_debug_are_well_formed() {
933
        assert_eq!(alloc::format!("{}", NodeId::new(0)), "0");
934
        assert_eq!(alloc::format!("{}", NodeId::new(7)), "7");
935
        assert_eq!(alloc::format!("{:?}", NodeId::new(7)), "NodeId(7)");
936
        // Extreme values must render without panicking and stay non-empty.
937
        let max = alloc::format!("{}", NodeId::new(usize::MAX));
938
        assert_eq!(max, alloc::format!("{}", usize::MAX));
939
        assert!(!max.is_empty());
940
        assert_eq!(
941
            alloc::format!("{:?}", NodeId::new(usize::MAX)),
942
            alloc::format!("NodeId({})", usize::MAX)
943
        );
944
    }
945

            
946
    // ---------------------------------------------------------------------
947
    // Node predicates + get_first_child
948
    // ---------------------------------------------------------------------
949

            
950
    #[test]
951
    fn node_root_and_default_have_no_relations() {
952
        for n in [Node::ROOT, Node::default()] {
953
            assert!(!n.has_parent());
954
            assert!(!n.has_previous_sibling());
955
            assert!(!n.has_next_sibling());
956
            assert!(!n.has_first_child());
957
            assert!(!n.has_last_child());
958
            assert_eq!(n.get_first_child(NodeId::new(0)), None);
959
        }
960
        assert_eq!(Node::default(), Node::ROOT);
961
    }
962

            
963
    #[test]
964
    fn node_predicates_report_each_populated_field() {
965
        let full = Node {
966
            parent: Some(NodeId::new(1)),
967
            previous_sibling: Some(NodeId::new(2)),
968
            next_sibling: Some(NodeId::new(3)),
969
            last_child: Some(NodeId::new(4)),
970
        };
971
        assert!(full.has_parent());
972
        assert!(full.has_previous_sibling());
973
        assert!(full.has_next_sibling());
974
        assert!(full.has_first_child());
975
        assert!(full.has_last_child());
976
    }
977

            
978
    /// INVARIANT: `has_first_child()` and `has_last_child()` read the same
979
    /// field, so they can never disagree — child-presence is all-or-nothing.
980
    #[test]
981
    fn has_first_child_always_agrees_with_has_last_child() {
982
        for last_child in [None, Some(NodeId::new(0)), Some(NodeId::new(usize::MAX))] {
983
            let n = Node {
984
                last_child,
985
                ..Node::ROOT
986
            };
987
            assert_eq!(n.has_first_child(), n.has_last_child());
988
            assert_eq!(n.has_first_child(), last_child.is_some());
989
        }
990
    }
991

            
992
    #[test]
993
    fn get_first_child_is_self_plus_one_and_saturates() {
994
        let parent = Node {
995
            last_child: Some(NodeId::new(9)),
996
            ..Node::ROOT
997
        };
998
        assert_eq!(parent.get_first_child(NodeId::new(0)), Some(NodeId::new(1)));
999
        assert_eq!(parent.get_first_child(NodeId::new(5)), Some(NodeId::new(6)));
        // Extreme id: the `+ 1` is saturating, so this must not panic/wrap. It
        // yields an obviously-invalid id that fails loudly at the next lookup.
        assert_eq!(
            parent.get_first_child(NodeId::new(usize::MAX)),
            Some(NodeId::new(usize::MAX))
        );
        // A leaf has no first child regardless of how extreme the id is.
        assert_eq!(Node::ROOT.get_first_child(NodeId::new(usize::MAX)), None);
    }
    // ---------------------------------------------------------------------
    // NodeHierarchy / NodeHierarchyRef
    // ---------------------------------------------------------------------
    #[test]
    fn hierarchy_new_preserves_len_and_contents() {
        let h = NodeHierarchy::new(tree_5());
        assert_eq!(h.as_ref().len(), 5);
        assert!(!h.as_ref().is_empty());
        assert_eq!(h.as_ref().get(NodeId::new(0)), Some(&tree_5()[0]));
        assert_eq!(h.as_ref().internal, &tree_5()[..]);
    }
    #[test]
    fn empty_hierarchy_is_empty_everywhere() {
        let h = NodeHierarchy::new(Vec::new());
        let r = h.as_ref();
        assert_eq!(r.len(), 0);
        assert!(r.is_empty());
        assert_eq!(r.get(NodeId::new(0)), None);
        assert_eq!(r.linear_iter().count(), 0);
        assert!(r.get_parents_sorted_by_depth().is_empty());
        let default = NodeHierarchy::default();
        assert!(default.as_ref().is_empty());
    }
    #[test]
    fn hierarchy_ref_from_slice_matches_len_and_emptiness() {
        let empty: [Node; 0] = [];
        assert_eq!(NodeHierarchyRef::from_slice(&empty).len(), 0);
        assert!(NodeHierarchyRef::from_slice(&empty).is_empty());
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        assert_eq!(r.len(), 5);
        assert!(!r.is_empty());
    }
    /// `get()` is the bounds-checked accessor: an out-of-range id must return
    /// `None`, never panic and never read out of bounds.
    #[test]
    fn hierarchy_ref_get_out_of_bounds_returns_none() {
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        assert!(r.get(NodeId::new(4)).is_some());
        assert_eq!(r.get(NodeId::new(5)), None);
        assert_eq!(r.get(NodeId::new(usize::MAX)), None);
    }
    /// `Index` (unlike `get`) is unchecked-by-contract: it must fail loudly
    /// rather than silently hand back an unrelated node.
    #[test]
    #[should_panic]
    fn hierarchy_ref_index_out_of_bounds_panics() {
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        let _ = &r[NodeId::new(5)];
    }
    #[test]
    fn hierarchy_linear_iter_walks_every_index_in_order() {
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        let ids: Vec<NodeId> = r.linear_iter().collect();
        assert_eq!(
            ids,
            (0..5).map(NodeId::new).collect::<Vec<_>>(),
            "linear_iter must yield 0..len exactly once, in order"
        );
    }
    /// The `arena_len < 1` guard exists because `arena_len - 1` would underflow
    /// on an empty arena; check the 0- and 1-element boundaries explicitly.
    #[test]
    fn linear_iter_len_zero_and_one_boundaries() {
        let empty: [Node; 0] = [];
        assert_eq!(
            NodeHierarchyRef::from_slice(&empty).linear_iter().next(),
            None
        );
        let one = [Node::ROOT];
        let mut it = NodeHierarchyRef::from_slice(&one).linear_iter();
        assert_eq!(it.next(), Some(NodeId::new(0)));
        assert_eq!(it.next(), None);
        // Exhausted iterators stay exhausted.
        assert_eq!(it.next(), None);
    }
    #[test]
    fn get_parents_sorted_by_depth_is_depth_ordered_and_leaf_free() {
        let h = NodeHierarchy::new(tree_5());
        let parents = h.as_ref().get_parents_sorted_by_depth();
        // Only 0 and 1 have children; 2/3/4 are leaves and must not appear.
        assert_eq!(parents, vec![(0, NodeId::new(0)), (1, NodeId::new(1))]);
        // Depths must be non-decreasing.
        assert!(parents.windows(2).all(|w| w[0].0 <= w[1].0));
    }
    // ---------------------------------------------------------------------
    // NodeId::children / preceding_siblings  (NodeHierarchyRef iterators)
    // ---------------------------------------------------------------------
    #[test]
    fn children_yields_direct_children_only() {
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        assert_eq!(
            NodeId::new(0).children(&r).collect::<Vec<_>>(),
            vec![NodeId::new(1), NodeId::new(4)]
        );
        assert_eq!(
            NodeId::new(1).children(&r).collect::<Vec<_>>(),
            vec![NodeId::new(2), NodeId::new(3)]
        );
        // Leaves have no children.
        assert_eq!(NodeId::new(2).children(&r).count(), 0);
        assert_eq!(NodeId::new(4).children(&r).count(), 0);
    }
    #[test]
    #[should_panic]
    fn children_of_out_of_bounds_node_panics_loudly() {
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        // `children()` indexes the hierarchy directly — an id past the end must
        // abort rather than fabricate a child list.
        let _ = NodeId::new(99).children(&r);
    }
    #[test]
    fn preceding_siblings_starts_with_self_then_walks_backwards() {
        let nodes = tree_5();
        let r = NodeHierarchyRef::from_slice(&nodes);
        assert_eq!(
            NodeId::new(3).preceding_siblings(&r).collect::<Vec<_>>(),
            vec![NodeId::new(3), NodeId::new(2)],
            "the iterator includes the node itself first"
        );
        // A first-born has only itself.
        assert_eq!(
            NodeId::new(2).preceding_siblings(&r).collect::<Vec<_>>(),
            vec![NodeId::new(2)]
        );
    }
    /// ADVERSARIAL: a corrupt hierarchy whose `previous_sibling` points at the
    /// node itself makes the iterator cycle forever. It must not panic — but a
    /// caller that `collect()`s it would hang, so only ever take a bounded
    /// prefix from an untrusted hierarchy.
    #[test]
    fn preceding_siblings_on_self_cycle_repeats_without_panicking() {
        let nodes = vec![
            Node::ROOT,
            Node {
                parent: Some(NodeId::new(0)),
                previous_sibling: Some(NodeId::new(1)), // points at itself
                next_sibling: None,
                last_child: None,
            },
        ];
        let r = NodeHierarchyRef::from_slice(&nodes);
        let first_4: Vec<NodeId> = NodeId::new(1).preceding_siblings(&r).take(4).collect();
        assert_eq!(first_4, vec![NodeId::new(1); 4]);
    }
    // ---------------------------------------------------------------------
    // NodeDataContainer / Ref / RefMut
    // ---------------------------------------------------------------------
    #[test]
    fn data_container_new_and_len_track_the_vec() {
        let c = NodeDataContainer::new(vec![10_u32, 20, 30]);
        assert_eq!(c.len(), 3);
        assert!(!c.is_empty());
        assert_eq!(c.as_ref().len(), 3);
        assert_eq!(c.as_ref().get(NodeId::new(2)), Some(&30));
        let empty: NodeDataContainer<u32> = NodeDataContainer::new(Vec::new());
        assert_eq!(empty.len(), 0);
        assert!(empty.is_empty());
        assert!(empty.as_ref().is_empty());
        assert_eq!(empty.as_ref().get(NodeId::new(0)), None);
        // Default and From<Vec<T>> agree with the explicit constructor.
        assert!(NodeDataContainer::<u32>::default().is_empty());
        assert_eq!(NodeDataContainer::from(vec![1_u32, 2]).len(), 2);
    }
    #[test]
    fn data_container_ref_get_out_of_bounds_returns_none() {
        let data = [1_u8, 2, 3];
        let r = NodeDataContainerRef::from_slice(&data);
        assert_eq!(r.get(NodeId::new(0)), Some(&1));
        assert_eq!(r.get(NodeId::new(3)), None);
        assert_eq!(r.get(NodeId::new(usize::MAX)), None);
    }
    #[test]
    #[should_panic]
    fn data_container_ref_index_out_of_bounds_panics() {
        let data = [1_u8, 2, 3];
        let r = NodeDataContainerRef::from_slice(&data);
        let _ = &r[NodeId::new(3)];
    }
    #[test]
    fn data_container_ref_iterators_cover_all_elements() {
        let data = [1_u8, 2, 3];
        let r = NodeDataContainerRef::from_slice(&data);
        assert_eq!(r.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
        assert_eq!((&r).into_iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
        assert_eq!(r.linear_iter().count(), 3);
        let empty: [u8; 0] = [];
        let e = NodeDataContainerRef::from_slice(&empty);
        assert_eq!(e.len(), 0);
        assert!(e.is_empty());
        assert_eq!(e.iter().count(), 0);
        assert_eq!(e.linear_iter().next(), None);
    }
    #[test]
    fn data_container_ref_mut_get_mut_is_bounds_checked() {
        let mut c = NodeDataContainer::new(vec![1_u32, 2, 3]);
        let mut m = c.as_ref_mut();
        assert_eq!(m.get_mut(NodeId::new(3)), None);
        assert_eq!(m.get_mut(NodeId::new(usize::MAX)), None);
        *m.get_mut(NodeId::new(1)).unwrap() = 99;
        m[NodeId::new(2)] = 7;
        assert_eq!(m[NodeId::new(2)], 7);
        assert_eq!(c.internal, vec![1, 99, 7]);
    }
    #[test]
    fn data_container_ref_mut_from_slice_on_empty_slice() {
        let mut empty: [u32; 0] = [];
        let mut m = NodeDataContainerRefMut::from_slice(&mut empty);
        assert_eq!(m.get_mut(NodeId::new(0)), None);
        assert!(m.internal.is_empty());
    }
    #[test]
    #[should_panic]
    fn data_container_ref_mut_index_out_of_bounds_panics() {
        let mut data = [1_u8, 2];
        let m = NodeDataContainerRefMut::from_slice(&mut data);
        let _ = &m[NodeId::new(2)];
    }
    // ---------------------------------------------------------------------
    // transform_nodeid_optional
    // ---------------------------------------------------------------------
    #[test]
    fn transform_nodeid_optional_maps_every_index() {
        let data = [0_u32; 4];
        let r = NodeDataContainerRef::from_slice(&data);
        let out = r.transform_nodeid_optional(|id| Some(id.index() as u32 * 10));
        assert_eq!(out.internal, vec![0, 10, 20, 30]);
        assert_eq!(out.len(), r.len());
    }
    #[test]
    fn transform_nodeid_optional_empty_input_never_calls_the_closure() {
        let empty: [u32; 0] = [];
        let r = NodeDataContainerRef::from_slice(&empty);
        let calls = AtomicUsize::new(0);
        let out = r.transform_nodeid_optional(|_| {
            calls.fetch_add(1, Ordering::SeqCst);
            Some(1_u32)
        });
        assert_eq!(calls.load(Ordering::SeqCst), 0);
        assert!(out.is_empty());
    }
    /// CONTRACT TRAP: `None` results are *filtered out*, so the output is
    /// COMPACTED — it is shorter than the input and its positions no longer
    /// line up with the `NodeId`s that produced them. Indexing the result by a
    /// `NodeId` therefore reads the wrong element (or goes out of bounds).
    /// Pinned here so the aliasing behaviour can't change silently.
    #[test]
    fn transform_nodeid_optional_compacts_and_breaks_nodeid_alignment() {
        let data = [0_u32; 5];
        let r = NodeDataContainerRef::from_slice(&data);
        // Keep only even node ids: 0, 2, 4.
        let out = r.transform_nodeid_optional(|id| {
            if id.index() % 2 == 0 {
                Some(id.index() as u32)
            } else {
                None
            }
        });
        assert_eq!(out.len(), 3, "output is compacted, NOT padded to the input len");
        assert_eq!(out.internal, vec![0, 2, 4]);
        // Position 1 holds node 2's value — the result is not index-aligned.
        assert_eq!(out.as_ref().get(NodeId::new(1)), Some(&2));
        assert_eq!(out.as_ref().get(NodeId::new(4)), None);
        // All-None closure yields an empty container rather than panicking.
        let none_out = r.transform_nodeid_optional(|_| -> Option<u32> { None });
        assert!(none_out.is_empty());
    }
    // ---------------------------------------------------------------------
    // NodeId::az_children / az_reverse_children / az_children_collect
    // ---------------------------------------------------------------------
    #[test]
    fn az_children_walks_forwards_and_reverse_walks_backwards() {
        let nodes = tree_5();
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        assert_eq!(
            NodeId::new(0).az_children_collect(&h),
            vec![NodeId::new(1), NodeId::new(4)]
        );
        assert_eq!(
            NodeId::new(1).az_children(&h).collect::<Vec<_>>(),
            vec![NodeId::new(2), NodeId::new(3)]
        );
        assert_eq!(
            NodeId::new(1).az_reverse_children(&h).collect::<Vec<_>>(),
            vec![NodeId::new(3), NodeId::new(2)],
            "reverse iteration starts at last_child and walks previous_sibling"
        );
        // Leaves yield nothing in either direction.
        assert_eq!(NodeId::new(2).az_children(&h).count(), 0);
        assert_eq!(NodeId::new(2).az_reverse_children(&h).count(), 0);
        assert!(NodeId::new(3).az_children_collect(&h).is_empty());
    }
    #[test]
    #[should_panic]
    fn az_children_of_out_of_bounds_node_panics_loudly() {
        let nodes = tree_5();
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        let _ = NodeId::new(5).az_children(&h);
    }
    /// ADVERSARIAL: a `next_sibling` that points back at the node itself makes
    /// `az_children` an infinite iterator. It must not panic, but
    /// `az_children_collect` on such a hierarchy would allocate until OOM —
    /// so only a bounded prefix is taken here.
    #[test]
    fn az_children_on_sibling_cycle_repeats_without_panicking() {
        let nodes = vec![
            Node {
                last_child: Some(NodeId::new(1)),
                ..Node::ROOT
            },
            Node {
                parent: Some(NodeId::new(0)),
                previous_sibling: None,
                next_sibling: Some(NodeId::new(1)), // points at itself
                last_child: None,
            },
        ];
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        let prefix: Vec<NodeId> = NodeId::new(0).az_children(&h).take(5).collect();
        assert_eq!(prefix, vec![NodeId::new(1); 5]);
    }
    // ---------------------------------------------------------------------
    // NodeId::get_nearest_matching_parent
    // ---------------------------------------------------------------------
    #[test]
    fn nearest_matching_parent_skips_non_matching_ancestors() {
        let nodes = tree_5();
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        // From node 2, the first ancestor is 1, then the root 0.
        assert_eq!(
            NodeId::new(2).get_nearest_matching_parent(&h, |_| true),
            Some(NodeId::new(1))
        );
        assert_eq!(
            NodeId::new(2).get_nearest_matching_parent(&h, |n| n == NodeId::new(0)),
            Some(NodeId::new(0))
        );
        // The root has no parent at all.
        assert_eq!(
            NodeId::new(0).get_nearest_matching_parent(&h, |_| true),
            None
        );
        // Nothing matches -> None, and the walk terminates.
        assert_eq!(
            NodeId::new(3).get_nearest_matching_parent(&h, |_| false),
            None
        );
    }
    #[test]
    fn nearest_matching_parent_out_of_bounds_self_returns_none() {
        let nodes = tree_5();
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        assert_eq!(
            NodeId::new(5).get_nearest_matching_parent(&h, |_| true),
            None
        );
        assert_eq!(
            NodeId::new(usize::MAX).get_nearest_matching_parent(&h, |_| true),
            None
        );
    }
    #[test]
    fn nearest_matching_parent_self_parent_cycle_terminates() {
        // node 1 is its own parent — the node-count cap must break the loop.
        let nodes = vec![
            Node::ROOT,
            Node {
                parent: Some(NodeId::new(1)),
                ..Node::ROOT
            },
        ];
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        assert_eq!(
            NodeId::new(1).get_nearest_matching_parent(&h, |_| false),
            None
        );
    }
    /// ADVERSARIAL: a corrupt `parent` index that points past the end of the
    /// arena. The lookup must not panic. Note the predicate is still called
    /// with that out-of-bounds id, so a permissive predicate hands the caller
    /// back an id that will panic when used to index the arena — callers must
    /// bounds-check the returned id, not assume it is valid.
    #[test]
    fn nearest_matching_parent_out_of_bounds_ancestor_does_not_panic() {
        let nodes = vec![
            Node::ROOT,
            Node {
                parent: Some(NodeId::new(99)), // dangling
                ..Node::ROOT
            },
        ];
        let it = items(&nodes);
        let h = NodeDataContainerRef::from_slice(&it);
        // Rejecting predicate: the dangling id fails the `get()` and yields None.
        assert_eq!(
            NodeId::new(1).get_nearest_matching_parent(&h, |_| false),
            None
        );
        // Accepting predicate: the dangling id is returned as-is.
        assert_eq!(
            NodeId::new(1).get_nearest_matching_parent(&h, |_| true),
            Some(NodeId::new(99))
        );
        assert!(h.get(NodeId::new(99)).is_none(), "and it is NOT a valid index");
    }
}