From 19ad2497637f86afb058198f483eae5205f8f7ca Mon Sep 17 00:00:00 2001 From: Kirpal Grewal Date: Thu, 5 Dec 2024 12:53:18 +0000 Subject: [PATCH 1/3] use cargo rdme for tree arena --- .github/workflows/ci.yml | 12 ++++--- tree_arena/README.md | 54 +++++++++++++++++++++++--------- tree_arena/src/lib.rs | 67 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 108 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2e3edd2d..6495c4b74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,10 +96,14 @@ jobs: with: tool: cargo-rdme - - name: cargo rdme + - name: cargo rdme masonry run: | cargo rdme --check --workspace-project=masonry + - name: cargo rdme tree_arena + run: | + cargo rdme --check --workspace-project=tree_arena + clippy-stable: name: cargo clippy runs-on: ${{ matrix.os }} @@ -195,7 +199,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - + - name: Cache git lfs id: lfs-cache uses: actions/cache@v4 @@ -209,7 +213,7 @@ jobs: - name: Fetch lfs data if: ${{ steps.lfs-cache.outputs.cache-hit != 'true' }} run: git lfs fetch - + test-stable: name: cargo test needs: prime-lfs-cache @@ -279,7 +283,7 @@ jobs: # because those require Vello rendering to be working # See also https://github.com/linebender/vello/pull/610 SKIP_RENDER_TESTS: ${{ matrix.skip_gpu }} - + - name: Upload test results due to failure uses: actions/upload-artifact@v4 if: failure() diff --git a/tree_arena/README.md b/tree_arena/README.md index 675401701..baa610315 100644 --- a/tree_arena/README.md +++ b/tree_arena/README.md @@ -1,22 +1,32 @@ # Tree Arena -This crate contains two implementations of a tree for use in masonry, one safe and the other unsafe. The safe tree is known to work, and serves as the baseline implementation and is used by default. The unsafe tree leverages a hashmap as an arena and is designed for higher performance: it leverages unsafe code to achieve this. The unsafe tree is not yet fully tested, and is not used by default. +[![Apache 2.0 license.](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](#license) +[![Linebender Zulip chat.](https://img.shields.io/badge/Linebender-%23masonry-blue?logo=Zulip)](https://xi.zulipchat.com/#narrow/stream/317477-masonry) +[![GitHub Actions CI status.](https://img.shields.io/github/actions/workflow/status/linebender/xilem/ci.yml?logo=github&label=CI)](https://github.com/linebender/xilem/actions) + + + +This crate contains two implementations of a tree for use in [Masonry], one safe and the other unsafe. The safe tree is known to work, and serves as the baseline implementation and is used by default. +The unsafe tree leverages a hashmap as an arena and is designed for higher performance: it leverages unsafe code to achieve this. The unsafe tree is not yet fully tested, and is not used by default. The safe tree is the priority. This means: * The safe version may have features / APIs that the unsafe version doesn't yet have. -* If both versions are at feature parity, Masonry can switch on the unsafe version for best performance. +* If both versions are at feature parity, [Masonry] can switch on the unsafe version for best performance. -* Otherwise, Masonry uses the safe version. +* Otherwise, [Masonry] uses the safe version. -## Architecture +### Architecture -### Safe Tree +#### Safe Tree -The safe tree contains a root `TreeArena` which owns the root nodes as `Vec>`, and a`parents_map` tracking the parent of every node. Each `TreeNode` subsequently owns its own children as `Vec>`. This model of owneship is thus checked by the rust compiler, but has the downside of requiring passing through every ancestor node to access the descendant - this requires an O(depth) determination of whether the node is a descendant, followed by O(children) time at each level to traverse the path to the child. +The safe tree contains a root `TreeArena` which owns the root nodes as `Vec>`, and a`parents_map` tracking the parent of every node. +Each `TreeNode` subsequently owns its own children as `Vec>`. This model of owneship is thus checked by the rust compiler, +but has the downside of requiring passing through every ancestor node to access the descendant - +this requires an O(depth) determination of whether the node is a descendant, followed by O(children) time at each level to traverse the path to the child. -### Unsafe Tree +#### Unsafe Tree The unsafe tree arena contains a `DataMap` which **owns** all nodes. The `DataMap` contains: @@ -26,24 +36,38 @@ The unsafe tree arena contains a `DataMap` which **owns** all nodes. The `DataMa * `Box>>` containing the roots of the tree -It is possible to get shared (immutable) access or exclusive (mutable) access to the tree. These return `ArenaRef<'arena, T>` or `ArenaMut<'arena, T>` respectively. We do this by leveraging a hash map to store the nodes: from this we can obtain either shared or exclusive access to nodes. To ensure that only one item is allowed to create new exclusive access to nodes, this action requires mutable access to the arena as a whole (and so is checked by the compiler) - what the compiler cannot check is that the nodes accessed mutably are distinct from one another - this is done by only allowing access to descendants of the node being accessed mutably. The aim of this is to reduce the time needed to access node, as given a node, we only need to determine whether it is a descendant of the node being accessed mutably, and do not need to iterate over the children and to flatten the overall tree graph into a hash map. +It is possible to get shared (immutable) access or exclusive (mutable) access to the tree. These return `ArenaRef<'arena, T>` or `ArenaMut<'arena, T>` respectively. +We do this by leveraging a hash map to store the nodes: from this we can obtain either shared or exclusive access to nodes. +To ensure that only one item is allowed to create new exclusive access to nodes, this action requires mutable access to the arena as a whole (and so is checked by the compiler) - +what the compiler cannot check is that the nodes accessed mutably are distinct from one another - this is done by only allowing access to descendants of the node being accessed mutably. +The aim of this is to reduce the time needed to access node, as given a node, we only need to determine whether it is a descendant of the node being accessed mutably, +and do not need to iterate over the children and to flatten the overall tree graph into a hash map. -#### Shared References +##### Shared References -`ArenaRef<'arena, T>` contains the identity of the parent node, a reference to the node data, and `ArenaRefChildren<'arena, T>`. The `ArenaRefChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a reference to the arena. From this `ArenaRefChildren<'arena, T>` it is possible to get shared access to children of the node. +`ArenaRef<'arena, T>` contains the identity of the parent node, a reference to the node data, and `ArenaRefChildren<'arena, T>`. +The `ArenaRefChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a reference to the arena. From this `ArenaRefChildren<'arena, T>` it is possible to get shared access to children of the node. -#### Exclusive References +##### Exclusive References -`ArenaMut<'arena, T>` contains the identity of the parent node, a mutable reference to the node data, and `ArenaMutChildren<'arena, T>`. The `ArenaMutChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a mutable reference to the arena. From this `ArenaMutChildren<'arena, T>` it is possible to get exclusive access to children of the node. +`ArenaMut<'arena, T>` contains the identity of the parent node, a mutable reference to the node data, and `ArenaMutChildren<'arena, T>`. +The `ArenaMutChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a mutable reference to the arena. +From this `ArenaMutChildren<'arena, T>` it is possible to get exclusive access to children of the node. -#### Safety +##### Safety -From the `ArenaMutChildren<'arena, T>`, it is important that we can only access descendants of that node, such that we can only ever have exclusive mutable access to the contents of a node, and never have multiple mutable references. This invariant is not checked by the compiler and thus relies on the logic to determine whether a node is a descendant being correct. +From the `ArenaMutChildren<'arena, T>`, it is important that we can only access descendants of that node, +such that we can only ever have exclusive mutable access to the contents of a node, and never have multiple mutable references. +This invariant is not checked by the compiler and thus relies on the logic to determine whether a node is a descendant being correct. -### Complexity +#### Complexity |Operation | Safe | Unsafe | | --- | --- | --- | |Find child | O(Children) | O(1) | |Descendant | O(Depth) | O(Depth) | |From root | O(Depth) | O(1) | + +[Masonry]: https://crates.io/crates/masonry + + diff --git a/tree_arena/src/lib.rs b/tree_arena/src/lib.rs index 069eeeb93..c5f861768 100644 --- a/tree_arena/src/lib.rs +++ b/tree_arena/src/lib.rs @@ -1,15 +1,70 @@ // Copyright 2024 the Xilem Authors // SPDX-License-Identifier: Apache-2.0 -//! This crate implements a tree data structure for use in Masonry -//! It contains both a safe implementation (that is used by default) -//! and an unsafe implementation that can be used to improve performance +//! This crate contains two implementations of a tree for use in [Masonry], one safe and the other unsafe. The safe tree is known to work, and serves as the baseline implementation and is used by default. +//! The unsafe tree leverages a hashmap as an arena and is designed for higher performance: it leverages unsafe code to achieve this. The unsafe tree is not yet fully tested, and is not used by default. //! -//! The safe version is the first class citizen +//! The safe tree is the priority. This means: //! //! * The safe version may have features / APIs that the unsafe version doesn't yet have. -//! * If both versions are at feature parity, Masonry can switch on the unsafe version for best performance. -//! * Otherwise, Masonry uses the safe version. +//! +//! * If both versions are at feature parity, [Masonry] can switch on the unsafe version for best performance. +//! +//! * Otherwise, [Masonry] uses the safe version. +//! +//! ## Architecture +//! +//! ### Safe Tree +//! +//! The safe tree contains a root `TreeArena` which owns the root nodes as `Vec>`, and a`parents_map` tracking the parent of every node. +//! Each `TreeNode` subsequently owns its own children as `Vec>`. This model of owneship is thus checked by the rust compiler, +//! but has the downside of requiring passing through every ancestor node to access the descendant - +//! this requires an O(depth) determination of whether the node is a descendant, followed by O(children) time at each level to traverse the path to the child. +//! +//! ### Unsafe Tree +//! +//! The unsafe tree arena contains a `DataMap` which **owns** all nodes. The `DataMap` contains: +//! +//! * A `HashMap` associating `NodeId` with `Box>>`, owning the node data, (boxed to prevent movement of the node when the `HashMap` is resized and `UnsafeCell` to express the interior mutability) +//! +//! * A `HashMap` associating `NodeId` with `Option`, containing the parent information for the nodes +//! +//! * `Box>>` containing the roots of the tree +//! +//! It is possible to get shared (immutable) access or exclusive (mutable) access to the tree. These return `ArenaRef<'arena, T>` or `ArenaMut<'arena, T>` respectively. +//! We do this by leveraging a hash map to store the nodes: from this we can obtain either shared or exclusive access to nodes. +//! To ensure that only one item is allowed to create new exclusive access to nodes, this action requires mutable access to the arena as a whole (and so is checked by the compiler) - +//! what the compiler cannot check is that the nodes accessed mutably are distinct from one another - this is done by only allowing access to descendants of the node being accessed mutably. +//! The aim of this is to reduce the time needed to access node, as given a node, we only need to determine whether it is a descendant of the node being accessed mutably, +//! and do not need to iterate over the children and to flatten the overall tree graph into a hash map. +//! +//! #### Shared References +//! +//! `ArenaRef<'arena, T>` contains the identity of the parent node, a reference to the node data, and `ArenaRefChildren<'arena, T>`. +//! The `ArenaRefChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a reference to the arena. From this `ArenaRefChildren<'arena, T>` it is possible to get shared access to children of the node. +//! +//! #### Exclusive References +//! +//! `ArenaMut<'arena, T>` contains the identity of the parent node, a mutable reference to the node data, and `ArenaMutChildren<'arena, T>`. +//! The `ArenaMutChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a mutable reference to the arena. +//! From this `ArenaMutChildren<'arena, T>` it is possible to get exclusive access to children of the node. +//! +//! #### Safety +//! +//! From the `ArenaMutChildren<'arena, T>`, it is important that we can only access descendants of that node, +//! such that we can only ever have exclusive mutable access to the contents of a node, and never have multiple mutable references. +//! This invariant is not checked by the compiler and thus relies on the logic to determine whether a node is a descendant being correct. +//! +//! ### Complexity +//! +//! |Operation | Safe | Unsafe | +//! | --- | --- | --- | +//! |Find child | O(Children) | O(1) | +//! |Descendant | O(Depth) | O(Depth) | +//! |From root | O(Depth) | O(1) | +//! +//! [Masonry]: https://crates.io/crates/masonry + type NodeId = u64; #[cfg(not(feature = "safe_tree"))] From 4369a7399b444d3c6627c3e36b10383a777f3690 Mon Sep 17 00:00:00 2001 From: Kirpal Grewal Date: Thu, 5 Dec 2024 13:00:33 +0000 Subject: [PATCH 2/3] fix heading levels --- tree_arena/README.md | 14 +++++++------- tree_arena/src/lib.rs | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tree_arena/README.md b/tree_arena/README.md index baa610315..03dde230c 100644 --- a/tree_arena/README.md +++ b/tree_arena/README.md @@ -17,16 +17,16 @@ The safe tree is the priority. This means: * Otherwise, [Masonry] uses the safe version. -### Architecture +## Architecture -#### Safe Tree +### Safe Tree The safe tree contains a root `TreeArena` which owns the root nodes as `Vec>`, and a`parents_map` tracking the parent of every node. Each `TreeNode` subsequently owns its own children as `Vec>`. This model of owneship is thus checked by the rust compiler, but has the downside of requiring passing through every ancestor node to access the descendant - this requires an O(depth) determination of whether the node is a descendant, followed by O(children) time at each level to traverse the path to the child. -#### Unsafe Tree +### Unsafe Tree The unsafe tree arena contains a `DataMap` which **owns** all nodes. The `DataMap` contains: @@ -43,24 +43,24 @@ what the compiler cannot check is that the nodes accessed mutably are distinct f The aim of this is to reduce the time needed to access node, as given a node, we only need to determine whether it is a descendant of the node being accessed mutably, and do not need to iterate over the children and to flatten the overall tree graph into a hash map. -##### Shared References +#### Shared References `ArenaRef<'arena, T>` contains the identity of the parent node, a reference to the node data, and `ArenaRefChildren<'arena, T>`. The `ArenaRefChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a reference to the arena. From this `ArenaRefChildren<'arena, T>` it is possible to get shared access to children of the node. -##### Exclusive References +#### Exclusive References `ArenaMut<'arena, T>` contains the identity of the parent node, a mutable reference to the node data, and `ArenaMutChildren<'arena, T>`. The `ArenaMutChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a mutable reference to the arena. From this `ArenaMutChildren<'arena, T>` it is possible to get exclusive access to children of the node. -##### Safety +#### Safety From the `ArenaMutChildren<'arena, T>`, it is important that we can only access descendants of that node, such that we can only ever have exclusive mutable access to the contents of a node, and never have multiple mutable references. This invariant is not checked by the compiler and thus relies on the logic to determine whether a node is a descendant being correct. -#### Complexity +### Complexity |Operation | Safe | Unsafe | | --- | --- | --- | diff --git a/tree_arena/src/lib.rs b/tree_arena/src/lib.rs index c5f861768..64a50d30c 100644 --- a/tree_arena/src/lib.rs +++ b/tree_arena/src/lib.rs @@ -12,16 +12,16 @@ //! //! * Otherwise, [Masonry] uses the safe version. //! -//! ## Architecture +//! # Architecture //! -//! ### Safe Tree +//! ## Safe Tree //! //! The safe tree contains a root `TreeArena` which owns the root nodes as `Vec>`, and a`parents_map` tracking the parent of every node. //! Each `TreeNode` subsequently owns its own children as `Vec>`. This model of owneship is thus checked by the rust compiler, //! but has the downside of requiring passing through every ancestor node to access the descendant - //! this requires an O(depth) determination of whether the node is a descendant, followed by O(children) time at each level to traverse the path to the child. //! -//! ### Unsafe Tree +//! ## Unsafe Tree //! //! The unsafe tree arena contains a `DataMap` which **owns** all nodes. The `DataMap` contains: //! @@ -38,24 +38,24 @@ //! The aim of this is to reduce the time needed to access node, as given a node, we only need to determine whether it is a descendant of the node being accessed mutably, //! and do not need to iterate over the children and to flatten the overall tree graph into a hash map. //! -//! #### Shared References +//! ### Shared References //! //! `ArenaRef<'arena, T>` contains the identity of the parent node, a reference to the node data, and `ArenaRefChildren<'arena, T>`. //! The `ArenaRefChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a reference to the arena. From this `ArenaRefChildren<'arena, T>` it is possible to get shared access to children of the node. //! -//! #### Exclusive References +//! ### Exclusive References //! //! `ArenaMut<'arena, T>` contains the identity of the parent node, a mutable reference to the node data, and `ArenaMutChildren<'arena, T>`. //! The `ArenaMutChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a mutable reference to the arena. //! From this `ArenaMutChildren<'arena, T>` it is possible to get exclusive access to children of the node. //! -//! #### Safety +//! ### Safety //! //! From the `ArenaMutChildren<'arena, T>`, it is important that we can only access descendants of that node, //! such that we can only ever have exclusive mutable access to the contents of a node, and never have multiple mutable references. //! This invariant is not checked by the compiler and thus relies on the logic to determine whether a node is a descendant being correct. //! -//! ### Complexity +//! ## Complexity //! //! |Operation | Safe | Unsafe | //! | --- | --- | --- | From 035496ae973bce0d243df063691bcb5ad455e114 Mon Sep 17 00:00:00 2001 From: Kirpal Grewal Date: Thu, 5 Dec 2024 13:20:03 +0000 Subject: [PATCH 3/3] update to be consistent with color --- .github/workflows/ci.yml | 2 +- tree_arena/README.md | 5 +++++ tree_arena/src/lib.rs | 14 +++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6495c4b74..b7071f009 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,7 +102,7 @@ jobs: - name: cargo rdme tree_arena run: | - cargo rdme --check --workspace-project=tree_arena + cargo rdme --check --heading-base-level=0 --workspace-project=tree_arena clippy-stable: name: cargo clippy diff --git a/tree_arena/README.md b/tree_arena/README.md index 03dde230c..9c4e2817c 100644 --- a/tree_arena/README.md +++ b/tree_arena/README.md @@ -4,6 +4,11 @@ [![Linebender Zulip chat.](https://img.shields.io/badge/Linebender-%23masonry-blue?logo=Zulip)](https://xi.zulipchat.com/#narrow/stream/317477-masonry) [![GitHub Actions CI status.](https://img.shields.io/github/actions/workflow/status/linebender/xilem/ci.yml?logo=github&label=CI)](https://github.com/linebender/xilem/actions) + + This crate contains two implementations of a tree for use in [Masonry], one safe and the other unsafe. The safe tree is known to work, and serves as the baseline implementation and is used by default. diff --git a/tree_arena/src/lib.rs b/tree_arena/src/lib.rs index 64a50d30c..c5f861768 100644 --- a/tree_arena/src/lib.rs +++ b/tree_arena/src/lib.rs @@ -12,16 +12,16 @@ //! //! * Otherwise, [Masonry] uses the safe version. //! -//! # Architecture +//! ## Architecture //! -//! ## Safe Tree +//! ### Safe Tree //! //! The safe tree contains a root `TreeArena` which owns the root nodes as `Vec>`, and a`parents_map` tracking the parent of every node. //! Each `TreeNode` subsequently owns its own children as `Vec>`. This model of owneship is thus checked by the rust compiler, //! but has the downside of requiring passing through every ancestor node to access the descendant - //! this requires an O(depth) determination of whether the node is a descendant, followed by O(children) time at each level to traverse the path to the child. //! -//! ## Unsafe Tree +//! ### Unsafe Tree //! //! The unsafe tree arena contains a `DataMap` which **owns** all nodes. The `DataMap` contains: //! @@ -38,24 +38,24 @@ //! The aim of this is to reduce the time needed to access node, as given a node, we only need to determine whether it is a descendant of the node being accessed mutably, //! and do not need to iterate over the children and to flatten the overall tree graph into a hash map. //! -//! ### Shared References +//! #### Shared References //! //! `ArenaRef<'arena, T>` contains the identity of the parent node, a reference to the node data, and `ArenaRefChildren<'arena, T>`. //! The `ArenaRefChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a reference to the arena. From this `ArenaRefChildren<'arena, T>` it is possible to get shared access to children of the node. //! -//! ### Exclusive References +//! #### Exclusive References //! //! `ArenaMut<'arena, T>` contains the identity of the parent node, a mutable reference to the node data, and `ArenaMutChildren<'arena, T>`. //! The `ArenaMutChildren<'arena, T>` contains the ids of the children of the node, the id of the node, and a mutable reference to the arena. //! From this `ArenaMutChildren<'arena, T>` it is possible to get exclusive access to children of the node. //! -//! ### Safety +//! #### Safety //! //! From the `ArenaMutChildren<'arena, T>`, it is important that we can only access descendants of that node, //! such that we can only ever have exclusive mutable access to the contents of a node, and never have multiple mutable references. //! This invariant is not checked by the compiler and thus relies on the logic to determine whether a node is a descendant being correct. //! -//! ## Complexity +//! ### Complexity //! //! |Operation | Safe | Unsafe | //! | --- | --- | --- |