gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 8TOML 3CSS 1317 Definiciones

Add a lower level node forest structure and adjust data layout

17acee6ac4bdb1a9ad3b03105b8e5d5e04ed6dca · por msiglreith · (hace 7 años) · padre cd191eadc · ver el árbol en este commit

Delta semántico · 48

src/algo.rs

src/id.rs

src/node.rs

3 archivos · +249 −200

diff --git a/src/algo.rs b/src/algo.rs
index 741512642..e814e8545 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -6,7 +6,8 @@ use libm::F32Ext;
 use core::any::Any;
 use core::f32;
 
-use crate::node::{Node, Storage, Stretch};
+use crate::id::NodeId;
+use crate::node::{Forest, NodeData};
 use crate::result;
 use crate::style::*;
 
@@ -21,7 +22,7 @@ pub struct ComputeResult {
 }
 
 struct FlexItem {
-    node: Node,
+    node: NodeId,
 
     size: Size<Number>,
     min_size: Size<Number>,
@@ -57,9 +58,9 @@ struct FlexLine {
     offset_cross: f32,
 }
 
-impl Stretch {
-    pub(crate) fn compute(&mut self, root: Node, size: Size<Number>) -> Result<(), Box<Any>> {
-        let style = self.style[&root];
+impl Forest {
+    pub(crate) fn compute(&mut self, root: NodeId, size: Size<Number>) -> Result<(), Box<Any>> {
+        let style = self.nodes[root].style;
         let has_root_min_max = style.min_size.width.is_defined()
             || style.min_size.height.is_defined()
             || style.max_size.width.is_defined()
@@ -101,24 +102,18 @@ impl Stretch {
             )?
         };
 
-        *self.layout.get_mut(root).unwrap() = result::Layout {
+        self.nodes[root].layout = result::Layout {
             order: 0,
             size: Size { width: result.size.width, height: result.size.height },
             location: Point { x: 0.0, y: 0.0 },
         };
 
-        Self::round_layout(&mut self.layout, &self.children, root, 0.0, 0.0);
+        Self::round_layout(&mut self.nodes, &self.children, root, 0.0, 0.0);
         Ok(())
     }
 
-    fn round_layout(
-        layouts: &mut Storage<result::Layout>,
-        children: &Storage<Vec<Node>>,
-        root: Node,
-        abs_x: f32,
-        abs_y: f32,
-    ) {
-        let layout = layouts.get_mut(root).unwrap();
+    fn round_layout(nodes: &mut Vec<NodeData>, children: &Vec<Vec<NodeId>>, root: NodeId, 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;
 
@@ -126,22 +121,22 @@ impl Stretch {
         layout.location.y = layout.location.y.round();
         layout.size.width = (abs_x + layout.size.width).round() - abs_x.round();
         layout.size.height = (abs_y + layout.size.height).round() - abs_y.round();
-        for child in &children[&root] {
-            Self::round_layout(layouts, children, *child, abs_x, abs_y);
+        for child in &children[root] {
+            Self::round_layout(nodes, children, *child, abs_x, abs_y);
         }
     }
 
     fn compute_internal(
         &mut self,
-        node: Node,
+        node: NodeId,
         node_size: Size<Number>,
         parent_size: Size<Number>,
         perform_layout: bool,
     ) -> Result<ComputeResult, Box<Any>> {
-        *self.is_dirty.get_mut(node).unwrap() = false;
+        self.nodes[node].is_dirty = false;
 
         // First we check if we have a result for the given input
-        if let Some(cache) = &self.layout_cache[&node] {
+        if let Some(ref cache) = self.nodes[node].layout_cache {
             if cache.perform_layout || !perform_layout {
                 let width_compatible = if let Number::Defined(width) = node_size.width {
                     (width - cache.result.size.width).abs() < f32::EPSILON
@@ -168,14 +163,14 @@ impl Stretch {
         // Define some general constants we will need for the remainder
         // of the algorithm.
 
-        let dir = self.style[&node].flex_direction;
+        let dir = self.nodes[node].style.flex_direction;
         let is_row = dir.is_row();
         let is_column = dir.is_column();
-        let is_wrap_reverse = self.style[&node].flex_wrap == FlexWrap::WrapReverse;
+        let is_wrap_reverse = self.nodes[node].style.flex_wrap == FlexWrap::WrapReverse;
 
-        let margin = self.style[&node].margin.map(|n| n.resolve(parent_size.width).or_else(0.0));
-        let padding = self.style[&node].padding.map(|n| n.resolve(parent_size.width).or_else(0.0));
-        let border = self.style[&node].border.map(|n| n.resolve(parent_size.width).or_else(0.0));
+        let margin = self.nodes[node].style.margin.map(|n| n.resolve(parent_size.width).or_else(0.0));
+        let padding = self.nodes[node].style.padding.map(|n| n.resolve(parent_size.width).or_else(0.0));
+        let border = self.nodes[node].style.border.map(|n| n.resolve(parent_size.width).or_else(0.0));
 
         let padding_border = Rect {
             start: padding.start + border.start,
@@ -193,14 +188,14 @@ impl Stretch {
         let mut inner_container_size = Size { width: 0.0, height: 0.0 };
 
         // If this is a leaf node we can skip a lot of this function in some cases
-        if self.children[&node].is_empty() {
+        if self.children[node].is_empty() {
             if node_size.width.is_defined() && node_size.height.is_defined() {
                 return Ok(ComputeResult { size: node_size.map(|s| s.or_else(0.0)) });
             }
 
-            if let Some(ref measure) = self.measure[&node] {
+            if let Some(ref measure) = self.nodes[node].measure {
                 let result = ComputeResult { size: measure(node_size)? };
-                *self.layout_cache.get_mut(node).unwrap() =
+                self.nodes[node].layout_cache =
                     Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
                 return Ok(result);
             }
@@ -230,9 +225,9 @@ impl Stretch {
             height: node_size.height.or_else(parent_size.height - margin.vertical()) - padding_border.vertical(),
         };
 
-        let mut flex_items: Vec<FlexItem> = self.children[&node]
+        let mut flex_items: Vec<FlexItem> = self.children[node]
             .iter()
-            .map(|child| (child, self.style[&child]))
+            .map(|child| (child, &self.nodes[*child].style))
             .filter(|(_, style)| style.position_type != PositionType::Absolute)
             .filter(|(_, style)| style.display != Display::None)
             .map(|(child, child_style)| FlexItem {
@@ -276,13 +271,13 @@ impl Stretch {
             .collect();
 
         let has_baseline_child = flex_items.iter().fold(false, |result, child| {
-            result || self.style[&child.node].align_self(&self.style[&node]) == AlignSelf::Baseline
+            result || self.nodes[child.node].style.align_self(&self.nodes[node].style) == AlignSelf::Baseline
         });
 
         // TODO - this does not follow spec. See commented out code below
         // 3. Determine the flex base size and hypothetical main size of each item:
         flex_items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
-            let child_style = self.style[&child.node];
+            let child_style = self.nodes[child.node].style;
 
             // A. If the item has a definite used flex basis, that’s the flex base size.
 
@@ -330,7 +325,7 @@ impl Stretch {
             //    flex item’s cross size. The flex base size is the item’s resulting main size.
 
             let width: Number = if !child.size.width.is_defined()
-                && child_style.align_self(&self.style[&node]) == AlignSelf::Stretch
+                && child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch
                 && is_column
             {
                 available_space.width
@@ -339,7 +334,7 @@ impl Stretch {
             };
 
             let height: Number = if !child.size.height.is_defined()
-                && child_style.align_self(&self.style[&node]) == AlignSelf::Stretch
+                && child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch
                 && is_row
             {
                 available_space.height
@@ -415,7 +410,7 @@ impl Stretch {
             let mut lines: Vec<FlexLine> = vec![];
             let mut line_length = 0.0;
 
-            if self.style[&node].flex_wrap == FlexWrap::NoWrap {
+            if self.nodes[node].style.flex_wrap == FlexWrap::NoWrap {
                 lines.push(FlexLine { items: flex_items, cross_size: 0.0, offset_cross: 0.0 });
             } else {
                 let mut line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
@@ -495,7 +490,7 @@ impl Stretch {
 
                 child.outer_target_size.set_main(dir, child.target_size.main(dir) + child.margin.main(dir));
 
-                let child_style = &self.style[&child.node];
+                let child_style = &self.nodes[child.node].style;
                 if (child_style.flex_grow == 0.0 && child_style.flex_shrink == 0.0)
                     || (growing && child.flex_basis > child.hypothetical_inner_size.main(dir))
                     || (shrinking && child.flex_basis < child.hypothetical_inner_size.main(dir))
@@ -556,7 +551,7 @@ impl Stretch {
 
                 let (sum_flex_grow, sum_flex_shrink): (f32, f32) =
                     unfrozen.iter().fold((0.0, 0.0), |(flex_grow, flex_shrink), item| {
-                        let style = &self.style[&item.node];
+                        let style = &self.nodes[item.node].style;
                         (flex_grow + style.flex_grow, flex_shrink + style.flex_shrink)
                     });
 
@@ -592,18 +587,20 @@ impl Stretch {
                         unfrozen.iter_mut().for_each(|child| {
                             child.target_size.set_main(
                                 dir,
-                                child.flex_basis + free_space * (self.style[&child.node].flex_grow / sum_flex_grow),
+                                child.flex_basis
+                                    + free_space * (self.nodes[child.node].style.flex_grow / sum_flex_grow),
                             );
                         });
                     } else if shrinking && sum_flex_shrink > 0.0 {
                         let sum_scaled_shrink_factor: f32 = unfrozen
                             .iter()
-                            .map(|child| child.inner_flex_basis * self.style[&child.node].flex_shrink)
+                            .map(|child| child.inner_flex_basis * self.nodes[child.node].style.flex_shrink)
                             .sum();
 
                         if sum_scaled_shrink_factor > 0.0 {
                             unfrozen.iter_mut().for_each(|child| {
-                                let scaled_shrink_factor = child.inner_flex_basis * self.style[&child.node].flex_shrink;
+                                let scaled_shrink_factor =
+                                    child.inner_flex_basis * self.nodes[child.node].style.flex_shrink;
                                 child.target_size.set_main(
                                     dir,
                                     child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor),
@@ -624,7 +621,7 @@ impl Stretch {
                     // webkit handled various scenarios. Can probably be solved better by passing in
                     // min-content max-content constraints from the top. Need to figure out correct thing to do here as
                     // just piling on more conditionals.
-                    let min_main = if is_row && self.measure[&child.node].is_none() {
+                    let min_main = if is_row && self.nodes[child.node].measure.is_none() {
                         self.compute_internal(
                             child.node,
                             Size { width: Undefined, height: Undefined },
@@ -730,12 +727,12 @@ impl Stretch {
         // TODO - probably should move this somewhere else as it doesn't make a ton of sense here but we need it below
         // TODO - This is expensive and should only be done if we really require a baseline. aka, make it lazy
 
-        fn calc_baseline(db: &Stretch, node: Node, layout: &result::Layout) -> f32 {
-            if db.children[&node].is_empty() {
+        fn calc_baseline(db: &Forest, node: NodeId, layout: &result::Layout) -> f32 {
+            if db.children[node].is_empty() {
                 layout.size.height
             } else {
-                let child = db.children[&node][0];
-                calc_baseline(db, child, &db.layout[&child])
+                let child = db.children[node][0];
+                calc_baseline(db, child, &db.nodes[child].layout)
             }
         };
 
@@ -767,7 +764,7 @@ impl Stretch {
                         self,
                         child.node,
                         &result::Layout {
-                            order: self.children[&node].iter().position(|n| *n == child.node).unwrap() as u32,
+                            order: self.children[node].iter().position(|n| *n == child.node).unwrap() as u32,
                             size: result.size,
                             location: Point { x: 0.0, y: 0.0 },
                         },
@@ -807,8 +804,8 @@ impl Stretch {
                     .items
                     .iter()
                     .map(|child| {
-                        let child_style = &self.style[&child.node];
-                        if child_style.align_self(&self.style[&node]) == AlignSelf::Baseline
+                        let child_style = &self.nodes[child.node].style;
+                        if child_style.align_self(&self.nodes[node].style) == AlignSelf::Baseline
                             && child_style.cross_margin_start(dir) != Dimension::Auto
                             && child_style.cross_margin_end(dir) != Dimension::Auto
                             && child_style.cross_size(dir) == Dimension::Auto
@@ -828,7 +825,7 @@ impl Stretch {
         //    by equal amounts such that the sum of their cross sizes exactly equals the
         //    flex container’s inner cross size.
 
-        if self.style[&node].align_content == AlignContent::Stretch && node_size.cross(dir).is_defined() {
+        if self.nodes[node].style.align_content == AlignContent::Stretch && node_size.cross(dir).is_defined() {
             let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
             let inner_cross = (node_size.cross(dir) - padding_border.cross(dir)).or_else(0.0);
 
@@ -870,10 +867,10 @@ impl Stretch {
             let line_cross_size = line.cross_size;
 
             line.items.iter_mut().for_each(|child| {
-                let child_style = &self.style[&child.node];
+                let child_style = &self.nodes[child.node].style;
                 child.target_size.set_cross(
                     dir,
-                    if child_style.align_self(&self.style[&node]) == AlignSelf::Stretch
+                    if child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch
                         && child_style.cross_margin_start(dir) != Dimension::Auto
                         && child_style.cross_margin_end(dir) != Dimension::Auto
                         && child_style.cross_size(dir) == Dimension::Auto
@@ -904,7 +901,7 @@ impl Stretch {
             let mut num_auto_margins = 0;
 
             line.items.iter_mut().for_each(|child| {
-                let child_style = &self.style[&child.node];
+                let child_style = &self.nodes[child.node].style;
                 if child_style.main_margin_start(dir) == Dimension::Auto {
                     num_auto_margins += 1;
                 }
@@ -917,7 +914,7 @@ impl Stretch {
                 let margin = free_space / num_auto_margins as f32;
 
                 line.items.iter_mut().for_each(|child| {
-                    let child_style = &self.style[&child.node];
+                    let child_style = &self.nodes[child.node].style;
                     if child_style.main_margin_start(dir) == Dimension::Auto {
                         if is_row {
                             child.margin.start = margin;
@@ -940,7 +937,7 @@ impl Stretch {
                 let justify_item = |(i, child): (usize, &mut FlexItem)| {
                     let is_first = i == 0;
 
-                    child.offset_main = match self.style[&node].justify_content {
+                    child.offset_main = match self.nodes[node].style.justify_content {
                         JustifyContent::FlexStart => {
                             if layout_reverse && is_first {
                                 free_space
@@ -1004,7 +1001,7 @@ impl Stretch {
 
             line.items.iter_mut().for_each(|child| {
                 let free_space = line_cross_size - child.outer_target_size.cross(dir);
-                let child_style = &self.style[&child.node];
+                let child_style = &self.nodes[child.node].style;
 
                 if child_style.cross_margin_start(dir) == Dimension::Auto
                     && child_style.cross_margin_end(dir) == Dimension::Auto
@@ -1032,7 +1029,7 @@ impl Stretch {
                     // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
                     //     cross-axis margins are auto.
 
-                    child.offset_cross = match child_style.align_self(&self.style[&node]) {
+                    child.offset_cross = match child_style.align_self(&self.nodes[node].style) {
                         AlignSelf::Auto => 0.0, // Should never happen
                         AlignSelf::FlexStart => {
                             if is_wrap_reverse {
@@ -1088,7 +1085,7 @@ impl Stretch {
         // layout we are done now.
         if !perform_layout {
             let result = ComputeResult { size: container_size };
-            *self.layout_cache.get_mut(node).unwrap() =
+            self.nodes[node].layout_cache =
                 Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
             return Ok(result);
         }
@@ -1101,7 +1098,7 @@ impl Stretch {
         let align_line = |(i, line): (usize, &mut FlexLine)| {
             let is_first = i == 0;
 
-            line.offset_cross = match self.style[&node].align_content {
+            line.offset_cross = match self.nodes[node].style.align_content {
                 AlignContent::FlexStart => {
                     if is_first && is_wrap_reverse {
                         free_space
@@ -1176,8 +1173,8 @@ impl Stretch {
                         + child.margin.cross_start(dir)
                         + (child.position.cross_start(dir).or_else(0.0) - child.position.cross_end(dir).or_else(0.0));
 
-                    *self.layout.get_mut(child.node).unwrap() = result::Layout {
-                        order: self.children[&node].iter().position(|n| *n == child.node).unwrap() as u32,
+                    self.nodes[child.node].layout = result::Layout {
+                        order: self.children[node].iter().position(|n| *n == child.node).unwrap() as u32,
                         size: result.size,
                         location: Point {
                             x: if is_row { offset_main } else { offset_cross },
@@ -1217,18 +1214,18 @@ impl Stretch {
         // Before returning we perform absolute layout on all absolutely positioned children
         {
             // TODO: remove number of Vec<_> generated
-            let candidates = self.children[&node]
+            let candidates = self.children[node]
                 .iter()
                 .cloned()
                 .enumerate()
-                .filter(|(_, child)| self.style[&child].position_type == PositionType::Absolute)
+                .filter(|(_, child)| self.nodes[*child].style.position_type == PositionType::Absolute)
                 .collect::<Vec<_>>();
 
             for (order, child) in candidates {
                 let container_width = container_size.width.to_number();
                 let container_height = container_size.height.to_number();
 
-                let child_style = self.style[&child];
+                let child_style = self.nodes[child].style;
 
                 let start = child_style.position.start.resolve(container_width)
                     + child_style.margin.start.resolve(container_width);
@@ -1292,7 +1289,7 @@ impl Stretch {
                 } else if end_main.is_defined() {
                     free_main_space - end_main.or_else(0.0) - border.main_end(dir)
                 } else {
-                    match self.style[&node].justify_content {
+                    match self.nodes[node].style.justify_content {
                         JustifyContent::SpaceBetween | JustifyContent::FlexStart => padding_border.main_start(dir),
                         JustifyContent::FlexEnd => free_main_space - padding_border.main_end(dir),
                         JustifyContent::SpaceEvenly | JustifyContent::SpaceAround | JustifyContent::Center => {
@@ -1306,7 +1303,7 @@ impl Stretch {
                 } else if end_cross.is_defined() {
                     free_cross_space - end_cross.or_else(0.0) - border.cross_end(dir)
                 } else {
-                    match child_style.align_self(&self.style[&node]) {
+                    match child_style.align_self(&self.nodes[node].style) {
                         AlignSelf::Auto => 0.0, // Should never happen
                         AlignSelf::FlexStart => {
                             if is_wrap_reverse {
@@ -1334,7 +1331,7 @@ impl Stretch {
                     }
                 };
 
-                *self.layout.get_mut(child).unwrap() = result::Layout {
+                self.nodes[child].layout = result::Layout {
                     order: order as u32,
                     size: result.size,
                     location: Point {
@@ -1345,23 +1342,23 @@ impl Stretch {
             }
         }
 
-        fn hidden_layout(layout: &mut Storage<result::Layout>, children: &Storage<Vec<Node>>, node: Node, order: u32) {
-            *layout.get_mut(node).unwrap() =
+        fn hidden_layout(nodes: &mut Vec<NodeData>, children: &Vec<Vec<NodeId>>, node: NodeId, order: u32) {
+            nodes[node].layout =
                 result::Layout { order, size: Size { width: 0.0, height: 0.0 }, location: Point { x: 0.0, y: 0.0 } };
 
-            for (order, child) in children[&node].iter().enumerate() {
-                hidden_layout(layout, children, *child, order as _);
+            for (order, child) in children[node].iter().enumerate() {
+                hidden_layout(nodes, children, *child, order as _);
             }
         }
 
-        for (order, child) in self.children[&node].iter().enumerate() {
-            if self.style[&child].display == Display::None {
-                hidden_layout(&mut self.layout, &self.children, *child, order as _);
+        for (order, child) in self.children[node].iter().enumerate() {
+            if self.nodes[*child].style.display == Display::None {
+                hidden_layout(&mut self.nodes, &self.children, *child, order as _);
             }
         }
 
         let result = ComputeResult { size: container_size };
-        *self.layout_cache.get_mut(node).unwrap() =
+        self.nodes[node].layout_cache =
             Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
         Ok(result)
     }
diff --git a/src/id.rs b/src/id.rs
index ce8cb3e03..0b76c8934 100644
--- a/src/id.rs
+++ b/src/id.rs
@@ -2,6 +2,9 @@
 //!
 //!
 
+/// Internal node id.
+pub(crate) type NodeId = usize;
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub(crate) struct Id {
     id: u32,
diff --git a/src/node.rs b/src/node.rs
index 6746c764e..6a0ebadda 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -8,7 +8,7 @@ use std::ops::Drop;
 use std::sync::Mutex;
 
 use crate::geometry::Size;
-use crate::id;
+use crate::id::{self, NodeId};
 use crate::number::Number;
 use crate::result::{Cache, Layout};
 use crate::style::*;
@@ -27,64 +27,28 @@ pub struct Node {
     local: id::Id,
 }
 
-pub(crate) struct Storage<T>(HashMap<Node, T>);
-
-impl<T> Storage<T> {
-    pub fn new() -> Self {
-        Storage(HashMap::new())
-    }
-
-    pub fn get(&self, node: Node) -> Result<&T, Error> {
-        match self.0.get(&node) {
-            Some(v) => Ok(v),
-            None => Err(Error::InvalidNode(node)),
-        }
-    }
-
-    pub fn get_mut(&mut self, node: Node) -> Result<&mut T, Error> {
-        match self.0.get_mut(&node) {
-            Some(v) => Ok(v),
-            None => Err(Error::InvalidNode(node)),
-        }
-    }
-
-    pub fn insert(&mut self, node: Node, value: T) -> Option<T> {
-        self.0.insert(node, value)
-    }
-}
-
-impl<T> std::ops::Index<&Node> for Storage<T> {
-    type Output = T;
-
-    fn index(&self, idx: &Node) -> &T {
-        &(self.0)[idx]
-    }
-}
-
 pub struct Stretch {
     id: id::Id,
     nodes: id::Allocator,
-    pub(crate) style: Storage<Style>,
-    pub(crate) parents: Storage<Vec<Node>>,
-    pub(crate) children: Storage<Vec<Node>>,
-    pub(crate) measure: Storage<Option<MeasureFunc>>,
-    pub(crate) layout: Storage<Layout>,
-    pub(crate) layout_cache: Storage<Option<Cache>>,
-    pub(crate) is_dirty: Storage<bool>,
+    nodes_to_ids: HashMap<Node, NodeId>,
+    ids_to_nodes: HashMap<NodeId, Node>,
+    forest: Forest,
 }
 
 impl Stretch {
+    const DEFAULT_CAPACITY: usize = 16;
+
     pub fn new() -> Self {
+        Self::with_capacity(Self::DEFAULT_CAPACITY)
+    }
+
+    pub fn with_capacity(capacity: usize) -> Self {
         Stretch {
             id: INSTANCE_ALLOCATOR.lock().unwrap().allocate(),
             nodes: id::Allocator::new(),
-            style: Storage::new(),
-            parents: Storage::new(),
-            children: Storage::new(),
-            measure: Storage::new(),
-            layout: Storage::new(),
-            layout_cache: Storage::new(),
-            is_dirty: Storage::new(),
+            nodes_to_ids: HashMap::with_capacity(capacity),
+            ids_to_nodes: HashMap::with_capacity(capacity),
+            forest: Forest::with_capacity(capacity),
         }
     }
 
@@ -93,143 +57,139 @@ impl Stretch {
         Node { instance: self.id, local }
     }
 
-    pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> Node {
-        let node = self.allocate_node();
+    fn add_node(&mut self, node: Node, id: NodeId) {
+        self.nodes_to_ids.insert(node, id);
+        self.ids_to_nodes.insert(id, node);
+    }
 
-        self.style.insert(node, style);
-        self.parents.insert(node, Vec::with_capacity(1));
-        self.children.insert(node, Vec::with_capacity(0));
-        self.measure.insert(node, Some(measure));
-        self.layout.insert(node, Layout::new());
-        self.layout_cache.insert(node, None);
-        self.is_dirty.insert(node, true);
+    // Find node in the forest.
+    fn find_node(&self, node: Node) -> Result<NodeId, Error> {
+        match self.nodes_to_ids.get(&node) {
+            Some(id) => Ok(*id),
+            None => Err(Error::InvalidNode(node)),
+        }
+    }
 
+    pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> Node {
+        let node = self.allocate_node();
+        let id = self.forest.new_leaf(style, measure);
+        self.add_node(node, id);
         node
     }
 
     pub fn new_node(&mut self, style: Style, children: Vec<Node>) -> Result<Node, Error> {
         let node = self.allocate_node();
-
-        for child in &children {
-            self.parents.get_mut(*child)?.push(node);
-        }
-
-        self.style.insert(node, style);
-        self.parents.insert(node, Vec::with_capacity(1));
-        self.children.insert(node, children);
-        self.measure.insert(node, None);
-        self.layout.insert(node, Layout::new());
-        self.layout_cache.insert(node, None);
-        self.is_dirty.insert(node, true);
-
+        let children = children.iter().map(|child| self.find_node(*child)).collect::<Result<Vec<_>, Error>>()?;
+        let id = self.forest.new_node(style, children);
+        self.add_node(node, id);
         Ok(node)
     }
 
     pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> Result<(), Error> {
-        *self.measure.get_mut(node)? = measure;
-        self.mark_dirty(node)?;
+        let id = self.find_node(node)?;
+        self.forest.nodes[id].measure = measure;
+        self.forest.mark_dirty(id);
         Ok(())
     }
 
     pub fn add_child(&mut self, node: Node, child: Node) -> Result<(), Error> {
-        self.parents.get_mut(child)?.push(node);
-        self.children.get_mut(node)?.push(child);
-        self.mark_dirty(node)
+        let node_id = self.find_node(node)?;
+        let child_id = self.find_node(child)?;
+
+        self.forest.add_child(node_id, child_id);
+        Ok(())
     }
 
     pub fn set_children(&mut self, node: Node, children: Vec<Node>) -> Result<(), Error> {
+        let node_id = self.find_node(node)?;
+        let children_id = children.iter().map(|child| self.find_node(*child)).collect::<Result<Vec<_>, _>>()?;
+
         // Remove node as parent from all its current children.
-        for child in self.children.get(node)? {
-            self.parents.get_mut(*child)?.retain(|p| *p != node);
+        for child in &self.forest.children[node_id] {
+            self.forest.parents[*child].retain(|p| *p != node_id);
         }
 
-        *self.children.get_mut(node)? = Vec::with_capacity(children.len());
-
         // Build up relation node <-> child
-        for child in children {
-            self.parents.get_mut(child)?.push(node);
-            self.children.get_mut(node)?.push(child);
+        for child in &children_id {
+            self.forest.parents[*child].push(node_id);
         }
+        self.forest.children[node_id] = children_id;
 
-        self.mark_dirty(node)
+        self.forest.mark_dirty(node_id);
+        Ok(())
     }
 
     pub fn remove_child(&mut self, node: Node, child: Node) -> Result<Node, Error> {
-        match self.children(node)?.iter().position(|n| *n == child) {
-            Some(index) => self.remove_child_at_index(node, index),
-            None => Err(Error::InvalidNode(child)),
-        }
+        let node_id = self.find_node(node)?;
+        let child_id = self.find_node(child)?;
+
+        let prev_id = unsafe { self.forest.remove_child(node_id, child_id) };
+        Ok(self.ids_to_nodes[&prev_id])
     }
 
     pub fn remove_child_at_index(&mut self, node: Node, index: usize) -> Result<Node, Error> {
-        let child = self.children.get_mut(node)?.remove(index);
-        self.parents.get_mut(child)?.retain(|p| *p != node);
+        let node_id = self.find_node(node)?;
+        // TODO: index check
 
-        self.mark_dirty(node)?;
-
-        Ok(child)
+        let prev_id = self.forest.remove_child_at_index(node_id, index);
+        Ok(self.ids_to_nodes[&prev_id])
     }
 
     pub fn replace_child_at_index(&mut self, node: Node, index: usize, child: Node) -> Result<Node, Error> {
-        self.parents.get_mut(child)?.push(node);
-        let old_child = std::mem::replace(&mut self.children.get_mut(node)?[index], child);
-        self.parents.get_mut(old_child)?.retain(|p| *p != node);
+        let node_id = self.find_node(node)?;
+        let child_id = self.find_node(child)?;
+        // TODO: index check
+
+        self.forest.parents[child_id].push(node_id);
+        let old_child = std::mem::replace(&mut self.forest.children[node_id][index], child_id);
+        self.forest.parents[old_child].retain(|p| *p != node_id);
 
-        self.mark_dirty(node)?;
+        self.forest.mark_dirty(node_id);
 
-        Ok(old_child)
+        Ok(self.ids_to_nodes[&old_child])
     }
 
     pub fn children(&self, node: Node) -> Result<Vec<Node>, Error> {
-        self.children.get(node).map(Clone::clone)
+        let id = self.find_node(node)?;
+        Ok(self.forest.children[id].iter().map(|child| self.ids_to_nodes[child]).collect())
     }
 
     pub fn child_count(&self, node: Node) -> Result<usize, Error> {
-        self.children.get(node).map(Vec::len)
+        let id = self.find_node(node)?;
+        Ok(self.forest.children[id].len())
     }
 
     pub fn set_style(&mut self, node: Node, style: Style) -> Result<(), Error> {
-        *self.style.get_mut(node)? = style;
-        self.mark_dirty(node)
+        let id = self.find_node(node)?;
+        self.forest.nodes[id].style = style;
+        self.forest.mark_dirty(id);
+        Ok(())
     }
 
     pub fn style(&self, node: Node) -> Result<&Style, Error> {
-        self.style.get(node)
+        let id = self.find_node(node)?;
+        Ok(&self.forest.nodes[id].style)
     }
 
     pub fn layout(&self, node: Node) -> Result<&Layout, Error> {
-        self.layout.get(node)
+        let id = self.find_node(node)?;
+        Ok(&self.forest.nodes[id].layout)
     }
 
     pub fn mark_dirty(&mut self, node: Node) -> Result<(), Error> {
-        fn mark_dirty_impl(
-            node: Node,
-            layout_cache: &mut Storage<Option<Cache>>,
-            is_dirty: &mut Storage<bool>,
-            parents: &Storage<Vec<Node>>,
-        ) -> Result<(), Error> {
-            *layout_cache.get_mut(node)? = None;
-            *is_dirty.get_mut(node)? = true;
-
-            for parent in parents.get(node)? {
-                mark_dirty_impl(*parent, layout_cache, is_dirty, parents)?;
-            }
-
-            Ok(())
-        }
-
-        mark_dirty_impl(node, &mut self.layout_cache, &mut self.is_dirty, &self.parents)
+        let id = self.find_node(node)?;
+        self.forest.mark_dirty(id);
+        Ok(())
     }
 
     pub fn dirty(&self, node: Node) -> Result<bool, Error> {
-        self.is_dirty.get(node).map(|v| *v)
+        let id = self.find_node(node)?;
+        Ok(self.forest.nodes[id].is_dirty)
     }
 
     pub fn compute_layout(&mut self, node: Node, size: Size<Number>) -> Result<(), Error> {
-        match self.layout.get(node) {
-            Ok(_) => self.compute(node, size).map_err(|err| Error::Measure(err)),
-            _ => Err(Error::InvalidNode(node)),
-        }
+        let id = self.find_node(node)?;
+        self.forest.compute(id, size).map_err(|err| Error::Measure(err))
     }
 }
 
@@ -238,3 +198,92 @@ impl Drop for Stretch {
         INSTANCE_ALLOCATOR.lock().unwrap().free(&[self.id]);
     }
 }
+
+pub(crate) struct NodeData {
+    pub(crate) style: Style,
+    pub(crate) measure: Option<MeasureFunc>,
+    pub(crate) layout: Layout,
+    pub(crate) layout_cache: Option<Cache>,
+    pub(crate) is_dirty: bool,
+}
+
+impl NodeData {
+    fn new_leaf(style: Style, measure: MeasureFunc) -> Self {
+        NodeData { style, measure: Some(measure), layout_cache: None, layout: Layout::new(), is_dirty: true }
+    }
+
+    fn new(style: Style) -> Self {
+        NodeData { style, measure: None, layout_cache: None, layout: Layout::new(), is_dirty: true }
+    }
+}
+
+pub(crate) struct Forest {
+    pub(crate) nodes: Vec<NodeData>,
+    pub(crate) children: Vec<Vec<id::NodeId>>,
+    pub(crate) parents: Vec<Vec<id::NodeId>>,
+}
+
+impl Forest {
+    pub fn with_capacity(capacity: usize) -> Self {
+        Forest {
+            nodes: Vec::with_capacity(capacity),
+            children: Vec::with_capacity(capacity),
+            parents: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> NodeId {
+        let id = self.nodes.len();
+        self.nodes.push(NodeData::new_leaf(style, measure));
+        self.children.push(Vec::with_capacity(0));
+        self.parents.push(Vec::with_capacity(1));
+        id
+    }
+
+    pub fn new_node(&mut self, style: Style, children: Vec<NodeId>) -> NodeId {
+        let id = self.nodes.len();
+        for child in &children {
+            self.parents[*child].push(id);
+        }
+        self.nodes.push(NodeData::new(style));
+        self.children.push(children);
+        self.parents.push(Vec::with_capacity(1));
+        id
+    }
+
+    pub fn add_child(&mut self, node: NodeId, child: NodeId) {
+        self.parents[child].push(node);
+        self.children[node].push(child);
+        self.mark_dirty(node)
+    }
+
+    pub unsafe fn remove_child(&mut self, node: NodeId, child: NodeId) -> NodeId {
+        let index = self.children[node].iter().position(|n| *n == child).unwrap();
+        self.remove_child_at_index(node, index)
+    }
+
+    pub fn remove_child_at_index(&mut self, node: NodeId, index: usize) -> NodeId {
+        let child = self.children[node].remove(index);
+        self.parents[child].retain(|p| *p != node);
+        self.mark_dirty(node);
+        child
+    }
+
+    pub fn mark_dirty(&mut self, node: NodeId) {
+        fn mark_dirty_impl(nodes: &mut Vec<NodeData>, parents: &Vec<Vec<NodeId>>, node_id: NodeId) {
+            let node = &mut nodes[node_id];
+            node.layout_cache = None;
+            node.is_dirty = true;
+
+            for parent in &parents[node_id] {
+                mark_dirty_impl(nodes, parents, *parent);
+            }
+        }
+
+        mark_dirty_impl(&mut self.nodes, &self.parents, node);
+    }
+
+    pub fn compute_layout(&mut self, node: NodeId, size: Size<Number>) -> Result<(), Error> {
+        self.compute(node, size).map_err(|err| Error::Measure(err))
+    }
+}