2022-11-02 01:42:29 +00:00
|
|
|
//! Support for storing lazy-nodes on the stack
|
|
|
|
//!
|
|
|
|
//! This module provides support for a type called `LazyNodes` which is a micro-heap located on the stack to make calls
|
|
|
|
//! to `rsx!` more efficient.
|
|
|
|
//!
|
2022-12-01 04:54:30 +00:00
|
|
|
//! To support returning rsx! from branches in match statements, we need to use dynamic dispatch on [`ScopeState`] closures.
|
2022-11-02 01:42:29 +00:00
|
|
|
//!
|
|
|
|
//! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
|
|
|
|
//! we build a tiny alloactor in the stack and allocate the closure into that.
|
|
|
|
//!
|
|
|
|
//! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
|
|
|
|
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
|
|
|
|
|
2022-12-07 21:48:25 +00:00
|
|
|
use smallbox::{smallbox, space::S16, SmallBox};
|
2022-12-06 20:24:35 +00:00
|
|
|
|
2022-11-02 01:42:29 +00:00
|
|
|
use crate::{innerlude::VNode, ScopeState};
|
|
|
|
|
|
|
|
/// A concrete type provider for closures that build [`VNode`] structures.
|
|
|
|
///
|
|
|
|
/// This struct wraps lazy structs that build [`VNode`] trees Normally, we cannot perform a blanket implementation over
|
2022-12-03 00:24:49 +00:00
|
|
|
/// closures, but if we wrap the closure in a concrete type, we can use it for different branches in matching.
|
2022-11-02 01:42:29 +00:00
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```rust, ignore
|
|
|
|
/// LazyNodes::new(|f| f.element("div", [], [], [] None))
|
|
|
|
/// ```
|
|
|
|
pub struct LazyNodes<'a, 'b> {
|
2022-12-17 04:39:19 +00:00
|
|
|
#[cfg(not(miri))]
|
|
|
|
inner: SmallBox<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b, S16>,
|
|
|
|
|
|
|
|
#[cfg(miri)]
|
2022-12-17 04:26:04 +00:00
|
|
|
inner: Box<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b>,
|
2022-11-02 01:42:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> LazyNodes<'a, 'b> {
|
|
|
|
/// Create a new [`LazyNodes`] closure, optimistically placing it onto the stack.
|
|
|
|
///
|
|
|
|
/// If the closure cannot fit into the stack allocation (16 bytes), then it
|
|
|
|
/// is placed on the heap. Most closures will fit into the stack, and is
|
|
|
|
/// the most optimal way to use the creation function.
|
2022-12-01 04:54:30 +00:00
|
|
|
pub fn new(val: impl FnOnce(&'a ScopeState) -> VNode<'a> + 'b) -> Self {
|
2022-11-02 01:42:29 +00:00
|
|
|
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
|
|
|
|
let mut slot = Some(val);
|
|
|
|
|
|
|
|
Self {
|
2022-12-17 04:39:19 +00:00
|
|
|
#[cfg(miri)]
|
2022-12-17 04:26:04 +00:00
|
|
|
inner: Box::new(move |f| {
|
2022-12-06 20:24:35 +00:00
|
|
|
let val = slot.take().expect("cannot call LazyNodes twice");
|
|
|
|
val(f)
|
|
|
|
}),
|
2022-12-17 04:39:19 +00:00
|
|
|
|
|
|
|
#[cfg(not(miri))]
|
|
|
|
inner: smallbox!(move |f| {
|
|
|
|
let val = slot.take().expect("cannot call LazyNodes twice");
|
|
|
|
val(f)
|
|
|
|
}),
|
2022-11-02 01:42:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Call the closure with the given factory to produce real [`VNode`].
|
|
|
|
///
|
|
|
|
/// ```rust, ignore
|
|
|
|
/// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
|
|
|
|
///
|
|
|
|
/// let node = f.call(cac);
|
|
|
|
/// ```
|
|
|
|
#[must_use]
|
2022-12-06 20:24:35 +00:00
|
|
|
pub fn call(mut self, f: &'a ScopeState) -> VNode<'a> {
|
|
|
|
(self.inner)(f)
|
2022-11-02 01:42:29 +00:00
|
|
|
}
|
|
|
|
}
|