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 · 2022-07-01T20:35:01Z (hace 4 años) · padre e8855e054 · ver el árbol en este commit
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
///