Skip to main content

freya_core/
runner.rs

1use std::{
2    any::TypeId,
3    cell::RefCell,
4    cmp::Ordering,
5    collections::{
6        HashMap,
7        HashSet,
8        VecDeque,
9    },
10    fmt::Debug,
11    rc::Rc,
12    sync::atomic::AtomicU64,
13};
14
15use futures_lite::{
16    FutureExt,
17    StreamExt,
18};
19use itertools::Itertools;
20use pathgraph::PathGraph;
21use rustc_hash::{
22    FxHashMap,
23    FxHashSet,
24};
25
26use crate::{
27    current_context::CurrentContext,
28    diff_key::DiffKey,
29    element::{
30        Element,
31        ElementExt,
32        EventHandlerType,
33    },
34    events::{
35        data::{
36            Event,
37            EventType,
38        },
39        name::EventName,
40    },
41    node_id::NodeId,
42    path_element::PathElement,
43    prelude::{
44        Task,
45        TaskId,
46    },
47    reactive_context::ReactiveContext,
48    scope::{
49        PathNode,
50        Scope,
51        ScopeStorage,
52    },
53    scope_id::ScopeId,
54    tree::DiffModifies,
55};
56
57#[derive(Debug, PartialEq, Eq)]
58pub enum MutationRemove {
59    /// Because elements always have a different parent we can easily get their position relatively to their parent
60    Element { id: NodeId, index: u32 },
61    /// In the other hand, roots of Scopes are manually connected to their parent scopes, so getting their index is not worth the effort.
62    Scope { id: NodeId },
63}
64
65impl PartialOrd for MutationRemove {
66    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
67        Some(self.cmp(other))
68    }
69}
70
71impl Ord for MutationRemove {
72    fn cmp(&self, other: &Self) -> Ordering {
73        use MutationRemove::*;
74        match (self, other) {
75            // Order Element removals by index descending (so larger indices come first)
76            (Element { index: a, .. }, Element { index: b, .. }) => b.cmp(a),
77            // Elements come before Scopes
78            (Element { .. }, Scope { .. }) => Ordering::Less,
79            (Scope { .. }, Element { .. }) => Ordering::Greater,
80            // Order Scopes by id descending as well
81            (Scope { id: a }, Scope { id: b }) => b.cmp(a),
82        }
83    }
84}
85
86impl MutationRemove {
87    pub fn node_id(&self) -> NodeId {
88        match self {
89            Self::Element { id, .. } => *id,
90            Self::Scope { id } => *id,
91        }
92    }
93}
94
95pub struct MutationAdd {
96    pub node_id: NodeId,
97    pub parent_id: NodeId,
98    pub index: u32,
99    pub element: Rc<dyn ElementExt>,
100}
101
102pub struct MutationModified {
103    pub node_id: NodeId,
104    pub element: Rc<dyn ElementExt>,
105    pub flags: DiffModifies,
106}
107
108#[derive(Debug)]
109pub struct MutationMove {
110    pub index: u32,
111    pub node_id: NodeId,
112}
113
114#[derive(Default)]
115pub struct Mutations {
116    pub added: Vec<MutationAdd>,
117    pub modified: Vec<MutationModified>,
118    pub removed: Vec<MutationRemove>,
119    pub moved: HashMap<NodeId, Vec<MutationMove>>,
120}
121
122impl Debug for Mutations {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        f.write_fmt(format_args!(
125            "Added: {:?} Modified: {:?} Removed: {:?} Moved: {:?}",
126            self.added
127                .iter()
128                .map(|m| (m.node_id, m.parent_id, m.index))
129                .collect::<Vec<_>>(),
130            self.modified.iter().map(|m| m.node_id).collect::<Vec<_>>(),
131            self.removed,
132            self.moved
133                .iter()
134                .map(|(parent_id, moves)| {
135                    (
136                        parent_id,
137                        moves
138                            .iter()
139                            .map(|m| (m.index, m.node_id))
140                            .collect::<Vec<_>>(),
141                    )
142                })
143                .collect::<Vec<_>>()
144        ))
145    }
146}
147
148pub enum Message {
149    MarkScopeAsDirty(ScopeId),
150    PollTask(TaskId),
151}
152
153pub struct Runner {
154    pub scopes: FxHashMap<ScopeId, Rc<RefCell<Scope>>>,
155    pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
156
157    pub(crate) dirty_scopes: FxHashSet<ScopeId>,
158    pub(crate) dirty_tasks: VecDeque<TaskId>,
159
160    pub node_to_scope: FxHashMap<NodeId, ScopeId>,
161
162    pub(crate) node_id_counter: NodeId,
163    pub(crate) scope_id_counter: ScopeId,
164    pub(crate) task_id_counter: Rc<AtomicU64>,
165
166    pub(crate) tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
167
168    pub(crate) sender: futures_channel::mpsc::UnboundedSender<Message>,
169    pub(crate) receiver: futures_channel::mpsc::UnboundedReceiver<Message>,
170}
171
172impl Debug for Runner {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        f.debug_struct("Runner")
175            .field("dirty_scopes", &self.dirty_scopes.len())
176            .field("dirty_tasks", &self.dirty_tasks.len())
177            .field("node_to_scope", &self.node_to_scope.len())
178            .field("scopes", &self.scopes.len())
179            .field("scopes_storages", &self.scopes_storages.borrow().len())
180            .field("tasks", &self.tasks.borrow().len())
181            .finish()
182    }
183}
184
185impl Drop for Runner {
186    fn drop(&mut self) {
187        // Graceful shutdown of scopes based on their height, starting from the deepest
188        for (scope_id, _scope) in self
189            .scopes
190            .drain()
191            .sorted_by_key(|s| s.1.borrow().height)
192            .rev()
193        {
194            CurrentContext::run_with_reactive(
195                CurrentContext {
196                    scope_id,
197                    scopes_storages: self.scopes_storages.clone(),
198                    tasks: self.tasks.clone(),
199                    task_id_counter: self.task_id_counter.clone(),
200                    sender: self.sender.clone(),
201                },
202                || {
203                    let mut _removed_tasks = Vec::new();
204
205                    self.tasks.borrow_mut().retain(|task_id, task| {
206                        if task.borrow().scope_id == scope_id {
207                            _removed_tasks.push((*task_id, task.clone()));
208                            false
209                        } else {
210                            true
211                        }
212                    });
213                    drop(_removed_tasks);
214                    let _scope = self.scopes_storages.borrow_mut().remove(&scope_id);
215                },
216            );
217        }
218    }
219}
220
221impl Runner {
222    pub fn new(root: impl Fn() -> Element + 'static) -> Self {
223        let (sender, receiver) = futures_channel::mpsc::unbounded::<Message>();
224        Self {
225            scopes: FxHashMap::from_iter([(
226                ScopeId::ROOT,
227                Rc::new(RefCell::new(Scope {
228                    parent_node_id_in_parent: NodeId::ROOT,
229                    path_in_parent: Box::from([]),
230                    height: 0,
231                    parent_id: None,
232                    id: ScopeId::ROOT,
233                    key: DiffKey::Root,
234                    comp: Rc::new(move |_| root()),
235                    props: Rc::new(()),
236                    element: None,
237                    nodes: {
238                        let mut map = PathGraph::new();
239                        map.insert(
240                            &[],
241                            PathNode {
242                                node_id: NodeId::ROOT,
243                                scope_id: None,
244                            },
245                        );
246                        map
247                    },
248                })),
249            )]),
250            scopes_storages: Rc::new(RefCell::new(FxHashMap::from_iter([(
251                ScopeId::ROOT,
252                ScopeStorage::new(None, |writer| {
253                    ReactiveContext::new_for_scope(sender.clone(), ScopeId::ROOT, writer)
254                }),
255            )]))),
256
257            node_to_scope: FxHashMap::from_iter([(NodeId::ROOT, ScopeId::ROOT)]),
258
259            node_id_counter: NodeId::ROOT,
260            scope_id_counter: ScopeId::ROOT,
261            task_id_counter: Rc::default(),
262
263            dirty_tasks: VecDeque::default(),
264            dirty_scopes: FxHashSet::from_iter([ScopeId::ROOT]),
265
266            tasks: Rc::default(),
267
268            sender,
269            receiver,
270        }
271    }
272
273    #[cfg(all(debug_assertions, feature = "debug-integrity"))]
274    #[cfg_attr(feature = "hotpath", hotpath::measure)]
275    pub fn verify_scopes_integrity(&self) {
276        let mut visited = FxHashSet::default();
277        let size = self.scopes.len();
278        let mut buffer = vec![ScopeId::ROOT];
279        while let Some(scope_id) = buffer.pop() {
280            if visited.contains(&scope_id) {
281                continue;
282            }
283            visited.insert(scope_id);
284            let scope = self.scopes.get(&scope_id).unwrap();
285            let scope = scope.borrow();
286            if let Some(parent) = scope.parent_id {
287                buffer.push(parent);
288            }
289            scope.nodes.traverse(&[], |_, &PathNode { scope_id, .. }| {
290                if let Some(scope_id) = scope_id {
291                    buffer.push(scope_id);
292                }
293            });
294        }
295        assert_eq!(size, visited.len())
296    }
297
298    pub fn provide_root_context<T: 'static + Clone>(&mut self, context: impl FnOnce() -> T) -> T {
299        CurrentContext::run(
300            CurrentContext {
301                scope_id: ScopeId::ROOT,
302                scopes_storages: self.scopes_storages.clone(),
303                tasks: self.tasks.clone(),
304                task_id_counter: self.task_id_counter.clone(),
305                sender: self.sender.clone(),
306            },
307            move || {
308                let context = context();
309                let mut scopes_storages = self.scopes_storages.borrow_mut();
310                let root_scope_storage = scopes_storages.get_mut(&ScopeId::ROOT).unwrap();
311                root_scope_storage
312                    .contexts
313                    .insert(TypeId::of::<T>(), Rc::new(context.clone()));
314
315                context
316            },
317        )
318    }
319
320    pub fn handle_event(
321        &mut self,
322        node_id: impl Into<NodeId>,
323        event_name: EventName,
324        event_type: EventType,
325        bubbles: bool,
326    ) -> bool {
327        let node_id = node_id.into();
328        #[cfg(debug_assertions)]
329        tracing::info!("Handling event {event_name:?} for {node_id:?}");
330        let propagate = Rc::new(RefCell::new(bubbles));
331        let default = Rc::new(RefCell::new(true));
332
333        let Some(scope_id) = self.node_to_scope.get(&node_id) else {
334            return false;
335        };
336        let Some(path) = self
337            .scopes
338            .get(scope_id)
339            .unwrap()
340            .borrow()
341            .nodes
342            .find_path(|value| {
343                value
344                    == Some(&PathNode {
345                        node_id,
346                        scope_id: None,
347                    })
348            })
349        else {
350            return false;
351        };
352
353        let mut current_target = Some((path, *scope_id));
354        while let Some((path, scope_id)) = current_target.take() {
355            let scope = self.scopes.get(&scope_id).cloned().unwrap();
356            scope.borrow().with_element(&path, |element| {
357                match element {
358                    PathElement::Component { .. } => {
359                        unreachable!()
360                    }
361                    PathElement::Element { element, .. } => {
362                        CurrentContext::run(
363                            CurrentContext {
364                                scope_id,
365                                scopes_storages: self.scopes_storages.clone(),
366                                tasks: self.tasks.clone(),
367                                task_id_counter: self.task_id_counter.clone(),
368                                sender: self.sender.clone(),
369                            },
370                            || {
371                                match &event_type {
372                                    EventType::Mouse(data) => {
373                                        let event_handlers = element.events_handlers();
374                                        if let Some(event_handlers) = event_handlers {
375                                            match event_handlers.get(&event_name) {
376                                                Some(EventHandlerType::Mouse(handler)) => {
377                                                    handler.call(Event {
378                                                        data: data.clone(),
379                                                        propagate: propagate.clone(),
380                                                        default: default.clone(),
381                                                    });
382                                                }
383                                                Some(_) => unreachable!(),
384                                                _ => {}
385                                            }
386                                        }
387                                    }
388                                    EventType::Keyboard(data) => {
389                                        let event_handlers = element.events_handlers();
390                                        if let Some(event_handlers) = event_handlers {
391                                            match event_handlers.get(&event_name) {
392                                                Some(EventHandlerType::Keyboard(handler)) => {
393                                                    handler.call(Event {
394                                                        data: data.clone(),
395                                                        propagate: propagate.clone(),
396                                                        default: default.clone(),
397                                                    });
398                                                }
399                                                Some(_) => unreachable!(),
400                                                _ => {}
401                                            }
402                                        }
403                                    }
404                                    EventType::Sized(data) => {
405                                        let event_handlers = element.events_handlers();
406                                        if let Some(event_handlers) = event_handlers {
407                                            match event_handlers.get(&event_name) {
408                                                Some(EventHandlerType::Sized(handler)) => {
409                                                    handler.call(Event {
410                                                        data: data.clone(),
411                                                        propagate: propagate.clone(),
412                                                        default: default.clone(),
413                                                    });
414                                                }
415                                                Some(_) => unreachable!(),
416                                                _ => {}
417                                            }
418                                        }
419                                    }
420                                    EventType::Visible(data) => {
421                                        let event_handlers = element.events_handlers();
422                                        if let Some(event_handlers) = event_handlers {
423                                            match event_handlers.get(&event_name) {
424                                                Some(EventHandlerType::Visible(handler)) => {
425                                                    handler.call(Event {
426                                                        data: data.clone(),
427                                                        propagate: propagate.clone(),
428                                                        default: default.clone(),
429                                                    });
430                                                }
431                                                Some(_) => unreachable!(),
432                                                _ => {}
433                                            }
434                                        }
435                                    }
436                                    EventType::Wheel(data) => {
437                                        let event_handlers = element.events_handlers();
438                                        if let Some(event_handlers) = event_handlers {
439                                            match event_handlers.get(&event_name) {
440                                                Some(EventHandlerType::Wheel(handler)) => {
441                                                    handler.call(Event {
442                                                        data: data.clone(),
443                                                        propagate: propagate.clone(),
444                                                        default: default.clone(),
445                                                    });
446                                                }
447                                                Some(_) => unreachable!(),
448                                                _ => {}
449                                            }
450                                        }
451                                    }
452                                    EventType::Touch(data) => {
453                                        let event_handlers = element.events_handlers();
454                                        if let Some(event_handlers) = event_handlers {
455                                            match event_handlers.get(&event_name) {
456                                                Some(EventHandlerType::Touch(handler)) => {
457                                                    handler.call(Event {
458                                                        data: data.clone(),
459                                                        propagate: propagate.clone(),
460                                                        default: default.clone(),
461                                                    });
462                                                }
463                                                Some(_) => unreachable!(),
464                                                _ => {}
465                                            }
466                                        }
467                                    }
468                                    EventType::Pointer(data) => {
469                                        let event_handlers = element.events_handlers();
470                                        if let Some(event_handlers) = event_handlers {
471                                            match event_handlers.get(&event_name) {
472                                                Some(EventHandlerType::Pointer(handler)) => {
473                                                    handler.call(Event {
474                                                        data: data.clone(),
475                                                        propagate: propagate.clone(),
476                                                        default: default.clone(),
477                                                    });
478                                                }
479                                                Some(_) => unreachable!(),
480                                                _ => {}
481                                            }
482                                        }
483                                    }
484                                    EventType::File(data) => {
485                                        let event_handlers = element.events_handlers();
486                                        if let Some(event_handlers) = event_handlers {
487                                            match event_handlers.get(&event_name) {
488                                                Some(EventHandlerType::File(handler)) => {
489                                                    handler.call(Event {
490                                                        data: data.clone(),
491                                                        propagate: propagate.clone(),
492                                                        default: default.clone(),
493                                                    });
494                                                }
495                                                Some(_) => unreachable!(),
496                                                _ => {}
497                                            }
498                                        }
499                                    }
500                                    EventType::ImePreedit(data) => {
501                                        let event_handlers = element.events_handlers();
502                                        if let Some(event_handlers) = event_handlers {
503                                            match event_handlers.get(&event_name) {
504                                                Some(EventHandlerType::ImePreedit(handler)) => {
505                                                    handler.call(Event {
506                                                        data: data.clone(),
507                                                        propagate: propagate.clone(),
508                                                        default: default.clone(),
509                                                    });
510                                                }
511                                                Some(_) => unreachable!(),
512                                                _ => {}
513                                            }
514                                        }
515                                    }
516                                }
517
518                                // Bubble up if desired
519                                if *propagate.borrow() {
520                                    if path.len() > 1 {
521                                        // Change the target to this element parent (still in the same Scope)
522                                        current_target
523                                            .replace((path[..path.len() - 1].to_vec(), scope_id));
524                                    } else {
525                                        let mut parent_scope_id = scope.borrow().parent_id;
526                                        // Otherwise change the target to this element parent in the parent Scope
527                                        loop {
528                                            if let Some(parent_id) = parent_scope_id.take() {
529                                                let parent_scope =
530                                                    self.scopes.get(&parent_id).unwrap();
531                                                let path = parent_scope.borrow().nodes.find_path(
532                                                    |value| {
533                                                        value
534                                                            == Some(&PathNode {
535                                                                node_id: scope
536                                                                    .borrow()
537                                                                    .parent_node_id_in_parent,
538                                                                scope_id: None,
539                                                            })
540                                                    },
541                                                );
542                                                if let Some(path) = path {
543                                                    current_target.replace((path, parent_id));
544                                                    break;
545                                                } else {
546                                                    parent_scope_id =
547                                                        parent_scope.borrow().parent_id;
548                                                }
549                                            } else {
550                                                return;
551                                            }
552                                        }
553                                    }
554                                }
555                            },
556                        )
557                    }
558                }
559            });
560        }
561        *default.borrow()
562    }
563
564    #[cfg_attr(feature = "hotpath", hotpath::measure)]
565    pub async fn handle_events(&mut self) {
566        loop {
567            while let Ok(msg) = self.receiver.try_recv() {
568                match msg {
569                    Message::MarkScopeAsDirty(scope_id) => {
570                        self.dirty_scopes.insert(scope_id);
571                    }
572                    Message::PollTask(task_id) => {
573                        self.dirty_tasks.push_back(task_id);
574                    }
575                }
576            }
577
578            if !self.dirty_scopes.is_empty() {
579                return;
580            }
581
582            while let Some(task_id) = self.dirty_tasks.pop_front() {
583                let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
584                    continue;
585                };
586                let mut task = task.borrow_mut();
587                let waker = task.waker.clone();
588
589                let mut cx = std::task::Context::from_waker(&waker);
590
591                CurrentContext::run(
592                    {
593                        let Some(scope) = self.scopes.get(&task.scope_id) else {
594                            continue;
595                        };
596                        CurrentContext {
597                            scope_id: scope.borrow().id,
598                            scopes_storages: self.scopes_storages.clone(),
599                            tasks: self.tasks.clone(),
600                            task_id_counter: self.task_id_counter.clone(),
601                            sender: self.sender.clone(),
602                        }
603                    },
604                    || {
605                        let poll_result = task.future.poll(&mut cx);
606                        if poll_result.is_ready() {
607                            let _ = self.tasks.borrow_mut().remove(&task_id);
608                        }
609                    },
610                );
611            }
612
613            if !self.dirty_scopes.is_empty() {
614                return;
615            }
616
617            while let Some(msg) = self.receiver.next().await {
618                match msg {
619                    Message::MarkScopeAsDirty(scope_id) => {
620                        self.dirty_scopes.insert(scope_id);
621                    }
622                    Message::PollTask(task_id) => {
623                        self.dirty_tasks.push_back(task_id);
624                    }
625                }
626            }
627        }
628    }
629
630    /// Useful for freya-testing
631    #[cfg_attr(feature = "hotpath", hotpath::measure)]
632    pub fn handle_events_immediately(&mut self) {
633        while let Ok(msg) = self.receiver.try_recv() {
634            match msg {
635                Message::MarkScopeAsDirty(scope_id) => {
636                    self.dirty_scopes.insert(scope_id);
637                }
638                Message::PollTask(task_id) => {
639                    self.dirty_tasks.push_back(task_id);
640                }
641            }
642        }
643
644        if !self.dirty_scopes.is_empty() {
645            return;
646        }
647
648        // Poll here
649        while let Some(task_id) = self.dirty_tasks.pop_front() {
650            let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
651                continue;
652            };
653            let mut task = task.borrow_mut();
654            let waker = task.waker.clone();
655
656            let mut cx = std::task::Context::from_waker(&waker);
657
658            CurrentContext::run(
659                {
660                    let Some(scope) = self.scopes.get(&task.scope_id) else {
661                        continue;
662                    };
663                    CurrentContext {
664                        scope_id: scope.borrow().id,
665                        scopes_storages: self.scopes_storages.clone(),
666                        tasks: self.tasks.clone(),
667                        task_id_counter: self.task_id_counter.clone(),
668                        sender: self.sender.clone(),
669                    }
670                },
671                || {
672                    let poll_result = task.future.poll(&mut cx);
673                    if poll_result.is_ready() {
674                        let _ = self.tasks.borrow_mut().remove(&task_id);
675                    }
676                },
677            );
678        }
679    }
680
681    #[cfg_attr(feature = "hotpath", hotpath::measure)]
682    pub fn sync_and_update(&mut self) -> Mutations {
683        self.handle_events_immediately();
684        use itertools::Itertools;
685
686        #[cfg(all(debug_assertions, feature = "debug-integrity"))]
687        self.verify_scopes_integrity();
688
689        let mut mutations = Mutations::default();
690
691        let dirty_scopes = self
692            .dirty_scopes
693            .drain()
694            .filter_map(|id| self.scopes.get(&id).cloned())
695            .sorted_by_key(|s| s.borrow().height)
696            .map(|s| s.borrow().id)
697            .collect::<Box<[_]>>();
698
699        let mut visited_scopes = FxHashSet::default();
700
701        for scope_id in dirty_scopes {
702            // No need to run scopes more than once
703            if visited_scopes.contains(&scope_id) {
704                continue;
705            }
706
707            let Some(scope_rc) = self.scopes.get(&scope_id).cloned() else {
708                continue;
709            };
710
711            let scope_id = scope_rc.borrow().id;
712
713            let element = CurrentContext::run_with_reactive(
714                CurrentContext {
715                    scope_id,
716                    scopes_storages: self.scopes_storages.clone(),
717                    tasks: self.tasks.clone(),
718                    task_id_counter: self.task_id_counter.clone(),
719                    sender: self.sender.clone(),
720                },
721                || {
722                    let scope = scope_rc.borrow();
723                    #[cfg(feature = "hotreload")]
724                    {
725                        subsecond::call(|| (scope.comp)(scope.props.clone()))
726                    }
727                    #[cfg(not(feature = "hotreload"))]
728                    {
729                        (scope.comp)(scope.props.clone())
730                    }
731                },
732            );
733
734            let path_element = PathElement::from_element(vec![0], element);
735            let mut diff = Diff::default();
736            path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
737
738            self.apply_diff(&scope_rc, diff, &mut mutations, &path_element);
739
740            self.run_scope(
741                &scope_rc,
742                &path_element,
743                &mut mutations,
744                &mut visited_scopes,
745            );
746
747            let mut scopes_storages = self.scopes_storages.borrow_mut();
748            let scope_storage = scopes_storages.get_mut(&scope_rc.borrow().id).unwrap();
749            scope_storage.current_value = 0;
750            scope_storage.current_run += 1;
751
752            scope_rc.borrow_mut().element = Some(path_element);
753        }
754
755        mutations
756    }
757
758    pub fn run_in<T>(&self, run: impl FnOnce() -> T) -> T {
759        CurrentContext::run(
760            CurrentContext {
761                scope_id: ScopeId::ROOT,
762                scopes_storages: self.scopes_storages.clone(),
763                tasks: self.tasks.clone(),
764                task_id_counter: self.task_id_counter.clone(),
765                sender: self.sender.clone(),
766            },
767            run,
768        )
769    }
770
771    #[cfg_attr(feature = "hotpath", hotpath::measure)]
772    fn run_scope(
773        &mut self,
774        scope: &Rc<RefCell<Scope>>,
775        element: &PathElement,
776        mutations: &mut Mutations,
777        visited_scopes: &mut FxHashSet<ScopeId>,
778    ) {
779        visited_scopes.insert(scope.borrow().id);
780        match element {
781            PathElement::Component {
782                comp,
783                props,
784                key,
785                path,
786            } => {
787                // Safe to unwrap because this is a component
788                let assigned_scope_id = scope
789                    .borrow()
790                    .nodes
791                    .get(path)
792                    .and_then(|path_node| path_node.scope_id)
793                    .unwrap();
794
795                let parent_node_id = if path.as_ref() == [0] {
796                    scope.borrow().parent_node_id_in_parent
797                } else {
798                    scope
799                        .borrow()
800                        .nodes
801                        .get(&path[..path.len() - 1])
802                        .unwrap()
803                        .node_id
804                };
805
806                if let Some(Ok(mut existing_scope)) = self
807                    .scopes
808                    .get(&assigned_scope_id)
809                    .map(|s| s.try_borrow_mut())
810                {
811                    let key_changed = existing_scope.key != *key;
812                    if key_changed || existing_scope.props.changed(props.as_ref()) {
813                        self.dirty_scopes.insert(assigned_scope_id);
814                        existing_scope.props = props.clone();
815
816                        if key_changed {
817                            self.scopes_storages
818                                .borrow_mut()
819                                .get_mut(&assigned_scope_id)
820                                .unwrap()
821                                .reset();
822                        }
823                    }
824                } else {
825                    self.scopes.insert(
826                        assigned_scope_id,
827                        Rc::new(RefCell::new(Scope {
828                            parent_node_id_in_parent: parent_node_id,
829                            path_in_parent: path.clone(),
830                            height: scope.borrow().height + 1,
831                            parent_id: Some(scope.borrow().id),
832                            id: assigned_scope_id,
833                            key: key.clone(),
834                            comp: comp.clone(),
835                            props: props.clone(),
836                            element: None,
837                            nodes: PathGraph::default(),
838                        })),
839                    );
840                    self.scopes_storages.borrow_mut().insert(
841                        assigned_scope_id,
842                        ScopeStorage::new(Some(scope.borrow().id), |writer| {
843                            ReactiveContext::new_for_scope(
844                                self.sender.clone(),
845                                assigned_scope_id,
846                                writer,
847                            )
848                        }),
849                    );
850                    self.dirty_scopes.insert(assigned_scope_id);
851                }
852
853                let was_dirty = self.dirty_scopes.remove(&assigned_scope_id);
854
855                if !was_dirty {
856                    // No need to rerun scope if it is not dirty
857                    return;
858                }
859
860                let scope_rc = self.scopes.get(&assigned_scope_id).cloned().unwrap();
861
862                let element = hotpath::measure_block!("Scope Rendering", {
863                    CurrentContext::run_with_reactive(
864                        CurrentContext {
865                            scope_id: assigned_scope_id,
866                            scopes_storages: self.scopes_storages.clone(),
867                            tasks: self.tasks.clone(),
868                            task_id_counter: self.task_id_counter.clone(),
869                            sender: self.sender.clone(),
870                        },
871                        || {
872                            let scope = scope_rc.borrow();
873                            #[cfg(feature = "hotreload")]
874                            {
875                                subsecond::call(|| (scope.comp)(scope.props.clone()))
876                            }
877                            #[cfg(not(feature = "hotreload"))]
878                            {
879                                (scope.comp)(scope.props.clone())
880                            }
881                        },
882                    )
883                });
884
885                let path_element = PathElement::from_element(vec![0], element);
886                let mut diff = Diff::default();
887                path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
888
889                self.apply_diff(&scope_rc, diff, mutations, &path_element);
890
891                self.run_scope(&scope_rc, &path_element, mutations, visited_scopes);
892                let mut scopes_storages = self.scopes_storages.borrow_mut();
893                let scope_storage = scopes_storages.get_mut(&assigned_scope_id).unwrap();
894                scope_storage.current_value = 0;
895                scope_storage.current_run += 1;
896
897                scope_rc.borrow_mut().element = Some(path_element);
898            }
899            PathElement::Element { elements, .. } => {
900                for element in elements.iter() {
901                    self.run_scope(scope, element, mutations, visited_scopes);
902                }
903            }
904        }
905    }
906
907    /// Recursively traverse up in the scopes tree until a suitable (non-root) slot is found to put an element.
908    /// Returns a parent node id and a slot index pointing to one of its children.
909    fn find_scope_root_parent_info(
910        &self,
911        parent_id: Option<ScopeId>,
912        parent_node_id: NodeId,
913        scope_id: ScopeId,
914    ) -> (NodeId, u32) {
915        let mut index_inside_parent = 0;
916
917        if let Some(parent_id) = parent_id {
918            let mut buffer = Some((parent_id, parent_node_id, scope_id));
919            while let Some((parent_id, parent_node_id, scope_id)) = buffer.take() {
920                let parent_scope = self.scopes.get(&parent_id).unwrap();
921                let parent_scope = parent_scope.borrow();
922
923                let scope = self.scopes.get(&scope_id).unwrap();
924                let scope = scope.borrow();
925
926                let path_node_parent = parent_scope.nodes.find(|v| {
927                    if let Some(v) = v {
928                        v.node_id == parent_node_id
929                    } else {
930                        false
931                    }
932                });
933
934                if let Some(path_node_parent) = path_node_parent {
935                    if let Some(scope_id) = path_node_parent.scope_id {
936                        if let Some(parent_id) = parent_scope.parent_id {
937                            // The found element turns out to be a component so go to it to continue looking
938                            buffer.replace((
939                                parent_id,
940                                parent_scope.parent_node_id_in_parent,
941                                scope_id,
942                            ));
943                        } else {
944                            assert_eq!(scope_id, ScopeId::ROOT);
945                        }
946                    } else {
947                        // Found an Element parent so we get the index from the path
948                        index_inside_parent = *scope.path_in_parent.last().unwrap();
949                        return (parent_node_id, index_inside_parent);
950                    }
951                } else if let Some(new_parent_id) = parent_scope.parent_id {
952                    // If no element was found we go to the parent scope
953                    buffer.replace((
954                        new_parent_id,
955                        parent_scope.parent_node_id_in_parent,
956                        parent_id,
957                    ));
958                }
959            }
960        } else {
961            assert_eq!(scope_id, ScopeId::ROOT);
962        }
963
964        (parent_node_id, index_inside_parent)
965    }
966
967    /// Recursively finds the root element [NodeId] of a scope.
968    /// When a scope's first child is another component (scope), this follows
969    /// the chain until it finds the first actual element.
970    fn find_scope_root_node_id(&self, scope_id: ScopeId) -> NodeId {
971        let scope_rc = self.scopes.get(&scope_id).unwrap();
972        let scope = scope_rc.borrow();
973        let path_node = scope.nodes.get(&[0]).unwrap();
974        if let Some(child_scope_id) = path_node.scope_id {
975            self.find_scope_root_node_id(child_scope_id)
976        } else {
977            path_node.node_id
978        }
979    }
980
981    fn process_addition(
982        &mut self,
983        scope: &Rc<RefCell<Scope>>,
984        added: &[u32],
985        path_element: &PathElement,
986        mutations: &mut Mutations,
987        parents_to_resync_scopes: &mut FxHashSet<Box<[u32]>>,
988    ) {
989        let (parent_node_id, index_inside_parent) = if added == [0] {
990            let parent_id = scope.borrow().parent_id;
991            let scope_id = scope.borrow().id;
992            let parent_node_id = scope.borrow().parent_node_id_in_parent;
993            self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
994        } else {
995            parents_to_resync_scopes.insert(Box::from(&added[..added.len() - 1]));
996            (
997                scope
998                    .borrow()
999                    .nodes
1000                    .get(&added[..added.len() - 1])
1001                    .unwrap()
1002                    .node_id,
1003                added[added.len() - 1],
1004            )
1005        };
1006
1007        self.node_id_counter += 1;
1008
1009        path_element.with_element(added, |element| match element {
1010            PathElement::Component { .. } => {
1011                self.scope_id_counter += 1;
1012                let scope_id = self.scope_id_counter;
1013
1014                scope.borrow_mut().nodes.insert(
1015                    added,
1016                    PathNode {
1017                        node_id: self.node_id_counter,
1018                        scope_id: Some(scope_id),
1019                    },
1020                );
1021            }
1022            PathElement::Element { element, .. } => {
1023                mutations.added.push(MutationAdd {
1024                    node_id: self.node_id_counter,
1025                    parent_id: parent_node_id,
1026                    index: index_inside_parent,
1027                    element: element.clone(),
1028                });
1029
1030                self.node_to_scope
1031                    .insert(self.node_id_counter, scope.borrow().id);
1032                scope.borrow_mut().nodes.insert(
1033                    added,
1034                    PathNode {
1035                        node_id: self.node_id_counter,
1036                        scope_id: None,
1037                    },
1038                );
1039            }
1040        });
1041    }
1042
1043    #[cfg_attr(feature = "hotpath", hotpath::measure)]
1044    fn apply_diff(
1045        &mut self,
1046        scope: &Rc<RefCell<Scope>>,
1047        diff: Diff,
1048        mutations: &mut Mutations,
1049        path_element: &PathElement,
1050    ) {
1051        let mut moved_nodes =
1052            FxHashMap::<Box<[u32]>, (NodeId, FxHashMap<u32, PathNode>)>::default();
1053        let mut parents_to_resync_scopes = FxHashSet::default();
1054
1055        // Store the moved nodes so that they can
1056        // later be rearranged once the removals and additions have been done
1057        for (parent, movements) in &diff.moved {
1058            parents_to_resync_scopes.insert(parent.clone());
1059            // `parent` is a new-tree path; if the parent itself was moved, its path in the
1060            // old nodes tree will differ — resolve it before any lookup.
1061            let old_parent = resolve_old_path(parent, &diff.moved);
1062            let paths = moved_nodes.entry(parent.clone()).or_insert_with(|| {
1063                let parent_node_id = scope.borrow().nodes.get(&old_parent).unwrap().node_id;
1064                (parent_node_id, FxHashMap::default())
1065            });
1066
1067            for (from, _to) in movements.iter() {
1068                let mut old_child_path = old_parent.clone();
1069                old_child_path.push(*from);
1070
1071                let path_node = scope.borrow().nodes.get(&old_child_path).cloned().unwrap();
1072
1073                paths.1.insert(*from, path_node);
1074            }
1075        }
1076
1077        // Collect a set of branches to remove in cascade
1078        let mut selected_roots: HashMap<&[u32], HashSet<&[u32]>> = HashMap::default();
1079        let mut scope_removal_buffer = vec![];
1080
1081        // Given some removals like:
1082        // [
1083        //     [0,2],
1084        //     [0,1,0,1],
1085        //     [0,1,0,2],
1086        //     [0,3],
1087        //     [0,1,5,8],
1088        // ]
1089        //
1090        // We want them ordered like:
1091        // [
1092        //     [0,3],
1093        //     [0,2],
1094        //     [0,1,5,8],
1095        //     [0,1,0,2],
1096        //     [0,1,0,1],
1097        // ]
1098        //
1099        // This way any removal does not move the next removals
1100        'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
1101            for (x, y) in a.iter().zip(b.iter()) {
1102                match x.cmp(y) {
1103                    Ordering::Equal => continue,
1104                    non_eq => return non_eq.reverse(),
1105                }
1106            }
1107            b.len().cmp(&a.len())
1108        }) {
1109            parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
1110
1111            let path_node = scope.borrow().nodes.get(removed).cloned();
1112            if let Some(PathNode { node_id, scope_id }) = path_node {
1113                if scope_id.is_none() {
1114                    let index_inside_parent = if removed.as_ref() == [0] {
1115                        let parent_id = scope.borrow().parent_id;
1116                        let scope_id = scope.borrow().id;
1117                        let parent_node_id = scope.borrow().parent_node_id_in_parent;
1118                        self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
1119                            .1
1120                    } else {
1121                        // Only do it for non-scope-roots because the root is is always in the same position therefore it doesnt make sense to resync from its parent
1122                        removed[removed.len() - 1]
1123                    };
1124
1125                    // plain element removal
1126                    mutations.removed.push(MutationRemove::Element {
1127                        id: node_id,
1128                        index: index_inside_parent,
1129                    });
1130                }
1131
1132                // Skip if this removed path is already covered by a previously selected root
1133                for (root, inner) in &mut selected_roots {
1134                    if is_descendant(removed, root) {
1135                        inner.insert(removed);
1136                        continue 'remove;
1137                    }
1138                }
1139
1140                // Remove any previously selected roots that are descendants of this new (higher) removed path
1141                selected_roots.retain(|root, _| !is_descendant(root, removed));
1142
1143                selected_roots
1144                    .entry(&removed[..removed.len() - 1])
1145                    .or_default()
1146                    .insert(removed);
1147            } else {
1148                unreachable!()
1149            }
1150        }
1151
1152        // Traverse each chosen branch root and queue nested scopes
1153        for (root, removed) in selected_roots.iter().sorted_by(|(a, _), (b, _)| {
1154            for (x, y) in a.iter().zip(b.iter()) {
1155                match x.cmp(y) {
1156                    Ordering::Equal => continue,
1157                    non_eq => return non_eq.reverse(),
1158                }
1159            }
1160            b.len().cmp(&a.len())
1161        }) {
1162            scope.borrow_mut().nodes.retain(
1163                root,
1164                |p, _| !removed.contains(p),
1165                |_, &PathNode { scope_id, node_id }| {
1166                    if let Some(scope_id) = scope_id {
1167                        // Queue scope to be removed
1168                        scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1169                    } else {
1170                        self.node_to_scope.remove(&node_id).unwrap();
1171                    }
1172                },
1173            );
1174        }
1175
1176        let mut scope_removal_queue = VecDeque::new();
1177
1178        while let Some(scope_rc) = scope_removal_buffer.pop() {
1179            // Push the scopes to a queue that will remove
1180            // them starting from the deepest to the highest ones
1181            scope_removal_queue.push_front(scope_rc.clone());
1182
1183            let scope = scope_rc.borrow_mut();
1184
1185            let mut scope_root_node_id = None;
1186
1187            // Queue nested scopes to be removed
1188            scope
1189                .nodes
1190                .traverse(&[], |path, &PathNode { scope_id, node_id }| {
1191                    if let Some(scope_id) = scope_id {
1192                        scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1193                    } else {
1194                        self.node_to_scope.remove(&node_id).unwrap();
1195                    }
1196                    if path == [0] {
1197                        scope_root_node_id = Some(node_id);
1198                    }
1199                });
1200
1201            // Nodes that have a scope id are components, so no need to mark those as removed in the tree
1202            // Instead we get their root node id and remove it
1203            mutations.removed.push(MutationRemove::Scope {
1204                id: scope_root_node_id.unwrap(),
1205            });
1206        }
1207
1208        // Finally drops the scopes and their storage
1209        for scope_rc in scope_removal_queue {
1210            let scope = scope_rc.borrow_mut();
1211
1212            self.scopes.remove(&scope.id);
1213
1214            // Dropped hooks might e.g spawn forever tasks, so they need access to the context
1215            CurrentContext::run_with_reactive(
1216                CurrentContext {
1217                    scope_id: scope.id,
1218                    scopes_storages: self.scopes_storages.clone(),
1219                    tasks: self.tasks.clone(),
1220                    task_id_counter: self.task_id_counter.clone(),
1221                    sender: self.sender.clone(),
1222                },
1223                || {
1224                    // TODO: Scopes could also maintain its own registry of assigned tasks
1225                    let mut _removed_tasks = Vec::new();
1226
1227                    self.tasks.borrow_mut().retain(|task_id, task| {
1228                        if task.borrow().scope_id == scope.id {
1229                            _removed_tasks.push((*task_id, task.clone()));
1230                            false
1231                        } else {
1232                            true
1233                        }
1234                    });
1235                    drop(_removed_tasks);
1236                    // This is very important, the scope storage must be dropped after the borrow in `scopes_storages` has been released
1237                    let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
1238                },
1239            );
1240        }
1241
1242        // Given some additions like:
1243        // [
1244        //     [0,2],
1245        //     [0,1,0,1],
1246        //     [0,1,0,2],
1247        //     [0,3],
1248        //     [0,1,5,8],
1249        // ]
1250        //
1251        // We want them ordered like:
1252        // [
1253        //     [0,1,0,1],
1254        //     [0,1,0,2],
1255        //     [0,1,5,8],
1256        //     [0,2],
1257        //     [0,3],
1258        // ]
1259        //
1260        // This way, no addition offsets the next additions in line.
1261        // Additions whose parent is a move destination must be deferred until
1262        // after moves are applied, because the nodes graph still has old-tree
1263        // layout and the parent element hasn't been repositioned yet.
1264        let mut deferred_adds = Vec::new();
1265
1266        for added in diff
1267            .added
1268            .iter()
1269            .sorted_by(|a, b| {
1270                for (x, y) in a.iter().zip(b.iter()) {
1271                    match x.cmp(y) {
1272                        Ordering::Equal => continue,
1273                        non_eq => return non_eq.reverse(),
1274                    }
1275                }
1276                b.len().cmp(&a.len())
1277            })
1278            .rev()
1279        {
1280            let parent = &added[..added.len() - 1];
1281            let has_moved_ancestor = resolve_old_path(parent, &diff.moved) != *parent;
1282            if has_moved_ancestor {
1283                deferred_adds.push(added.clone());
1284                continue;
1285            }
1286
1287            self.process_addition(
1288                scope,
1289                added,
1290                path_element,
1291                mutations,
1292                &mut parents_to_resync_scopes,
1293            );
1294        }
1295
1296        for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1297            for (x, y) in a.iter().zip(b.iter()) {
1298                match x.cmp(y) {
1299                    Ordering::Equal => continue,
1300                    non_eq => return non_eq.reverse(),
1301                }
1302            }
1303            a.len().cmp(&b.len())
1304        }) {
1305            parents_to_resync_scopes.insert(parent.clone());
1306
1307            let (parent_node_id, paths) = moved_nodes.get_mut(&parent).unwrap();
1308
1309            for (from, to) in movements.into_iter().sorted_by_key(|e| e.1) {
1310                let path_node = paths.remove(&from).unwrap();
1311
1312                let PathNode { node_id, scope_id } = path_node;
1313
1314                // Search for this moved node current position
1315                let from_path = scope
1316                    .borrow()
1317                    .nodes
1318                    .find_child_path(&parent, |v| v == Some(&path_node))
1319                    .unwrap();
1320
1321                let mut to_path = parent.to_vec();
1322                to_path.push(to);
1323
1324                if from_path == to_path {
1325                    continue;
1326                }
1327
1328                // Remove the node from the old position and add it to the new one
1329                let path_entry = scope.borrow_mut().nodes.remove(&from_path).unwrap();
1330                scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1331
1332                if let Some(scope_id) = scope_id {
1333                    let scope_root_node_id = self.find_scope_root_node_id(scope_id);
1334                    let scope_rc = self.scopes.get(&scope_id).cloned().unwrap();
1335                    let scope = scope_rc.borrow();
1336
1337                    // Mark the scope root node id as moved
1338                    mutations
1339                        .moved
1340                        .entry(scope.parent_node_id_in_parent)
1341                        .or_default()
1342                        .push(MutationMove {
1343                            index: to,
1344                            node_id: scope_root_node_id,
1345                        });
1346                } else {
1347                    // Mark the element as moved
1348                    mutations
1349                        .moved
1350                        .entry(*parent_node_id)
1351                        .or_default()
1352                        .push(MutationMove { index: to, node_id });
1353                }
1354            }
1355        }
1356
1357        // Process deferred additions now that moves have repositioned parents
1358        for added in &deferred_adds {
1359            self.process_addition(
1360                scope,
1361                added,
1362                path_element,
1363                mutations,
1364                &mut parents_to_resync_scopes,
1365            );
1366        }
1367
1368        for (modified, flags) in diff.modified {
1369            path_element.with_element(&modified, |element| match element {
1370                PathElement::Component { .. } => {
1371                    // Components never change when being diffed
1372                    unreachable!()
1373                }
1374                PathElement::Element { element, .. } => {
1375                    let node_id = scope
1376                        .borrow()
1377                        .nodes
1378                        .get(&modified)
1379                        .map(|path_node| path_node.node_id)
1380                        .unwrap();
1381                    mutations.modified.push(MutationModified {
1382                        node_id,
1383                        element: element.clone(),
1384                        flags,
1385                    });
1386                }
1387            });
1388        }
1389
1390        // When a parent gets a new child, or a child is removed or moved we
1391        // resync its 1 level children scopes with their new path
1392        for parent in parents_to_resync_scopes {
1393            // But only if the parent already existed before otherwise its pointless
1394            // as Scopes will be created with the latest path already
1395            if diff.added.contains(&parent) {
1396                // TODO: Maybe do this check before inserting
1397                continue;
1398            }
1399
1400            // Update all the nested scopes in this Scope with their up to date paths
1401            scope
1402                .borrow_mut()
1403                .nodes
1404                .traverse_1_level(&parent, |p, path_node| {
1405                    if let Some(scope_id) = path_node.scope_id
1406                        && let Some(scope_rc) = self.scopes.get(&scope_id)
1407                    {
1408                        let mut scope = scope_rc.borrow_mut();
1409                        scope.path_in_parent = Box::from(p);
1410                    }
1411                });
1412        }
1413    }
1414
1415    /// Reloads the runner for a hot-reload: cancels tasks, reloads every scope's hooks
1416    /// (contexts are preserved), and marks every scope dirty. Task cancellation must
1417    /// happen first so stale wakers can't fire [`Message::PollTask`] against
1418    /// freshly-reloaded scopes.
1419    pub fn reload(&mut self) {
1420        self.tasks.borrow_mut().clear();
1421        self.dirty_tasks.clear();
1422        while self.receiver.try_recv().is_ok() {}
1423
1424        let mut scopes_storages = self.scopes_storages.borrow_mut();
1425        let scopes = self
1426            .scopes
1427            .iter()
1428            .sorted_by_key(|(_, s)| s.borrow().height)
1429            .map(|(_, s)| s.borrow().id)
1430            .collect::<Vec<_>>();
1431
1432        for scope_id in scopes {
1433            CurrentContext::run(
1434                CurrentContext {
1435                    scope_id,
1436                    scopes_storages: self.scopes_storages.clone(),
1437                    tasks: self.tasks.clone(),
1438                    task_id_counter: self.task_id_counter.clone(),
1439                    sender: self.sender.clone(),
1440                },
1441                || {
1442                    if let Some(storage) = scopes_storages.get_mut(&scope_id) {
1443                        storage.reset_hooks();
1444                    }
1445                },
1446            );
1447        }
1448
1449        self.dirty_scopes.extend(self.scopes.keys());
1450        let _ = self
1451            .sender
1452            .unbounded_send(Message::MarkScopeAsDirty(ScopeId::ROOT));
1453    }
1454}
1455
1456#[derive(Default, Debug)]
1457pub struct Diff {
1458    pub added: Vec<Box<[u32]>>,
1459
1460    pub modified: Vec<(Box<[u32]>, DiffModifies)>,
1461
1462    pub removed: Vec<Box<[u32]>>,
1463
1464    pub moved: HashMap<Box<[u32]>, Vec<(u32, u32)>>,
1465}
1466
1467/// Converts a new-tree path to its corresponding old-tree path by checking, for each
1468/// segment, whether that position was the destination of a movement in `moved`. If so,
1469/// the original (`from`) index is substituted so the result can be used to look up nodes
1470/// in the pre-diff nodes tree.
1471fn resolve_old_path(new_path: &[u32], moved: &HashMap<Box<[u32]>, Vec<(u32, u32)>>) -> Vec<u32> {
1472    let mut old_path = Vec::with_capacity(new_path.len());
1473    for i in 0..new_path.len() {
1474        let new_parent = &new_path[..i];
1475        let new_index = new_path[i];
1476        if let Some(movements) = moved.get(new_parent)
1477            && let Some(&(from, _)) = movements.iter().find(|(_, to)| *to == new_index)
1478        {
1479            old_path.push(from);
1480            continue;
1481        }
1482        old_path.push(new_index);
1483    }
1484    old_path
1485}
1486
1487fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1488    if ancestor.len() > candidate.len() {
1489        return false;
1490    }
1491    candidate[..ancestor.len()] == *ancestor
1492}