Skip to main content

dioxus_core_template/
path.rs

1use std::num::NonZeroU128;
2
3/// Maximum raw [`TemplatePath`] bits that can be stored in a [`TemplateSlotPath`].
4///
5/// Slot paths reserve one low tag bit and store the path payload in the remaining bits.
6pub const TEMPLATE_SLOT_PATH_MAX_PATH_BITS: usize = u128::BITS as usize - 1;
7
8/// A compact path from a template root to a static node or dynamic attribute.
9#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct TemplatePath {
11    path: u128,
12}
13
14impl TemplatePath {
15    /// Create an empty path.
16    pub(crate) const fn empty() -> Self {
17        Self { path: 0 }
18    }
19
20    /// Create a path from compact path bits.
21    pub(crate) const fn from_bits(path: u128) -> Self {
22        Self { path }
23    }
24
25    /// Return the path for a root position.
26    ///
27    /// Root indices are bounded well below 128 by the rsx splitter's path-bit
28    /// limit, so an out-of-range index indicates a bug rather than a value to
29    /// silently collapse to the empty path.
30    pub(crate) const fn root(index: usize) -> Self {
31        debug_assert!(
32            index < u128::BITS as usize,
33            "template root index exceeds path bit width"
34        );
35
36        Self {
37            path: 1u128 << index,
38        }
39    }
40
41    /// Return the compact path bits.
42    pub(crate) const fn bits(self) -> u128 {
43        self.path
44    }
45
46    /// Return the path for the first child of this path.
47    pub const fn next_child(self) -> Self {
48        Self {
49            path: (self.path << 1) | 1,
50        }
51    }
52
53    /// Return the path for the next sibling of this path.
54    pub const fn next_sibling(self) -> Self {
55        Self {
56            path: self.path << 1,
57        }
58    }
59
60    /// Split a path to a static node into its parent path and the node's index among that
61    /// parent's children.
62    pub const fn split_insertion(self) -> (TemplatePath, usize) {
63        if self.path == 0 {
64            return (TemplatePath::from_bits(0), 0);
65        }
66
67        let insertion_index = self.path.trailing_zeros() as usize;
68        let parent = (self.path >> insertion_index) >> 1;
69        (TemplatePath::from_bits(parent), insertion_index)
70    }
71
72    /// Return true if this path points at a template root node (a direct child of the root).
73    pub const fn is_root(self) -> bool {
74        self.depth() == 1
75    }
76
77    /// Return the number of path segments from the template root to this path.
78    pub const fn depth(self) -> usize {
79        self.path.count_ones() as usize
80    }
81
82    /// Return true if this path has no segments.
83    pub const fn is_empty(self) -> bool {
84        self.path == 0
85    }
86
87    /// Iterate sibling indexes from the root to this path.
88    pub fn segments(self) -> TemplatePathSegments {
89        TemplatePathSegments { path: self.path }
90    }
91
92    /// Return true if this compact path starts with `ancestor`.
93    pub fn starts_with(self, ancestor: TemplatePath) -> bool {
94        let ancestor_depth = ancestor.depth();
95        if ancestor_depth == 0 {
96            return true;
97        }
98
99        if ancestor_depth > self.depth() {
100            return false;
101        }
102
103        if ancestor_depth == self.depth() {
104            return self.path == ancestor.path;
105        }
106
107        let self_bits = self.bit_len();
108        let ancestor_bits = ancestor.bit_len();
109        if ancestor_bits > self_bits {
110            return false;
111        }
112
113        let suffix_bits = self_bits - ancestor_bits;
114        suffix_bits > 0
115            && (self.path >> suffix_bits) == ancestor.path
116            && ((self.path >> (suffix_bits - 1)) & 1) == 1
117    }
118
119    /// Return the number of raw child/sibling bits in this path.
120    pub(crate) fn bit_len(self) -> u32 {
121        u128::BITS - self.path.leading_zeros()
122    }
123}
124
125/// Iterator over the sibling indexes encoded in a [`TemplatePath`].
126#[derive(Clone, Copy)]
127pub struct TemplatePathSegments {
128    path: u128,
129}
130
131impl Iterator for TemplatePathSegments {
132    type Item = usize;
133
134    fn next(&mut self) -> Option<Self::Item> {
135        let bit_len = u128::BITS - self.path.leading_zeros();
136        if bit_len == 0 {
137            return None;
138        }
139
140        let marker = 1u128 << (bit_len - 1);
141        let remaining_path = self.path ^ marker;
142        let next_marker_bit_len = u128::BITS - remaining_path.leading_zeros();
143        let segment = if next_marker_bit_len == 0 {
144            bit_len - 1
145        } else {
146            bit_len - next_marker_bit_len - 1
147        };
148
149        self.path = remaining_path;
150        Some(segment as usize)
151    }
152
153    fn size_hint(&self) -> (usize, Option<usize>) {
154        let len = self.len();
155        (len, Some(len))
156    }
157}
158
159impl ExactSizeIterator for TemplatePathSegments {
160    fn len(&self) -> usize {
161        self.path.count_ones() as usize
162    }
163}
164
165#[cfg(feature = "serialize")]
166impl serde::Serialize for TemplatePath {
167    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
168    where
169        S: serde::Serializer,
170    {
171        serde::Serialize::serialize(&self.path, serializer)
172    }
173}
174
175#[cfg(feature = "serialize")]
176impl<'de> serde::Deserialize<'de> for TemplatePath {
177    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178    where
179        D: serde::Deserializer<'de>,
180    {
181        let path = <u128 as serde::Deserialize>::deserialize(deserializer)?;
182        Ok(Self { path })
183    }
184}
185
186#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
187#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
188pub struct TemplateSlotPath(NonZeroU128);
189
190/// The static path an anchor binds to and whether that path is the last static node at its level.
191#[derive(Clone, Copy, PartialEq, Eq)]
192pub struct TemplateSlotTarget {
193    static_path: TemplatePath,
194    is_last_static_node: bool,
195}
196
197impl TemplateSlotTarget {
198    /// The static path this anchor binds to.
199    pub const fn static_path(self) -> TemplatePath {
200        self.static_path
201    }
202
203    /// Whether this anchor points at the last static node before its dynamic slot.
204    pub const fn is_last_static_node(self) -> bool {
205        self.is_last_static_node
206    }
207}
208
209impl TemplateSlotPath {
210    const TARGET_LAST_STATIC_NODE: u128 = 1;
211    const MAX_PAYLOAD: u128 = u128::MAX >> (u128::BITS as usize - TEMPLATE_SLOT_PATH_MAX_PATH_BITS);
212
213    const fn new(bits: u128) -> Self {
214        match NonZeroU128::new(bits) {
215            Some(bits) => Self(bits),
216            None => panic!("bad slot path"),
217        }
218    }
219
220    const fn encode_payload(path: TemplatePath) -> u128 {
221        let payload = path.bits();
222        if payload > Self::MAX_PAYLOAD {
223            panic!("slot path overflow");
224        }
225        payload << 1
226    }
227
228    pub(crate) const fn static_node(path: TemplatePath) -> Self {
229        if path.is_empty() {
230            panic!("bad slot target");
231        }
232        Self::new(Self::encode_payload(path))
233    }
234
235    pub(crate) const fn last_static_node(path: TemplatePath) -> Self {
236        Self::new(Self::encode_payload(path) | Self::TARGET_LAST_STATIC_NODE)
237    }
238
239    /// Return the raw tagged bits.
240    pub(crate) const fn bits(self) -> u128 {
241        self.0.get()
242    }
243
244    pub(crate) const fn target(self) -> TemplateSlotTarget {
245        let bits = self.bits();
246        TemplateSlotTarget {
247            static_path: TemplatePath::from_bits(bits >> 1),
248            is_last_static_node: bits & Self::TARGET_LAST_STATIC_NODE
249                == Self::TARGET_LAST_STATIC_NODE,
250        }
251    }
252
253    pub(crate) const fn static_path(self) -> TemplatePath {
254        self.target().static_path()
255    }
256
257    pub(crate) const fn is_last_static_node(self) -> bool {
258        self.target().is_last_static_node()
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265
266    #[test]
267    fn slot_path_payload_uses_all_but_the_tag_bit() {
268        assert_eq!(TEMPLATE_SLOT_PATH_MAX_PATH_BITS, 127);
269        assert_eq!(TemplateSlotPath::MAX_PAYLOAD, u128::MAX >> 1);
270    }
271
272    #[test]
273    fn root_paths_are_single_marker_bits() {
274        assert_eq!(TemplatePath::root(0).bits(), 0b1);
275        assert_eq!(TemplatePath::root(1).bits(), 0b10);
276        assert_eq!(TemplatePath::root(3).bits(), 0b1000);
277        assert_eq!(TemplatePath::root(127).bits(), 1u128 << 127);
278    }
279
280    #[test]
281    fn depth_counts_path_segments() {
282        assert_eq!(TemplatePath::empty().depth(), 0);
283        assert_eq!(TemplatePath::from_bits(0b1).depth(), 1);
284        assert_eq!(TemplatePath::from_bits(0b100101).depth(), 3);
285    }
286
287    #[test]
288    fn segments_reads_sibling_indexes() {
289        let path = TemplatePath::root(2)
290            .next_child()
291            .next_sibling()
292            .next_child();
293
294        assert_eq!(path.bits(), 0b100101);
295        assert_eq!(path.segments().collect::<Vec<_>>(), vec![2, 1, 0]);
296    }
297
298    #[test]
299    fn segments_stop_at_end() {
300        let mut segments = TemplatePath::root(0).segments();
301        assert_eq!(segments.next(), Some(0));
302        assert_eq!(segments.next(), None);
303    }
304
305    #[test]
306    fn starts_with_matches_segment_prefixes() {
307        let root_zero = TemplatePath::root(0);
308        let root_one = TemplatePath::root(1);
309        let root_zero_child_one = root_zero.next_child().next_sibling();
310        let root_one_child_zero = root_one.next_child();
311
312        assert!(root_zero.starts_with(TemplatePath::empty()));
313        assert!(root_zero.starts_with(root_zero));
314        assert!(root_zero_child_one.starts_with(root_zero));
315        assert!(root_one_child_zero.starts_with(root_one));
316
317        assert!(!root_one.starts_with(root_zero));
318        assert!(!root_one_child_zero.starts_with(root_zero));
319        assert!(!root_zero.starts_with(root_zero_child_one));
320    }
321
322    #[test]
323    fn starts_with_rejects_raw_bit_prefixes_across_segment_boundaries() {
324        assert!(!TemplatePath::from_bits(0b10).starts_with(TemplatePath::from_bits(0b1)));
325        assert!(!TemplatePath::from_bits(0b101).starts_with(TemplatePath::from_bits(0b1)));
326        assert!(!TemplatePath::from_bits(0b1001).starts_with(TemplatePath::from_bits(0b10)));
327        assert!(
328            !TemplatePath::root(0)
329                .next_child()
330                .starts_with(TemplatePath::root(5))
331        );
332
333        assert!(TemplatePath::from_bits(0b110).starts_with(TemplatePath::from_bits(0b1)));
334        assert!(TemplatePath::from_bits(0b101).starts_with(TemplatePath::from_bits(0b10)));
335    }
336
337    #[test]
338    fn split_insertion_returns_parent_and_sibling_index() {
339        let path = TemplatePath::root(1)
340            .next_child()
341            .next_sibling()
342            .next_sibling();
343        let (parent, index) = path.split_insertion();
344
345        assert_eq!(path.bits(), 0b10100);
346        assert_eq!(parent.bits(), TemplatePath::root(1).bits());
347        assert_eq!(index, 2);
348    }
349
350    #[test]
351    fn slot_path_tag_marks_last_static_node() {
352        let path = TemplatePath::root(0).next_child().next_sibling();
353        let static_node = TemplateSlotPath::static_node(path);
354        let last_static_node = TemplateSlotPath::last_static_node(path);
355
356        assert_eq!(static_node.static_path().bits(), path.bits());
357        assert!(!static_node.is_last_static_node());
358        assert_eq!(last_static_node.static_path().bits(), path.bits());
359        assert!(last_static_node.is_last_static_node());
360    }
361}