dioxus_core_template/
path.rs1use std::num::NonZeroU128;
2
3pub const TEMPLATE_SLOT_PATH_MAX_PATH_BITS: usize = u128::BITS as usize - 1;
7
8#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct TemplatePath {
11 path: u128,
12}
13
14impl TemplatePath {
15 pub(crate) const fn empty() -> Self {
17 Self { path: 0 }
18 }
19
20 pub(crate) const fn from_bits(path: u128) -> Self {
22 Self { path }
23 }
24
25 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 pub(crate) const fn bits(self) -> u128 {
43 self.path
44 }
45
46 pub const fn next_child(self) -> Self {
48 Self {
49 path: (self.path << 1) | 1,
50 }
51 }
52
53 pub const fn next_sibling(self) -> Self {
55 Self {
56 path: self.path << 1,
57 }
58 }
59
60 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 pub const fn is_root(self) -> bool {
74 self.depth() == 1
75 }
76
77 pub const fn depth(self) -> usize {
79 self.path.count_ones() as usize
80 }
81
82 pub const fn is_empty(self) -> bool {
84 self.path == 0
85 }
86
87 pub fn segments(self) -> TemplatePathSegments {
89 TemplatePathSegments { path: self.path }
90 }
91
92 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 pub(crate) fn bit_len(self) -> u32 {
121 u128::BITS - self.path.leading_zeros()
122 }
123}
124
125#[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#[derive(Clone, Copy, PartialEq, Eq)]
192pub struct TemplateSlotTarget {
193 static_path: TemplatePath,
194 is_last_static_node: bool,
195}
196
197impl TemplateSlotTarget {
198 pub const fn static_path(self) -> TemplatePath {
200 self.static_path
201 }
202
203 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 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}