From 0c83db093854b7f6bc2a8e8a7cd5354825ee4a78 Mon Sep 17 00:00:00 2001 From: Ugur-Arikan Date: Tue, 27 Feb 2024 22:08:49 +0100 Subject: [PATCH] major refactoring applied The following major revision is applied to the pinned vector: * pinned elements guarantee is formalized and documented, * unsafe methods such as `clone` or `insert` are now safe. pinned vector on its own cannot build inter-element references; hence, these methods are not unsafe. this responsibility is passed to the struct enabling these references, namely, orx_selfref_col. * in addition to being a marker trait, this crate now provides `test_pinned_vec` function which is essential in asserting that a pinned vector implementation satisfies the required guarantees. --- .gitignore | 2 + Cargo.toml | 6 +- README.md | 90 +++---- src/lib.rs | 98 +++---- src/not_self_ref.rs | 113 -------- src/pinned_vec.rs | 245 ++++++------------ src/pinned_vec_simple.rs | 129 --------- src/pinned_vec_tests/extend.rs | 66 +++++ src/pinned_vec_tests/insert.rs | 50 ++++ src/pinned_vec_tests/mod.rs | 10 + src/pinned_vec_tests/pop.rs | 58 +++++ src/pinned_vec_tests/push.rs | 50 ++++ src/pinned_vec_tests/refmap.rs | 149 +++++++++++ src/pinned_vec_tests/remove.rs | 53 ++++ src/pinned_vec_tests/test_all.rs | 152 +++++++++++ src/pinned_vec_tests/testvec.rs | 120 +++++++++ src/pinned_vec_tests/truncate.rs | 53 ++++ src/self_referential_elements/mod.rs | 7 - src/self_referential_elements/next_prev.rs | 109 -------- .../next_prev_arr.rs | 37 --- .../next_prev_vec.rs | 191 -------------- src/utils/slice.rs | 28 ++ 22 files changed, 936 insertions(+), 880 deletions(-) delete mode 100644 src/not_self_ref.rs delete mode 100644 src/pinned_vec_simple.rs create mode 100644 src/pinned_vec_tests/extend.rs create mode 100644 src/pinned_vec_tests/insert.rs create mode 100644 src/pinned_vec_tests/mod.rs create mode 100644 src/pinned_vec_tests/pop.rs create mode 100644 src/pinned_vec_tests/push.rs create mode 100644 src/pinned_vec_tests/refmap.rs create mode 100644 src/pinned_vec_tests/remove.rs create mode 100644 src/pinned_vec_tests/test_all.rs create mode 100644 src/pinned_vec_tests/testvec.rs create mode 100644 src/pinned_vec_tests/truncate.rs delete mode 100644 src/self_referential_elements/mod.rs delete mode 100644 src/self_referential_elements/next_prev.rs delete mode 100644 src/self_referential_elements/next_prev_arr.rs delete mode 100644 src/self_referential_elements/next_prev_vec.rs diff --git a/.gitignore b/.gitignore index 6985cf1..99f32d5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + +.vscode/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1b15151..d9c24e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [package] name = "orx-pinned-vec" -version = "1.0.1" +version = "2.0.0" edition = "2021" authors = ["orxfun "] -description = "`PinnedVec` trait defines the interface for vectors which guarantee that elements are pinned to their memory locations with the aim to enable convenient self-referential collections." +description = "`PinnedVec` trait defines the interface for vectors which guarantee that elements added to the vector are pinned to their memory locations unless explicitly changed." license = "MIT" repository = "https://github.com/orxfun/orx-pinned-vec/" keywords = ["vec", "array", "vector", "pinned", "memory"] categories = ["data-structures", "rust-patterns"] [dependencies] +rand = "0.8.5" +rand_chacha = "0.3.1" diff --git a/README.md b/README.md index 97c9d17..811130e 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,49 @@ # orx-pinned-vec -`PinnedVec` trait defines the interface for vectors which guarantee that elements are pinned to their memory locations with the aim to enable convenient self-referential collections. +`PinnedVec` trait defines the interface for vectors which guarantee that elements added to the vector are pinned to their memory locations unless explicitly changed. -## A. Implementations +## A. Pinned Elements Guarantee -A `PinnedVec` guarantees that positions of its elements are not changed implicitly. Note that `std::vec::Vec` does not satisfy this requirement. +A `PinnedVec` guarantees that positions of its elements **do not change implicitly**. -[`SplitVec`](https://crates.io/crates/orx-split-vec) and [`FixedVec`](https://crates.io/crates/orx-fixed-vec) are two efficient implementations. - -## B. Motivation - -There might be various situations where pinned elements are helpful. - -* It is somehow required for async code, following [blog](https://blog.cloudflare.com/pin-and-unpin-in-rust) could be useful for the interested. -* It is crucial in representing self-referential types with thin references. - -This crate focuses on the latter. Particularly, it aims to make it safe and convenient to build **performant self-referential collections** such as linked lists, trees or graphs. - -As explained in rust-docs [here](https://doc.rust-lang.org/std/pin/index.html), there exist types `Pin` and `Unpin` for this very purpose. Through the theoretical discussions, one can easily agree on the safety. However, the solution is complicated with all words `PhantomPinned`, `NonNull`, `dangling`, `Box::pin`, etc. which are alien to the self-referential data structures we are trying to build. - -This crate suggests the following approach: - -* Instances of the self-referential type will be collected together in a vector. -* Referencing each other will be through the natural `&` way rather than requiring any of the smart pointers. -* In terms of convenience, building the collection will be close to building a regular vector. - -## C. Self-Referential-Collection Element Traits +To be specific, let's assume that a pinned vector currently has `n` elements: -This crate also defines under the `orx_pinned_vec::self_referential_elements` module the required traits to enable building self referential collections with thin references. +| Method | Expected Behavior | +| -------- | ------- | +| `push(new_element)` | does not change the memory locations of the `n` elements | +| `extend_from_slice(slice)` | does not change the memory locations of the first `n` elements | +| `insert(a, new_element)` | does not change the memory locations of the first `a` elements, where `a <= n`; elements to the right of the inserted element might be changed, commonly shifted to right | +| `pop()` | does not change the memory locations of the first `n-1` elements, the `n`-th element is removed | +| `remove(a)` | does not change the memory locations of the first `a` elements, where `a < n`; elements to the right of the removed element might be changed, commonly shifted to left | +| `truncate(a)` | does not change the memory locations of the first `a` elements, where `a < n` | -* `SelfRefNext` trait simply requires: - * `fn next(&self) -> Option<&'a Self>;` and - * `fn set_next(&mut self, next: Option<&'a Self>);` methods. +`PinnedVec` trait on its own cannot provide the pinned element guarantee; hence, it could be considered as a marker trait. -`SelfRefPrev` is the previous counterpart. +However, this crate additionally provides the test function to assert these guarantees: -Notice that these two traits are sufficient to define a linked list. [`orx_linked_list::LinkedList`](https://crates.io/crates/orx-linked-list) implements `SelfRefPrev` and `SelfRefNext` to conveniently define a recurisve doubly linked list. +```rust ignore +pub fn test_pinned_vec>(pinned_vec: P, test_vec_len: usize) { + // ... +} +``` -Further, there exist multiple reference counterparts. They are useful in defining relations such as the *children* of a tree node or *heads of outgoing arcs* from a graph node, etc. There exist *vec* variants to be used for holding variable number of references. However, there also exist constant sized array versions which are useful in structures such as binary search trees where the number of references is bounded by a const. +This function performs an extensive test on the specific implementation `P` and fails if any of the above guarantees is not provided. -The table below presents the complete list of traits which suffice to define all aforementioned relations: +Note that `std::vec::Vec` does not provide the pinned elements during growth guarantee. You may find a wrapper struct `JustVec` which is nothing but the standard vec here: [src/pinned_vec_tests/test_all.rs](https://github.com/orxfun/orx-pinned-vec/blob/main/src/pinned_vec_tests/test_all.rs). As expected, `test_pinned_vec` method fails for this struct. -| | prev | next | -|---------------------------------------------|----------------|----------------| -| single reference | SelfRefPrev | SelfRefNext | -| dynamic number of references | SelfRefPrevVec | SelfRefNextVec | -| multiple elements with a `const` max-length | SelfRefPrevArr | SelfRefNextArr | - -## D. Safety - -With self referential collections, some mutating methods can lead to critical problems. These are the methods which change positions of already pushed elements or remove elements from the vector: - -* `insert` -* `remove` -* `pop` -* `swap` -* `truncate` - -These methods can invalidate the references among elements. Therefore, `PinnedVec` defines them as **unsafe**. One exception is the `clear` method which is safe since all elements are removed together with their references at once. - -In addition, `clone` method as well is **unsafe**, since the elements of the clone would be referencing the elements of the original vector. - -These are due to the fact that, naive implementations would cause false references. This does not mean that it is not possible to provide a safe implementation. Instead, it means that each data structure would need a different implementation (insert method of a tree and that of a linked-list cannot be implemented in the same way, they will need to update references differently). - -Implementors can provide careful safe implementations, such as `orx_linked_list::LinkedList` safely implement `Clone`, although it uses any `PinnedVec` as the underlying storage. +## B. Motivation -There are a few cases other than self referencing collections, where a `PinnedVec` is useful. And there is no reason to treat these methods as unsafe if the elements are not referencing each other. For this purpose, `NotSelfRefVecItem` marker trait is defined. This trait works as follows: +There are various situations where pinned elements are necessary. -* if `V` implements `PinnedVec`, and -* if `T` implements the marker trait `NotSelfRefVecItem`, -* => then, `V` also implements `PinnedVecSimple` which provides the safe versions of the abovementioned methods. +* It is critical in enabling **efficient, convenient and safe self-referential collections** with thin references, see [`SelfRefCol`](https://crates.io/crates/orx-self-ref-col) for details. +* It is essential in allowing an **immutable push** vector; i.e., [`ImpVec`](https://crates.io/crates/orx-imp-vec). This is a very useful operation when the desired collection is a bag or a container of things, rather than having a collective meaning. In such cases, `ImpVec` avoids heap allocations and wide pointers such as `Box` or `Rc` or etc. +* It is important for **async** code; following [blog](https://blog.cloudflare.com/pin-and-unpin-in-rust) could be useful for the interested. -`NotSelfRefVecItem` trait is implemented for most primitives; however, one needs to implement for new types to explicitly state that the type is not self-referential. +*As explained in [rust-docs](https://doc.rust-lang.org/std/pin/index.html), there exist `Pin` and `Unpin` types for similar purposes. However, the solution is complicated and low level using `PhantomPinned`, `NonNull`, `dangling`, `Box::pin`, pointer accesses, etc.* -## E. Relation with the `ImpVec` +## C. Implementations -Providing pinned memory location elements with `PinnedVec` is the first block for building self referential structures; the second building block is the [`ImpVec`](https://crates.io/crates/orx-imp-vec). An `ImpVec` wraps any `PinnedVec` implementation and provides specialized methods built on the pinned element guarantee in order to allow building self referential collections. +[`SplitVec`](https://crates.io/crates/orx-split-vec) and [`FixedVec`](https://crates.io/crates/orx-fixed-vec) are two efficient implementations. ## License diff --git a/src/lib.rs b/src/lib.rs index 1e8323a..3a4343a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,81 +1,49 @@ //! # orx-pinned-vec //! -//! `PinnedVec` trait defines the interface for vectors which guarantee that elements are pinned to their memory locations with the aim to enable convenient self-referential collections. +//! `PinnedVec` trait defines the interface for vectors which guarantee that elements added to the vector are pinned to their memory locations unless explicitly changed. //! -//! ## A. Implementations +//! ## A. Pinned Elements Guarantee //! -//! A `PinnedVec` guarantees that positions of its elements are not changed implicitly. Note that `std::vec::Vec` does not satisfy this requirement. +//! A `PinnedVec` guarantees that positions of its elements **do not change implicitly**. //! -//! [`SplitVec`](https://crates.io/crates/orx-split-vec) and [`FixedVec`](https://crates.io/crates/orx-fixed-vec) are two efficient implementations. -//! -//! ## B. Motivation -//! -//! There might be various situations where pinned elements are helpful. -//! -//! * It is somehow required for async code, following [blog](https://blog.cloudflare.com/pin-and-unpin-in-rust) could be useful for the interested. -//! * It is crucial in representing self-referential types with thin references. -//! -//! This crate focuses on the latter. Particularly, it aims to make it safe and convenient to build **performant self-referential collections** such as linked lists, trees or graphs. -//! -//! As explained in rust-docs [here](https://doc.rust-lang.org/std/pin/index.html), there exist types `Pin` and `Unpin` for this very purpose. Through the theoretical discussions, one can easily agree on the safety. However, the solution is complicated with all words `PhantomPinned`, `NonNull`, `dangling`, `Box::pin`, etc. which are alien to the self-referential data structures we are trying to build. -//! -//! This crate suggests the following approach: -//! -//! * Instances of the self-referential type will be collected together in a vector. -//! * Referencing each other will be through the natural `&` way rather than requiring any of the smart pointers. -//! * In terms of convenience, building the collection will be close to building a regular vector. -//! -//! ## C. Self-Referential-Collection Element Traits +//! To be specific, let's assume that a pinned vector currently has `n` elements: //! -//! This crate also defines under the `orx_pinned_vec::self_referential_elements` module the required traits to enable building self referential collections with thin references. +//! | Method | Expected Behavior | +//! | -------- | ------- | +//! | `push(new_element)` | does not change the memory locations of the `n` elements | +//! | `extend_from_slice(slice)` | does not change the memory locations of the first `n` elements | +//! | `insert(a, new_element)` | does not change the memory locations of the first `a` elements, where `a <= n`; elements to the right of the inserted element might be changed, commonly shifted to right | +//! | `pop()` | does not change the memory locations of the first `n-1` elements, the `n`-th element is removed | +//! | `remove(a)` | does not change the memory locations of the first `a` elements, where `a < n`; elements to the right of the removed element might be changed, commonly shifted to left | +//! | `truncate(a)` | does not change the memory locations of the first `a` elements, where `a < n` | //! -//! * `SelfRefNext` trait simply requires: -//! * `fn next(&self) -> Option<&'a Self>;` and -//! * `fn set_next(&mut self, next: Option<&'a Self>);` methods. +//! `PinnedVec` trait on its own cannot provide the pinned element guarantee; hence, it could be considered as a marker trait. //! -//! `SelfRefPrev` is the previous counterpart. +//! However, this crate additionally provides the test function to assert these guarantees: //! -//! Notice that these two traits are sufficient to define a linked list. [`orx_linked_list::LinkedList`](https://crates.io/crates/orx-linked-list) implements `SelfRefPrev` and `SelfRefNext` to conveniently define a recurisve doubly linked list. +//! ```rust ignore +//! pub fn test_pinned_vec>(pinned_vec: P, test_vec_len: usize) { +//! // ... +//! } +//! ``` //! -//! Further, there exist multiple reference counterparts. They are useful in defining relations such as the *children* of a tree node or *heads of outgoing arcs* from a graph node, etc. There exist *vec* variants to be used for holding variable number of references. However, there also exist constant sized array versions which are useful in structures such as binary search trees where the number of references is bounded by a const. +//! This function performs an extensive test on the specific implementation `P` and fails if any of the above guarantees is not provided. //! -//! The table below presents the complete list of traits which suffice to define all aforementioned relations: +//! Note that `std::vec::Vec` does not provide the pinned elements during growth guarantee. You may find a wrapper struct `JustVec` which is nothing but the standard vec here: [src/pinned_vec_tests/test_all.rs](https://github.com/orxfun/orx-pinned-vec/blob/main/src/pinned_vec_tests/test_all.rs). As expected, `test_pinned_vec` method fails for this struct. //! -//! | | prev | next | -//! |---------------------------------------------|----------------|----------------| -//! | single reference | SelfRefPrev | SelfRefNext | -//! | dynamic number of references | SelfRefPrevVec | SelfRefNextVec | -//! | multiple elements with a `const` max-length | SelfRefPrevArr | SelfRefNextArr | -//! -//! ## D. Safety -//! -//! With self referential collections, some mutating methods can lead to critical problems. These are the methods which change positions of already pushed elements or remove elements from the vector: -//! -//! * `insert` -//! * `remove` -//! * `pop` -//! * `swap` -//! * `truncate` -//! -//! These methods can invalidate the references among elements. Therefore, `PinnedVec` defines them as **unsafe**. One exception is the `clear` method which is safe since all elements are removed together with their references at once. -//! -//! In addition, `clone` method as well is **unsafe**, since the elements of the clone would be referencing the elements of the original vector. -//! -//! These are due to the fact that, naive implementations would cause false references. This does not mean that it is not possible to provide a safe implementation. Instead, it means that each data structure would need a different implementation (insert method of a tree and that of a linked-list cannot be implemented in the same way, they will need to update references differently). -//! -//! Implementors can provide careful safe implementations, such as `orx_linked_list::LinkedList` safely implement `Clone`, although it uses any `PinnedVec` as the underlying storage. +//! ## B. Motivation //! -//! There are a few cases other than self referencing collections, where a `PinnedVec` is useful. And there is no reason to treat these methods as unsafe if the elements are not referencing each other. For this purpose, `NotSelfRefVecItem` marker trait is defined. This trait works as follows: +//! There are various situations where pinned elements are necessary. //! -//! * if `V` implements `PinnedVec`, and -//! * if `T` implements the marker trait `NotSelfRefVecItem`, -//! * => then, `V` also implements `PinnedVecSimple` which provides the safe versions of the abovementioned methods. +//! * It is critical in enabling **efficient, convenient and safe self-referential collections** with thin references, see [`SelfRefCol`](https://crates.io/crates/orx-self-ref-col) for details. +//! * It is essential in allowing an **immutable push** vector; i.e., [`ImpVec`](https://crates.io/crates/orx-imp-vec). This is a very useful operation when the desired collection is a bag or a container of things, rather than having a collective meaning. In such cases, `ImpVec` avoids heap allocations and wide pointers such as `Box` or `Rc` or etc. +//! * It is important for **async** code; following [blog](https://blog.cloudflare.com/pin-and-unpin-in-rust) could be useful for the interested. //! -//! `NotSelfRefVecItem` trait is implemented for most primitives; however, one needs to implement for new types to explicitly state that the type is not self-referential. +//! *As explained in [rust-docs](https://doc.rust-lang.org/std/pin/index.html), there exist `Pin` and `Unpin` types for similar purposes. However, the solution is complicated and low level using `PhantomPinned`, `NonNull`, `dangling`, `Box::pin`, pointer accesses, etc.* //! -//! ## E. Relation with the `ImpVec` +//! ## C. Implementations //! -//! Providing pinned memory location elements with `PinnedVec` is the first block for building self referential structures; the second building block is the [`ImpVec`](https://crates.io/crates/orx-imp-vec). An `ImpVec` wraps any `PinnedVec` implementation and provides specialized methods built on the pinned element guarantee in order to allow building self referential collections. +//! [`SplitVec`](https://crates.io/crates/orx-split-vec) and [`FixedVec`](https://crates.io/crates/orx-fixed-vec) are two efficient implementations. //! //! ## License //! @@ -93,14 +61,10 @@ clippy::todo )] -mod not_self_ref; mod pinned_vec; -mod pinned_vec_simple; -/// Traits to define variants of self-referential-collection elements. -pub mod self_referential_elements; +mod pinned_vec_tests; /// Utility functions to make PinnedVec implementations more convenient. pub mod utils; -pub use not_self_ref::NotSelfRefVecItem; pub use pinned_vec::PinnedVec; -pub use pinned_vec_simple::PinnedVecSimple; +pub use pinned_vec_tests::test_all::test_pinned_vec; diff --git a/src/not_self_ref.rs b/src/not_self_ref.rs deleted file mode 100644 index e08eaca..0000000 --- a/src/not_self_ref.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::num::*; - -/// Marker trait for types to be contained in a `PinnedVec` which do not hold a field -/// which is a reference to a another element of the same vector. -/// -/// Note that any type which does not implement `NotSelfRefVecItem` marker trait -/// is assumed to be a self-referencing-vector-item due to the following: -/// -/// * `PinnedVec` is particularly useful for defining complex data structures -/// which elements of which often references each other; -/// in other words, most of the time `PinnedVec` will be used. -/// * To be able to use `PinnedVecSimple: PinnedVec` for safe calls to `insert`, `remove`, `pop` and `clone`, -/// one must explicitly implement `NotSelfRefVecItem` for the elements -/// explicitly stating the safety of the usage. -/// * Finally, this trait is already implemented for most of the common -/// primitive types. -/// -/// It is more brief to describe what a `SelfRefVecItem` is -/// rather than describing `NotSelfRefVecItem`. -/// Such data types are particularly useful for data types defining -/// relations about its children such as trees or graphs. -/// An example struct is demonstrated in the following section. -/// -/// # Example -/// -/// Consider the following type which is common to tree structures: -/// -/// ```rust -/// struct TreeNode<'a, T> { -/// value: T, -/// parent: Option<&'a TreeNode<'a, T>>, -/// } -/// ``` -/// -/// Further assume that we want to keep nodes of all trees in the same vector. -/// Compared to alternatives, this is helpful at least for the following reasons: -/// -/// * keeping all nodes together helps in achieving better cache locality, -/// * the references defining the tree structure are thin rather than wide pointers, -/// * requires less heap allocations: only the vector is allocated together with all its elements, -/// as opposed to allocating each node separately in an arbitrary memory location. -/// -/// # Safety -/// -/// On the other hand, such data structures require more care about safety and correctness. -/// Since each vector element can hold a reference to another vector element, -/// `mut` methods need to be investigated carefully. -/// -/// # Significance -/// -/// ## Unsafe methods when `T` is not a `NotSelfRefVecItem` -/// -/// Whether the element type of a `PinnedVec` is `NotSelfRefVecItem` or not -/// has an impact on the mutable operations which change positions of already pushed elements. -/// -/// The following is the list of these methods: -/// -/// * `insert` -/// * `remove` -/// * `pop` -/// * `swap` -/// * `truncate` -/// * `clone` -/// -/// These methods can be called safely when `T: NotSelfRefVecItem`; -/// only within an `unsafe` block otherwise. -/// -/// # Trait Implementations -/// -/// `NotSelfRefVecItem` is a marker trait which is implemented for most primitives; however, one needs to implement -/// for new types to explicitly state that the type is not self-referential. -pub trait NotSelfRefVecItem {} - -// auto impl -impl NotSelfRefVecItem for &T where T: NotSelfRefVecItem {} -impl NotSelfRefVecItem for Option where T: NotSelfRefVecItem {} -impl NotSelfRefVecItem for Result -where - T: NotSelfRefVecItem, - E: NotSelfRefVecItem, -{ -} - -impl NotSelfRefVecItem for bool {} -impl NotSelfRefVecItem for char {} -impl NotSelfRefVecItem for f32 {} -impl NotSelfRefVecItem for f64 {} -impl NotSelfRefVecItem for i128 {} -impl NotSelfRefVecItem for i16 {} -impl NotSelfRefVecItem for i32 {} -impl NotSelfRefVecItem for i64 {} -impl NotSelfRefVecItem for i8 {} -impl NotSelfRefVecItem for isize {} -impl NotSelfRefVecItem for NonZeroI128 {} -impl NotSelfRefVecItem for NonZeroI16 {} -impl NotSelfRefVecItem for NonZeroI32 {} -impl NotSelfRefVecItem for NonZeroI64 {} -impl NotSelfRefVecItem for NonZeroI8 {} -impl NotSelfRefVecItem for NonZeroIsize {} -impl NotSelfRefVecItem for NonZeroU128 {} -impl NotSelfRefVecItem for NonZeroU16 {} -impl NotSelfRefVecItem for NonZeroU32 {} -impl NotSelfRefVecItem for NonZeroU64 {} -impl NotSelfRefVecItem for NonZeroU8 {} -impl NotSelfRefVecItem for NonZeroUsize {} -impl NotSelfRefVecItem for str {} -impl NotSelfRefVecItem for String {} -impl NotSelfRefVecItem for u128 {} -impl NotSelfRefVecItem for u16 {} -impl NotSelfRefVecItem for u32 {} -impl NotSelfRefVecItem for u64 {} -impl NotSelfRefVecItem for u8 {} -impl NotSelfRefVecItem for usize {} diff --git a/src/pinned_vec.rs b/src/pinned_vec.rs index 13514d3..8be694b 100644 --- a/src/pinned_vec.rs +++ b/src/pinned_vec.rs @@ -1,60 +1,28 @@ -use std::fmt::{Debug, Formatter, Result}; - /// Trait for vector representations differing from `std::vec::Vec` by the following: /// /// => memory location of an element already pushed to the collection never changes unless any of the following mut-methods is called: +/// * `remove`, `pop`, +/// * `insert`, +/// * `clear`, `truncate`. /// -/// * `clear`, `resize`, `truncate`, -/// * `insert` -/// * `remove`, `pop`. -/// -/// Equivalently: -/// -/// => the mut-methods `push` or `extend_from_slice` do not change memory locations of priorly added elements. -/// -/// This pinned property is crticial and required for building collections such as [`orx-imp-vec::ImpVec`](https://crates.io/crates/orx-imp-vec). -/// -/// # Safety: Pinned Elements -/// -/// Note that, together with `PinnedVecSimple`, this trait defines the required signature for implementing pinned vectors. -/// However, the implementor struct is responsible for guaranteeing that `push` and `extend_from_slice` do not change memory locations -/// of already added elements. -/// -/// As expected, `push` and `extend_from_slice` are mut-methods. However, when the `PinnedVec` with pinned guarantees is -/// wrapped in an `ImpVec`, these methods require only `&self` allowing to safely and conveniently represent complex-for-rust -/// data structures. -/// -/// # Safety: Self Referential Vectors -/// -/// One of the two main goals of the target `ImpVec` is to make it safe and convenient to build self-referential data structures, -/// such as linked lists or trees. Elements of the vector being self-referential or not has a critical impact on safety of the -/// mut-methods. -/// -/// *The arguments below are based on the possibility of self-referential vectors where elements can hold references to each other. -/// This is not common in rust; however, it is safely possible with data structures such as `ImpVec`.* +/// In other words, /// -/// For instance, as expected `insert` is a safe method if the elements of the vector do not hold references to each other. However, -/// if they do, since `insert` will change memory locations of existing elements, this operation is `unsafe`. +/// => the mut-methods `push` or `extend_from_slice` do not change memory locations of already added elements. /// -/// Therefore, the distinction is provided by the trait `NotSelfRefVecItem`. +/// # Pinned Elements Guarantee /// -/// For `PinnedVec` -/// * all mut-methods which change the memory locations of existing items such as `insert`, `remove` and `swap` are `unsafe` -/// since this would invalidate the references held by other elements, -/// * all mut-methods which remove elements from the vector such as `pop`, `truncate` or `remove` are `unsafe` -/// since there might be elements holding a reference to the removed items, -/// * with the exception that `clear` is safe since all elements are gone at once, -/// * additionally `clone` is `unsafe` because the cloned elements would be holding references to the original vector. +/// A `PinnedVec` guarantees that positions of its elements **do not change implicitly**. /// -/// However, all these methods become safe if `T: NotSelfRefVecItem` through the type system as follows: +/// To be specific, let's assume that a pinned vector currently has `n` elements: /// -/// * every `V: PinnedVec` also automatically implements `PinnedVecSimple`, -/// * and `PinnedVecSimple` provides safe versions of the above mentioned methods. -/// -/// This makes sense since all the mentioned unsafety stems from the possibility of elements holding references to each other. -/// -/// Note that `NotSelfRefVecItem` is a marker trait which means that the implementor takes the responsibility. -/// Further, it is implemented for most primitive types. +/// | Method | Expected Behavior | +/// | -------- | ------- | +/// | `push(new_element)` | does not change the memory locations of the `n` elements | +/// | `extend_from_slice(slice)` | does not change the memory locations of the first `n` elements | +/// | `insert(a, new_element)` | does not change the memory locations of the first `a` elements, where `a <= n`; elements to the right of the inserted element might be changed, commonly shifted to right | +/// | `pop()` | does not change the memory locations of the first `n-1` elements, the `n`-th element is removed | +/// | `remove(a)` | does not change the memory locations of the first `a` elements, where `a < n`; elements to the right of the removed element might be changed, commonly shifted to left | +/// | `truncate(a)` | does not change the memory locations of the first `a` elements, where `a < n` | pub trait PinnedVec { /// Iterator yielding references to the elements of the vector. type Iter<'a>: Iterator @@ -63,6 +31,16 @@ pub trait PinnedVec { Self: 'a; /// Iterator yielding mutable references to the elements of the vector. type IterMut<'a>: Iterator + where + T: 'a, + Self: 'a; + /// Iterator yielding references to the elements of the vector. + type IterRev<'a>: Iterator + where + T: 'a, + Self: 'a; + /// Iterator yielding mutable references to the elements of the vector. + type IterMutRev<'a>: Iterator where T: 'a, Self: 'a; @@ -127,8 +105,32 @@ pub trait PinnedVec { /// even if the resulting reference is not used. unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T; + /// Returns a reference to the first element of the vector; returns None if the vector is empty. + fn first(&self) -> Option<&T>; + /// Returns a reference to the last element of the vector; returns None if the vector is empty. + fn last(&self) -> Option<&T>; + + /// Returns a reference to the first element of the vector without bounds checking. + /// + /// For a safe alternative see `first`. + /// + /// # Safety + /// + /// Calling this method when the vector is empty is *[undefined behavior]* even if the resulting reference is not used. + unsafe fn first_unchecked(&self) -> &T; + /// Returns a reference to the last element of the vector without bounds checking. + /// + /// For a safe alternative see `last`. + /// + /// # Safety + /// + /// Calling this method when the vector is empty is *[undefined behavior]* even if the resulting reference is not used. + unsafe fn last_unchecked(&self) -> &T; + /// Returns true if the vector contains no elements. - fn is_empty(&self) -> bool; + fn is_empty(&self) -> bool { + self.len() == 0 + } /// Returns the number of elements in the vector, also referred to as its length. fn len(&self) -> usize; /// Appends an element to the back of a collection. @@ -139,59 +141,15 @@ pub trait PinnedVec { /// /// # Panics /// Panics if `index >= len`. - /// - /// # Safety - /// - /// This operation is **unsafe** when `T` is not `NotSelfRefVecItem`. - /// - /// `insert` is unsafe since insertion of a new element at an arbitrary position of the vector - /// changes the positions or memory locations of already existing elements. - /// This is considered **unsafe** since it violates `PinnedVec` guarantees. - /// - /// `PinnedVec` takes the conservative approach: - /// every T which does not implement the marker trait `NotSelfRefVecItem` - /// is assumed to be a vector item referencing other vector items. - /// - /// On the other hand, safe version of this method is available for `T: NotSelfRefVecItem`. - unsafe fn unsafe_insert(&mut self, index: usize, element: T); + fn insert(&mut self, index: usize, element: T); /// Removes and returns the element at position index within the vector, shifting all elements after it to the left. /// /// # Panics /// /// Panics if index is out of bounds. - /// - /// # Safety - /// - /// This operation is **unsafe** when `T` is not `NotSelfRefVecItem`. - /// - /// `remove` is unsafe since removal of an element at an arbitrary position of the vector - /// changes the positions of already existing elements. - /// This is considered **unsafe** since it violates `PinnedVec` guarantees. - /// - /// Further, it is possible that one of the remaining elements is - /// pointing to the element which is being removed. - /// - /// `PinnedVec` takes the conservative approach: - /// every T which does not implement the marker trait `NotSelfRefVecItem` - /// is assumed to be a vector item referencing other vector items. - /// - /// On the other hand, safe version of this method is available for `T: NotSelfRefVecItem`. - unsafe fn unsafe_remove(&mut self, index: usize) -> T; + fn remove(&mut self, index: usize) -> T; /// Removes the last element from a vector and returns it, or None if it is empty. - /// - /// # Safety - /// - /// This operation is **unsafe** when `T` is not `NotSelfRefVecItem`. - /// - /// `pop` is unsafe since it is possible that one of the remaining elements is holding a - /// reference to the last element which is being popped. - /// - /// `PinnedVec` takes the conservative approach: - /// every T which does not implement the marker trait `NotSelfRefVecItem` - /// is assumed to be a vector item referencing other vector items. - /// - /// On the other hand, safe version of this method is available for `T: NotSelfRefVecItem`. - unsafe fn unsafe_pop(&mut self) -> Option; + fn pop(&mut self) -> Option; /// Swaps two elements in the slice. /// /// If `a` equals to `b`, it's guaranteed that elements won't change value. @@ -199,83 +157,42 @@ pub trait PinnedVec { /// # Arguments /// /// * a - The index of the first element - /// * b - The index of the second element - /// - /// # Safety - /// - /// This operation is **unsafe** when `T` is not `NotSelfRefVecItem`. - /// - /// `swap` is unsafe since it is possible that other elements are referencing the - /// elements to be swapped. The swap operation would invalidate these references, - /// pointing at wrong elements. - /// - /// `PinnedVec` takes the conservative approach: - /// every T which does not implement the marker trait `NotSelfRefVecItem` - /// is assumed to be a vector item referencing other vector items. - /// - /// On the other hand, safe version of this method is available for `T: NotSelfRefVecItem`. - unsafe fn unsafe_swap(&mut self, a: usize, b: usize); + /// * b - The index of the second element. + fn swap(&mut self, a: usize, b: usize); /// Shortens the vector, keeping the first `len` elements and dropping /// the rest. /// /// If `len` is greater than the vector's current length, this has no /// effect. - /// - /// # Safety - /// - /// This operation is **unsafe** when `T` is not `NotSelfRefVecItem`. - /// - /// `truncate` is unsafe since it is possible that remaining elements are referencing - /// to elements which are dropped by the truncate method. - /// - /// `PinnedVec` takes the conservative approach: - /// every T which does not implement the marker trait `NotSelfRefVecItem` - /// is assumed to be a vector item referencing other vector items. - /// - /// On the other hand, safe version of this method is available for `T: NotSelfRefVecItem`. - unsafe fn unsafe_truncate(&mut self, len: usize); - /// Creates and returns a clone of the vector. - /// - /// # Safety - /// - /// This operation is **unsafe** when `T` is not `NotSelfRefVecItem`. - /// - /// To understand why `clone` is unsafe when `T` is not `NotSelfRefVecItem`, - /// consider the following example. - /// - /// ```rust ignore - /// let vec = ...; // vec is a self-referential PinnedVec, equivalently, - /// // it is a `PinnedVec` where T is not `NotSelfRefVecItem` - /// let clone = vec.clone(); - /// ``` - /// - /// Now the elements of `clone` are holding references to the `vec` which is bad! - /// - /// * these references are meant to be kept internal to the vector, so is the name self-ref, - /// * further, if `vec` is dropped, `clone` elements will be holding invalid references leading to UB. - /// - /// `PinnedVec` takes the conservative approach: - /// every T which does not implement the marker trait `NotSelfRefVecItem` - /// is assumed to be a vector item referencing other vector items. - /// - /// On the other hand, safe version of this method is available for `T: NotSelfRefVecItem`. - unsafe fn unsafe_clone(&self) -> Self - where - T: Clone; + fn truncate(&mut self, len: usize); /// Returns an iterator to elements of the vector. fn iter(&self) -> Self::Iter<'_>; /// Returns an iterator of mutable references to elements of the vector. fn iter_mut(&mut self) -> Self::IterMut<'_>; + /// Returns a reversed back-to-front iterator to elements of the vector. + fn iter_rev(&self) -> Self::IterRev<'_>; + /// Returns a reversed back-to-front iterator mutable references to elements of the vector. + fn iter_mut_rev(&mut self) -> Self::IterMutRev<'_>; +} - // required for common trait implementations - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - fn partial_eq(&self, other: S) -> bool - where - S: AsRef<[T]>, - T: PartialEq; - /// Formats the value using the given formatter. - fn debug(&self, f: &mut Formatter<'_>) -> Result - where - T: Debug; +#[cfg(test)] +mod tests { + use crate::{pinned_vec_tests::testvec::TestVec, PinnedVec}; + + #[test] + fn is_empty() { + let mut vec = TestVec::new(5); + assert!(vec.is_empty()); + + vec.push(1); + assert!(!vec.is_empty()); + + vec.push(2); + vec.push(3); + assert!(!vec.is_empty()); + + vec.clear(); + assert!(vec.is_empty()); + } } diff --git a/src/pinned_vec_simple.rs b/src/pinned_vec_simple.rs deleted file mode 100644 index 7ae9de5..0000000 --- a/src/pinned_vec_simple.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::{NotSelfRefVecItem, PinnedVec}; - -/// `PinnedVecSimple` is a `PinnedVec` where the elements satisfy the trait bound -/// `T: NotSelfRefVecItem`. -/// -/// We never manually implement this trait. Instead: -/// -/// * if `V` implements `PinnedVec` for some `T` such that -/// * `T` implements `NotSelfRefVecItem` -/// -/// => then `V` auto-implements `PinnedVecSimple`. -/// -/// Therefore, `T: NotSelfRefVecItem` is sufficient to inform that the elements of the vector do not hold references to each other. -/// This is critical as it enables the safe versions of the following methods which are otherwise unsafe: -/// -/// * `insert` -/// * `remove` -/// * `pop` -/// * `swap` -/// * `truncate` -/// * `clone` -/// -/// Note that having `NotSelfRefVecItem` elements is actually the usual in rust, as the opposite is challenging. -/// Safely and conveniently enabling such rust-difficult data structures (linked lists, trees, graphs, etc) -/// is exactly the goal of the `PinnedVec` trait and benefiting data structures such as the `orx_imp_vec::ImpVec`. -/// -/// `NotSelfRefVecItem` is a marker trait which is implemented for most primitives; however, one needs to implement -/// for new types to explicitly state that the type is not self-referential. -pub trait PinnedVecSimple: PinnedVec -where - T: NotSelfRefVecItem, -{ - /// Inserts an element at position index within the vector, shifting all elements after it to the right. - /// - /// # Panics - /// Panics if `index >= len`. - /// - /// # Safety - /// - /// `insert` operation for a `PinnedVecSimple` where the elements are `T: NotSelfRefVecItem` is **safe**; - /// in this case, pinned vector shares the same safety requirements as `std::vec::Vec` which is readily - /// provided by the borrow checker. - fn insert(&mut self, index: usize, element: T); - /// Removes and returns the element at position index within the vector, shifting all elements after it to the left. - /// - /// # Panics - /// - /// Panics if index is out of bounds. - /// - /// # Safety - /// - /// `remove` operation for a `PinnedVecSimple` where the elements are `T: NotSelfRefVecItem` is **safe**; - /// in this case, pinned vector shares the same safety requirements as `std::vec::Vec` which is readily - /// provided by the borrow checker. - fn remove(&mut self, index: usize) -> T; - /// Removes the last element from a vector and returns it, or None if it is empty. - /// - /// # Safety - /// - /// `pop` operation for a `PinnedVecSimple` where the elements are `T: NotSelfRefVecItem` is **safe**; - /// in this case, pinned vector shares the same safety requirements as `std::vec::Vec` which is readily - /// provided by the borrow checker. - fn pop(&mut self) -> Option; - /// Swaps two elements in the slice. - /// - /// If `a` equals to `b`, it's guaranteed that elements won't change value. - /// - /// # Arguments - /// - /// * a - The index of the first element - /// * b - The index of the second element - /// - /// # Safety - /// - /// `swap` operation for a `PinnedVecSimple` where the elements are `T: NotSelfRefVecItem` is **safe**; - /// in this case, pinned vector shares the same safety requirements as `std::vec::Vec` which is readily - /// provided by the borrow checker. - fn swap(&mut self, a: usize, b: usize); - /// Shortens the vector, keeping the first `len` elements and dropping - /// the rest. - /// - /// If `len` is greater than the vector's current length, this has no - /// effect. - fn truncate(&mut self, len: usize); - /// Creates and returns a clone of the vector. - /// - /// # Safety - /// - /// `clone` operation for a `PinnedVecSimple` where the elements are `T: NotSelfRefVecItem` is **safe**; - /// in this case, pinned vector shares the same safety requirements as `std::vec::Vec` which is readily - /// provided by the borrow checker. - fn clone(&self) -> Self - where - T: Clone; -} - -impl PinnedVecSimple for V -where - T: NotSelfRefVecItem, - V: PinnedVec, -{ - #[inline(always)] - fn insert(&mut self, index: usize, element: T) { - unsafe { self.unsafe_insert(index, element) } - } - #[inline(always)] - fn remove(&mut self, index: usize) -> T { - unsafe { self.unsafe_remove(index) } - } - #[inline(always)] - fn pop(&mut self) -> Option { - unsafe { self.unsafe_pop() } - } - #[inline(always)] - fn swap(&mut self, a: usize, b: usize) { - unsafe { self.unsafe_swap(a, b) } - } - #[inline(always)] - fn truncate(&mut self, len: usize) { - unsafe { self.unsafe_truncate(len) } - } - #[inline(always)] - fn clone(&self) -> Self - where - T: Clone, - { - unsafe { self.unsafe_clone() } - } -} diff --git a/src/pinned_vec_tests/extend.rs b/src/pinned_vec_tests/extend.rs new file mode 100644 index 0000000..93ac5d7 --- /dev/null +++ b/src/pinned_vec_tests/extend.rs @@ -0,0 +1,66 @@ +use super::refmap::RefMap; +use crate::PinnedVec; + +pub fn extend + Sized>(pinned_vec: P, max_allowed_test_len: usize) -> P { + let mut vec = pinned_vec; + vec.clear(); + + let mut refmap = RefMap::new(200, max_allowed_test_len); + + let average_extend_length = (max_allowed_test_len / 37).max(1); + let num_chunks = max_allowed_test_len / average_extend_length; + let mut extend_lengths = vec![]; + for _ in 0..num_chunks { + extend_lengths.push(average_extend_length); + } + let last_chunk_len = max_allowed_test_len - num_chunks * average_extend_length; + extend_lengths.push(last_chunk_len); + assert_eq!(extend_lengths.iter().sum::(), max_allowed_test_len); + + for slice_len in extend_lengths { + let begin = vec.len(); + let slice: Vec<_> = (begin..(begin + slice_len)).collect(); + vec.extend_from_slice(&slice); + + for i in begin..(begin + slice_len) { + refmap.set_reference(&vec, i); + } + + refmap.validate_references(&vec); + } + + vec +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pinned_vec_tests::testvec::TestVec; + + #[test] + fn test_extend_empty() { + let pinned_vec = TestVec::new(0); + extend(pinned_vec, 0); + } + + #[test] + fn test_extend_small() { + let capacity = 40; + let pinned_vec = TestVec::new(capacity); + extend(pinned_vec, capacity); + } + + #[test] + fn test_extend_medium() { + let capacity = 1024 * 64; + let pinned_vec = TestVec::new(capacity); + extend(pinned_vec, capacity); + } + + #[test] + fn test_extend_large() { + let capacity = 1024 * 1024; + let pinned_vec = TestVec::new(capacity); + extend(pinned_vec, capacity); + } +} diff --git a/src/pinned_vec_tests/insert.rs b/src/pinned_vec_tests/insert.rs new file mode 100644 index 0000000..5bb345b --- /dev/null +++ b/src/pinned_vec_tests/insert.rs @@ -0,0 +1,50 @@ +use super::refmap::RefMap; +use crate::PinnedVec; + +pub fn insert>(pinned_vec: P, max_allowed_test_len: usize) -> P { + let mut vec = pinned_vec; + vec.clear(); + + let first_half = max_allowed_test_len / 2; + + let mut refmap = RefMap::new(200, first_half); + + for i in 0..first_half { + vec.push(i); + refmap.set_reference(&vec, i); + refmap.validate_references(&vec); + } + + for i in first_half..max_allowed_test_len { + vec.insert(first_half, i); + refmap.validate_references(&vec); + } + + vec +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pinned_vec_tests::testvec::TestVec; + + #[test] + fn test_insert_empty() { + let pinned_vec = TestVec::new(0); + insert(pinned_vec, 0); + } + + #[test] + fn test_insert_small() { + let capacity = 40; + let pinned_vec = TestVec::new(capacity); + insert(pinned_vec, capacity); + } + + #[test] + fn test_insert_medium() { + let capacity = 1024 * 64; + let pinned_vec = TestVec::new(capacity); + insert(pinned_vec, capacity); + } +} diff --git a/src/pinned_vec_tests/mod.rs b/src/pinned_vec_tests/mod.rs new file mode 100644 index 0000000..eab7fa5 --- /dev/null +++ b/src/pinned_vec_tests/mod.rs @@ -0,0 +1,10 @@ +mod extend; +mod insert; +mod pop; +mod push; +mod refmap; +mod remove; +pub mod test_all; +#[cfg(test)] +pub(crate) mod testvec; +mod truncate; diff --git a/src/pinned_vec_tests/pop.rs b/src/pinned_vec_tests/pop.rs new file mode 100644 index 0000000..651e6b4 --- /dev/null +++ b/src/pinned_vec_tests/pop.rs @@ -0,0 +1,58 @@ +use super::refmap::RefMap; +use crate::PinnedVec; + +pub fn pop>(pinned_vec: P, max_allowed_test_len: usize) -> P { + let mut vec = pinned_vec; + vec.clear(); + + let mut refmap = RefMap::new(200, max_allowed_test_len); + + for i in 0..max_allowed_test_len { + vec.push(i); + refmap.set_reference(&vec, i); + refmap.validate_references(&vec); + } + + for i in 0..max_allowed_test_len { + let i = max_allowed_test_len - 1 - i; + let value = vec.pop().expect("is some"); + assert_eq!(i, value); + refmap.drop_reference(i); + refmap.validate_references(&vec); + } + + vec +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pinned_vec_tests::testvec::TestVec; + + #[test] + fn test_pop_empty() { + let pinned_vec = TestVec::new(0); + pop(pinned_vec, 0); + } + + #[test] + fn test_pop_small() { + let capacity = 40; + let pinned_vec = TestVec::new(capacity); + pop(pinned_vec, capacity); + } + + #[test] + fn test_pop_medium() { + let capacity = 1024 * 64; + let pinned_vec = TestVec::new(capacity); + pop(pinned_vec, capacity); + } + + #[test] + fn test_pop_large() { + let capacity = 1024 * 1024; + let pinned_vec = TestVec::new(capacity); + pop(pinned_vec, capacity); + } +} diff --git a/src/pinned_vec_tests/push.rs b/src/pinned_vec_tests/push.rs new file mode 100644 index 0000000..c2034e1 --- /dev/null +++ b/src/pinned_vec_tests/push.rs @@ -0,0 +1,50 @@ +use super::refmap::RefMap; +use crate::PinnedVec; + +pub fn push>(pinned_vec: P, max_allowed_test_len: usize) -> P { + let mut vec = pinned_vec; + vec.clear(); + + let mut refmap = RefMap::new(200, max_allowed_test_len); + + for i in 0..max_allowed_test_len { + vec.push(i); + refmap.set_reference(&vec, i); + refmap.validate_references(&vec); + } + + vec +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pinned_vec_tests::testvec::TestVec; + + #[test] + fn test_push_empty() { + let pinned_vec = TestVec::new(0); + push(pinned_vec, 0); + } + + #[test] + fn test_push_small() { + let capacity = 40; + let pinned_vec = TestVec::new(capacity); + push(pinned_vec, capacity); + } + + #[test] + fn test_push_medium() { + let capacity = 1024 * 64; + let pinned_vec = TestVec::new(capacity); + push(pinned_vec, capacity); + } + + #[test] + fn test_push_large() { + let capacity = 1024 * 1024; + let pinned_vec = TestVec::new(capacity); + push(pinned_vec, capacity); + } +} diff --git a/src/pinned_vec_tests/refmap.rs b/src/pinned_vec_tests/refmap.rs new file mode 100644 index 0000000..249b382 --- /dev/null +++ b/src/pinned_vec_tests/refmap.rs @@ -0,0 +1,149 @@ +use crate::PinnedVec; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; + +pub struct RefMap(HashMap>); + +impl Deref for RefMap { + type Target = HashMap>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for RefMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl RefMap { + pub fn new(max_num_indices: usize, max_len: usize) -> Self { + use rand::prelude::*; + let mut map = std::collections::HashMap::new(); + if max_len > 0 { + let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(171217); + + for _ in 0..max_num_indices { + let i = rng.gen_range(0..max_len); + map.entry(i).or_insert(None); + } + } + + Self(map) + } + + pub fn set_reference>(&mut self, pinned_vec: &P, i: usize) { + if let Some(reference) = self.get_mut(&i) { + let element = pinned_vec.get(i).expect("entry exists"); + let addr = element as *const usize; + *reference = Some(addr); + } + } + + pub fn drop_reference(&mut self, i: usize) { + if let Some(reference) = self.get_mut(&i) { + *reference = None; + } + } + + pub fn validate_references>(&self, pinned_vec: &P) { + for (i, addr) in &self.0 { + if let Some(addr) = addr { + let element = pinned_vec.get(*i).expect("must be some"); + assert_eq!(i, element); + + let element_addr = element as *const usize; + assert_eq!( + *addr, element_addr, + "element address has changed while growing" + ); + let value_at_addr = unsafe { std::ptr::read(*addr) }; + assert_eq!(*i, value_at_addr, "value at address has changed"); + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::pinned_vec_tests::testvec::TestVec; + + use super::*; + + #[test] + fn deref() { + let max_num_indices = 10; + let max_len = 20; + let refmap = RefMap::new(max_num_indices, max_len); + assert_eq!(refmap.deref(), &refmap.0); + } + + #[test] + fn deref_mut() { + let max_num_indices = 10; + let max_len = 1; + let mut refmap1 = RefMap::new(max_num_indices, max_len); + let mut refmap2 = RefMap::new(max_num_indices, max_len); + + refmap1.remove(&0); + refmap2.0.remove(&0); + + assert_eq!(&refmap1.0, &refmap2.0); + } + + #[test] + fn new() { + let max_num_indices = 10; + let max_len = 20; + let refmap = RefMap::new(max_num_indices, max_len); + assert!(refmap.0.len() <= max_num_indices); + assert!(refmap.0.keys().all(|x| x < &max_len)); + } + + #[test] + fn set_reference() { + let mut pinned_vec = TestVec::new(10); + pinned_vec.push(10); + + let max_num_indices = 10; + let max_len = 1; + let mut refmap = RefMap::new(max_num_indices, max_len); + + assert!(refmap.get(&0).expect("is some").is_none()); + + refmap.set_reference(&pinned_vec, 0); + + assert_eq!( + refmap.get(&0).expect("is-some"), + &Some(pinned_vec.get(0).expect("is-some") as *const usize) + ); + } + + #[test] + fn drop_reference() { + let mut pinned_vec = TestVec::new(10); + pinned_vec.push(10); + pinned_vec.push(20); + + let max_num_indices = 10; + let max_len = 1; + let mut refmap = RefMap::new(max_num_indices, max_len); + + refmap.set_reference(&pinned_vec, 0); + assert_eq!( + refmap.get(&0).expect("is-some"), + &Some(pinned_vec.get(0).expect("is-some") as *const usize) + ); + + refmap.drop_reference(1); + assert_eq!( + refmap.get(&0).expect("is-some"), + &Some(pinned_vec.get(0).expect("is-some") as *const usize) + ); + + refmap.drop_reference(0); + assert!(refmap.get(&0).expect("is some").is_none()); + } +} diff --git a/src/pinned_vec_tests/remove.rs b/src/pinned_vec_tests/remove.rs new file mode 100644 index 0000000..021fd26 --- /dev/null +++ b/src/pinned_vec_tests/remove.rs @@ -0,0 +1,53 @@ +use super::refmap::RefMap; +use crate::PinnedVec; + +pub fn remove>(pinned_vec: P, max_allowed_test_len: usize) -> P { + let mut vec = pinned_vec; + vec.clear(); + + let first_half = max_allowed_test_len / 2; + + let mut refmap = RefMap::new(200, first_half); + + for i in 0..first_half { + vec.push(i); + refmap.set_reference(&vec, i); + } + for i in first_half..max_allowed_test_len { + vec.push(i); + } + + for i in first_half..max_allowed_test_len { + let removed = vec.remove(first_half); + assert_eq!(i, removed); + refmap.validate_references(&vec); + } + + vec +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pinned_vec_tests::testvec::TestVec; + + #[test] + fn test_remove_empty() { + let pinned_vec = TestVec::new(0); + remove(pinned_vec, 0); + } + + #[test] + fn test_remove_small() { + let capacity = 40; + let pinned_vec = TestVec::new(capacity); + remove(pinned_vec, capacity); + } + + #[test] + fn test_remove_medium() { + let capacity = 1024 * 64; + let pinned_vec = TestVec::new(capacity); + remove(pinned_vec, capacity); + } +} diff --git a/src/pinned_vec_tests/test_all.rs b/src/pinned_vec_tests/test_all.rs new file mode 100644 index 0000000..a099748 --- /dev/null +++ b/src/pinned_vec_tests/test_all.rs @@ -0,0 +1,152 @@ +use crate::PinnedVec; + +/// Tests the pinned vector guarantees of the specific `PinnedVec` implementation `P`. Assertions fail if any of the guarantees are not satisfied. +/// +/// To be specific on the required guarantees, let's assume that a pinned vector `pinned` currently has `n` elements: +/// +/// * `pinned.push(new_element)`: does not change the memory locations of the first `n` elements; +/// * `pinned.extend_from_slice(slice)`: does not change the memory locations of the first `n` elements; +/// * `pinned.insert(a, new_element)`: does not change the memory locations of the first `a` elements, where `a <= n`; elements to the right of the inserted element might be changed (commonly shifted to right). +/// * `pinned.pop()`: does not change the memory locations of the first `n-1` elements (the n-th element will be removed); +/// * `pinned.remove(a)`: does not change the memory locations of the first `a` elements, where `a < n`; elements to the right of the removed element might be changed (commonly shifted to left). +/// * `pinned.truncate(a)`: does not change the memory locations of the first `a` elements, where `a < n`. +pub fn test_pinned_vec>(pinned_vec: P, test_vec_len: usize) { + let pinned_vec = super::push::push(pinned_vec, test_vec_len); + let pinned_vec = super::extend::extend(pinned_vec, test_vec_len); + let pinned_vec = super::insert::insert(pinned_vec, test_vec_len); + let pinned_vec = super::pop::pop(pinned_vec, test_vec_len); + let pinned_vec = super::remove::remove(pinned_vec, test_vec_len); + let _ = super::truncate::truncate(pinned_vec, test_vec_len); +} + +#[cfg(test)] +mod tests { + use super::*; + + struct JustVec(Vec); + impl PinnedVec for JustVec { + type Iter<'a> = std::slice::Iter<'a, T> where T: 'a, Self: 'a; + type IterMut<'a> = std::slice::IterMut<'a, T> where T: 'a, Self: 'a; + type IterRev<'a> =std::iter:: Rev> where T: 'a, Self: 'a; + type IterMutRev<'a> =std::iter:: Rev> where T: 'a, Self: 'a; + + fn index_of(&self, data: &T) -> Option { + crate::utils::slice::index_of(&self.0, data) + } + + fn clear(&mut self) { + self.0.clear(); + } + + fn capacity(&self) -> usize { + self.0.capacity() + } + + fn extend_from_slice(&mut self, other: &[T]) + where + T: Clone, + { + self.0.extend_from_slice(other) + } + + fn get(&self, index: usize) -> Option<&T> { + self.0.get(index) + } + + fn get_mut(&mut self, index: usize) -> Option<&mut T> { + self.0.get_mut(index) + } + + unsafe fn get_unchecked(&self, index: usize) -> &T { + self.0.get_unchecked(index) + } + + unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { + self.0.get_unchecked_mut(index) + } + + fn first(&self) -> Option<&T> { + self.0.first() + } + + fn last(&self) -> Option<&T> { + self.0.last() + } + + unsafe fn first_unchecked(&self) -> &T { + &(self.0)[0] + } + + unsafe fn last_unchecked(&self) -> &T { + &(self.0)[self.len() - 1] + } + + fn len(&self) -> usize { + self.0.len() + } + + fn push(&mut self, value: T) { + self.0.push(value) + } + + fn insert(&mut self, index: usize, element: T) { + self.0.insert(index, element) + } + + fn remove(&mut self, index: usize) -> T { + self.0.remove(index) + } + + fn pop(&mut self) -> Option { + self.0.pop() + } + + fn swap(&mut self, a: usize, b: usize) { + self.0.swap(a, b) + } + + fn truncate(&mut self, len: usize) { + self.0.truncate(len) + } + + fn iter(&self) -> Self::Iter<'_> { + self.0.iter() + } + + fn iter_mut(&mut self) -> Self::IterMut<'_> { + self.0.iter_mut() + } + + fn iter_rev(&self) -> Self::IterRev<'_> { + self.0.iter().rev() + } + + fn iter_mut_rev(&mut self) -> Self::IterMutRev<'_> { + self.0.iter_mut().rev() + } + } + + #[test] + fn empty_vec_passes() { + let vec = JustVec(vec![]); + test_pinned_vec(vec, 0); + } + + #[test] + fn within_capacity_vec_passes() { + let capacity = 1024 * 4; + let vec = JustVec(Vec::with_capacity(capacity)); + test_pinned_vec(vec, capacity); + } + + #[test] + #[should_panic] + fn capacity_exceeding_vec_fails() { + // not necessarily fails in every expansion, but will eventually fail. + let lengths = [8, 32, 1024, 32768]; + for len in lengths { + let vec = JustVec(Vec::with_capacity(0)); + test_pinned_vec(vec, len); + } + } +} diff --git a/src/pinned_vec_tests/testvec.rs b/src/pinned_vec_tests/testvec.rs new file mode 100644 index 0000000..e94f32f --- /dev/null +++ b/src/pinned_vec_tests/testvec.rs @@ -0,0 +1,120 @@ +use crate::PinnedVec; +use std::iter::Rev; + +pub struct TestVec(Vec); + +impl TestVec { + #[cfg(test)] + pub fn new(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + fn assert_has_room(&self, required_additional_space: usize) { + assert!(self.len() + required_additional_space <= self.0.capacity()) + } +} + +impl PinnedVec for TestVec { + type Iter<'a> = std::slice::Iter<'a, T> where T: 'a, Self: 'a; + type IterMut<'a> = std::slice::IterMut<'a, T> where T: 'a, Self: 'a; + type IterRev<'a> = Rev> where T: 'a, Self: 'a; + type IterMutRev<'a> = Rev> where T: 'a, Self: 'a; + + fn index_of(&self, data: &T) -> Option { + crate::utils::slice::index_of(&self.0, data) + } + + fn clear(&mut self) { + self.0.clear(); + } + + fn capacity(&self) -> usize { + self.0.capacity() + } + + fn extend_from_slice(&mut self, other: &[T]) + where + T: Clone, + { + self.assert_has_room(other.len()); + self.0.extend_from_slice(other) + } + + fn get(&self, index: usize) -> Option<&T> { + self.0.get(index) + } + + fn get_mut(&mut self, index: usize) -> Option<&mut T> { + self.0.get_mut(index) + } + + unsafe fn get_unchecked(&self, index: usize) -> &T { + self.0.get_unchecked(index) + } + + unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { + self.0.get_unchecked_mut(index) + } + + fn first(&self) -> Option<&T> { + self.0.first() + } + + fn last(&self) -> Option<&T> { + self.0.last() + } + + unsafe fn first_unchecked(&self) -> &T { + &(self.0)[0] + } + + unsafe fn last_unchecked(&self) -> &T { + &(self.0)[self.len() - 1] + } + + fn len(&self) -> usize { + self.0.len() + } + + fn push(&mut self, value: T) { + self.assert_has_room(1); + self.0.push(value) + } + + fn insert(&mut self, index: usize, element: T) { + self.assert_has_room(1); + self.0.insert(index, element) + } + + fn remove(&mut self, index: usize) -> T { + self.0.remove(index) + } + + fn pop(&mut self) -> Option { + self.0.pop() + } + + fn swap(&mut self, a: usize, b: usize) { + self.0.swap(a, b) + } + + fn truncate(&mut self, len: usize) { + self.0.truncate(len) + } + + fn iter(&self) -> Self::Iter<'_> { + self.0.iter() + } + + fn iter_mut(&mut self) -> Self::IterMut<'_> { + self.0.iter_mut() + } + + fn iter_rev(&self) -> Self::IterRev<'_> { + self.0.iter().rev() + } + + fn iter_mut_rev(&mut self) -> Self::IterMutRev<'_> { + self.0.iter_mut().rev() + } +} diff --git a/src/pinned_vec_tests/truncate.rs b/src/pinned_vec_tests/truncate.rs new file mode 100644 index 0000000..74123c7 --- /dev/null +++ b/src/pinned_vec_tests/truncate.rs @@ -0,0 +1,53 @@ +use super::refmap::RefMap; +use crate::PinnedVec; + +pub fn truncate>(pinned_vec: P, max_allowed_test_len: usize) -> P { + let mut vec = pinned_vec; + vec.clear(); + + let first_half = max_allowed_test_len / 2; + + let mut refmap = RefMap::new(200, first_half); + + for i in 0..first_half { + vec.push(i); + refmap.set_reference(&vec, i); + } + for i in first_half..max_allowed_test_len { + vec.push(i); + } + + for _ in first_half..max_allowed_test_len { + let new_len = vec.len() - 1; + vec.truncate(new_len); + refmap.validate_references(&vec); + } + + vec +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pinned_vec_tests::testvec::TestVec; + + #[test] + fn test_truncate_empty() { + let pinned_vec = TestVec::new(0); + truncate(pinned_vec, 0); + } + + #[test] + fn test_truncate_small() { + let capacity = 40; + let pinned_vec = TestVec::new(capacity); + truncate(pinned_vec, capacity); + } + + #[test] + fn test_truncate_medium() { + let capacity = 1024 * 64; + let pinned_vec = TestVec::new(capacity); + truncate(pinned_vec, capacity); + } +} diff --git a/src/self_referential_elements/mod.rs b/src/self_referential_elements/mod.rs deleted file mode 100644 index dc96a3b..0000000 --- a/src/self_referential_elements/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod next_prev; -mod next_prev_arr; -mod next_prev_vec; - -pub use next_prev::{SelfRefNext, SelfRefPrev}; -pub use next_prev_arr::{SelfRefNextArr, SelfRefPrevArr}; -pub use next_prev_vec::{SelfRefNextVec, SelfRefPrevVec}; diff --git a/src/self_referential_elements/next_prev.rs b/src/self_referential_elements/next_prev.rs deleted file mode 100644 index 78fb4e8..0000000 --- a/src/self_referential_elements/next_prev.rs +++ /dev/null @@ -1,109 +0,0 @@ -/// Trait for elements that optionally holds a reference to a `next` element. -/// -/// A common example is the linked-list where each node holds a reference to the next node. -/// -/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential next elements. -/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections -/// where each element live together and share the same lifetime. -/// -/// # Example -/// -/// ```rust -/// use orx_pinned_vec::self_referential_elements::*; -/// -/// #[derive(PartialEq, Debug)] -/// struct LinkedListNode<'a, Data> { -/// data: Data, -/// next: Option<&'a Self>, -/// prev: Option<&'a Self>, -/// } -/// -/// impl<'a, Data> SelfRefNext<'a> for LinkedListNode<'a, Data> { -/// fn next(&self) -> Option<&'a Self> { -/// self.next -/// } -/// fn set_next(&mut self, next: Option<&'a Self>) { -/// self.next = next; -/// } -/// } -/// -/// let mut a = LinkedListNode { -/// data: 'a', -/// next: None, -/// prev: None, -/// }; -/// assert_eq!(a.next(), None); -/// -/// let b = LinkedListNode { -/// data: 'b', -/// next: None, -/// prev: None, -/// }; -/// a.set_next(Some(&b)); -/// assert_eq!(a.next(), Some(&b)); -/// -/// a.set_next(None); -/// assert_eq!(a.next(), None); -/// ``` -pub trait SelfRefNext<'a> { - /// Reference to the next element of this element; None if this element does not have a next. - fn next(&self) -> Option<&'a Self>; - - /// Sets next of this element to the given `next`, which is a reference to the element to be linked. - fn set_next(&mut self, next: Option<&'a Self>); -} - -/// Trait for elements that optionally holds a reference to a `prev` element. -/// -/// A common example is the doubly-linked-list where each node holds a reference to the prev node. -/// -/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential previous elements. -/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections -/// where each element live together and share the same lifetime. -/// -/// # Example -/// -/// ```rust -/// use orx_pinned_vec::self_referential_elements::*; -/// -/// #[derive(PartialEq, Debug)] -/// struct LinkedListNode<'a, Data> { -/// data: Data, -/// next: Option<&'a Self>, -/// prev: Option<&'a Self>, -/// } -/// -/// impl<'a, Data> SelfRefPrev<'a> for LinkedListNode<'a, Data> { -/// fn prev(&self) -> Option<&'a Self> { -/// self.prev -/// } -/// fn set_prev(&mut self, prev: Option<&'a Self>) { -/// self.prev = prev; -/// } -/// } -/// -/// let mut a = LinkedListNode { -/// data: 'a', -/// next: None, -/// prev: None, -/// }; -/// assert_eq!(a.prev(), None); -/// -/// let b = LinkedListNode { -/// data: 'b', -/// next: None, -/// prev: None, -/// }; -/// a.set_prev(Some(&b)); -/// assert_eq!(a.prev(), Some(&b)); -/// -/// a.set_prev(None); -/// assert_eq!(a.prev(), None); -/// ``` -pub trait SelfRefPrev<'a> { - /// Reference to the next element of this element; None if this element does not have a next. - fn prev(&self) -> Option<&'a Self>; - - /// Sets next of this element to the given `next`, which is a reference to the element to be linked. - fn set_prev(&mut self, next: Option<&'a Self>); -} diff --git a/src/self_referential_elements/next_prev_arr.rs b/src/self_referential_elements/next_prev_arr.rs deleted file mode 100644 index 1ba5a7a..0000000 --- a/src/self_referential_elements/next_prev_arr.rs +++ /dev/null @@ -1,37 +0,0 @@ -/// Trait for elements that holds a constant-sized array of references to a collection of next elements. -/// -/// This can be considered as a constant-sized generalization of the [`crate::self_referential_elements::SelfRefNext`] trait. -/// *Constant number of linked element references is often useful to avoid additional indirection.* -/// -/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential linked elements. -/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections -/// where each element live together and share the same lifetime. -pub trait SelfRefNextArr<'a, const N: usize> { - /// Reference to the slice of next elements of this element. - fn next_vec(&self) -> &[Option<&'a Self>; N]; - - /// Sets next elements of this element to the given `next` collection. - fn set_next_vec(&mut self, next: [Option<&'a Self>; N]); - - /// Returns a mutable reference to the collection of next elements. - fn next_mut(&mut self) -> &mut [Option<&'a Self>; N]; -} - -/// Trait for elements that holds a constant-sized array of references to a collection of prev elements. -/// -/// This can be considered as a constant-sized generalization of the [`crate::self_referential_elements::SelfRefPrev`] trait. -/// *Constant number of linked element references is often useful to avoid additional indirection.* -/// -/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential linked elements. -/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections -/// where each element live together and share the same lifetime. -pub trait SelfRefPrevArr<'a, const N: usize> { - /// Reference to the slice of prev elements of this element. - fn prev_vec(&self) -> &[Option<&'a Self>; N]; - - /// Sets prev elements of this element to the given `prev` collection. - fn set_prev_vec(&mut self, prev: [Option<&'a Self>; N]); - - /// Returns a mutable reference to the collection of prev elements. - fn prev_mut(&mut self) -> &mut [Option<&'a Self>; N]; -} diff --git a/src/self_referential_elements/next_prev_vec.rs b/src/self_referential_elements/next_prev_vec.rs deleted file mode 100644 index 2e5ba0c..0000000 --- a/src/self_referential_elements/next_prev_vec.rs +++ /dev/null @@ -1,191 +0,0 @@ -/// Trait for elements that holds a vector of references to a collection of next elements. -/// -/// This can be considered as a dynamically sized generalization of the [`crate::self_referential_elements::SelfRefNext`] trait. -/// -/// A common example is a tree where each node holds a collection of references to the children. -/// Here, we decide to use next as deeper in the tree; however, prev could be used interchangeably. -/// *Chosen convention will be internal to the created self-referential-collection which will expose proper methods such as `children` in this example*. -/// -/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential linked elements. -/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections -/// where each element live together and share the same lifetime. -/// -/// # Example -/// -/// ```rust -/// use orx_pinned_vec::self_referential_elements::*; -/// -/// #[derive(PartialEq, Debug)] -/// struct TreeNode<'a, Data> { -/// data: Data, -/// children: Vec<&'a Self>, -/// parent: Option<&'a Self>, -/// } -/// -/// impl<'a, Data> SelfRefNextVec<'a, Data> for TreeNode<'a, Data> { -/// fn next_vec(&self) -> &[&'a Self] { -/// &self.children -/// } -/// fn next_vec_mut(&mut self) -> &mut Vec<&'a Self> { -/// &mut self.children -/// } -/// fn set_next_vec(&mut self, next: Vec<&'a Self>) { -/// self.children = next; -/// } -/// } -/// -/// impl<'a, Data> SelfRefPrev<'a> for TreeNode<'a, Data> { -/// fn prev(&self) -> Option<&'a Self> { -/// self.parent -/// } -/// fn set_prev(&mut self, prev: Option<&'a Self>) { -/// self.parent = prev; -/// } -/// } -/// ``` -/// -/// In the following example, we want to be able to build the following tree using our `TreeNode`: -/// -/// ```text -/// // 0 --+--a -/// // |--b -- c -/// ``` -/// -/// However, as expected, the following code block will not compile, but it is helpful to demonstrate the goal. -/// -/// ```rust ignore -/// let new_node = |data| TreeNode { -/// data, -/// children: vec![], -/// parent: None, -/// }; -/// -/// let mut root = new_node('0'); -/// let mut a = new_node('a'); -/// let mut b = new_node('b'); -/// let mut c = new_node('c'); -/// -/// root.set_next_vec(vec![&a, &b]); -/// a.set_prev(Some(&root)); -/// b.set_prev(Some(&root)); -/// -/// b.set_next_vec(vec![&c]); -/// c.set_prev(Some(&root)); -/// ``` -/// -/// Although this code block will not compile here, it will be possible to build a tree made out of our `TreeNode`s: -/// -/// * We will use an implementation of a `PinnedVec` where the elements are `TreeNode<'a, T>`, such as `orx_split_vec::SplitVec`, -/// * We will wrap our `SplitVec>` within an `orx_imp_vec::ImpVec`, -/// * `ImpVec` will allow us to build the relations safely and conveniently: -/// * using the pinned-element guarantees of the underlying `PinnedVec`, and -/// * using the required referential relations provided by implementing `SelfRefNextVec` and `SelfRefPrev` traits, which represents a common tree node. -/// * The tree can be cheaply toggled between an `ImpVec` and its underlying `PinnedVec` storage to enable / disable mutation. -/// -/// Please see [orx_imp_vec](https://crates.io/crates/orx-imp-vec) for details and example implementations. -pub trait SelfRefNextVec<'a, Data> { - /// Reference to the slice of next elements of this element. - fn next_vec(&self) -> &[&'a Self]; - - /// Returns a mutable reference to the collection of next elements. - fn next_vec_mut(&mut self) -> &mut Vec<&'a Self>; - - /// Sets next elements of this element to the given `next` collection. - fn set_next_vec(&mut self, next: Vec<&'a Self>); -} - -/// Trait for elements that holds a vector of references to a collection of prev elements. -/// -/// This can be considered as a dynamically sized generalization of the [`crate::self_referential_elements::SelfRefPrev`] trait. -/// -/// A common example is a tree where each node holds a collection of references to the children. -/// Here, we decide to use prev as deeper in the tree; however, prev could be used interchangeably. -/// *Chosen convention will be internal to the created self-referential-collection which will expose proper methods such as `children` in this example*. -/// -/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential linked elements. -/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections -/// where each element live together and share the same lifetime. -/// -/// # Example -/// -/// ```rust -/// use orx_pinned_vec::self_referential_elements::*; -/// -/// #[derive(PartialEq, Debug)] -/// struct TreeNode<'a, Data> { -/// data: Data, -/// children: Vec<&'a Self>, -/// parent: Option<&'a Self>, -/// } -/// -/// impl<'a, Data> SelfRefPrevVec<'a, Data> for TreeNode<'a, Data> { -/// fn prev_vec(&self) -> &[&'a Self] { -/// &self.children -/// } -/// fn prev_vec_mut(&mut self) -> &mut Vec<&'a Self> { -/// &mut self.children -/// } -/// fn set_prev_vec(&mut self, prev: Vec<&'a Self>) { -/// self.children = prev; -/// } -/// } -/// -/// impl<'a, Data> SelfRefPrev<'a> for TreeNode<'a, Data> { -/// fn prev(&self) -> Option<&'a Self> { -/// self.parent -/// } -/// fn set_prev(&mut self, prev: Option<&'a Self>) { -/// self.parent = prev; -/// } -/// } -/// ``` -/// -/// In the following example, we want to be able to build the following tree using our `TreeNode`: -/// -/// ```text -/// // 0 --+--a -/// // |--b -- c -/// ``` -/// -/// However, as expected, the following code block will not compile, but it is helpful to demonstrate the goal. -/// -/// ```rust ignore -/// let new_node = |data| TreeNode { -/// data, -/// children: vec![], -/// parent: None, -/// }; -/// -/// let mut root = new_node('0'); -/// let mut a = new_node('a'); -/// let mut b = new_node('b'); -/// let mut c = new_node('c'); -/// -/// root.set_prev_vec(vec![&a, &b]); -/// a.set_prev(Some(&root)); -/// b.set_prev(Some(&root)); -/// -/// b.set_prev_vec(vec![&c]); -/// c.set_prev(Some(&root)); -/// ``` -/// -/// Although this code block will not compile here, it will be possible to build a tree made out of our `TreeNode`s: -/// -/// * We will use an implementation of a `PinnedVec` where the elements are `TreeNode<'a, T>`, such as `orx_split_vec::SplitVec`, -/// * We will wrap our `SplitVec>` within an `orx_imp_vec::ImpVec`, -/// * `ImpVec` will allow us to build the relations safely and conveniently: -/// * using the pinned-element guarantees of the underlying `PinnedVec`, and -/// * using the required referential relations provided by implementing `SelfRefPrevVec` and `SelfRefPrev` traits, which represents a common tree node. -/// * The tree can be cheaply toggled between an `ImpVec` and its underlying `PinnedVec` storage to enable / disable mutation. -/// -/// Please see [orx_imp_vec](https://crates.io/crates/orx-imp-vec) for details and example implementations. -pub trait SelfRefPrevVec<'a, Data> { - /// Reference to the slice of prev elements of this element. - fn prev_vec(&self) -> &[&'a Self]; - - /// Returns a mutable reference to the collection of prev elements. - fn prev_vec_mut(&mut self) -> &mut Vec<&'a Self>; - - /// Sets prev elements of this element to the given `prev` collection. - fn set_prev_vec(&mut self, prev: Vec<&'a Self>); -} diff --git a/src/utils/slice.rs b/src/utils/slice.rs index 1f95777..cb323dc 100644 --- a/src/utils/slice.rs +++ b/src/utils/slice.rs @@ -27,3 +27,31 @@ pub fn index_of(slice: &[T], element: &T) -> Option { } } } + +#[cfg(test)] +mod tests { + pub use super::*; + + #[test] + fn index_of_wrong() { + let array1 = [0, 1, 2, 3]; + let array2 = [0, 1, 2]; + + let index1 = index_of(&array1, &array2[0]); + assert!(index1.is_none()); + + let index2 = index_of(&array2, &array1[0]); + assert!(index2.is_none()); + } + + #[test] + fn index_of_some() { + let array = [0, 1, 2, 3]; + + for i in 0..array.len() { + let element = &array[i]; + let index = index_of(&array, element); + assert_eq!(Some(i), index); + } + } +}