Skip to main content

dioxus_core/
arena.rs

1use crate::{ScopeId, Template, scheduler::ScopeOrder, virtual_dom::VirtualDom};
2use slab::Slab;
3use std::collections::HashMap;
4use std::num::NonZeroUsize;
5
6/// An Element's unique identifier.
7///
8/// `ElementId` is a `usize` that is unique within one render target - but not
9/// unique across targets or time. If an element is removed, then the `ElementId`
10/// may be reused for a new element in that target.
11#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct ElementId(usize);
14
15impl ElementId {
16    /// The root element within a render target.
17    pub const ROOT: Self = Self(0);
18
19    pub(crate) const fn new(index: usize) -> Self {
20        Self(index)
21    }
22
23    pub(crate) const fn index(self) -> usize {
24        self.0
25    }
26
27    /// Create an element id from its raw renderer-local index.
28    ///
29    /// Renderers use this when translating platform event targets back into
30    /// Dioxus element ids.
31    pub const fn from_raw(index: usize) -> Self {
32        Self(index)
33    }
34
35    /// Return this element id's raw renderer-local index.
36    ///
37    /// Renderers use this to store Dioxus element ids in their backing DOM or
38    /// interpreter node maps.
39    pub const fn raw(self) -> usize {
40        self.0
41    }
42}
43
44/// An allocated, non-root renderer element id.
45///
46/// This type is used inside mounted-state tables so absence is represented by
47/// `Option<MountedElementId>` instead of sentinel `ElementId` values.
48#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub(crate) struct MountedElementId(NonZeroUsize);
50
51impl MountedElementId {
52    pub(crate) fn new(id: ElementId) -> Self {
53        Self::from_index(id.index())
54    }
55
56    pub(crate) fn from_index(index: usize) -> Self {
57        Self(NonZeroUsize::new(index).expect("root id"))
58    }
59
60    pub(crate) fn element_id(self) -> ElementId {
61        ElementId::new(self.index())
62    }
63
64    pub(crate) fn index(self) -> usize {
65        self.0.get()
66    }
67}
68
69/// A renderer target's unique identifier.
70///
71/// Each render target has its own [`ElementId`] arena. This lets multiple
72/// renderers share one logical [`VirtualDom`] while reusing renderer-local ids.
73#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
74#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
75pub struct RenderTargetId(usize);
76
77impl RenderTargetId {
78    /// The root/default render target.
79    pub const ROOT: Self = Self(0);
80
81    pub(crate) const fn new(index: usize) -> Self {
82        Self(index)
83    }
84
85    pub(crate) const fn index(self) -> usize {
86        self.0
87    }
88}
89
90/// The mounted target for one dynamic node slot.
91///
92/// Text slots record their renderer element, component slots record their child
93/// scope (whose scope state owns the root mount), and fragment slots record the
94/// start offset of their child-mount range. These slots live on the
95/// VirtualDom-side [`Mount`](crate::mount::Mount), indexed by dynamic-node index.
96#[derive(Clone, Debug, PartialEq, Eq)]
97pub(crate) enum MountedDynamicNodeSlot {
98    Empty,
99    Text(MountedElementId),
100    Component(ScopeId),
101    Fragment(usize),
102}
103
104/// Renderer-local state for a render target.
105#[derive(Debug)]
106pub(crate) struct RenderTargetState {
107    pub(crate) elements: Slab<Option<MountId>>,
108    pub(crate) template_roots: HashMap<(Template, usize), MountedElementId>,
109}
110
111impl RenderTargetState {
112    pub(crate) fn new() -> Self {
113        let mut elements = Slab::default();
114        // The root element is always renderer-local element ID 0.
115        elements.insert(None);
116
117        Self {
118            elements,
119            template_roots: HashMap::new(),
120        }
121    }
122}
123
124/// A live mount's unique identifier.
125///
126/// `MountId` is a `usize` that is unique across the current `VirtualDom` - but not unique across time. If a mount is
127/// unmounted, then the `MountId` may be reused for a new mount.
128#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
129#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
130pub(crate) struct MountId(pub(crate) usize);
131
132impl VirtualDom {
133    pub(crate) fn current_render_target_id(&self) -> RenderTargetId {
134        self.runtime.current_render_target_id()
135    }
136
137    pub(crate) fn next_element_in_target(&mut self, target_id: RenderTargetId) -> MountedElementId {
138        let mut targets = self.runtime.render_targets.borrow_mut();
139        let target = targets.get_mut(target_id.index()).expect("target");
140        MountedElementId::new(ElementId::new(target.elements.insert(None)))
141    }
142
143    pub(crate) fn cached_template_root(
144        &self,
145        target_id: RenderTargetId,
146        template: Template,
147        root_anchor_idx: usize,
148    ) -> Option<MountedElementId> {
149        self.runtime
150            .render_targets
151            .borrow()
152            .get(target_id.index())
153            .and_then(|target| {
154                target
155                    .template_roots
156                    .get(&(template, root_anchor_idx))
157                    .copied()
158            })
159    }
160
161    pub(crate) fn allocate_template_root(
162        &mut self,
163        target_id: RenderTargetId,
164        template: Template,
165        root_anchor_idx: usize,
166    ) -> MountedElementId {
167        let mut targets = self.runtime.render_targets.borrow_mut();
168        let target = targets.get_mut(target_id.index()).expect("target");
169        let id = MountedElementId::new(ElementId::new(target.elements.insert(None)));
170        target
171            .template_roots
172            .insert((template, root_anchor_idx), id);
173        id
174    }
175
176    pub(crate) fn set_element_ref_for_mount(&self, mount: MountId, el: MountedElementId) {
177        let target_id = self.mount_target_id(mount);
178        let mut targets = self.runtime.render_targets.borrow_mut();
179        let target = targets.get_mut(target_id.index()).expect("target");
180        let element = target.elements.get_mut(el.index()).expect("element");
181        *element = Some(mount);
182    }
183
184    pub(crate) fn reclaim_for_mount(&mut self, mount: MountId, el: MountedElementId) {
185        let target_id = self.mount_target_id(mount);
186        let mut targets = self.runtime.render_targets.borrow_mut();
187        let target = targets
188            .get_mut(target_id.index())
189            .expect("reclaim target must still be registered");
190        target.elements.try_remove(el.index());
191    }
192
193    // Drop a scope without dropping its children
194    //
195    // Note: This will not remove any ids from the arena
196    pub(crate) fn drop_scope(&mut self, id: ScopeId) {
197        let height = {
198            let scope = self.scopes.remove(id.index());
199            let context = scope.state();
200            context.height
201        };
202
203        self.dirty_scopes.remove(&ScopeOrder::new(height, id));
204
205        // If this scope was a suspense boundary, remove it from the resolved scopes
206        self.resolved_scopes.retain(|s| s != &id);
207    }
208}