tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 10TOML 3CSS 1348 Definiciones

Use slotmap instead of bespoke arena, limiting to one parent only (#196)

* feat: use slab instead of bespoke arena

* fix: all but one test

* chore: rip out forest and merge it into taffy

* fix: final tests

* polish: fix up some warnings

* chore: clean up forest and data fails

* chore: make clippy happier

* fix: compilation for no-std targets

* chore: clean up some more clippy stuff and renable more tests

* chore: remove outdated indexmap

* fix: update benches to not unwrap new elements

* fix: revert back to errors everywhere

* fix: revert complex bench too

* chore: remove unused import

* fix: removing parents

* chore: clean up and remove references to forest

* chore: remove final reference to forest

* chore: update release log

* Update error name in RELEASES

* Add single parent fix to RELEASES

* Remove extra whitespace

* chore: add warning to mark dirty function

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>

c3f6a1727f92b16eb13ae4d5acac331a122a7f59 · por Jon Kelley · (hace 4 años) · padre e8855e054 · ver el árbol en este commit

Delta semántico · 345

examples/basic.rs

  • cambió de firma fn main

examples/nested.rs

  • cambió de firma fn main

src/data.rs

src/error.rs

src/flexbox.rs

src/forest.rs

src/indexmap.rs

src/lib.rs

src/node.rs

src/sys.rs

14 archivos · +287 −3191

diff --git a/Cargo.toml b/Cargo.toml
index 444622ec1..39f68ffa3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ num-traits = { version = "0.2", default-features = false }
 typenum = "1"
 hashbrown = { version = "0.12", optional = true }
 serde = { version = "1.0", optional = true, features = ["serde_derive"] }
+slotmap = "1.0.6"
 
 [features]
 default = ["std"]
diff --git a/RELEASES.md b/RELEASES.md
index 4f9560ed8..64f40990a 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -18,11 +18,16 @@
 - renamed `taffy::forest::Forest.new-node(..)` `taffy::forest::Forest.new_with_children(..)`
 - renamed `taffy::node::Taffy.new-node(..)` -> `taffy::node::Taffy.new_with_children(..)`
 - renamed `taffy::style::Style` -> `taffy::style::FlexboxLayout` to more precicely indicate its purpose
-- renamed `taffy::Error` -> `taffy::error::InvalidNode`
+- renamed `taffy::Error` -> `taffy::error::TaffyError`
 - `taffy::Taffy::remove_child_at_index`, `taffy::Taffy::replace_child_at_index`, and `taffy::Taffy::child_at_index` now return `taffy::InvalidChild::ChildIndexOutOfBounds` instead of panicing
+- `taffy::Node` is now unique only to the Taffy instance from which it was created.
+- `taffy::error::InvalidChild` is now `taffy::error::TaffyError`
+- `taffy::error::InvalidNode` has been removed and is now just a branch on the `TaffyError` enum
+- `taffy::forest::Forest` has been merged into `taffy::node::Taffy` for a performance boost up to 90%
 
 ### 0.2.0 Fixed
 
+- nodes can only ever have one parent
 - fixed rounding of fractional values to follow latest Chrome - values are now rounded the same regardless of their position
 - fixed computing free space when using both `flex-grow` and a minimum size
 - padding is now only subtracted when determining the available space if the node size is unspecified, following [section 9.2.2 of the flexbox spec](https://www.w3.org/TR/css-flexbox-1/#line-sizing)
diff --git a/examples/basic.rs b/examples/basic.rs
index ae6835068..05e665294 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -1,6 +1,6 @@
 use taffy::prelude::*;
 
-fn main() -> Result<(), taffy::error::InvalidNode> {
+fn main() -> Result<(), taffy::error::TaffyError> {
     let mut taffy = Taffy::new();
 
     let child = taffy.new_leaf(FlexboxLayout {
diff --git a/examples/nested.rs b/examples/nested.rs
index 1915a7bca..afbc2adca 100644
--- a/examples/nested.rs
+++ b/examples/nested.rs
@@ -1,6 +1,6 @@
 use taffy::prelude::*;
 
-fn main() -> Result<(), taffy::error::InvalidNode> {
+fn main() -> Result<(), taffy::error::TaffyError> {
     let mut taffy = Taffy::new();
 
     // left
diff --git a/scripts/gentest/Cargo.toml b/scripts/gentest/Cargo.toml
index 9070e7863..64e086c10 100644
--- a/scripts/gentest/Cargo.toml
+++ b/scripts/gentest/Cargo.toml
@@ -13,4 +13,4 @@ proc-macro2 = "1.0.6"
 quote = "1.0.2"
 serde_json = "1"
 syn = "1.0.7"
-tokio = { version = "1.18", features = [ "full" ] }
+tokio = { version = "1.18", features = ["full"] }
diff --git a/src/data.rs b/src/data.rs
new file mode 100644
index 000000000..186710ddc
--- /dev/null
+++ b/src/data.rs
@@ -0,0 +1,63 @@
+//! Node Data - important layout and styling data for nodes
+//!
+//! Used to compute layout for Taffy trees
+//!
+use crate::layout::{Cache, Layout};
+use crate::node::MeasureFunc;
+use crate::style::FlexboxLayout;
+
+/// Layout information for a given [`Node`](crate::node::Node)
+///
+/// Stored in a [`Taffy`].
+pub(crate) struct NodeData {
+    /// The layout strategy used by this node
+    pub(crate) style: FlexboxLayout,
+    /// The mapping from the Size<Option<f32>> (in real units) to Size<f32> (in points) for this node
+    pub(crate) measure: Option<MeasureFunc>,
+    /// The results of the layout computation
+    pub(crate) layout: Layout,
+    /// The primary cached results of the layout computation
+    pub(crate) main_size_layout_cache: Option<Cache>,
+    /// Secondary cached results of the layout computation
+    pub(crate) other_layout_cache: Option<Cache>,
+    /// Does this node's layout need to be recomputed?
+    pub(crate) is_dirty: bool,
+}
+
+impl NodeData {
+    /// Create the data for a new node with a [`MeasureFunc`]
+    #[must_use]
+    pub fn new_with_measure(style: FlexboxLayout, measure: MeasureFunc) -> Self {
+        Self {
+            style,
+            measure: Some(measure),
+            main_size_layout_cache: None,
+            other_layout_cache: None,
+            layout: Layout::new(),
+            is_dirty: true,
+        }
+    }
+
+    /// Create the data for a new node
+    #[must_use]
+    pub fn new(style: FlexboxLayout) -> Self {
+        Self {
+            style,
+            measure: None,
+            main_size_layout_cache: None,
+            other_layout_cache: None,
+            layout: Layout::new(),
+            is_dirty: true,
+        }
+    }
+
+    /// Marks a node and all of its parents (recursively) as dirty
+    ///
+    /// This clears any cached data and signals that the data must be recomputed.
+    #[inline]
+    pub fn mark_dirty(&mut self) {
+        self.main_size_layout_cache = None;
+        self.other_layout_cache = None;
+        self.is_dirty = true;
+    }
+}
diff --git a/src/error.rs b/src/error.rs
index 30a2e8d94..88cef3bea 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -4,23 +4,12 @@ use core::fmt::{Display, Formatter, Result};
 
 use crate::node::Node;
 
-/// The [`Node`] was not found in the [`Taffy`](crate::Taffy) instance
-#[derive(Debug)]
-pub struct InvalidNode(pub Node);
-
-#[cfg(feature = "std")]
-impl Display for InvalidNode {
-    fn fmt(&self, f: &mut Formatter) -> Result {
-        write!(f, "Node {:?} is not in the Taffy instance", self.0)
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for InvalidNode {}
+/// The error Taffy generates on invalid operations
+pub type TaffyResult<T> = core::result::Result<T, TaffyError>;
 
 /// An error that occurs while trying to access or modify a [`Node`]'s children by index.
 #[derive(Debug)]
-pub enum InvalidChild {
+pub enum TaffyError {
     /// The parent [`Node`] does not have a child at `child_index`. It only has `child_count` children
     ChildIndexOutOfBounds {
         /// The parent node whose child was being looked up
@@ -34,24 +23,27 @@ pub enum InvalidChild {
     InvalidParentNode(Node),
     /// The child [`Node`] was not found in the [`Taffy`](crate::Taffy) instance.
     InvalidChildNode(Node),
+    /// The supplied [`Node`] was not found in the [`Taffy`](crate::Taffy) instance.
+    InvalidInputNode(Node),
 }
 
 #[cfg(feature = "std")]
-impl Display for InvalidChild {
+impl Display for TaffyError {
     fn fmt(&self, f: &mut Formatter) -> Result {
         match self {
-            InvalidChild::ChildIndexOutOfBounds { parent, child_index, child_count } => write!(
+            TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count } => write!(
                 f,
                 "Index (is {}) should be < child_count ({}) for parent node {:?}",
                 child_index, child_count, parent
             ),
-            InvalidChild::InvalidParentNode(parent) => {
+            TaffyError::InvalidParentNode(parent) => {
                 write!(f, "Parent Node {:?} is not in the Taffy instance", parent)
             }
-            InvalidChild::InvalidChildNode(child) => write!(f, "Child Node {:?} is not in the Taffy instance", child),
+            TaffyError::InvalidChildNode(child) => write!(f, "Child Node {:?} is not in the Taffy instance", child),
+            TaffyError::InvalidInputNode(node) => write!(f, "Supplied Node {:?} is not in the Taffy instance", node),
         }
     }
 }
 
 #[cfg(feature = "std")]
-impl std::error::Error for InvalidChild {}
+impl std::error::Error for TaffyError {}
diff --git a/src/flexbox.rs b/src/flexbox.rs
index bfb0d9ff2..79ba5374d 100644
--- a/src/flexbox.rs
+++ b/src/flexbox.rs
@@ -1,22 +1,25 @@
-//! Computes the [flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) layout algorithm on a [`Forest`](crate::forest::Forest) according to the [spec](https://www.w3.org/TR/css-flexbox-1/)
+//! Computes the [flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) layout algorithm on [`Taffy`](crate::Taffy) according to the [spec](https://www.w3.org/TR/css-flexbox-1/)
 //!
 //! Note that some minor steps appear to be missing: see https://github.com/DioxusLabs/taffy/issues for more information.
 use core::f32;
 
-use crate::forest::{Forest, NodeData};
+use slotmap::SlotMap;
+
+use crate::data::NodeData;
 use crate::geometry::{Point, Rect, Size};
 use crate::layout::{Cache, Layout};
 use crate::math::MaybeMath;
-use crate::node::{MeasureFunc, NodeId};
+use crate::node::{MeasureFunc, Node};
 use crate::resolve::{MaybeResolve, ResolveOrDefault};
 use crate::style::{AlignContent, AlignSelf, Dimension, Display, FlexWrap, JustifyContent, PositionType};
 use crate::style::{FlexDirection, FlexboxLayout};
 use crate::sys::{abs, round, ChildrenVec, Vec};
+use crate::Taffy;
 
 /// The intermediate results of a flexbox calculation for a single item
 struct FlexItem {
     /// The identifier for the associated [`Node`](crate::node::Node)
-    node: NodeId,
+    node: Node,
 
     /// The base size of this item
     size: Size<Option<f32>>,
@@ -103,9 +106,9 @@ struct AlgoConstants {
     inner_container_size: Size<f32>,
 }
 
-impl Forest {
-    /// Computes the layout of this [`Forest`] according to the flexbox algorithm
-    pub(crate) fn compute(&mut self, root: NodeId, size: Size<Option<f32>>) {
+impl Taffy {
+    /// Computes the layout of [`Taffy`] according to the flexbox algorithm
+    pub(crate) fn compute(&mut self, root: Node, size: Size<Option<f32>>) {
         let style = self.nodes[root].style;
         let has_root_min_max = style.min_size.width.is_defined()
             || style.min_size.height.is_defined()
@@ -143,7 +146,13 @@ impl Forest {
     }
 
     /// Rounds the calculated [`NodeData`] according to the spec
-    fn round_layout(nodes: &mut [NodeData], children: &[ChildrenVec<NodeId>], root: NodeId, abs_x: f32, abs_y: f32) {
+    fn round_layout(
+        nodes: &mut SlotMap<Node, NodeData>,
+        children: &SlotMap<Node, ChildrenVec<Node>>,
+        root: Node,
+        abs_x: f32,
+        abs_y: f32,
+    ) {
         let layout = &mut nodes[root].layout;
         let abs_x = abs_x + layout.location.x;
         let abs_y = abs_y + layout.location.y;
@@ -160,7 +169,7 @@ impl Forest {
     }
 
     /// Saves intermediate results to a [`Cache`]
-    fn cache(&mut self, node: NodeId, main_size: bool) -> &mut Option<Cache> {
+    fn cache(&mut self, node: Node, main_size: bool) -> &mut Option<Cache> {
         if main_size {
             &mut self.nodes[node].main_size_layout_cache
         } else {
@@ -172,7 +181,7 @@ impl Forest {
     #[inline]
     fn compute_from_cache(
         &mut self,
-        node: NodeId,
+        node: Node,
         node_size: Size<Option<f32>>,
         parent_size: Size<Option<f32>>,
         perform_layout: bool,
@@ -256,7 +265,7 @@ impl Forest {
     ///
     /// - [**Generate anonymous flex items**](https://www.w3.org/TR/css-flexbox-1/#algo-anon-box) as described in [§4 Flex Items](https://www.w3.org/TR/css-flexbox-1/#flex-items).
     #[inline]
-    fn generate_anonymous_flex_items(&self, node: NodeId, constants: &AlgoConstants) -> Vec<FlexItem> {
+    fn generate_anonymous_flex_items(&self, node: Node, constants: &AlgoConstants) -> Vec<FlexItem> {
         self.children[node]
             .iter()
             .map(|child| (child, &self.nodes[*child].style))
@@ -355,7 +364,7 @@ impl Forest {
     #[inline]
     fn determine_flex_base_size(
         &mut self,
-        node: NodeId,
+        node: Node,
         node_size: Size<Option<f32>>,
         constants: &AlgoConstants,
         available_space: Size<Option<f32>>,
@@ -494,7 +503,7 @@ impl Forest {
     #[inline]
     fn collect_flex_lines<'a>(
         &self,
-        node: NodeId,
+        node: Node,
         constants: &AlgoConstants,
         available_space: Size<Option<f32>>,
         flex_items: &'a mut Vec<FlexItem>,
@@ -816,13 +825,13 @@ impl Forest {
     #[inline]
     fn calculate_children_base_lines(
         &mut self,
-        node: NodeId,
+        node: Node,
         node_size: Size<Option<f32>>,
         flex_lines: &mut [FlexLine],
         constants: &AlgoConstants,
     ) {
         /// Recursively calculates the baseline for children
-        fn calc_baseline(db: &Forest, node: NodeId, layout: &Layout) -> f32 {
+        fn calc_baseline(db: &Taffy, node: Node, layout: &Layout) -> f32 {
             if db.children[node].is_empty() {
                 layout.size.height
             } else {
@@ -896,7 +905,7 @@ impl Forest {
     fn calculate_cross_size(
         &mut self,
         flex_lines: &mut [FlexLine],
-        node: NodeId,
+        node: Node,
         node_size: Size<Option<f32>>,
         constants: &AlgoConstants,
     ) {
@@ -950,7 +959,7 @@ impl Forest {
     fn handle_align_content_stretch(
         &mut self,
         flex_lines: &mut [FlexLine],
-        node: NodeId,
+        node: Node,
         node_size: Size<Option<f32>>,
         constants: &AlgoConstants,
     ) {
@@ -980,7 +989,7 @@ impl Forest {
     ///
     ///     **Note that this step does not affect the main size of the flex item, even if it has an intrinsic aspect ratio**.
     #[inline]
-    fn determine_used_cross_size(&mut self, flex_lines: &mut [FlexLine], node: NodeId, constants: &AlgoConstants) {
+    fn determine_used_cross_size(&mut self, flex_lines: &mut [FlexLine], node: Node, constants: &AlgoConstants) {
         for line in flex_lines {
             let line_cross_size = line.cross_size;
 
@@ -1020,12 +1029,7 @@ impl Forest {
     ///
     ///     2. Align the items along the main-axis per `justify-content`.
     #[inline]
-    fn distribute_remaining_free_space(
-        &mut self,
-        flex_lines: &mut [FlexLine],
-        node: NodeId,
-        constants: &AlgoConstants,
-    ) {
+    fn distribute_remaining_free_space(&mut self, flex_lines: &mut [FlexLine], node: Node, constants: &AlgoConstants) {
         for line in flex_lines {
             let used_space: f32 = line.items.iter().map(|child| child.outer_target_size.main(constants.dir)).sum();
             let free_space = constants.inner_container_size.main(constants.dir) - used_space;
@@ -1130,12 +1134,7 @@ impl Forest {
     ///     - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis) is auto, set it to zero.
     ///         Set the opposite margin so that the outer cross size of the item equals the cross size of its flex line.
     #[inline]
-    fn resolve_cross_axis_auto_margins(
-        &mut self,
-        flex_lines: &mut [FlexLine],
-        node: NodeId,
-        constants: &AlgoConstants,
-    ) {
+    fn resolve_cross_axis_auto_margins(&mut self, flex_lines: &mut [FlexLine], node: Node, constants: &AlgoConstants) {
         for line in flex_lines {
             let line_cross_size = line.cross_size;
             let max_baseline: f32 = line.items.iter_mut().map(|child| child.baseline).fold(0.0, |acc, x| acc.max(x));
@@ -1190,7 +1189,7 @@ impl Forest {
     #[inline]
     fn align_flex_items_along_cross_axis(
         &self,
-        node: NodeId,
+        node: Node,
         child: &mut FlexItem,
         child_style: &FlexboxLayout,
         free_space: f32,
@@ -1279,7 +1278,7 @@ impl Forest {
     fn align_flex_lines_per_align_content(
         &self,
         flex_lines: &mut [FlexLine],
-        node: NodeId,
+        node: Node,
         constants: &AlgoConstants,
         total_cross_size: f32,
     ) {
@@ -1338,7 +1337,7 @@ impl Forest {
 
     /// Do a final layout pass and collect the resulting layouts.
     #[inline]
-    fn final_layout_pass(&mut self, node: NodeId, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
+    fn final_layout_pass(&mut self, node: Node, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
         let mut total_offset_cross = constants.padding_border.cross_start(constants.dir);
 
         let layout_line = |line: &mut FlexLine| {
@@ -1399,7 +1398,7 @@ impl Forest {
 
     /// Perform absolute layout on all absolutely positioned children.
     #[inline]
-    fn perform_absolute_layout_on_absolute_children(&mut self, node: NodeId, constants: &AlgoConstants) {
+    fn perform_absolute_layout_on_absolute_children(&mut self, node: Node, constants: &AlgoConstants) {
         // TODO: remove number of Vec<_> generated
         let candidates = self.children[node]
             .iter()
@@ -1556,7 +1555,7 @@ impl Forest {
     /// Compute a preliminary size for an item
     fn compute_preliminary(
         &mut self,
-        node: NodeId,
+        node: Node,
         node_size: Size<Option<f32>>,
         parent_size: Size<Option<f32>>,
         perform_layout: bool,
@@ -1570,7 +1569,7 @@ impl Forest {
         }
 
         // Define some general constants we will need for the remainder of the algorithm.
-        let mut constants = Forest::compute_constants(&self.nodes[node], node_size, parent_size);
+        let mut constants = Taffy::compute_constants(&self.nodes[node], node_size, parent_size);
 
         // If this is a leaf node we can skip a lot of this function in some cases
         if self.children[node].is_empty() {
@@ -1581,6 +1580,7 @@ impl Forest {
             if let Some(ref measure) = self.nodes[node].measure {
                 let converted_size = match measure {
                     MeasureFunc::Raw(measure) => measure(node_size),
+
                     #[cfg(any(feature = "std", feature = "alloc"))]
                     MeasureFunc::Boxed(measure) => measure(node_size),
                 };
@@ -1718,7 +1718,12 @@ impl Forest {
         /// Lay out all hidden nodes recursively
         ///
         /// Each hidden node has zero size and is placed at the origin
-        fn hidden_layout(nodes: &mut [NodeData], children: &[ChildrenVec<NodeId>], node: NodeId, order: u32) {
+        fn hidden_layout(
+            nodes: &mut SlotMap<Node, NodeData>,
+            children: &SlotMap<Node, ChildrenVec<Node>>,
+            node: Node,
+            order: u32,
+        ) {
             nodes[node].layout = Layout { order, size: Size::ZERO, location: Point::ZERO };
 
             for (order, child) in children[node].iter().enumerate() {
@@ -1742,25 +1747,25 @@ impl Forest {
 #[cfg(test)]
 mod tests {
     use crate::{
-        forest::Forest,
         math::MaybeMath,
         prelude::{Rect, Size},
         resolve::ResolveOrDefault,
         style::{FlexWrap, FlexboxLayout},
+        Taffy,
     };
 
     // Make sure we get correct constants
     #[test]
     fn correct_constants() {
-        let mut forest = Forest::with_capacity(16);
+        let mut tree = Taffy::with_capacity(16);
 
         let style = FlexboxLayout::default();
-        let node_id = forest.new_leaf(style);
+        let node_id = tree.new_leaf(style).unwrap();
 
         let node_size = Size::undefined();
         let parent_size = Size::undefined();
 
-        let constants = Forest::compute_constants(&forest.nodes[node_id], node_size, parent_size);
+        let constants = Taffy::compute_constants(&tree.nodes[node_id], node_size, parent_size);
 
         assert!(constants.dir == style.flex_direction);
         assert!(constants.is_row == style.flex_direction.is_row());
diff --git a/src/forest.rs b/src/forest.rs
deleted file mode 100644
index 97f6ae120..000000000
--- a/src/forest.rs
+++ /dev/null
@@ -1,597 +0,0 @@
-//! Forest - a struct-of-arrays data structure for storing node trees.
-//!
-//! Backing data structure for `Taffy` structs.
-use crate::layout::{Cache, Layout};
-use crate::node::{MeasureFunc, NodeId};
-use crate::style::FlexboxLayout;
-use crate::sys::{new_vec_with_capacity, ChildrenVec, ParentsVec, Vec};
-
-/// Layout information for a given [`Node`](crate::node::Node)
-///
-/// Stored in a [`Forest`].
-pub(crate) struct NodeData {
-    /// The layout strategy used by this node
-    pub(crate) style: FlexboxLayout,
-    /// The mapping from the Size<Option<f32>> (in real units) to Size<f32> (in points) for this node
-    pub(crate) measure: Option<MeasureFunc>,
-    /// The results of the layout computation
-    pub(crate) layout: Layout,
-    /// The primary cached results of the layout computation
-    pub(crate) main_size_layout_cache: Option<Cache>,
-    /// Secondary cached results of the layout computation
-    pub(crate) other_layout_cache: Option<Cache>,
-    /// Does this node's layout need to be recomputed?
-    pub(crate) is_dirty: bool,
-}
-
-impl NodeData {
-    /// Create the data for a new node with a [`MeasureFunc`]
-    #[must_use]
-    fn new_with_measure(style: FlexboxLayout, measure: MeasureFunc) -> Self {
-        Self {
-            style,
-            measure: Some(measure),
-            main_size_layout_cache: None,
-            other_layout_cache: None,
-            layout: Layout::new(),
-            is_dirty: true,
-        }
-    }
-
-    /// Create the data for a new node
-    #[must_use]
-    fn new(style: FlexboxLayout) -> Self {
-        Self {
-            style,
-            measure: None,
-            main_size_layout_cache: None,
-            other_layout_cache: None,
-            layout: Layout::new(),
-            is_dirty: true,
-        }
-    }
-
-    /// Marks a node and all of its parents (recursively) as dirty
-    ///
-    /// This clears any cached data and signals that the data must be recomputed.
-    #[inline]
-    fn mark_dirty(&mut self) {
-        self.main_size_layout_cache = None;
-        self.other_layout_cache = None;
-        self.is_dirty = true;
-    }
-}
-
-/// A collection of UI layout trees used to store [`NodeData`] associated with specific [`Nodes`](crate::node::Node)
-pub(crate) struct Forest {
-    /// The [`NodeData`] for each node stored in this forest
-    pub(crate) nodes: Vec<NodeData>,
-    /// The children of each node
-    ///
-    /// The indexes in the outer vector correspond to the position of the parent [`NodeData`]
-    pub(crate) children: Vec<ChildrenVec<NodeId>>,
-    /// The parents of each node
-    ///
-    /// The indexes in the outer vector correspond to the position of the child [`NodeData`]
-    pub(crate) parents: Vec<ParentsVec<NodeId>>,
-}
-
-impl Forest {
-    /// Creates a new [`Forest`] that can store up to `capacity` [`Nodes`](crate::node::Node) before needing to be expanded
-    #[must_use]
-    pub(crate) fn with_capacity(capacity: usize) -> Self {
-        Self {
-            nodes: new_vec_with_capacity(capacity),
-            children: new_vec_with_capacity(capacity),
-            parents: new_vec_with_capacity(capacity),
-        }
-    }
-
-    /// Creates and adds a new unattached leaf node to the forest, and returns the [`NodeId`] of the new node
-    pub(crate) fn new_leaf(&mut self, layout: FlexboxLayout) -> NodeId {
-        let id = self.len();
-        self.nodes.push(NodeData::new(layout));
-        self.children.push(new_vec_with_capacity(0));
-        self.parents.push(new_vec_with_capacity(1));
-        id
-    }
-
-    /// Creates and adds a new unattached leaf node to the forest, and returns the [`NodeId`] of the new node
-    ///
-    /// The node must have a [`MeasureFunc`] supplied
-    pub(crate) fn new_leaf_with_measure(&mut self, layout: FlexboxLayout, measure: MeasureFunc) -> NodeId {
-        let id = self.len();
-        self.nodes.push(NodeData::new_with_measure(layout, measure));
-        self.children.push(new_vec_with_capacity(0));
-        self.parents.push(new_vec_with_capacity(1));
-        id
-    }
-
-    /// Creates and adds a new unparented node to the forest with the associated children attached, and returns the [`NodeId`] of the new node
-    pub(crate) fn new_with_children(&mut self, layout: FlexboxLayout, children: ChildrenVec<NodeId>) -> NodeId {
-        let id = self.len();
-        for child in &children {
-            self.parents[*child].push(id);
-        }
-        self.nodes.push(NodeData::new(layout));
-        self.children.push(children);
-        self.parents.push(new_vec_with_capacity(1));
-        id
-    }
-
-    /// Adds a `child` node to the `parent` node
-    pub(crate) fn add_child(&mut self, parent: NodeId, child: NodeId) {
-        self.parents[child].push(parent);
-        self.children[parent].push(child);
-        self.mark_dirty(parent)
-    }
-
-    /// Removes all nodes and resets the data structure
-    ///
-    /// The capacity is retained.
-    pub(crate) fn clear(&mut self) {
-        self.nodes.clear();
-        self.children.clear();
-        self.parents.clear();
-    }
-
-    /// Removes the specified `node`
-    ///
-    /// The last existing node is moved to its previous position, in order to ensure compactness.
-    /// Returns the previous [`NodeId`] of the moved node, if one was moved.
-    pub(crate) fn swap_remove(&mut self, node: NodeId) -> Option<NodeId> {
-        self.nodes.swap_remove(node);
-
-        // Now the last element is swapped in at index `node`.
-        if self.nodes.is_empty() {
-            self.children.clear();
-            self.parents.clear();
-            return None;
-        }
-
-        // Remove old node as parent from all its children.
-        for child in &self.children[node] {
-            let parents_child = &mut self.parents[*child];
-            let mut pos = 0;
-            while pos < parents_child.len() {
-                if parents_child[pos] == node {
-                    parents_child.swap_remove(pos);
-                } else {
-                    pos += 1;
-                }
-            }
-        }
-
-        // Remove old node as child from all its parents.
-        for parent in &self.parents[node] {
-            let childrens_parent = &mut self.children[*parent];
-            let mut pos = 0;
-            while pos < childrens_parent.len() {
-                if childrens_parent[pos] == node {
-                    childrens_parent.swap_remove(pos);
-                } else {
-                    pos += 1;
-                }
-            }
-        }
-
-        let last = self.len();
-        if last != node {
-            // Update ids for every child of the swapped in node.
-            for child in &self.children[last] {
-                for parent in &mut self.parents[*child] {
-                    if *parent == last {
-                        *parent = node;
-                    }
-                }
-            }
-
-            // Update ids for every parent of the swapped in node.
-            for parent in &self.parents[last] {
-                for child in &mut self.children[*parent] {
-                    if *child == last {
-                        *child = node;
-                    }
-                }
-            }
-
-            self.children.swap_remove(node);
-            self.parents.swap_remove(node);
-
-            Some(last)
-        } else {
-            self.children.swap_remove(node);
-            self.parents.swap_remove(node);
-            None
-        }
-    }
-
-    /// Breaks the link between the `parent` node and the `child` node
-    ///
-    /// The `child`'s data is not removed.
-    pub(crate) fn remove_child(&mut self, parent: NodeId, child: NodeId) -> NodeId {
-        let index = self.children[parent].iter().position(|n| *n == child).unwrap();
-        self.remove_child_at_index(parent, index)
-    }
-
-    /// Breaks the link between the `parent` node and the n-th child node
-    ///
-    /// The child's data is not removed.
-    pub(crate) fn remove_child_at_index(&mut self, parent: NodeId, child_index: usize) -> NodeId {
-        let child = self.children[parent].remove(child_index);
-        self.parents[child].retain(|p| *p != parent);
-        self.mark_dirty(parent);
-        child
-    }
-
-    /// Marks the `node` as needing layout recalculation
-    ///
-    /// Any cached layout information is cleared.
-    pub(crate) fn mark_dirty(&mut self, node: NodeId) {
-        /// Performs a recursive depth-first search up the tree until the root node is reached
-        ///
-        ///  WARNING: this will stack-overflow if the tree contains a cycle
-        fn mark_dirty_recursive(nodes: &mut Vec<NodeData>, parents: &[ParentsVec<NodeId>], node_id: NodeId) {
-            nodes[node_id].mark_dirty();
-
-            for parent in &parents[node_id] {
-                mark_dirty_recursive(nodes, parents, *parent);
-            }
-        }
-
-        mark_dirty_recursive(&mut self.nodes, &self.parents, node);
-    }
-
-    /// Returns the number of elements in the forest.
-    #[inline]
-    pub(crate) fn len(&self) -> usize {
-        // This could be either [nodes], [parents] or [children] since they all must always have
-        // the same length, given the data structure properties.
-        self.nodes.len()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::{Forest, NodeData};
-    use crate::geometry::Size;
-    use crate::node::{MeasureFunc, NodeId};
-    use crate::style::FlexboxLayout;
-    use crate::sys::ChildrenVec;
-
-    fn assert_forest_size(forest: &Forest, size: usize) {
-        // This should assert the forest consistency, each vector must be have the same length
-        assert_eq!(forest.nodes.len(), size);
-        assert_eq!(forest.children.len(), size);
-        assert_eq!(forest.parents.len(), size);
-    }
-
-    fn node_measure_eq(node: &NodeData, measure_fn: fn(Size<Option<f32>>) -> Size<f32>) -> bool {
-        match node.measure.as_ref().unwrap() {
-            MeasureFunc::Raw(m) => measure_fn(Size::NONE) == m(Size::NONE),
-            #[cfg(any(feature = "std", feature = "alloc"))]
-            _ => false,
-        }
-    }
-
-    fn add_default_leaf(forest: &mut Forest) -> NodeId {
-        forest.new_leaf(FlexboxLayout::default())
-    }
-
-    /// Generates a non-default FlexboxLayout that is used to compare NodeData instances
-    fn get_non_default_layout(val: f32) -> FlexboxLayout {
-        FlexboxLayout { flex_grow: val, ..Default::default() }
-    }
-
-    #[test]
-    fn new_leaf_first_leaf() {
-        let mut forest = Forest::with_capacity(1);
-        let s1 = get_non_default_layout(1.0);
-
-        let id = forest.new_leaf(s1);
-
-        let node = &forest.nodes[id];
-        assert_eq!(id, 0);
-        assert_eq!(node.style, s1);
-        assert_forest_size(&forest, 1);
-    }
-
-    #[test]
-    fn new_leaf_second_leaf() {
-        let mut forest = Forest::with_capacity(2);
-        let s1 = get_non_default_layout(1.0);
-        let s2 = get_non_default_layout(2.0);
-        forest.new_leaf(s1);
-        let id = forest.new_leaf(s2);
-
-        let node = &forest.nodes[id];
-        assert_eq!(id, 1);
-        assert_eq!(node.style, s2);
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn new_leaf_with_measure_first_leaf() {
-        let mut forest = Forest::with_capacity(1);
-        let s1 = get_non_default_layout(1.0);
-        let measure_fn1 = |_| Size { width: 1.0, height: 1.0 };
-
-        let id = forest.new_leaf_with_measure(s1, MeasureFunc::Raw(measure_fn1));
-
-        let node = &forest.nodes[id];
-        assert_eq!(id, 0);
-        assert_eq!(node.style, s1);
-        assert!(node_measure_eq(&node, measure_fn1));
-        assert_forest_size(&forest, 1);
-    }
-
-    #[test]
-    fn new_leaf_with_measure_second_leaf() {
-        let mut forest = Forest::with_capacity(2);
-        let s1 = get_non_default_layout(1.0);
-        let s2 = get_non_default_layout(2.0);
-        let measure_fn1 = |_| Size { width: 1.0, height: 1.0 };
-        let measure_fn2 = |_| Size { width: 2.0, height: 2.0 };
-
-        forest.new_leaf_with_measure(s1, MeasureFunc::Raw(measure_fn1));
-        let id = forest.new_leaf_with_measure(s2, MeasureFunc::Raw(measure_fn2));
-
-        let node = &forest.nodes[id];
-        assert_eq!(id, 1);
-        assert_eq!(node.style, s2);
-        assert!(node_measure_eq(&node, measure_fn2));
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn new_with_children_single() {
-        let mut forest = Forest::with_capacity(2);
-        let style = get_non_default_layout(1.0);
-        let c1_id = add_default_leaf(&mut forest);
-        let children = ChildrenVec::from_iter([c1_id]);
-
-        let id = forest.new_with_children(style, children);
-        let new_node = &forest.nodes[id];
-
-        assert_eq!(id, 1);
-        assert_eq!(new_node.style, style);
-        assert_eq!(forest.parents[c1_id][0], id);
-        assert_eq!(forest.children[id][0], c1_id);
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn new_with_children_multiple() {
-        let mut forest = Forest::with_capacity(3);
-        let style = get_non_default_layout(1.0);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        let children = ChildrenVec::from_iter([c1_id, c2_id]);
-
-        let id = forest.new_with_children(style, children);
-        let new_node = &forest.nodes[id];
-
-        assert_eq!(id, 2);
-        assert_eq!(new_node.style, style);
-        assert_eq!(forest.parents[c1_id][0], id);
-        assert_eq!(forest.parents[c2_id][0], id);
-        assert_eq!(forest.children[id][0], c1_id);
-        assert_eq!(forest.children[id][1], c2_id);
-        assert_forest_size(&forest, 3);
-    }
-
-    #[test]
-    fn add_child() {
-        let mut forest = Forest::with_capacity(2);
-        let parent_id = add_default_leaf(&mut forest);
-        let child_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, child_id);
-
-        let parent = &forest.nodes[parent_id];
-
-        assert_eq!(forest.parents[child_id][0], parent_id);
-        assert_eq!(forest.children[parent_id][0], child_id);
-        assert!(parent.is_dirty);
-    }
-
-    #[test]
-    fn add_child_multiple() {
-        let mut forest = Forest::with_capacity(3);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(parent_id, c2_id);
-
-        let parent = &forest.nodes[parent_id];
-
-        assert_eq!(forest.parents[c1_id][0], parent_id);
-        assert_eq!(forest.parents[c2_id][0], parent_id);
-        assert_eq!(forest.children[parent_id][0], c1_id);
-        assert_eq!(forest.children[parent_id][1], c2_id);
-        assert!(parent.is_dirty);
-    }
-
-    #[test]
-    fn clear() {
-        let mut forest = Forest::with_capacity(1);
-        add_default_leaf(&mut forest);
-        forest.clear();
-        assert_forest_size(&forest, 0);
-    }
-
-    #[test]
-    fn swap_remove_single() {
-        let mut forest = Forest::with_capacity(1);
-        let parent_id = add_default_leaf(&mut forest);
-
-        let moved_id = forest.swap_remove(parent_id);
-
-        assert_eq!(moved_id, None);
-        assert_forest_size(&forest, 0);
-    }
-
-    #[test]
-    fn swap_remove_parent() {
-        let mut forest = Forest::with_capacity(3);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(parent_id, c2_id);
-
-        let moved_id = forest.swap_remove(parent_id);
-        let new_c1_id = parent_id.clone();
-        let new_c2_id = c1_id.clone();
-
-        assert_eq!(moved_id, Some(c2_id));
-        assert_eq!(forest.parents[new_c1_id].len(), 0);
-        assert_eq!(forest.parents[new_c2_id].len(), 0);
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn swap_remove_parent_nested() {
-        let mut forest = Forest::with_capacity(3);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(c1_id, c2_id);
-
-        let moved_id = forest.swap_remove(parent_id);
-        let new_c1_id = c1_id.clone();
-        let new_c2_id = parent_id.clone();
-
-        assert_eq!(moved_id, Some(c2_id));
-        assert_eq!(forest.parents[new_c1_id].len(), 0);
-        assert_eq!(forest.parents[new_c2_id].len(), 1);
-        assert_eq!(forest.parents[new_c2_id][0], new_c1_id);
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn swap_remove_first_child_nested() {
-        let mut forest = Forest::with_capacity(3);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(parent_id, c2_id);
-
-        let moved_id = forest.swap_remove(c1_id);
-        let new_c2_id = c1_id.clone();
-
-        assert_eq!(moved_id, Some(c2_id));
-        assert_eq!(forest.children[parent_id].len(), 1);
-        assert_eq!(forest.children[parent_id][0], new_c2_id);
-        assert_eq!(forest.parents[new_c2_id].len(), 1);
-        assert_eq!(forest.parents[new_c2_id][0], parent_id);
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn swap_remove_last_child_nested() {
-        let mut forest = Forest::with_capacity(3);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(parent_id, c2_id);
-
-        let moved_id = forest.swap_remove(c2_id);
-
-        assert_eq!(moved_id, None);
-        assert_eq!(forest.children[parent_id].len(), 1);
-        assert_eq!(forest.parents[c1_id].len(), 1);
-        assert_forest_size(&forest, 2);
-    }
-
-    #[test]
-    fn swap_remove_disjoint() {
-        let mut forest = Forest::with_capacity(2);
-        let n1_id = add_default_leaf(&mut forest);
-        let n2_id = add_default_leaf(&mut forest);
-
-        let moved_id = forest.swap_remove(n1_id);
-
-        assert_eq!(moved_id, Some(n2_id));
-        assert_forest_size(&forest, 1);
-    }
-
-    #[test]
-    fn remove_child() {
-        let mut forest = Forest::with_capacity(3);
-        let layout = get_non_default_layout(1.0);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = forest.new_leaf(layout);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(parent_id, c2_id);
-
-        let removed_id = forest.remove_child(parent_id, c1_id);
-        let parent = &forest.nodes[parent_id];
-
-        // node data should be preserved
-        assert_forest_size(&forest, 3);
-        assert_eq!(forest.nodes[removed_id].style, layout);
-        assert_eq!(forest.children[parent_id].len(), 1);
-        assert_eq!(forest.parents[c1_id].len(), 0);
-        assert_eq!(forest.parents[c2_id].len(), 1);
-        assert_eq!(removed_id, c1_id);
-        assert!(parent.is_dirty);
-    }
-
-    #[test]
-    fn remove_child_at_index() {
-        let mut forest = Forest::with_capacity(3);
-        let layout = get_non_default_layout(1.0);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = forest.new_leaf(layout);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(parent_id, c2_id);
-
-        let removed_id = forest.remove_child_at_index(parent_id, 0);
-        let parent = &forest.nodes[parent_id];
-
-        // node data should be preserved
-        assert_forest_size(&forest, 3);
-        assert_eq!(&forest.nodes[removed_id].style, &layout);
-        assert_eq!(forest.children[parent_id].len(), 1);
-        assert_eq!(forest.parents[c1_id].len(), 0);
-        assert_eq!(forest.parents[c2_id].len(), 1);
-        assert_eq!(removed_id, c1_id);
-        assert!(parent.is_dirty);
-    }
-
-    #[test]
-    fn mark_dirty_propagates_to_parents() {
-        let mut forest = Forest::with_capacity(3);
-        let parent_id = add_default_leaf(&mut forest);
-        let c1_id = add_default_leaf(&mut forest);
-        let c2_id = add_default_leaf(&mut forest);
-        forest.add_child(parent_id, c1_id);
-        forest.add_child(c1_id, c2_id);
-
-        forest.mark_dirty(c2_id);
-
-        assert!(forest.nodes[c2_id].is_dirty);
-        assert!(forest.nodes[c1_id].is_dirty);
-        assert!(forest.nodes[parent_id].is_dirty);
-    }
-
-    #[test]
-    fn len_zero() {
-        let forest = Forest::with_capacity(1);
-        assert_eq!(forest.len(), 0);
-    }
-
-    #[test]
-    fn len_multiple() {
-        let mut forest = Forest::with_capacity(2);
-        _ = add_default_leaf(&mut forest);
-        _ = add_default_leaf(&mut forest);
-        assert_eq!(forest.len(), 2);
-    }
-}
diff --git a/src/indexmap.rs b/src/indexmap.rs
deleted file mode 100644
index 92a7ec7b2..000000000
--- a/src/indexmap.rs
+++ /dev/null
@@ -1,2200 +0,0 @@
-//! We used to have a dependency on the entirety of the [`heapless`] crate and
-//! therefore all of its dependencies. We only use heapless::FnvIndexMap from
-//! that crate and then only in the config
-//! `#[cfg(all(not(feature = "alloc"), not(feature = "std")))]` so we decided to
-//! vendor the relevant code. The only changes we have made are removing the entry
-//! api because we didn't need it, fixing some of the things clippy complained
-//! about, and migrating some of the doctests to unit tests. We advise future
-//! contributors to use caution if you need to touch this code.
-//! This is vendored well-tested code that we didn't want to modify so we opted to
-//! switch from `#![forbid(unsafe_code)]` to `#![deny(unsafe_code)] in our lib.rs,
-//! and to `#![allow(unsafe_code)]` for this file only for the reasons listed.
-//!
-//! [`heapless`]: https://github.com/japaric/heapless
-//! https://web.archive.org/web/20220609184943/https://github.com/japaric/heapless
-#![allow(unsafe_code)]
-
-// Copyright (c) 2017 Jorge Aparicio
-
-// Permission is hereby granted, free of charge, to any
-// person obtaining a copy of this software and associated
-// documentation files (the "Software"), to deal in the
-// Software without restriction, including without
-// limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software
-// is furnished to do so, subject to the following
-// conditions:
-
-// The above copyright notice and this permission notice
-// shall be included in all copies or substantial portions
-// of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-
-mod sealed {
-    // copied from
-    // https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/sealed.rs
-    // https://web.archive.org/web/20220609184545/https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/sealed.rs
-
-    #[allow(clippy::no_effect)]
-    #[allow(dead_code)]
-    #[allow(path_statements)]
-    pub(super) const fn smaller_than<const N: usize, const MAX: usize>() {
-        Assert::<N, MAX>::LESS;
-    }
-
-    #[allow(clippy::no_effect)]
-    #[allow(dead_code)]
-    #[allow(path_statements)]
-    pub(super) const fn greater_than_eq_0<const N: usize>() {
-        Assert::<N, 0>::GREATER_EQ;
-    }
-
-    #[allow(clippy::no_effect)]
-    #[allow(dead_code)]
-    #[allow(path_statements)]
-    pub(super) const fn greater_than_0<const N: usize>() {
-        Assert::<N, 0>::GREATER;
-    }
-
-    #[allow(clippy::no_effect)]
-    #[allow(dead_code)]
-    #[allow(path_statements)]
-    pub(super) const fn greater_than_1<const N: usize>() {
-        Assert::<N, 1>::GREATER;
-    }
-
-    #[allow(clippy::no_effect)]
-    #[allow(dead_code)]
-    #[allow(path_statements)]
-    pub(super) const fn power_of_two<const N: usize>() {
-        Assert::<N, 0>::GREATER;
-        Assert::<N, 0>::POWER_OF_TWO;
-    }
-
-    #[allow(clippy::no_effect)]
-    #[allow(dead_code)]
-    /// Const assert hack
-    pub(super) struct Assert<const L: usize, const R: usize>;
-
-    #[allow(dead_code)]
-    impl<const L: usize, const R: usize> Assert<L, R> {
-        /// Const assert hack
-        pub(super) const GREATER_EQ: usize = L - R;
-
-        /// Const assert hack
-        pub(super) const LESS_EQ: usize = R - L;
-
-        #[allow(clippy::erasing_op)]
-        /// Const assert hack
-        pub(super) const NOT_EQ: isize = 0 / (R as isize - L as isize);
-
-        /// Const assert hack
-        pub(super) const EQ: usize = (R - L) + (L - R);
-
-        /// Const assert hack
-        pub(super) const GREATER: usize = L - R - 1;
-
-        /// Const assert hack
-        pub(super) const LESS: usize = R - L - 1;
-
-        /// Const assert hack
-        pub(super) const POWER_OF_TWO: usize = 0 - (L & (L - 1));
-    }
-}
-
-mod vec {
-    // copied from
-    // https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/vec.rs
-    // https://web.archive.org/web/20220609184505/https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/vec.rs
-
-    use core::{cmp::Ordering, convert::TryFrom, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, ptr, slice};
-
-    /// A fixed capacity [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)
-    pub(super) struct Vec<T, const N: usize> {
-        // NOTE order is important for optimizations. the `len` first layout lets the compiler optimize
-        // `new` to: reserve stack space and zero the first word. With the fields in the reverse order
-        // the compiler optimizes `new` to `memclr`-ing the *entire* stack space, including the `buffer`
-        // field which should be left uninitialized. Optimizations were last checked with Rust 1.60
-        len: usize,
-
-        buffer: [MaybeUninit<T>; N],
-    }
-
-    impl<T, const N: usize> Vec<T, N> {
-        const ELEM: MaybeUninit<T> = MaybeUninit::uninit();
-        const INIT: [MaybeUninit<T>; N] = [Self::ELEM; N]; // important for optimization of `new`
-
-        /// Constructs a new, empty vector with a fixed capacity of `N`
-        /// `Vec` `const` constructor; wrap the returned value in [`Vec`](../struct.Vec.html)
-        pub(super) const fn new() -> Self {
-            // Const assert N >= 0
-            super::sealed::greater_than_eq_0::<N>();
-
-            Self { len: 0, buffer: Self::INIT }
-        }
-
-        /// Constructs a new vector with a fixed capacity of `N` and fills it
-        /// with the provided slice.
-        #[inline]
-        pub(super) fn from_slice(other: &[T]) -> Result<Self, ()>
-        where
-            T: Clone,
-        {
-            let mut v = Vec::new();
-            v.extend_from_slice(other)?;
-            Ok(v)
-        }
-
-        /// Clones a vec into a new vec
-        pub(super) fn clone(&self) -> Self
-        where
-            T: Clone,
-        {
-            let mut new = Self::new();
-            // avoid `extend_from_slice` as that introduces a runtime check / panicking branch
-            for elem in self {
-                unsafe {
-                    new.push_unchecked(elem.clone());
-                }
-            }
-            new
-        }
-
-        /// Extracts a slice containing the entire vector.
-        ///
-        /// Equivalent to `&s[..]`.
-        pub(super) fn as_slice(&self) -> &[T] {
-            // NOTE(unsafe) avoid bound checks in the slicing operation
-            // &buffer[..self.len]
-            unsafe { slice::from_raw_parts(self.buffer.as_ptr() as *const T, self.len) }
-        }
-
-        /// Returns the contents of the vector as an array of length `M` if the length
-        /// of the vector is exactly `M`, otherwise returns `Err(self)`.
-        #[allow(dead_code)]
-        pub(super) fn into_array<const M: usize>(self) -> Result<[T; M], Self> {
-            if self.len() == M {
-                // This is how the unstable `MaybeUninit::array_assume_init` method does it
-                let array = unsafe { (&self.buffer as *const _ as *const [T; M]).read() };
-
-                // We don't want `self`'s destructor to be called because that would drop all the
-                // items in the array
-                core::mem::forget(self);
-
-                Ok(array)
-            } else {
-                Err(self)
-            }
-        }
-
-        /// Extracts a mutable slice containing the entire vector.
-        ///
-        /// Equivalent to `&s[..]`.
-        pub(super) fn as_mut_slice(&mut self) -> &mut [T] {
-            // NOTE(unsafe) avoid bound checks in the slicing operation
-            // &mut buffer[..self.len]
-            unsafe { slice::from_raw_parts_mut(self.buffer.as_mut_ptr() as *mut T, self.len) }
-        }
-
-        /// Returns the maximum number of elements the vector can hold.
-        pub(super) const fn capacity(&self) -> usize {
-            N
-        }
-
-        /// Clears the vector, removing all values.
-        pub(super) fn clear(&mut self) {
-            self.truncate(0);
-        }
-
-        /// Extends the vec from an iterator.
-        ///
-        /// # Panic
-        ///
-        /// Panics if the vec cannot hold all elements of the iterator.
-        pub(super) fn extend<I>(&mut self, iter: I)
-        where
-            I: IntoIterator<Item = T>,
-        {
-            for elem in iter {
-                self.push(elem).ok().unwrap()
-            }
-        }
-
-        /// Clones and appends all elements in a slice to the `Vec`.
-        ///
-        /// Iterates over the slice `other`, clones each element, and then appends
-        /// it to this `Vec`. The `other` vector is traversed in-order.
-        pub(super) fn extend_from_slice(&mut self, other: &[T]) -> Result<(), ()>
-        where
-            T: Clone,
-        {
-            if self.len + other.len() > self.capacity() {
-                // won't fit in the `Vec`; don't modify anything and return an error
-                Err(())
-            } else {
-                for elem in other {
-                    unsafe {
-                        self.push_unchecked(elem.clone());
-                    }
-                }
-                Ok(())
-            }
-        }
-
-        /// Removes the last element from a vector and returns it, or `None` if it's empty
-        pub(super) fn pop(&mut self) -> Option<T> {
-            if self.len != 0 {
-                Some(unsafe { self.pop_unchecked() })
-            } else {
-                None
-            }
-        }
-
-        /// Appends an `item` to the back of the collection
-        ///
-        /// Returns back the `item` if the vector is full
-        pub(super) fn push(&mut self, item: T) -> Result<(), T> {
-            if self.len < self.capacity() {
-                unsafe { self.push_unchecked(item) }
-                Ok(())
-            } else {
-                Err(item)
-            }
-        }
-
-        /// Removes the last element from a vector and returns it
-        ///
-        /// # Safety
-        ///
-        /// This assumes the vec to have at least one element.
-        pub(super) unsafe fn pop_unchecked(&mut self) -> T {
-            debug_assert!(!self.is_empty());
-
-            self.len -= 1;
-            (self.buffer.get_unchecked_mut(self.len).as_ptr() as *const T).read()
-        }
-
-        /// Appends an `item` to the back of the collection
-        ///
-        /// # Safety
-        ///
-        /// This assumes the vec is not full.
-        pub(super) unsafe fn push_unchecked(&mut self, item: T) {
-            // NOTE(ptr::write) the memory slot that we are about to write to is uninitialized. We
-            // use `ptr::write` to avoid running `T`'s destructor on the uninitialized memory
-            debug_assert!(!self.is_full());
-
-            *self.buffer.get_unchecked_mut(self.len) = MaybeUninit::new(item);
-
-            self.len += 1;
-        }
-
-        /// Shortens the vector, keeping the first `len` elements and dropping the rest.
-        pub(super) fn truncate(&mut self, len: usize) {
-            // This is safe because:
-            //
-            // * the slice passed to `drop_in_place` is valid; the `len > self.len`
-            //   case avoids creating an invalid slice, and
-            // * the `len` of the vector is shrunk before calling `drop_in_place`,
-            //   such that no value will be dropped twice in case `drop_in_place`
-            //   were to panic once (if it panics twice, the program aborts).
-            unsafe {
-                // Note: It's intentional that this is `>` and not `>=`.
-                //       Changing it to `>=` has negative performance
-                //       implications in some cases. See rust-lang/rust#78884 for more.
-                if len > self.len {
-                    return;
-                }
-                let remaining_len = self.len - len;
-                let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len);
-                self.len = len;
-                ptr::drop_in_place(s);
-            }
-        }
-
-        /// Resizes the Vec in-place so that len is equal to new_len.
-        ///
-        /// If new_len is greater than len, the Vec is extended by the
-        /// difference, with each additional slot filled with value. If
-        /// new_len is less than len, the Vec is simply truncated.
-        ///
-        /// See also [`resize_default`](struct.Vec.html#method.resize_default).
-        #[allow(dead_code)]
-        pub(super) fn resize(&mut self, new_len: usize, value: T) -> Result<(), ()>
-        where
-            T: Clone,
-        {
-            if new_len > self.capacity() {
-                return Err(());
-            }
-
-            if new_len > self.len {
-                while self.len < new_len {
-                    self.push(value.clone()).ok();
-                }
-            } else {
-                self.truncate(new_len);
-            }
-
-            Ok(())
-        }
-
-        /// Resizes the `Vec` in-place so that `len` is equal to `new_len`.
-        ///
-        /// If `new_len` is greater than `len`, the `Vec` is extended by the
-        /// difference, with each additional slot filled with `Default::default()`.
-        /// If `new_len` is less than `len`, the `Vec` is simply truncated.
-        ///
-        /// See also [`resize`](struct.Vec.html#method.resize).
-        #[allow(dead_code)]
-        pub(super) fn resize_default(&mut self, new_len: usize) -> Result<(), ()>
-        where
-            T: Clone + Default,
-        {
-            self.resize(new_len, T::default())
-        }
-
-        /// Forces the length of the vector to `new_len`.
-        ///
-        /// This is a low-level operation that maintains none of the normal
-        /// invariants of the type. Normally changing the length of a vector
-        /// is done using one of the safe operations instead, such as
-        /// [`truncate`], [`resize`], [`extend`], or [`clear`].
-        ///
-        /// [`truncate`]: #method.truncate
-        /// [`resize`]: #method.resize
-        /// [`extend`]: https://doc.rust-lang.org/stable/core/iter/trait.Extend.html#tymethod.extend
-        /// [`clear`]: #method.clear
-        ///
-        /// # Safety
-        ///
-        /// - `new_len` must be less than or equal to [`capacity()`].
-        /// - The elements at `old_len..new_len` must be initialized.
-        ///
-        /// [`capacity()`]: #method.capacity
-        #[allow(dead_code)]
-        pub(super) unsafe fn set_len(&mut self, new_len: usize) {
-            debug_assert!(new_len <= self.capacity());
-
-            self.len = new_len
-        }
-
-        /// Removes an element from the vector and returns it.
-        ///
-        /// The removed element is replaced by the last element of the vector.
-        ///
-        /// This does not preserve ordering, but is O(1).
-        ///
-        /// # Panics
-        ///
-        /// Panics if `index` is out of bounds.
-        #[allow(dead_code)]
-        pub(super) fn swap_remove(&mut self, index: usize) -> T {
-            assert!(index < self.len);
-            unsafe { self.swap_remove_unchecked(index) }
-        }
-
-        /// Removes an element from the vector and returns it.
-        ///
-        /// The removed element is replaced by the last element of the vector.
-        ///
-        /// This does not preserve ordering, but is O(1).
-        ///
-        /// # Safety
-        ///
-        ///  Assumes `index` within bounds.
-        pub(super) unsafe fn swap_remove_unchecked(&mut self, index: usize) -> T {
-            let length = self.len();
-            debug_assert!(index < length);
-            let value = ptr::read(self.as_ptr().add(index));
-            let base_ptr = self.as_mut_ptr();
-            ptr::copy(base_ptr.add(length - 1), base_ptr.add(index), 1);
-            self.len -= 1;
-            value
-        }
-
-        /// Returns true if the vec is full
-        #[inline]
-        pub(super) fn is_full(&self) -> bool {
-            self.len == self.capacity()
-        }
-
-        /// Returns true if the vec is empty
-        #[inline]
-        pub(super) fn is_empty(&self) -> bool {
-            self.len == 0
-        }
-
-        /// Returns `true` if `needle` is a prefix of the Vec.
-        ///
-        /// Always returns `true` if `needle` is an empty slice.
-        #[inline]
-        #[allow(dead_code)]
-        pub(super) fn starts_with(&self, needle: &[T]) -> bool
-        where
-            T: PartialEq,
-        {
-            let n = needle.len();
-            self.len >= n && needle == &self[..n]
-        }
-
-        /// Returns `true` if `needle` is a suffix of the Vec.
-        ///
-        /// Always returns `true` if `needle` is an empty slice.
-        #[inline]
-        #[allow(dead_code)]
-        pub(super) fn ends_with(&self, needle: &[T]) -> bool
-        where
-            T: PartialEq,
-        {
-            let (v, n) = (self.len(), needle.len());
-            v >= n && needle == &self[v - n..]
-        }
-    }
-
-    // Trait implementations
-
-    impl<T, const N: usize> Default for Vec<T, N> {
-        fn default() -> Self {
-            Self::new()
-        }
-    }
-
-    impl<T, const N: usize> fmt::Debug for Vec<T, N>
-    where
-        T: fmt::Debug,
-    {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            <[T] as fmt::Debug>::fmt(self, f)
-        }
-    }
-
-    impl<const N: usize> fmt::Write for Vec<u8, N> {
-        fn write_str(&mut self, s: &str) -> fmt::Result {
-            match self.extend_from_slice(s.as_bytes()) {
-                Ok(()) => Ok(()),
-                Err(_) => Err(fmt::Error),
-            }
-        }
-    }
-
-    impl<T, const N: usize> Drop for Vec<T, N> {
-        fn drop(&mut self) {
-            // We drop each element used in the vector by turning into a &mut[T]
-            unsafe {
-                ptr::drop_in_place(self.as_mut_slice());
-            }
-        }
-    }
-
-    impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec<T, N> {
-        type Error = ();
-
-        fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
-            Vec::from_slice(slice)
-        }
-    }
-
-    impl<T, const N: usize> Extend<T> for Vec<T, N> {
-        fn extend<I>(&mut self, iter: I)
-        where
-            I: IntoIterator<Item = T>,
-        {
-            self.extend(iter)
-        }
-    }
-
-    impl<'a, T, const N: usize> Extend<&'a T> for Vec<T, N>
-    where
-        T: 'a + Copy,
-    {
-        fn extend<I>(&mut self, iter: I)
-        where
-            I: IntoIterator<Item = &'a T>,
-        {
-            self.extend(iter.into_iter().cloned())
-        }
-    }
-
-    impl<T, const N: usize> hash::Hash for Vec<T, N>
-    where
-        T: core::hash::Hash,
-    {
-        fn hash<H: hash::Hasher>(&self, state: &mut H) {
-            <[T] as hash::Hash>::hash(self, state)
-        }
-    }
-
-    impl<T, const N: usize> hash32::Hash for Vec<T, N>
-    where
-        T: hash32::Hash,
-    {
-        fn hash<H: hash32::Hasher>(&self, state: &mut H) {
-            <[T] as hash32::Hash>::hash(self, state)
-        }
-    }
-
-    impl<'a, T, const N: usize> IntoIterator for &'a Vec<T, N> {
-        type Item = &'a T;
-        type IntoIter = slice::Iter<'a, T>;
-
-        fn into_iter(self) -> Self::IntoIter {
-            self.iter()
-        }
-    }
-
-    impl<'a, T, const N: usize> IntoIterator for &'a mut Vec<T, N> {
-        type Item = &'a mut T;
-        type IntoIter = slice::IterMut<'a, T>;
-
-        fn into_iter(self) -> Self::IntoIter {
-            self.iter_mut()
-        }
-    }
-
-    impl<T, const N: usize> FromIterator<T> for Vec<T, N> {
-        fn from_iter<I>(iter: I) -> Self
-        where
-            I: IntoIterator<Item = T>,
-        {
-            let mut vec = Vec::new();
-            for i in iter {
-                vec.push(i).ok().expect("Vec::from_iter overflow");
-            }
-            vec
-        }
-    }
-
-    /// An iterator that moves out of an [`Vec`][`Vec`].
-    ///
-    /// This struct is created by calling the `into_iter` method on [`Vec`][`Vec`].
-    ///
-    /// [`Vec`]: (https://doc.rust-lang.org/std/vec/struct.Vec.html)
-    ///
-    pub(super) struct IntoIter<T, const N: usize> {
-        vec: Vec<T, N>,
-        next: usize,
-    }
-
-    impl<T, const N: usize> Iterator for IntoIter<T, N> {
-        type Item = T;
-        fn next(&mut self) -> Option<Self::Item> {
-            if self.next < self.vec.len() {
-                let item = unsafe { (self.vec.buffer.get_unchecked_mut(self.next).as_ptr() as *const T).read() };
-                self.next += 1;
-                Some(item)
-            } else {
-                None
-            }
-        }
-    }
-
-    impl<T, const N: usize> Clone for IntoIter<T, N>
-    where
-        T: Clone,
-    {
-        fn clone(&self) -> Self {
-            let mut vec = Vec::new();
-
-            if self.next < self.vec.len() {
-                let s = unsafe {
-                    slice::from_raw_parts(
-                        (self.vec.buffer.as_ptr() as *const T).add(self.next),
-                        self.vec.len() - self.next,
-                    )
-                };
-                vec.extend_from_slice(s).ok();
-            }
-
-            Self { vec, next: 0 }
-        }
-    }
-
-    impl<T, const N: usize> Drop for IntoIter<T, N> {
-        fn drop(&mut self) {
-            unsafe {
-                // Drop all the elements that have not been moved out of vec
-                ptr::drop_in_place(&mut self.vec.as_mut_slice()[self.next..]);
-                // Prevent dropping of other elements
-                self.vec.len = 0;
-            }
-        }
-    }
-
-    impl<T, const N: usize> IntoIterator for Vec<T, N> {
-        type Item = T;
-        type IntoIter = IntoIter<T, N>;
-
-        fn into_iter(self) -> Self::IntoIter {
-            IntoIter { vec: self, next: 0 }
-        }
-    }
-
-    impl<A, B, const N1: usize, const N2: usize> PartialEq<Vec<B, N2>> for Vec<A, N1>
-    where
-        A: PartialEq<B>,
-    {
-        fn eq(&self, other: &Vec<B, N2>) -> bool {
-            <[A]>::eq(self, &**other)
-        }
-    }
-
-    // Vec<A, N> == [B]
-    impl<A, B, const N: usize> PartialEq<[B]> for Vec<A, N>
-    where
-        A: PartialEq<B>,
-    {
-        fn eq(&self, other: &[B]) -> bool {
-            <[A]>::eq(self, other)
-        }
-    }
-
-    // Vec<A, N> == &[B]
-    impl<A, B, const N: usize> PartialEq<&[B]> for Vec<A, N>
-    where
-        A: PartialEq<B>,
-    {
-        fn eq(&self, other: &&[B]) -> bool {
-            <[A]>::eq(self, &other[..])
-        }
-    }
-
-    // Vec<A, N> == &mut [B]
-    impl<A, B, const N: usize> PartialEq<&mut [B]> for Vec<A, N>
-    where
-        A: PartialEq<B>,
-    {
-        fn eq(&self, other: &&mut [B]) -> bool {
-            <[A]>::eq(self, &other[..])
-        }
-    }
-
-    // Vec<A, N> == [B; M]
-    // Equality does not require equal capacity
-    impl<A, B, const N: usize, const M: usize> PartialEq<[B; M]> for Vec<A, N>
-    where
-        A: PartialEq<B>,
-    {
-        fn eq(&self, other: &[B; M]) -> bool {
-            <[A]>::eq(self, &other[..])
-        }
-    }
-
-    // Vec<A, N> == &[B; M]
-    // Equality does not require equal capacity
-    impl<A, B, const N: usize, const M: usize> PartialEq<&[B; M]> for Vec<A, N>
-    where
-        A: PartialEq<B>,
-    {
-        fn eq(&self, other: &&[B; M]) -> bool {
-            <[A]>::eq(self, &other[..])
-        }
-    }
-
-    // Implements Eq if underlying data is Eq
-    impl<T, const N: usize> Eq for Vec<T, N> where T: Eq {}
-
-    impl<T, const N1: usize, const N2: usize> PartialOrd<Vec<T, N2>> for Vec<T, N1>
-    where
-        T: PartialOrd,
-    {
-        fn partial_cmp(&self, other: &Vec<T, N2>) -> Option<Ordering> {
-            PartialOrd::partial_cmp(&**self, &**other)
-        }
-    }
-
-    impl<T, const N: usize> Ord for Vec<T, N>
-    where
-        T: Ord,
-    {
-        #[inline]
-        fn cmp(&self, other: &Self) -> Ordering {
-            Ord::cmp(&**self, &**other)
-        }
-    }
-
-    impl<T, const N: usize> ops::Deref for Vec<T, N> {
-        type Target = [T];
-
-        fn deref(&self) -> &[T] {
-            self.as_slice()
-        }
-    }
-
-    impl<T, const N: usize> ops::DerefMut for Vec<T, N> {
-        fn deref_mut(&mut self) -> &mut [T] {
-            self.as_mut_slice()
-        }
-    }
-
-    impl<T, const N: usize> AsRef<Vec<T, N>> for Vec<T, N> {
-        #[inline]
-        fn as_ref(&self) -> &Self {
-            self
-        }
-    }
-
-    impl<T, const N: usize> AsMut<Vec<T, N>> for Vec<T, N> {
-        #[inline]
-        fn as_mut(&mut self) -> &mut Self {
-            self
-        }
-    }
-
-    impl<T, const N: usize> AsRef<[T]> for Vec<T, N> {
-        #[inline]
-        fn as_ref(&self) -> &[T] {
-            self
-        }
-    }
-
-    impl<T, const N: usize> AsMut<[T]> for Vec<T, N> {
-        #[inline]
-        fn as_mut(&mut self) -> &mut [T] {
-            self
-        }
-    }
-
-    impl<T, const N: usize> Clone for Vec<T, N>
-    where
-        T: Clone,
-    {
-        fn clone(&self) -> Self {
-            self.clone()
-        }
-    }
-
-    #[cfg(test)]
-    mod tests {
-        use super::Vec;
-        use core::fmt::Write;
-
-        // droppable macro copied from this file
-        // https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/test_helpers.rs
-        // https://web.archive.org/web/20220609184423/https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/test_helpers.rs
-        macro_rules! droppable {
-            () => {
-                static COUNT: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(0);
-
-                #[derive(Eq, Ord, PartialEq, PartialOrd)]
-                struct Droppable(i32);
-                impl Droppable {
-                    fn new() -> Self {
-                        COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
-                        Droppable(Self::count())
-                    }
-
-                    fn count() -> i32 {
-                        COUNT.load(core::sync::atomic::Ordering::Relaxed)
-                    }
-                }
-                impl Drop for Droppable {
-                    fn drop(&mut self) {
-                        COUNT.fetch_sub(1, core::sync::atomic::Ordering::Relaxed);
-                    }
-                }
-            };
-        }
-
-        #[test]
-        fn static_new() {
-            static mut _V: Vec<i32, 4> = Vec::new();
-        }
-
-        #[test]
-        fn stack_new() {
-            let mut _v: Vec<i32, 4> = Vec::new();
-        }
-
-        #[test]
-        fn is_full_empty() {
-            let mut v: Vec<i32, 4> = Vec::new();
-
-            assert!(v.is_empty());
-            assert!(!v.is_full());
-
-            v.push(1).unwrap();
-            assert!(!v.is_empty());
-            assert!(!v.is_full());
-
-            v.push(1).unwrap();
-            assert!(!v.is_empty());
-            assert!(!v.is_full());
-
-            v.push(1).unwrap();
-            assert!(!v.is_empty());
-            assert!(!v.is_full());
-
-            v.push(1).unwrap();
-            assert!(!v.is_empty());
-            assert!(v.is_full());
-        }
-
-        #[test]
-        fn drop() {
-            droppable!();
-
-            {
-                let mut v: Vec<Droppable, 2> = Vec::new();
-                v.push(Droppable::new()).ok().unwrap();
-                v.push(Droppable::new()).ok().unwrap();
-                v.pop().unwrap();
-            }
-
-            assert_eq!(Droppable::count(), 0);
-
-            {
-                let mut v: Vec<Droppable, 2> = Vec::new();
-                v.push(Droppable::new()).ok().unwrap();
-                v.push(Droppable::new()).ok().unwrap();
-            }
-
-            assert_eq!(Droppable::count(), 0);
-        }
-
-        #[test]
-        fn eq() {
-            let mut xs: Vec<i32, 4> = Vec::new();
-            let mut ys: Vec<i32, 8> = Vec::new();
-
-            assert_eq!(xs, ys);
-
-            xs.push(1).unwrap();
-            ys.push(1).unwrap();
-
-            assert_eq!(xs, ys);
-        }
-
-        #[test]
-        fn cmp() {
-            let mut xs: Vec<i32, 4> = Vec::new();
-            let mut ys: Vec<i32, 4> = Vec::new();
-
-            assert_eq!(xs, ys);
-
-            xs.push(1).unwrap();
-            ys.push(2).unwrap();
-
-            assert!(xs < ys);
-        }
-
-        #[test]
-        fn cmp_heterogenous_size() {
-            let mut xs: Vec<i32, 4> = Vec::new();
-            let mut ys: Vec<i32, 8> = Vec::new();
-
-            assert_eq!(xs, ys);
-
-            xs.push(1).unwrap();
-            ys.push(2).unwrap();
-
-            assert!(xs < ys);
-        }
-
-        #[test]
-        fn full() {
-            let mut v: Vec<i32, 4> = Vec::new();
-
-            v.push(0).unwrap();
-            v.push(1).unwrap();
-            v.push(2).unwrap();
-            v.push(3).unwrap();
-
-            assert!(v.push(4).is_err());
-        }
-
-        #[test]
-        fn iter() {
-            let mut v: Vec<i32, 4> = Vec::new();
-
-            v.push(0).unwrap();
-            v.push(1).unwrap();
-            v.push(2).unwrap();
-            v.push(3).unwrap();
-
-            let mut items = v.iter();
-
-            assert_eq!(items.next(), Some(&0));
-            assert_eq!(items.next(), Some(&1));
-            assert_eq!(items.next(), Some(&2));
-            assert_eq!(items.next(), Some(&3));
-            assert_eq!(items.next(), None);
-        }
-
-        #[test]
-        fn iter_mut() {
-            let mut v: Vec<i32, 4> = Vec::new();
-
-            v.push(0).unwrap();
-            v.push(1).unwrap();
-            v.push(2).unwrap();
-            v.push(3).unwrap();
-
-            let mut items = v.iter_mut();
-
-            assert_eq!(items.next(), Some(&mut 0));
-            assert_eq!(items.next(), Some(&mut 1));
-            assert_eq!(items.next(), Some(&mut 2));
-            assert_eq!(items.next(), Some(&mut 3));
-            assert_eq!(items.next(), None);
-        }
-
-        #[test]
-        fn collect_from_iter() {
-            let slice = &[1, 2, 3];
-            let vec: Vec<i32, 4> = slice.iter().cloned().collect();
-            assert_eq!(&vec, slice);
-        }
-
-        #[test]
-        #[should_panic]
-        fn collect_from_iter_overfull() {
-            let slice = &[1, 2, 3];
-            let _vec = slice.iter().cloned().collect::<Vec<_, 2>>();
-        }
-
-        #[test]
-        fn iter_move() {
-            let mut v: Vec<i32, 4> = Vec::new();
-            v.push(0).unwrap();
-            v.push(1).unwrap();
-            v.push(2).unwrap();
-            v.push(3).unwrap();
-
-            let mut items = v.into_iter();
-
-            assert_eq!(items.next(), Some(0));
-            assert_eq!(items.next(), Some(1));
-            assert_eq!(items.next(), Some(2));
-            assert_eq!(items.next(), Some(3));
-            assert_eq!(items.next(), None);
-        }
-
-        #[test]
-        fn iter_move_drop() {
-            droppable!();
-
-            {
-                let mut vec: Vec<Droppable, 2> = Vec::new();
-                vec.push(Droppable::new()).ok().unwrap();
-                vec.push(Droppable::new()).ok().unwrap();
-                let mut items = vec.into_iter();
-                // Move all
-                let _ = items.next();
-                let _ = items.next();
-            }
-
-            assert_eq!(Droppable::count(), 0);
-
-            {
-                let mut vec: Vec<Droppable, 2> = Vec::new();
-                vec.push(Droppable::new()).ok().unwrap();
-                vec.push(Droppable::new()).ok().unwrap();
-                let _items = vec.into_iter();
-                // Move none
-            }
-
-            assert_eq!(Droppable::count(), 0);
-
-            {
-                let mut vec: Vec<Droppable, 2> = Vec::new();
-                vec.push(Droppable::new()).ok().unwrap();
-                vec.push(Droppable::new()).ok().unwrap();
-                let mut items = vec.into_iter();
-                let _ = items.next(); // Move partly
-            }
-
-            assert_eq!(Droppable::count(), 0);
-        }
-
-        #[test]
-        fn push_and_pop() {
-            let mut v: Vec<i32, 4> = Vec::new();
-            assert_eq!(v.len(), 0);
-
-            assert_eq!(v.pop(), None);
-            assert_eq!(v.len(), 0);
-
-            v.push(0).unwrap();
-            assert_eq!(v.len(), 1);
-
-            assert_eq!(v.pop(), Some(0));
-            assert_eq!(v.len(), 0);
-
-            assert_eq!(v.pop(), None);
-            assert_eq!(v.len(), 0);
-        }
-
-        #[test]
-        fn resize_size_limit() {
-            let mut v: Vec<u8, 4> = Vec::new();
-
-            v.resize(0, 0).unwrap();
-            v.resize(4, 0).unwrap();
-            v.resize(5, 0).err().expect("full");
-        }
-
-        #[test]
-        fn resize_length_cases() {
-            let mut v: Vec<u8, 4> = Vec::new();
-
-            assert_eq!(v.len(), 0);
-
-            // Grow by 1
-            v.resize(1, 0).unwrap();
-            assert_eq!(v.len(), 1);
-
-            // Grow by 2
-            v.resize(3, 0).unwrap();
-            assert_eq!(v.len(), 3);
-
-            // Resize to current size
-            v.resize(3, 0).unwrap();
-            assert_eq!(v.len(), 3);
-
-            // Shrink by 1
-            v.resize(2, 0).unwrap();
-            assert_eq!(v.len(), 2);
-
-            // Shrink by 2
-            v.resize(0, 0).unwrap();
-            assert_eq!(v.len(), 0);
-        }
-
-        #[test]
-        fn resize_contents() {
-            let mut v: Vec<u8, 4> = Vec::new();
-
-            // New entries take supplied value when growing
-            v.resize(1, 17).unwrap();
-            assert_eq!(v[0], 17);
-
-            // Old values aren't changed when growing
-            v.resize(2, 18).unwrap();
-            assert_eq!(v[0], 17);
-            assert_eq!(v[1], 18);
-
-            // Old values aren't changed when length unchanged
-            v.resize(2, 0).unwrap();
-            assert_eq!(v[0], 17);
-            assert_eq!(v[1], 18);
-
-            // Old values aren't changed when shrinking
-            v.resize(1, 0).unwrap();
-            assert_eq!(v[0], 17);
-        }
-
-        #[test]
-        fn resize_default() {
-            let mut v: Vec<u8, 4> = Vec::new();
-
-            // resize_default is implemented using resize, so just check the
-            // correct value is being written.
-            v.resize_default(1).unwrap();
-            assert_eq!(v[0], 0);
-        }
-
-        #[test]
-        fn write() {
-            let mut v: Vec<u8, 4> = Vec::new();
-            write!(v, "{:x}", 1234).unwrap();
-            assert_eq!(&v[..], b"4d2");
-        }
-
-        #[test]
-        fn extend_from_slice() {
-            let mut v: Vec<u8, 4> = Vec::new();
-            assert_eq!(v.len(), 0);
-            v.extend_from_slice(&[1, 2]).unwrap();
-            assert_eq!(v.len(), 2);
-            assert_eq!(v.as_slice(), &[1, 2]);
-            v.extend_from_slice(&[3]).unwrap();
-            assert_eq!(v.len(), 3);
-            assert_eq!(v.as_slice(), &[1, 2, 3]);
-            assert!(v.extend_from_slice(&[4, 5]).is_err());
-            assert_eq!(v.len(), 3);
-            assert_eq!(v.as_slice(), &[1, 2, 3]);
-        }
-
-        #[test]
-        fn from_slice() {
-            // Successful construction
-            let v: Vec<u8, 4> = Vec::from_slice(&[1, 2, 3]).unwrap();
-            assert_eq!(v.len(), 3);
-            assert_eq!(v.as_slice(), &[1, 2, 3]);
-
-            // Slice too large
-            assert!(Vec::<u8, 2>::from_slice(&[1, 2, 3]).is_err());
-        }
-
-        #[test]
-        fn starts_with() {
-            let v: Vec<_, 8> = Vec::from_slice(b"ab").unwrap();
-            assert!(v.starts_with(&[]));
-            assert!(v.starts_with(b""));
-            assert!(v.starts_with(b"a"));
-            assert!(v.starts_with(b"ab"));
-            assert!(!v.starts_with(b"abc"));
-            assert!(!v.starts_with(b"ba"));
-            assert!(!v.starts_with(b"b"));
-        }
-
-        #[test]
-        fn ends_with() {
-            let v: Vec<_, 8> = Vec::from_slice(b"ab").unwrap();
-            assert!(v.ends_with(&[]));
-            assert!(v.ends_with(b""));
-            assert!(v.ends_with(b"b"));
-            assert!(v.ends_with(b"ab"));
-            assert!(!v.ends_with(b"abc"));
-            assert!(!v.ends_with(b"ba"));
-            assert!(!v.ends_with(b"a"));
-        }
-
-        #[test]
-        fn zero_capacity() {
-            let mut v: Vec<u8, 0> = Vec::new();
-            // Validate capacity
-            assert_eq!(v.capacity(), 0);
-
-            // Make sure there is no capacity
-            assert!(v.push(1).is_err());
-
-            // Validate length
-            assert_eq!(v.len(), 0);
-
-            // Validate pop
-            assert_eq!(v.pop(), None);
-
-            // Validate slice
-            assert_eq!(v.as_slice(), &[]);
-
-            // Validate empty
-            assert!(v.is_empty());
-
-            // Validate full
-            assert!(v.is_full());
-        }
-
-        #[test]
-        fn vec() {
-            // A vector with a fixed capacity of 8 elements allocated on the stack
-            let mut vec = Vec::<_, 8>::new();
-            vec.push(1).unwrap();
-            vec.push(2).unwrap();
-            assert_eq!(vec.len(), 2);
-            assert_eq!(vec[0], 1);
-            assert_eq!(vec.pop(), Some(2));
-            assert_eq!(vec.len(), 1);
-            vec[0] = 7;
-            assert_eq!(vec[0], 7);
-            vec.extend([1, 2, 3].iter().cloned());
-            assert_eq!(*vec, [7, 1, 2, 3]);
-        }
-
-        #[test]
-        fn as_slice() {
-            let buffer: Vec<u8, 5> = Vec::from_slice(&[1, 2, 3, 5, 8]).unwrap();
-            assert_eq!(buffer.as_slice(), &[1, 2, 3, 5, 8]);
-        }
-
-        #[test]
-        fn into_array() {
-            let buffer: Vec<u8, 42> = Vec::from_slice(&[1, 2, 3, 5, 8]).unwrap();
-            let array: [u8; 5] = buffer.into_array().unwrap();
-            assert_eq!(array, [1, 2, 3, 5, 8]);
-        }
-
-        #[test]
-        fn as_mut_slice() {
-            let mut buffer: Vec<u8, 5> = Vec::from_slice(&[1, 2, 3, 5, 8]).unwrap();
-            buffer[0] = 9;
-            assert_eq!(buffer.as_slice(), &[9, 2, 3, 5, 8]);
-        }
-
-        #[test]
-        fn swap_remove() {
-            let mut v: Vec<_, 8> = Vec::new();
-            v.push("foo").unwrap();
-            v.push("bar").unwrap();
-            v.push("baz").unwrap();
-            v.push("qux").unwrap();
-            assert_eq!(v.swap_remove(1), "bar");
-            assert_eq!(&*v, ["foo", "qux", "baz"]);
-            assert_eq!(v.swap_remove(0), "foo");
-            assert_eq!(&*v, ["baz", "qux"]);
-        }
-
-        #[test]
-        fn swap_remove_unchecked() {
-            let mut v: Vec<_, 8> = Vec::new();
-            v.push("foo").unwrap();
-            v.push("bar").unwrap();
-            v.push("baz").unwrap();
-            v.push("qux").unwrap();
-            assert_eq!(unsafe { v.swap_remove_unchecked(1) }, "bar");
-            assert_eq!(&*v, ["foo", "qux", "baz"]);
-            assert_eq!(unsafe { v.swap_remove_unchecked(0) }, "foo");
-            assert_eq!(&*v, ["baz", "qux"]);
-        }
-    }
-}
-
-// copied from
-// https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/indexmap.rs
-// https://web.archive.org/web/20220609184614/https://github.com/japaric/heapless/blob/39273893deda34fa741f79438134fd4d4d7ac3ae/src/indexmap.rs
-
-use core::{borrow::Borrow, fmt, iter::FromIterator, mem, num::NonZeroU32, ops, slice};
-
-use hash32::{BuildHasher, BuildHasherDefault, FnvHasher, Hash, Hasher};
-
-use vec::Vec;
-
-/// A [`taffy::indexmap::IndexMap`](./struct.IndexMap.html) using the default FNV hasher
-///
-/// A list of all Methods and Traits available for `FnvIndexMap` can be found in
-/// the [`taffy::indexmap::IndexMap`](./struct.IndexMap.html) documentation.
-pub type FnvIndexMap<K, V, const N: usize> = IndexMap<K, V, BuildHasherDefault<FnvHasher>, N>;
-
-#[derive(Clone, Copy, Eq, PartialEq)]
-struct HashValue(u16);
-
-impl HashValue {
-    fn desired_pos(&self, mask: usize) -> usize {
-        usize::from(self.0) & mask
-    }
-
-    fn probe_distance(&self, mask: usize, current: usize) -> usize {
-        current.wrapping_sub(self.desired_pos(mask) as usize) & mask
-    }
-}
-
-#[derive(Clone)]
-struct Bucket<K, V> {
-    hash: HashValue,
-    key: K,
-    value: V,
-}
-
-#[derive(Clone, Copy, PartialEq)]
-struct Pos {
-    // compact representation of `{ hash_value: u16, index: u16 }`
-    // To get the most from `NonZero` we store the *value minus 1*. This way `None::Option<Pos>`
-    // is equivalent to the very unlikely value of  `{ hash_value: 0xffff, index: 0xffff }` instead
-    // the more likely of `{ hash_value: 0x00, index: 0x00 }`
-    nz: NonZeroU32,
-}
-
-impl Pos {
-    fn new(index: usize, hash: HashValue) -> Self {
-        Pos { nz: unsafe { NonZeroU32::new_unchecked(((u32::from(hash.0) << 16) + index as u32).wrapping_add(1)) } }
-    }
-
-    fn hash(&self) -> HashValue {
-        HashValue((self.nz.get().wrapping_sub(1) >> 16) as u16)
-    }
-
-    fn index(&self) -> usize {
-        self.nz.get().wrapping_sub(1) as u16 as usize
-    }
-}
-
-enum Insert<K, V> {
-    Success(Inserted<V>),
-    Full((K, V)),
-}
-
-struct Inserted<V> {
-    #[allow(dead_code)]
-    index: usize,
-    old_value: Option<V>,
-}
-
-macro_rules! probe_loop {
-    ($probe_var: ident < $len: expr, $body: expr) => {
-        loop {
-            if $probe_var < $len {
-                $body
-                    $probe_var += 1;
-            } else {
-                $probe_var = 0;
-            }
-        }
-    }
-}
-
-struct CoreMap<K, V, const N: usize> {
-    entries: Vec<Bucket<K, V>, N>,
-    indices: [Option<Pos>; N],
-}
-
-impl<K, V, const N: usize> CoreMap<K, V, N> {
-    const fn new() -> Self {
-        const INIT: Option<Pos> = None;
-
-        CoreMap { entries: Vec::new(), indices: [INIT; N] }
-    }
-}
-
-impl<K, V, const N: usize> CoreMap<K, V, N>
-where
-    K: Eq + Hash,
-{
-    fn capacity() -> usize {
-        N
-    }
-
-    fn mask() -> usize {
-        Self::capacity() - 1
-    }
-
-    fn find<Q>(&self, hash: HashValue, query: &Q) -> Option<(usize, usize)>
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Eq,
-    {
-        let mut probe = hash.desired_pos(Self::mask());
-        let mut dist = 0;
-
-        probe_loop!(probe < self.indices.len(), {
-            if let Some(pos) = self.indices[probe] {
-                let entry_hash = pos.hash();
-                // NOTE(i) we use unchecked indexing below
-                let i = pos.index();
-                debug_assert!(i < self.entries.len());
-
-                if dist > entry_hash.probe_distance(Self::mask(), probe) {
-                    // give up when probe distance is too long
-                    return None;
-                } else if entry_hash == hash && unsafe { self.entries.get_unchecked(i).key.borrow() == query } {
-                    return Some((probe, i));
-                }
-            } else {
-                return None;
-            }
-
-            dist += 1;
-        });
-    }
-
-    fn insert(&mut self, hash: HashValue, key: K, value: V) -> Insert<K, V> {
-        let mut probe = hash.desired_pos(Self::mask());
-        let mut dist = 0;
-
-        probe_loop!(probe < self.indices.len(), {
-            let pos = &mut self.indices[probe];
-
-            if let Some(pos) = *pos {
-                let entry_hash = pos.hash();
-                // NOTE(i) we use unchecked indexing below
-                let i = pos.index();
-                debug_assert!(i < self.entries.len());
-
-                let their_dist = entry_hash.probe_distance(Self::mask(), probe);
-
-                if their_dist < dist {
-                    if self.entries.is_full() {
-                        return Insert::Full((key, value));
-                    }
-                    // robin hood: steal the spot if it's better for us
-                    let index = self.entries.len();
-                    unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) };
-                    return Insert::Success(Inserted {
-                        index: self.insert_phase_2(probe, Pos::new(index, hash)),
-                        old_value: None,
-                    });
-                } else if entry_hash == hash && unsafe { self.entries.get_unchecked(i).key == key } {
-                    return Insert::Success(Inserted {
-                        index: i,
-                        old_value: Some(mem::replace(unsafe { &mut self.entries.get_unchecked_mut(i).value }, value)),
-                    });
-                }
-            } else {
-                if self.entries.is_full() {
-                    return Insert::Full((key, value));
-                }
-                // empty bucket, insert here
-                let index = self.entries.len();
-                *pos = Some(Pos::new(index, hash));
-                unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) };
-                return Insert::Success(Inserted { index, old_value: None });
-            }
-            dist += 1;
-        });
-    }
-
-    // phase 2 is post-insert where we forward-shift `Pos` in the indices.
-    fn insert_phase_2(&mut self, mut probe: usize, mut old_pos: Pos) -> usize {
-        probe_loop!(probe < self.indices.len(), {
-            let pos = unsafe { self.indices.get_unchecked_mut(probe) };
-
-            let mut is_none = true; // work around lack of NLL
-            if let Some(pos) = pos.as_mut() {
-                old_pos = mem::replace(pos, old_pos);
-                is_none = false;
-            }
-
-            if is_none {
-                *pos = Some(old_pos);
-                return probe;
-            }
-        });
-    }
-
-    fn remove_found(&mut self, probe: usize, found: usize) -> (K, V) {
-        // index `probe` and entry `found` is to be removed
-        // use swap_remove, but then we need to update the index that points
-        // to the other entry that has to move
-        self.indices[probe] = None;
-        let entry = unsafe { self.entries.swap_remove_unchecked(found) };
-
-        // correct index that points to the entry that had to swap places
-        if let Some(entry) = self.entries.get(found) {
-            // was not last element
-            // examine new element in `found` and find it in indices
-            let mut probe = entry.hash.desired_pos(Self::mask());
-
-            probe_loop!(probe < self.indices.len(), {
-                if let Some(pos) = self.indices[probe] {
-                    if pos.index() >= self.entries.len() {
-                        // found it
-                        self.indices[probe] = Some(Pos::new(found, entry.hash));
-                        break;
-                    }
-                }
-            });
-        }
-
-        self.backward_shift_after_removal(probe);
-
-        (entry.key, entry.value)
-    }
-
-    fn backward_shift_after_removal(&mut self, probe_at_remove: usize) {
-        // backward shift deletion in self.indices
-        // after probe, shift all non-ideally placed indices backward
-        let mut last_probe = probe_at_remove;
-        let mut probe = probe_at_remove + 1;
-
-        probe_loop!(probe < self.indices.len(), {
-            if let Some(pos) = self.indices[probe] {
-                let entry_hash = pos.hash();
-
-                if entry_hash.probe_distance(Self::mask(), probe) > 0 {
-                    unsafe { *self.indices.get_unchecked_mut(last_probe) = self.indices[probe] }
-                    self.indices[probe] = None;
-                } else {
-                    break;
-                }
-            } else {
-                break;
-            }
-            last_probe = probe;
-        });
-    }
-}
-
-impl<K, V, const N: usize> Clone for CoreMap<K, V, N>
-where
-    K: Eq + Hash + Clone,
-    V: Clone,
-{
-    fn clone(&self) -> Self {
-        Self { entries: self.entries.clone(), indices: self.indices }
-    }
-}
-
-/// Fixed capacity [`IndexMap`](https://docs.rs/indexmap/1/indexmap/map/struct.IndexMap.html)
-///
-/// Note that you cannot use `IndexMap` directly, since it is generic around the hashing algorithm
-/// in use. Pick a concrete instantiation like [`FnvIndexMap`](./type.FnvIndexMap.html) instead
-/// or create your own.
-///
-/// Note that the capacity of the `IndexMap` must be a power of 2.
-pub struct IndexMap<K, V, S, const N: usize> {
-    core: CoreMap<K, V, N>,
-    build_hasher: S,
-}
-
-impl<K, V, S, const N: usize> IndexMap<K, V, BuildHasherDefault<S>, N> {
-    /// Creates an empty `IndexMap`.
-    pub const fn new() -> Self {
-        // Const assert
-        sealed::greater_than_1::<N>();
-        sealed::power_of_two::<N>();
-
-        IndexMap { build_hasher: BuildHasherDefault::new(), core: CoreMap::new() }
-    }
-}
-
-impl<K, V, S, const N: usize> IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher,
-{
-    /* Public API */
-    /// Returns the number of elements the map can hold
-    #[allow(dead_code)]
-    pub(crate) fn capacity(&self) -> usize {
-        N
-    }
-
-    /// Return an iterator over the keys of the map, in their order
-    #[allow(dead_code)]
-    fn keys(&self) -> impl Iterator<Item = &K> {
-        self.core.entries.iter().map(|bucket| &bucket.key)
-    }
-
-    /// Return an iterator over the values of the map, in their order
-    #[allow(dead_code)]
-    fn values(&self) -> impl Iterator<Item = &V> {
-        self.core.entries.iter().map(|bucket| &bucket.value)
-    }
-
-    /// Return an iterator over mutable references to the the values of the map, in their order
-    #[allow(dead_code)]
-    fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
-        self.core.entries.iter_mut().map(|bucket| &mut bucket.value)
-    }
-
-    /// Return an iterator over the key-value pairs of the map, in their order
-    fn iter(&self) -> Iter<'_, K, V> {
-        Iter { iter: self.core.entries.iter() }
-    }
-
-    /// Return an iterator over the key-value pairs of the map, in their order
-    fn iter_mut(&mut self) -> IterMut<'_, K, V> {
-        IterMut { iter: self.core.entries.iter_mut() }
-    }
-
-    /// Get the first key-value pair
-    ///
-    /// Computes in **O(1)** time
-    #[allow(dead_code)]
-    fn first(&self) -> Option<(&K, &V)> {
-        self.core.entries.first().map(|bucket| (&bucket.key, &bucket.value))
-    }
-
-    /// Get the first key-value pair, with mutable access to the value
-    ///
-    /// Computes in **O(1)** time
-    #[allow(dead_code)]
-    fn first_mut(&mut self) -> Option<(&K, &mut V)> {
-        self.core.entries.first_mut().map(|bucket| (&bucket.key, &mut bucket.value))
-    }
-
-    /// Get the last key-value pair
-    ///
-    /// Computes in **O(1)** time
-    #[allow(dead_code)]
-    fn last(&self) -> Option<(&K, &V)> {
-        self.core.entries.last().map(|bucket| (&bucket.key, &bucket.value))
-    }
-
-    /// Get the last key-value pair, with mutable access to the value
-    ///
-    /// Computes in **O(1)** time
-    #[allow(dead_code)]
-    fn last_mut(&mut self) -> Option<(&K, &mut V)> {
-        self.core.entries.last_mut().map(|bucket| (&bucket.key, &mut bucket.value))
-    }
-
-    /// Return the number of key-value pairs in the map.
-    ///
-    /// Computes in **O(1)** time.
-    fn len(&self) -> usize {
-        self.core.entries.len()
-    }
-
-    /// Returns true if the map contains no elements.
-    ///
-    /// Computes in **O(1)** time.
-    fn is_empty(&self) -> bool {
-        self.len() == 0
-    }
-
-    /// Remove all key-value pairs in the map, while preserving its capacity.
-    ///
-    /// Computes in **O(n)** time.
-    pub(crate) fn clear(&mut self) {
-        self.core.entries.clear();
-        for pos in self.core.indices.iter_mut() {
-            *pos = None;
-        }
-    }
-
-    /// Returns a reference to the value corresponding to the key.
-    ///
-    /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed
-    /// form *must* match those for the key type.
-    ///
-    /// Computes in **O(1)** time (average).
-    pub(crate) fn get<Q>(&self, key: &Q) -> Option<&V>
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Hash + Eq,
-    {
-        self.find(key).map(|(_, found)| unsafe { &self.core.entries.get_unchecked(found).value })
-    }
-
-    /// Returns true if the map contains a value for the specified key.
-    ///
-    /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed
-    /// form *must* match those for the key type.
-    ///
-    /// Computes in **O(1)** time (average).
-    #[allow(dead_code)]
-    pub(crate) fn contains_key<Q>(&self, key: &Q) -> bool
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Eq + Hash,
-    {
-        self.find(key).is_some()
-    }
-
-    /// Returns a mutable reference to the value corresponding to the key.
-    ///
-    /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed
-    /// form *must* match those for the key type.
-    ///
-    /// Computes in **O(1)** time (average).
-    fn get_mut<'v, Q>(&'v mut self, key: &Q) -> Option<&'v mut V>
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Hash + Eq,
-    {
-        if let Some((_, found)) = self.find(key) {
-            Some(unsafe { &mut self.core.entries.get_unchecked_mut(found).value })
-        } else {
-            None
-        }
-    }
-
-    /// Inserts a key-value pair into the map.
-    ///
-    /// If an equivalent key already exists in the map: the key remains and retains in its place in
-    /// the order, its corresponding value is updated with `value` and the older value is returned
-    /// inside `Some(_)`.
-    ///
-    /// If no equivalent key existed in the map: the new key-value pair is inserted, last in order,
-    /// and `None` is returned.
-    ///
-    /// Computes in **O(1)** time (average).
-    ///
-    /// See also entry if you you want to insert or modify or if you need to get the index of the
-    /// corresponding key-value pair.
-    pub(crate) fn insert(&mut self, key: K, value: V) -> Result<Option<V>, (K, V)> {
-        let hash = hash_with(&key, &self.build_hasher);
-        match self.core.insert(hash, key, value) {
-            Insert::Success(inserted) => Ok(inserted.old_value),
-            Insert::Full((k, v)) => Err((k, v)),
-        }
-    }
-
-    /// Same as [`swap_remove`](struct.IndexMap.html#method.swap_remove)
-    ///
-    /// Computes in **O(1)** time (average).
-    pub(crate) fn remove<Q>(&mut self, key: &Q) -> Option<V>
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Hash + Eq,
-    {
-        self.swap_remove(key)
-    }
-
-    /// Remove the key-value pair equivalent to `key` and return its value.
-    ///
-    /// Like `Vec::swap_remove`, the pair is removed by swapping it with the last element of the map
-    /// and popping it off. **This perturbs the postion of what used to be the last element!**
-    ///
-    /// Return `None` if `key` is not in map.
-    ///
-    /// Computes in **O(1)** time (average).
-    fn swap_remove<Q>(&mut self, key: &Q) -> Option<V>
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Hash + Eq,
-    {
-        self.find(key).map(|(probe, found)| self.core.remove_found(probe, found).1)
-    }
-
-    /* Private API */
-    /// Return probe (indices) and position (entries)
-    fn find<Q>(&self, key: &Q) -> Option<(usize, usize)>
-    where
-        K: Borrow<Q>,
-        Q: ?Sized + Hash + Eq,
-    {
-        if self.is_empty() {
-            return None;
-        }
-        let h = hash_with(key, &self.build_hasher);
-        self.core.find(h, key)
-    }
-}
-
-impl<'a, K, Q, V, S, const N: usize> ops::Index<&'a Q> for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash + Borrow<Q>,
-    Q: ?Sized + Eq + Hash,
-    S: BuildHasher,
-{
-    type Output = V;
-
-    fn index(&self, key: &Q) -> &V {
-        self.get(key).expect("key not found")
-    }
-}
-
-impl<'a, K, Q, V, S, const N: usize> ops::IndexMut<&'a Q> for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash + Borrow<Q>,
-    Q: ?Sized + Eq + Hash,
-    S: BuildHasher,
-{
-    fn index_mut(&mut self, key: &Q) -> &mut V {
-        self.get_mut(key).expect("key not found")
-    }
-}
-
-impl<K, V, S, const N: usize> Clone for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash + Clone,
-    V: Clone,
-    S: Clone,
-{
-    fn clone(&self) -> Self {
-        Self { core: self.core.clone(), build_hasher: self.build_hasher.clone() }
-    }
-}
-
-impl<K, V, S, const N: usize> fmt::Debug for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash + fmt::Debug,
-    V: fmt::Debug,
-    S: BuildHasher,
-{
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_map().entries(self.iter()).finish()
-    }
-}
-
-impl<K, V, S, const N: usize> Default for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher + Default,
-{
-    fn default() -> Self {
-        IndexMap { build_hasher: <_>::default(), core: CoreMap::new() }
-    }
-}
-
-impl<K, V, S, S2, const N: usize, const N2: usize> PartialEq<IndexMap<K, V, S2, N2>> for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    V: Eq,
-    S: BuildHasher,
-    S2: BuildHasher,
-{
-    fn eq(&self, other: &IndexMap<K, V, S2, N2>) -> bool {
-        self.len() == other.len() && self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v))
-    }
-}
-
-impl<K, V, S, const N: usize> Eq for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    V: Eq,
-    S: BuildHasher,
-{
-}
-
-impl<K, V, S, const N: usize> Extend<(K, V)> for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher,
-{
-    fn extend<I>(&mut self, iterable: I)
-    where
-        I: IntoIterator<Item = (K, V)>,
-    {
-        for (k, v) in iterable {
-            self.insert(k, v).ok().unwrap();
-        }
-    }
-}
-
-impl<'a, K, V, S, const N: usize> Extend<(&'a K, &'a V)> for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash + Copy,
-    V: Copy,
-    S: BuildHasher,
-{
-    fn extend<I>(&mut self, iterable: I)
-    where
-        I: IntoIterator<Item = (&'a K, &'a V)>,
-    {
-        self.extend(iterable.into_iter().map(|(&key, &value)| (key, value)))
-    }
-}
-
-impl<K, V, S, const N: usize> FromIterator<(K, V)> for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher + Default,
-{
-    fn from_iter<I>(iterable: I) -> Self
-    where
-        I: IntoIterator<Item = (K, V)>,
-    {
-        let mut map = IndexMap::default();
-        map.extend(iterable);
-        map
-    }
-}
-
-#[derive(Clone)]
-pub struct IntoIter<K, V, const N: usize> {
-    entries: Vec<Bucket<K, V>, N>,
-}
-
-impl<K, V, const N: usize> Iterator for IntoIter<K, V, N> {
-    type Item = (K, V);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.entries.pop().map(|bucket| (bucket.key, bucket.value))
-    }
-}
-
-impl<K, V, S, const N: usize> IntoIterator for IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher,
-{
-    type Item = (K, V);
-    type IntoIter = IntoIter<K, V, N>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        IntoIter { entries: self.core.entries }
-    }
-}
-
-impl<'a, K, V, S, const N: usize> IntoIterator for &'a IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher,
-{
-    type Item = (&'a K, &'a V);
-    type IntoIter = Iter<'a, K, V>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.iter()
-    }
-}
-
-impl<'a, K, V, S, const N: usize> IntoIterator for &'a mut IndexMap<K, V, S, N>
-where
-    K: Eq + Hash,
-    S: BuildHasher,
-{
-    type Item = (&'a K, &'a mut V);
-    type IntoIter = IterMut<'a, K, V>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.iter_mut()
-    }
-}
-
-pub struct Iter<'a, K, V> {
-    iter: slice::Iter<'a, Bucket<K, V>>,
-}
-
-impl<'a, K, V> Iterator for Iter<'a, K, V> {
-    type Item = (&'a K, &'a V);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().map(|bucket| (&bucket.key, &bucket.value))
-    }
-}
-
-impl<'a, K, V> Clone for Iter<'a, K, V> {
-    fn clone(&self) -> Self {
-        Self { iter: self.iter.clone() }
-    }
-}
-
-pub struct IterMut<'a, K, V> {
-    iter: slice::IterMut<'a, Bucket<K, V>>,
-}
-
-impl<'a, K, V> Iterator for IterMut<'a, K, V> {
-    type Item = (&'a K, &'a mut V);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().map(|bucket| (&bucket.key, &mut bucket.value))
-    }
-}
-
-fn hash_with<K, S>(key: &K, build_hasher: &S) -> HashValue
-where
-    K: ?Sized + Hash,
-    S: BuildHasher,
-{
-    let mut h = build_hasher.build_hasher();
-    key.hash(&mut h);
-    HashValue(h.finish() as u16)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::FnvIndexMap;
-
-    use core::mem;
-
-    #[test]
-    fn size() {
-        const CAP: usize = 4;
-        assert_eq!(
-            mem::size_of::<FnvIndexMap<i16, u16, CAP>>(),
-            CAP * mem::size_of::<u32>() + // indices
-                CAP * (mem::size_of::<i16>() + // key
-                     mem::size_of::<u16>() + // value
-                     mem::size_of::<u16>() // hash
-                ) + // buckets
-                mem::size_of::<usize>() // entries.length
-        )
-    }
-
-    #[test]
-    fn partial_eq() {
-        {
-            let mut a: FnvIndexMap<_, _, 4> = FnvIndexMap::new();
-            a.insert("k1", "v1").unwrap();
-
-            let mut b: FnvIndexMap<_, _, 4> = FnvIndexMap::new();
-            b.insert("k1", "v1").unwrap();
-
-            assert!(a == b);
-
-            b.insert("k2", "v2").unwrap();
-
-            assert!(a != b);
-        }
-
-        {
-            let mut a: FnvIndexMap<_, _, 4> = FnvIndexMap::new();
-            a.insert("k1", "v1").unwrap();
-            a.insert("k2", "v2").unwrap();
-
-            let mut b: FnvIndexMap<_, _, 4> = FnvIndexMap::new();
-            b.insert("k2", "v2").unwrap();
-            b.insert("k1", "v1").unwrap();
-
-            assert!(a == b);
-        }
-    }
-
-    #[test]
-    fn into_iter() {
-        let mut src: FnvIndexMap<_, _, 4> = FnvIndexMap::new();
-        src.insert("k1", "v1").unwrap();
-        src.insert("k2", "v2").unwrap();
-        src.insert("k3", "v3").unwrap();
-        src.insert("k4", "v4").unwrap();
-        let clone = src.clone();
-        for (k, v) in clone.into_iter() {
-            assert_eq!(v, *src.get(k).unwrap());
-        }
-    }
-
-    #[test]
-    fn insert_replaces_on_full_map() {
-        let mut a: FnvIndexMap<_, _, 2> = FnvIndexMap::new();
-        a.insert("k1", "v1").unwrap();
-        a.insert("k2", "v2").unwrap();
-        a.insert("k1", "v2").unwrap();
-        assert_eq!(a.get("k1"), a.get("k2"));
-    }
-
-    #[test]
-    fn first_last() {
-        let mut map = FnvIndexMap::<_, _, 4>::new();
-
-        assert_eq!(None, map.first());
-        assert_eq!(None, map.last());
-
-        map.insert(0, 0).unwrap();
-        map.insert(2, 2).unwrap();
-
-        assert_eq!(Some((&0, &0)), map.first());
-        assert_eq!(Some((&2, &2)), map.last());
-
-        map.insert(1, 1).unwrap();
-
-        assert_eq!(Some((&1, &1)), map.last());
-
-        *map.first_mut().unwrap().1 += 1;
-        *map.last_mut().unwrap().1 += 1;
-
-        assert_eq!(Some((&0, &1)), map.first());
-        assert_eq!(Some((&1, &2)), map.last());
-    }
-
-    #[cfg(feature = "std")]
-    #[test]
-    fn fnv_index_map() {
-        // A hash map with a capacity of 16 key-value pairs allocated on the stack
-        let mut book_reviews = FnvIndexMap::<_, _, 16>::new();
-        // review some books.
-        book_reviews.insert("Adventures of Huckleberry Finn", "My favorite book.").unwrap();
-        book_reviews.insert("Grimms' Fairy Tales", "Masterpiece.").unwrap();
-        book_reviews.insert("Pride and Prejudice", "Very enjoyable.").unwrap();
-        book_reviews.insert("The Adventures of Sherlock Holmes", "Eye lyked it alot.").unwrap();
-        // check for a specific one.
-        if !book_reviews.contains_key("Les Misérables") {
-            println!("We've got {} reviews, but Les Misérables ain't one.", book_reviews.len());
-        }
-        // oops, this review has a lot of spelling mistakes, let's delete it.
-        book_reviews.remove("The Adventures of Sherlock Holmes");
-        // look up the values associated with some keys.
-        let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"];
-        for book in &to_find {
-            match book_reviews.get(book) {
-                Some(review) => println!("{}: {}", book, review),
-                None => println!("{} is unreviewed.", book),
-            }
-        }
-        // iterate over everything.
-        for (book, review) in &book_reviews {
-            println!("{}: \"{}\"", book, review);
-        }
-    }
-
-    #[test]
-    fn keys() {
-        let mut map = FnvIndexMap::<_, _, 16>::new();
-        map.insert("a", 1).unwrap();
-        map.insert("b", 2).unwrap();
-        map.insert("c", 3).unwrap();
-    }
-
-    #[test]
-    fn values() {
-        let mut map = FnvIndexMap::<_, _, 16>::new();
-        map.insert("a", 1).unwrap();
-        map.insert("b", 2).unwrap();
-        map.insert("c", 3).unwrap();
-    }
-
-    #[test]
-    fn values_mut() {
-        let mut map = FnvIndexMap::<_, _, 16>::new();
-        map.insert("a", 1).unwrap();
-        map.insert("b", 2).unwrap();
-        map.insert("c", 3).unwrap();
-        for val in map.values_mut() {
-            *val += 10;
-        }
-    }
-
-    #[cfg(feature = "std")]
-    #[test]
-    fn iter() {
-        let mut map = FnvIndexMap::<_, _, 16>::new();
-        map.insert("a", 1).unwrap();
-        map.insert("b", 2).unwrap();
-        map.insert("c", 3).unwrap();
-        for (key, val) in map.iter() {
-            println!("key: {} val: {}", key, val);
-        }
-    }
-
-    #[test]
-    fn iter_mut() {
-        let mut map = FnvIndexMap::<_, _, 16>::new();
-        map.insert("a", 1).unwrap();
-        map.insert("b", 2).unwrap();
-        map.insert("c", 3).unwrap();
-        for (_, val) in map.iter_mut() {
-            *val = 2;
-        }
-    }
-
-    #[test]
-    fn len() {
-        let mut a = FnvIndexMap::<_, _, 16>::new();
-        assert_eq!(a.len(), 0);
-        a.insert(1, "a").unwrap();
-        assert_eq!(a.len(), 1);
-    }
-
-    #[test]
-    fn is_empty() {
-        let mut a = FnvIndexMap::<_, _, 16>::new();
-        assert!(a.is_empty());
-        a.insert(1, "a").unwrap();
-        assert!(!a.is_empty());
-    }
-
-    #[test]
-    fn clear() {
-        let mut a = FnvIndexMap::<_, _, 16>::new();
-        a.insert(1, "a").unwrap();
-        a.clear();
-        assert!(a.is_empty());
-    }
-
-    #[test]
-    fn get() {
-        let mut map = FnvIndexMap::<_, _, 16>::new();
-        map.insert(1, "a").unwrap();
-        assert_eq!(map.get(&1), Some(&"a"));
-        assert_eq!(map.get(&2), None);
-    }
-
-    #[test]
-    fn contains_key() {
-        let mut map = FnvIndexMap::<_, _, 8>::new();
-        map.insert(1, "a").unwrap();
-        assert_eq!(map.contains_key(&1), true);
-        assert_eq!(map.contains_key(&2), false);
-    }
-
-    #[test]
-    fn get_mut() {
-        let mut map = FnvIndexMap::<_, _, 8>::new();
-        map.insert(1, "a").unwrap();
-        if let Some(x) = map.get_mut(&1) {
-            *x = "b";
-        }
-        assert_eq!(map[&1], "b");
-    }
-
-    #[test]
-    fn insert() {
-        let mut map = FnvIndexMap::<_, _, 8>::new();
-        assert_eq!(map.insert(37, "a"), Ok(None));
-        assert_eq!(map.is_empty(), false);
-        map.insert(37, "b").unwrap();
-        assert_eq!(map.insert(37, "c"), Ok(Some("b")));
-        assert_eq!(map[&37], "c");
-    }
-
-    #[test]
-    fn remove() {
-        let mut map = FnvIndexMap::<_, _, 8>::new();
-        map.insert(1, "a").unwrap();
-        assert_eq!(map.remove(&1), Some("a"));
-        assert_eq!(map.remove(&1), None);
-    }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 3ccf788bd..3e43875fa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,6 +3,7 @@
 #![deny(unsafe_code)]
 #![warn(missing_docs)]
 #![warn(clippy::missing_docs_in_private_items)]
+#![forbid(unsafe_code)]
 
 // We always need std for the tests
 // See <https://github.com/la10736/rstest/issues/149#issuecomment-1156402989>
@@ -25,10 +26,8 @@ pub mod node;
 pub mod prelude;
 pub mod style;
 
+mod data;
 mod flexbox;
-mod forest;
-#[cfg(all(not(feature = "alloc"), not(feature = "std")))]
-mod indexmap;
 mod resolve;
 mod sys;
 
diff --git a/src/node.rs b/src/node.rs
index 1fcabc877..beba30032 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -1,84 +1,50 @@
 //! UI [`Node`] types and related data structures.
 //!
-//! Layouts are composed of multiple nodes, which live in a forest-like data structure.
-use crate::error;
-use crate::forest::Forest;
+//! Layouts are composed of multiple nodes, which live in a tree-like data structure.
+use slotmap::SlotMap;
+
+/// A node in a layout.
+pub type Node = slotmap::DefaultKey;
+
+use crate::error::{TaffyError, TaffyResult};
 use crate::geometry::Size;
 use crate::layout::Layout;
 use crate::style::FlexboxLayout;
 #[cfg(any(feature = "std", feature = "alloc"))]
 use crate::sys::Box;
-use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec};
-use core::sync::atomic::{AtomicUsize, Ordering};
-
-/// Internal node id.
-pub(crate) type NodeId = usize;
-
-/// The identifier of a [`Node`]
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))]
-pub(crate) struct Id(usize);
-
-/// An bump-allocator index that tracks how many [`Nodes`](Node) have been allocated in a [`Taffy`].
-pub(crate) struct Allocator {
-    /// The last reserved [`NodeId`]
-    last_id: AtomicUsize,
-}
-
-impl Allocator {
-    /// Creates a fresh [`Allocator`]
-    #[must_use]
-    pub const fn new() -> Self {
-        Self { last_id: AtomicUsize::new(0) }
-    }
-
-    /// Allocates space for one more [`Node`]
-    pub fn allocate(&self) -> Id {
-        Id(self.last_id.fetch_add(1, Ordering::Relaxed))
-    }
-}
+use crate::sys::{new_vec_with_capacity, ChildrenVec, Vec};
+use crate::{data::NodeData, error};
 
 /// A function type that can be used in a [`MeasureFunc`]
 ///
 /// This trait is automatically implemented for all types (including closures) that define a function with the appropriate type signature.
 pub trait Measurable: Send + Sync + Fn(Size<Option<f32>>) -> Size<f32> {}
-
 impl<F: Send + Sync + Fn(Size<Option<f32>>) -> Size<f32>> Measurable for F {}
 
 /// A function that can be used to compute the intrinsic size of a node
 pub enum MeasureFunc {
     /// Stores an unboxed function
     Raw(fn(Size<Option<f32>>) -> Size<f32>),
+
     /// Stores a boxed function
     #[cfg(any(feature = "std", feature = "alloc"))]
     Boxed(Box<dyn Measurable>),
 }
 
-/// Global taffy instance id allocator.
-static INSTANCE_ALLOCATOR: Allocator = Allocator::new();
-
-/// An [`Id`]-containing identifier
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))]
-pub struct Node {
-    /// The id of the forest that this node lives with
-    instance: Id,
-    /// The identifier of this particular node within the tree
-    local: Id,
-}
-
-/// A forest of UI [`Nodes`](`Node`), suitable for UI layout
+/// A tree of UI [`Nodes`](`Node`), suitable for UI layout
 pub struct Taffy {
-    /// The ID of the root node
-    id: Id,
-    /// A monotonically-increasing index that tracks the [`Id`] of the next node
-    allocator: Allocator,
-    /// A map from Node -> NodeId
-    nodes_to_ids: Map<Node, NodeId>,
-    /// A map from NodeId -> Node
-    ids_to_nodes: Map<NodeId, Node>,
-    /// An efficient data structure that stores the node trees
-    forest: Forest,
+    /// The [`NodeData`] for each node stored in this tree
+    pub(crate) nodes: SlotMap<Node, NodeData>,
+
+    /// The children of each node
+    ///
+    /// The indexes in the outer vector correspond to the position of the parent [`NodeData`]
+    pub(crate) children: SlotMap<Node, ChildrenVec<Node>>,
+
+    /// The parents of each node
+    ///
+    /// The indexes in the outer vector correspond to the position of the child [`NodeData`]
+    pub(crate) parents: SlotMap<Node, Option<Node>>,
 }
 
 impl Default for Taffy {
@@ -87,255 +53,234 @@ impl Default for Taffy {
     }
 }
 
+#[allow(clippy::iter_cloned_collect)] // due to no-std support, we need to use `iter_cloned` instead of `collect`
 impl Taffy {
     /// Creates a new [`Taffy`]
     ///
     /// The default capacity of a [`Taffy`] is 16 nodes.
     #[must_use]
     pub fn new() -> Self {
-        Default::default()
+        Taffy::default()
     }
 
     /// Creates a new [`Taffy`] that can store `capacity` nodes before reallocation
+    #[must_use]
     pub fn with_capacity(capacity: usize) -> Self {
         Self {
-            id: INSTANCE_ALLOCATOR.allocate(),
-            allocator: Allocator::new(),
-            nodes_to_ids: new_map_with_capacity(capacity),
-            ids_to_nodes: new_map_with_capacity(capacity),
-            forest: Forest::with_capacity(capacity),
+            nodes: SlotMap::with_capacity(capacity),
+            children: SlotMap::with_capacity(capacity),
+            parents: SlotMap::with_capacity(capacity),
         }
     }
 
-    /// Allocates memory for a new node, and returns a matching generated [`Node`]
-    fn allocate_node(&mut self) -> Node {
-        let local = self.allocator.allocate();
-        Node { instance: self.id, local }
-    }
+    /// Creates and adds a new unattached leaf node to the tree, and returns the [`NodeId`] of the new node
+    pub fn new_leaf(&mut self, layout: FlexboxLayout) -> TaffyResult<Node> {
+        let id = self.nodes.insert(NodeData::new(layout));
+        let _ = self.children.insert(new_vec_with_capacity(0));
+        let _ = self.parents.insert(None);
 
-    /// Stores a new node in the tree
-    fn add_node(&mut self, node: Node, id: NodeId) {
-        let _ = self.nodes_to_ids.insert(node, id);
-        let _ = self.ids_to_nodes.insert(id, node);
-    }
-
-    /// Returns the `NodeId` of the provided node within the forest
-    fn find_node(&self, node: Node) -> Result<NodeId, error::InvalidNode> {
-        match self.nodes_to_ids.get(&node) {
-            Some(id) => Ok(*id),
-            None => Err(error::InvalidNode(node)),
-        }
-    }
-
-    /// Creates and adds a new leaf node
-    pub fn new_leaf(&mut self, layout: FlexboxLayout) -> Result<Node, error::InvalidNode> {
-        let node = self.allocate_node();
-        let id = self.forest.new_leaf(layout);
-        self.add_node(node, id);
-        Ok(node)
+        Ok(id)
     }
 
+    /// Creates and adds a new unattached leaf node to the tree, and returns the [`NodeId`] of the new node
+    ///
     /// Creates and adds a new leaf node with a supplied [`MeasureFunc`]
-    pub fn new_leaf_with_measure(
-        &mut self,
-        layout: FlexboxLayout,
-        measure: MeasureFunc,
-    ) -> Result<Node, error::InvalidNode> {
-        let node = self.allocate_node();
-        let id = self.forest.new_leaf_with_measure(layout, measure);
-        self.add_node(node, id);
-        Ok(node)
+    pub fn new_leaf_with_measure(&mut self, layout: FlexboxLayout, measure: MeasureFunc) -> TaffyResult<Node> {
+        let id = self.nodes.insert(NodeData::new_with_measure(layout, measure));
+        let _ = self.children.insert(new_vec_with_capacity(0));
+        let _ = self.parents.insert(None);
+
+        Ok(id)
     }
 
     /// Creates and adds a new node, which may have any number of `children`
-    pub fn new_with_children(&mut self, layout: FlexboxLayout, children: &[Node]) -> Result<Node, error::InvalidNode> {
-        let node = self.allocate_node();
-        let children = children
-            .iter()
-            .map(|child| self.find_node(*child))
-            .collect::<Result<ChildrenVec<_>, error::InvalidNode>>()?;
-        let id = self.forest.new_with_children(layout, children);
-        self.add_node(node, id);
-        Ok(node)
+    pub fn new_with_children(&mut self, layout: FlexboxLayout, children: &[Node]) -> TaffyResult<Node> {
+        let id = self.nodes.insert(NodeData::new(layout));
+
+        for child in children {
+            self.parents[*child] = Some(id);
+        }
+
+        let _ = self.children.insert(children.iter().copied().collect::<_>());
+        let _ = self.parents.insert(None);
+
+        Ok(id)
     }
 
     /// Removes all nodes
     ///
     /// All associated [`Id`] will be rendered invalid.
     pub fn clear(&mut self) {
-        self.nodes_to_ids.clear();
-        self.ids_to_nodes.clear();
-        self.forest.clear();
+        self.nodes.clear();
+        self.children.clear();
+        self.parents.clear();
     }
 
     /// Remove a specific [`Node`] from the tree
     ///
     /// Its [`Id`] is marked as invalid. Returns the id of the node removed.
-    pub fn remove(&mut self, node: Node) -> Result<usize, error::InvalidNode> {
-        let id = self.find_node(node)?;
-
-        self.nodes_to_ids.remove(&node);
-        self.ids_to_nodes.remove(&id);
-
-        if let Some(new_id) = self.forest.swap_remove(id) {
-            let new = self.ids_to_nodes.remove(&new_id).unwrap();
-            let _ = self.nodes_to_ids.insert(new, id);
-            let _ = self.ids_to_nodes.insert(id, new);
+    pub fn remove(&mut self, node: Node) -> TaffyResult<Node> {
+        if let Some(parent) = self.parents[node] {
+            if let Some(children) = self.children.get_mut(parent) {
+                children.retain(|f| *f != node);
+            }
         }
 
-        Ok(id)
+        let _ = self.children.remove(node);
+        let _ = self.parents.remove(node);
+        let _ = self.nodes.remove(node);
+
+        Ok(node)
     }
 
     /// Sets the [`MeasureFunc`] of the associated node
-    pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> Result<(), error::InvalidNode> {
-        let id = self.find_node(node)?;
-        self.forest.nodes[id].measure = measure;
-        self.forest.mark_dirty(id);
+    pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> TaffyResult<()> {
+        self.nodes[node].measure = measure;
+        self.mark_dirty(node)?;
+
         Ok(())
     }
 
     /// Adds a `child` [`Node`] under the supplied `parent`
-    pub fn add_child(&mut self, parent: Node, child: Node) -> Result<(), error::InvalidNode> {
-        let node_id = self.find_node(parent)?;
-        let child_id = self.find_node(child)?;
+    pub fn add_child(&mut self, parent: Node, child: Node) -> TaffyResult<()> {
+        self.parents[child] = Some(parent);
+        self.children[parent].push(child);
+        self.mark_dirty(parent)?;
 
-        self.forest.add_child(node_id, child_id);
         Ok(())
     }
 
     /// Directly sets the `children` of the supplied `parent`
-    pub fn set_children(&mut self, parent: Node, children: &[Node]) -> Result<(), error::InvalidNode> {
-        let node_id = self.find_node(parent)?;
-        let children_id = children.iter().map(|child| self.find_node(*child)).collect::<Result<ChildrenVec<_>, _>>()?;
-
+    pub fn set_children(&mut self, parent: Node, children: &[Node]) -> TaffyResult<()> {
         // Remove node as parent from all its current children.
-        for child in &self.forest.children[node_id] {
-            self.forest.parents[*child].retain(|p| *p != node_id);
+        for child in &self.children[parent] {
+            self.parents[*child] = None;
         }
 
         // Build up relation node <-> child
-        for child in &children_id {
-            self.forest.parents[*child].push(node_id);
+        for child in children {
+            self.parents[*child] = Some(parent);
         }
-        self.forest.children[node_id] = children_id;
 
-        self.forest.mark_dirty(node_id);
+        self.children[parent] = children.iter().copied().collect::<_>();
+
+        self.mark_dirty(parent)?;
+
         Ok(())
     }
 
     /// Removes the `child` of the parent `node`
     ///
-    /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent.
-    pub fn remove_child(&mut self, parent: Node, child: Node) -> Result<Node, error::InvalidNode> {
-        let node_id = self.find_node(parent)?;
-        let child_id = self.find_node(child)?;
-
-        let prev_id = self.forest.remove_child(node_id, child_id);
-        Ok(self.ids_to_nodes[&prev_id])
+    /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
+    pub fn remove_child(&mut self, parent: Node, child: Node) -> TaffyResult<Node> {
+        let index = self.children[parent].iter().position(|n| *n == child).unwrap();
+        self.remove_child_at_index(parent, index)
     }
 
     /// Removes the child at the given `index` from the `parent`
     ///
-    /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent.
-    pub fn remove_child_at_index(&mut self, parent: Node, child_index: usize) -> Result<Node, error::InvalidChild> {
-        let node_id = self.find_node(parent).map_err(|e| error::InvalidChild::InvalidParentNode(e.0))?;
-
-        let child_count = self.forest.children[node_id].len();
+    /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
+    pub fn remove_child_at_index(&mut self, parent: Node, child_index: usize) -> TaffyResult<Node> {
+        let child_count = self.children[parent].len();
         if child_index >= child_count {
-            return Err(error::InvalidChild::ChildIndexOutOfBounds { parent, child_index, child_count });
+            return Err(error::TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
         }
 
-        let prev_id = self.forest.remove_child_at_index(node_id, child_index);
-        Ok(self.ids_to_nodes[&prev_id])
+        let child = self.children[parent].remove(child_index);
+        self.parents[child] = None;
+
+        self.mark_dirty(parent)?;
+
+        Ok(child)
     }
 
     /// Replaces the child at the given `child_index` from the `parent` node with the new `child` node
     ///
-    /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent.
-    pub fn replace_child_at_index(
-        &mut self,
-        parent: Node,
-        child_index: usize,
-        new_child: Node,
-    ) -> Result<Node, error::InvalidChild> {
-        let node_id = self.find_node(parent).map_err(|e| error::InvalidChild::InvalidParentNode(e.0))?;
-        let child_id = self.find_node(new_child).map_err(|e| error::InvalidChild::InvalidChildNode(e.0))?;
-
-        let child_count = self.forest.children[node_id].len();
+    /// The child is not removed from the tree entirely, it is simply no longer attached to its previous parent.
+    pub fn replace_child_at_index(&mut self, parent: Node, child_index: usize, new_child: Node) -> TaffyResult<Node> {
+        let child_count = self.children[parent].len();
         if child_index >= child_count {
-            return Err(error::InvalidChild::ChildIndexOutOfBounds { parent, child_index, child_count });
+            return Err(error::TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
         }
 
-        self.forest.parents[child_id].push(node_id);
-        let old_child = core::mem::replace(&mut self.forest.children[node_id][child_index], child_id);
-        self.forest.parents[old_child].retain(|p| *p != node_id);
+        self.parents[new_child] = Some(parent);
+        let old_child = core::mem::replace(&mut self.children[parent][child_index], new_child);
+        self.parents[old_child] = None;
 
-        self.forest.mark_dirty(node_id);
+        self.mark_dirty(parent)?;
 
-        Ok(self.ids_to_nodes[&old_child])
+        Ok(old_child)
     }
 
     /// Returns the child [`Node`] of the parent `node` at the provided `child_index`
-    pub fn child_at_index(&self, parent: Node, child_index: usize) -> Result<Node, error::InvalidChild> {
-        let id = self.find_node(parent).map_err(|e| error::InvalidChild::InvalidParentNode(e.0))?;
-
-        let child_count = self.forest.children[id].len();
+    pub fn child_at_index(&self, parent: Node, child_index: usize) -> TaffyResult<Node> {
+        let child_count = self.children[parent].len();
         if child_index >= child_count {
-            return Err(error::InvalidChild::ChildIndexOutOfBounds { parent, child_index, child_count });
+            return Err(error::TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
         }
 
-        Ok(self.ids_to_nodes[&self.forest.children[id][child_index]])
+        Ok(self.children[parent][child_index])
     }
 
     /// Returns the number of children of the `parent` [`Node`]
-    pub fn child_count(&self, parent: Node) -> Result<usize, error::InvalidNode> {
-        let id = self.find_node(parent)?;
-        Ok(self.forest.children[id].len())
+    pub fn child_count(&self, parent: Node) -> TaffyResult<usize> {
+        Ok(self.children[parent].len())
     }
 
     /// Returns a list of children that belong to the [`Parent`]
-    pub fn children(&self, parent: Node) -> Result<Vec<Node>, error::InvalidNode> {
-        let id = self.find_node(parent)?;
-        Ok(self.forest.children[id].iter().map(|child| self.ids_to_nodes[child]).collect())
+    pub fn children(&self, parent: Node) -> TaffyResult<Vec<Node>> {
+        Ok(self.children[parent].iter().copied().collect::<_>())
     }
 
     /// Sets the [`Style`] of the provided `node`
-    pub fn set_style(&mut self, node: Node, style: FlexboxLayout) -> Result<(), error::InvalidNode> {
-        let id = self.find_node(node)?;
-        self.forest.nodes[id].style = style;
-        self.forest.mark_dirty(id);
+    pub fn set_style(&mut self, node: Node, style: FlexboxLayout) -> TaffyResult<()> {
+        self.nodes[node].style = style;
+        self.mark_dirty(node)?;
         Ok(())
     }
 
     /// Gets the [`Style`] of the provided `node`
-    pub fn style(&self, node: Node) -> Result<&FlexboxLayout, error::InvalidNode> {
-        let id = self.find_node(node)?;
-        Ok(&self.forest.nodes[id].style)
+    pub fn style(&self, node: Node) -> TaffyResult<&FlexboxLayout> {
+        Ok(&self.nodes[node].style)
     }
 
     /// Return this node layout relative to its parent
-    pub fn layout(&self, node: Node) -> Result<&Layout, error::InvalidNode> {
-        let id = self.find_node(node)?;
-        Ok(&self.forest.nodes[id].layout)
+    pub fn layout(&self, node: Node) -> TaffyResult<&Layout> {
+        Ok(&self.nodes[node].layout)
     }
 
     /// Marks the layout computation of this node and its children as outdated
-    pub fn mark_dirty(&mut self, node: Node) -> Result<(), error::InvalidNode> {
-        let id = self.find_node(node)?;
-        self.forest.mark_dirty(id);
+    ///
+    /// Performs a recursive depth-first search up the tree until the root node is reached
+    ///
+    /// WARNING: this will stack-overflow if the tree contains a cycle
+    pub fn mark_dirty(&mut self, node: Node) -> TaffyResult<()> {
+        /// WARNING: this will stack-overflow if the tree contains a cycle
+        fn mark_dirty_recursive(
+            nodes: &mut SlotMap<Node, NodeData>,
+            parents: &SlotMap<Node, Option<Node>>,
+            node_id: Node,
+        ) {
+            nodes[node_id].mark_dirty();
+
+            if let Some(Some(node)) = parents.get(node_id) {
+                mark_dirty_recursive(nodes, parents, *node);
+            }
+        }
+
+        mark_dirty_recursive(&mut self.nodes, &self.parents, node);
+
         Ok(())
     }
 
     /// Indicates whether the layout of this node (and its children) need to be recomputed
-    pub fn dirty(&self, node: Node) -> Result<bool, error::InvalidNode> {
-        let id = self.find_node(node)?;
-        Ok(self.forest.nodes[id].is_dirty)
+    pub fn dirty(&self, node: Node) -> TaffyResult<bool> {
+        Ok(self.nodes[node].is_dirty)
     }
 
     /// Updates the stored layout of the provided `node` and its children
-    pub fn compute_layout(&mut self, node: Node, size: Size<Option<f32>>) -> Result<(), error::InvalidNode> {
-        let id = self.find_node(node)?;
-        self.forest.compute(id, size);
+    pub fn compute_layout(&mut self, node: Node, size: Size<Option<f32>>) -> Result<(), TaffyError> {
+        self.compute(node, size);
         Ok(())
     }
 }
@@ -353,11 +298,9 @@ mod tests {
         const DEFAULT_CAPACITY: usize = 16; // This is the capacity defined in the `impl Default`
         let taffy = Taffy::new();
 
-        assert!(taffy.nodes_to_ids.capacity() >= DEFAULT_CAPACITY);
-        assert!(taffy.ids_to_nodes.capacity() >= DEFAULT_CAPACITY);
-        assert!(taffy.forest.children.capacity() >= DEFAULT_CAPACITY);
-        assert!(taffy.forest.parents.capacity() >= DEFAULT_CAPACITY);
-        assert!(taffy.forest.nodes.capacity() >= DEFAULT_CAPACITY);
+        assert!(taffy.children.capacity() >= DEFAULT_CAPACITY);
+        assert!(taffy.parents.capacity() >= DEFAULT_CAPACITY);
+        assert!(taffy.nodes.capacity() >= DEFAULT_CAPACITY);
     }
 
     #[test]
@@ -365,51 +308,9 @@ mod tests {
         const CAPACITY: usize = 8;
         let taffy = Taffy::with_capacity(CAPACITY);
 
-        assert!(taffy.nodes_to_ids.capacity() >= CAPACITY);
-        assert!(taffy.ids_to_nodes.capacity() >= CAPACITY);
-        assert!(taffy.forest.children.capacity() >= CAPACITY);
-        assert!(taffy.forest.parents.capacity() >= CAPACITY);
-        assert!(taffy.forest.nodes.capacity() >= CAPACITY);
-    }
-
-    #[test]
-    fn add_node() {
-        let mut taffy = Taffy::new();
-        let node = taffy.allocate_node();
-        let id = taffy.forest.new_leaf(FlexboxLayout::default());
-
-        // Should not contain nodes
-        assert!(!taffy.nodes_to_ids.contains_key(&node));
-        assert!(!taffy.ids_to_nodes.contains_key(&id));
-
-        taffy.add_node(node, id);
-
-        // Should contain nodes
-        assert!(taffy.nodes_to_ids.contains_key(&node));
-        assert!(taffy.ids_to_nodes.contains_key(&id));
-    }
-
-    #[test]
-    fn find_node() {
-        let mut taffy = Taffy::new();
-
-        let child0 = taffy.new_leaf(FlexboxLayout::default()).unwrap();
-        let child1 = taffy.new_leaf(FlexboxLayout::default()).unwrap();
-        let node = taffy.new_with_children(FlexboxLayout::default(), &[child0, child1]).unwrap();
-
-        // Should find the nodes
-        assert!(if let Ok(node_id) = taffy.find_node(node) { node_id == node.local.0 } else { false });
-        assert!(if let Ok(node_id) = taffy.find_node(child0) { node_id == child0.local.0 } else { false });
-        assert!(if let Ok(node_id) = taffy.find_node(child1) { node_id == child1.local.0 } else { false });
-
-        let _ = taffy.remove(node);
-        let _ = taffy.remove(child0);
-        let _ = taffy.remove(child1);
-
-        // Should not find the nodes
-        assert!(taffy.find_node(node).is_err());
-        assert!(taffy.find_node(child0).is_err());
-        assert!(taffy.find_node(child1).is_err());
+        assert!(taffy.children.capacity() >= CAPACITY);
+        assert!(taffy.parents.capacity() >= CAPACITY);
+        assert!(taffy.nodes.capacity() >= CAPACITY);
     }
 
     #[test]
@@ -421,7 +322,6 @@ mod tests {
         let node = res.unwrap();
 
         // node should be in the taffy tree and have no children
-        assert!(taffy.find_node(node).is_ok());
         assert!(taffy.child_count(node).unwrap() == 0);
     }
 
@@ -434,7 +334,6 @@ mod tests {
         let node = res.unwrap();
 
         // node should be in the taffy tree and have no children
-        assert!(taffy.find_node(node).is_ok());
         assert!(taffy.child_count(node).unwrap() == 0);
     }
 
@@ -452,39 +351,13 @@ mod tests {
         assert_eq!(taffy.children(node).unwrap()[1], child1);
     }
 
-    #[test]
-    fn clear_should_clear_nodes() {
-        let mut taffy = Taffy::new();
-        let child0 = taffy.new_leaf(FlexboxLayout::default()).unwrap();
-        let child1 = taffy.new_leaf(FlexboxLayout::default()).unwrap();
-        let node = taffy.new_with_children(FlexboxLayout::default(), &[child0, child1]).unwrap();
-
-        // Nodes should be present
-        assert!(taffy.find_node(node).is_ok());
-        assert!(taffy.find_node(child0).is_ok());
-        assert!(taffy.find_node(child1).is_ok());
-
-        taffy.clear();
-
-        // nodes should not be present
-        assert!(taffy.find_node(node).is_err());
-        assert!(taffy.find_node(child0).is_err());
-        assert!(taffy.find_node(child1).is_err());
-    }
-
     #[test]
     fn remove_node_should_remove() {
         let mut taffy = Taffy::new();
 
         let node = taffy.new_leaf(FlexboxLayout::default()).unwrap();
 
-        // node should exist
-        assert!(taffy.find_node(node).is_ok());
-
         let _ = taffy.remove(node).unwrap();
-
-        // node should no longer exist
-        assert!(taffy.find_node(node).is_err());
     }
 
     #[test]
@@ -496,9 +369,6 @@ mod tests {
         let node1 = taffy.new_with_children(FlexboxLayout::default(), &[node2]).unwrap();
         let node0 = taffy.new_with_children(FlexboxLayout::default(), &[node1]).unwrap();
 
-        // Node1 should exist
-        assert!(taffy.find_node(node1).is_ok());
-
         // Both node0 and node1 should have 1 child nodes
         assert_eq!(taffy.children(node0).unwrap().as_slice(), &[node1]);
         assert_eq!(taffy.children(node1).unwrap().as_slice(), &[node2]);
@@ -506,9 +376,6 @@ mod tests {
         // Disconnect the tree: <0> <2>
         let _ = taffy.remove(node1).unwrap();
 
-        // Node1 should no longer exist
-        assert!(taffy.find_node(node1).is_err());
-
         // Both remaining nodes should have no child nodes
         assert!(taffy.children(node0).unwrap().is_empty());
         assert!(taffy.children(node2).unwrap().is_empty());
diff --git a/src/style.rs b/src/style.rs
index d57e38358..a11d33ba8 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -540,6 +540,7 @@ impl FlexboxLayout {
     }
 }
 
+#[allow(clippy::bool_assert_comparison)]
 #[cfg(test)]
 mod tests {
     mod test_flex_direction {
diff --git a/src/sys.rs b/src/sys.rs
index 6392cbc1a..5018e0aba 100644
--- a/src/sys.rs
+++ b/src/sys.rs
@@ -17,25 +17,12 @@ pub(crate) use self::core::*;
 mod std {
     /// An allocation-backend agnostic [`Box`] type
     pub(crate) type Box<A> = std::boxed::Box<A>;
-    /// An allocation-backend agnostic map type
-    pub(crate) type Map<K, V> = std::collections::HashMap<K, V>;
     /// An allocation-backend agnostic vector type
     pub(crate) type Vec<A> = std::vec::Vec<A>;
     /// A vector of child nodes
     pub(crate) type ChildrenVec<A> = std::vec::Vec<A>;
-    /// A vector of parent nodes
-    pub(crate) type ParentsVec<A> = std::vec::Vec<A>;
 
-    /// Creates a new map with the capacity for the specified number of items before it must be resized
-    #[must_use]
-    pub(crate) fn new_map_with_capacity<K, V>(capacity: usize) -> Map<K, V>
-    where
-        K: Eq + std::hash::Hash,
-    {
-        Map::with_capacity(capacity)
-    }
-
-    /// Creates a new vector with the capacity for the specified number of items before it must be resized    
+    /// Creates a new vector with the capacity for the specified number of items before it must be resized
     #[must_use]
     pub(crate) fn new_vec_with_capacity<A>(capacity: usize) -> Vec<A> {
         Vec::with_capacity(capacity)
@@ -61,20 +48,10 @@ mod alloc {
 
     /// An allocation-backend agnostic `Box` type
     pub(crate) type Box<A> = alloc::boxed::Box<A>;
-    /// An allocation-backend agnostic map type
-    pub(crate) type Map<K, V> = hashbrown::HashMap<K, V>;
     /// An allocation-backend agnostic vector type
     pub(crate) type Vec<A> = alloc::vec::Vec<A>;
     /// A vector of child nodes
     pub(crate) type ChildrenVec<A> = alloc::vec::Vec<A>;
-    /// A vector of parent nodes
-    pub(crate) type ParentsVec<A> = alloc::vec::Vec<A>;
-
-    /// Creates a new map with the capacity for the specified number of items before it must be resized
-    #[must_use]
-    pub(crate) fn new_map_with_capacity<K, V>(capacity: usize) -> Map<K, V> {
-        Map::with_capacity(capacity)
-    }
 
     /// Creates a new vector with the capacity for the specified number of items before it must be resized
     #[must_use]
@@ -98,32 +75,15 @@ mod alloc {
 /// For when neither `alloc` nor `std` is enabled
 #[cfg(all(not(feature = "alloc"), not(feature = "std")))]
 mod core {
-    /// The maximum number of nodes in the forest
+    /// The maximum number of nodes in the tree
     pub const MAX_NODE_COUNT: usize = 256;
     /// The maximum number of children of any given node
     pub const MAX_CHILD_COUNT: usize = 16;
-    /// The maximum number of parents for each child
-    pub const MAX_PARENTS_COUNT: usize = 1;
 
-    /// An allocation-backend agnostic map type
-    pub(crate) type Map<K, V> = crate::indexmap::FnvIndexMap<K, V, MAX_NODE_COUNT>;
     /// An allocation-backend agnostic vector type
     pub(crate) type Vec<A> = arrayvec::ArrayVec<A, MAX_NODE_COUNT>;
     /// A vector of child nodes, whose length cannot exceed [`MAX_CHILD_COUNT`]
     pub(crate) type ChildrenVec<A> = arrayvec::ArrayVec<A, MAX_CHILD_COUNT>;
-    /// A vector of parent nodes, whose length cannot exceed [`MAX_PARENTS_COUNT`]
-    pub(crate) type ParentsVec<A> = arrayvec::ArrayVec<A, MAX_PARENTS_COUNT>;
-
-    /// Creates a new map with the capacity for the specified number of items
-    ///
-    /// This map cannot be resized.
-    #[must_use]
-    pub(crate) fn new_map_with_capacity<K, V>(_capacity: usize) -> Map<K, V>
-    where
-        K: Eq + ::hash32::Hash,
-    {
-        Map::new()
-    }
 
     /// Creates a new map with the capacity for the specified number of items before it must be resized
     ///