From e35d43e8e71540d6fa51d3f79f7cbd70d1bc2fa9 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Wed, 10 Jul 2024 17:26:09 +0200 Subject: [PATCH 01/14] Sketch new Path struct --- Cargo.lock | 7 + data-model/Cargo.toml | 5 +- data-model/src/lib.rs | 6 +- data-model/src/path.rs | 1648 +++++++++++++++++++++++++--------------- 4 files changed, 1043 insertions(+), 623 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6982458..031de04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + [[package]] name = "cc" version = "1.0.101" @@ -105,6 +111,7 @@ name = "willow-data-model" version = "0.1.0" dependencies = [ "arbitrary", + "bytes", ] [[package]] diff --git a/data-model/Cargo.toml b/data-model/Cargo.toml index c2f09ac..8ba26f4 100644 --- a/data-model/Cargo.toml +++ b/data-model/Cargo.toml @@ -4,4 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -arbitrary = { version = "1.0.2", features = ["derive"]} \ No newline at end of file +arbitrary = { version = "1.0.2", features = [ + "derive", +] } # TODO feature-gate this +bytes = "1.6.0" diff --git a/data-model/src/lib.rs b/data-model/src/lib.rs index b1c8148..c2df1a4 100644 --- a/data-model/src/lib.rs +++ b/data-model/src/lib.rs @@ -1,4 +1,4 @@ -pub mod entry; -pub mod grouping; -pub mod parameters; +// pub mod entry; +// pub mod grouping; +// pub mod parameters; pub mod path; diff --git a/data-model/src/path.rs b/data-model/src/path.rs index 657cb4c..751a2e5 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -1,99 +1,51 @@ -use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured}; -use std::rc::Rc; +use core::borrow::Borrow; +use core::convert::AsRef; +use core::iter; +use core::mem::size_of; +use core::ops::Deref; -#[derive(Debug)] -/// An error indicating a [`PathComponent`'s bytestring is too long. -pub struct ComponentTooLongError; +use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; -/// A bytestring representing an individual component of a [`Path`]. Provides access to the raw bytes via the [`AsRef`] trait. -/// -/// ## Implementation notes +/// A [component](https://willowprotocol.org/specs/data-model/index.html#Component) of a Willow Path. /// -/// - This trait provides an immutable interface, so cloning of a [`PathComponent`] implementation is expected to be cheap. -/// - Implementations of the [`Ord`] trait must correspond to lexicographically comparing the AsRef bytes. -pub trait PathComponent: Eq + AsRef<[u8]> + Clone + PartialOrd + Ord { - /// The maximum bytelength of a path component, corresponding to Willow's [`max_component_length`](https://willowprotocol.org/specs/data-model/index.html#max_component_length) parameter. - const MAX_COMPONENT_LENGTH: usize; - - /// Construct a new [`PathComponent`] from the provided slice, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. - fn new(components: &[u8]) -> Result; - - /// Construct a new [`PathComponent`] from the concatenation of `head` and the `tail`, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. - /// - /// This operation occurs when computing prefix successors, and the default implementation needs to perform an allocation. Implementers of this trait can override this with something more efficient if possible. - fn new_with_tail(head: &[u8], tail: u8) -> Result { - let mut vec = Vec::with_capacity(head.len() + 1); - vec.extend_from_slice(head); - vec.push(tail); - - Self::new(&vec) - } - - /// Return a new [`PathComponent`] which corresponds to the empty string. - fn empty() -> Self { - Self::new(&[]).unwrap() - } - - /// The length of the component's `as_ref` bytes. - fn len(&self) -> usize { - self.as_ref().len() - } - - /// Whether the component is an empty bytestring or not. - fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Try to append a zero byte to the end of the component. - /// Return `None` if the resulting component would be too long. - fn try_append_zero_byte(&self) -> Option { - if self.len() == Self::MAX_COMPONENT_LENGTH { +/// This type is a thin wrapper around `&'a [u8]` that enforces a const-generic [maximum component length](https://willowprotocol.org/specs/data-model/index.html#max_component_length). Use the `AsRef`, `DeRef`, or `Borrow` implementation to access the immutable byte slice. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Component<'a, const MAX_COMPONENT_LENGTH: usize>(&'a [u8]); + +impl<'a, const MAX_COMPONENT_LENGTH: usize> Component<'a, MAX_COMPONENT_LENGTH> { + /// Create a `Component` from a byte slice. Return `None` if the slice is longer than `MaxComponentLength`. + pub fn new(slice: &'a [u8]) -> Option { + if slice.len() <= MAX_COMPONENT_LENGTH { + return Some(unsafe { Self::new_unchecked(slice) }); // Safe because we just checked the length. + } else { return None; } - - let mut new_component_vec = Vec::with_capacity(self.len() + 1); - - new_component_vec.extend_from_slice(self.as_ref()); - new_component_vec.push(0); - - Some(Self::new(&new_component_vec).unwrap()) } - /// Create a new copy which differs at index `i`. - /// Implementers may panic if `i` is out of bound. - fn set_byte(&self, i: usize, value: u8) -> Self; - - /// Interpret the component as a binary number, and increment that number by 1. - /// If doing so would increase the bytelength of the component, return `None`. - fn try_increment_fixed_width(&self) -> Option { - // Wish we could avoid this allocation somehow. - let mut new_component = self.clone(); - - for i in (0..self.len()).rev() { - let byte = self.as_ref()[i]; + /// Create a `Component` from a byte slice, without verifying its length. + pub unsafe fn new_unchecked(slice: &'a [u8]) -> Self { + Self(slice) + } +} - if byte == 255 { - new_component = new_component.set_byte(i, 0); - } else { - return Some(new_component.set_byte(i, byte + 1)); - } - } +impl<'a, const MAX_COMPONENT_LENGTH: usize> Deref for Component<'a, MAX_COMPONENT_LENGTH> { + type Target = [u8]; - None + fn deref(&self) -> &Self::Target { + &self.0 } +} - /// Return the least component which is greater than `self` but which is not prefixed by `self`. - fn prefix_successor(&self) -> Option { - for i in (0..self.len()).rev() { - if self.as_ref()[i] != 255 { - // Since we are not adjusting the length of the component this will always succeed. - return Some( - Self::new_with_tail(&self.as_ref()[0..i], self.as_ref()[i] + 1).unwrap(), - ); - } - } +impl<'a, const MAX_COMPONENT_LENGTH: usize> AsRef<[u8]> for Component<'a, MAX_COMPONENT_LENGTH> { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} - None +impl<'a, const MAX_COMPONENT_LENGTH: usize> Borrow<[u8]> for Component<'a, MAX_COMPONENT_LENGTH> { + fn borrow(&self) -> &[u8] { + &self.0 } } @@ -105,615 +57,1073 @@ pub enum InvalidPathError { /// The path has too many components. TooManyComponents, } +// TODO trait impls -/// A sequence of [`PathComponent`], used to name and query entries in [Willow's data model](https://willowprotocol.org/specs/data-model/index.html#data_model). +/// An immutable Willow [path](https://willowprotocol.org/specs/data-model/index.html#Path). Thread-safe, cheap to clone, cheap to take prefixes of. /// -pub trait Path: PartialEq + Eq + PartialOrd + Ord + Clone { - type Component: PathComponent; - - /// The maximum number of [`PathComponent`] a [`Path`] may have, corresponding to Willow's [`max_component_count`](https://willowprotocol.org/specs/data-model/index.html#max_component_count) parameter - const MAX_COMPONENT_COUNT: usize; - /// The maximum total number of bytes a [`Path`] may have, corresponding to Willow's [`max_path_length`](https://willowprotocol.org/specs/data-model/index.html#max_path_length) parameter - const MAX_PATH_LENGTH: usize; - - /// Contruct a new [`Path`] from a slice of [`PathComponent`], or return a [`InvalidPathError`] if the resulting path would be too long or have too many components. - fn new(components: &[Self::Component]) -> Result; - - /// Construct a new [`Path`] with no components. - fn empty() -> Self; - - /// Return a new [`Path`] with the components of this path suffixed with the given [`PathComponent`](PathComponent), or return [`InvalidPathError`] if the resulting path would be invalid in some way. - fn append(&self, component: Self::Component) -> Result; +/// Enforces that each component has a length of at most `MCL` ([**m**ax\_**c**omponent\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_component_length)), that each path has at most `MCC` ([**m**ax\_**c**omponent\_**c**count](https://willowprotocol.org/specs/data-model/index.html#max_component_count)) components, and that the total size in bytes of all components is at most `MPL` ([**m**ax\_**p**ath\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_path_length)). +pub struct Path { + /// The data of the underlying path. + data: HeapEncoding, + /// Number of components of the `data` to consider for this particular path. Must be less than or equal to the total number of components. + number_of_components: usize, +} - /// Return an iterator of all this path's components. +impl Path { + /// Construct an empty path, i.e., a path of zero components. /// - /// The `.len` method of the returned [`ExactSizeIterator`] must coincide with [`Self::component_count`]. - fn components( - &self, - ) -> impl DoubleEndedIterator + ExactSizeIterator; - - /// Return the number of [`PathComponent`] in the path. - fn component_count(&self) -> usize; - - /// Return whether the path has any [`PathComponent`] or not. - fn is_empty(&self) -> bool { - self.component_count() == 0 + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn new_empty() -> Self { + return Path { + // 16 zero bytes, to work even on platforms on which `usize` has a size of 16. + data: HeapEncoding(Bytes::from_static(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ])), + number_of_components: 0, + }; } - /// Create a new [`Path`] by taking the first `length` components of this path. - fn create_prefix(&self, length: usize) -> Self; - - /// Return all possible prefixes of a path, including the empty path and the path itself. - fn all_prefixes(&self) -> impl Iterator { - let self_len = self.components().count(); - - (0..=self_len).map(|i| self.create_prefix(i)) + /// Construct a singleton path, i.e., a path of exactly one component. + /// + /// Copies the bytes of the component into an owned allocation on the heap. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the length of the component. Performs a single allocation of `O(n)` bytes. + pub fn new_singleton<'a>(comp: Component<'a, MCL>) -> Self { + let mut buf = BytesMut::with_capacity((2 * size_of::()) + comp.len()); + buf.extend_from_slice(&(1usize.to_ne_bytes())[..]); + buf.extend_from_slice(&(comp.len().to_ne_bytes())[..]); + buf.extend_from_slice(comp.as_ref()); + + return Path { + data: HeapEncoding(buf.freeze()), + number_of_components: 1, + }; } - /// Test whether this path is a prefix of the given path. - /// Paths are always a prefix of themselves. - fn is_prefix_of(&self, other: &Self) -> bool { - for (comp_a, comp_b) in self.components().zip(other.components()) { - if comp_a != comp_b { - return false; - } + /// Construct a path of known total length from an [`ExactSizeIterator`][core::iter::ExactSizeIterator] of components. + /// + /// Copies the bytes of the components into an owned allocation on the heap. + /// + /// Panics if the claimed `total_length` does not match the sum of the lengths of all the components. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the path in bytes. Performs a single allocation of `O(n)` bytes. + pub fn new_from_iter<'a, I>(total_length: usize, iter: &mut I) -> Result + where + I: ExactSizeIterator>, + { + if total_length > MPL { + return Err(InvalidPathError::PathTooLong); } - self.component_count() <= other.component_count() - } - - /// Test whether this path is prefixed by the given path. - /// Paths are always a prefix of themselves. - fn is_prefixed_by(&self, other: &Self) -> bool { - other.is_prefix_of(self) - } + let component_count = iter.len(); - /// Return the longest common prefix of this path and the given path. - fn longest_common_prefix(&self, other: &Self) -> Self { - let mut lcp_len = 0; - - for (comp_a, comp_b) in self.components().zip(other.components()) { - if comp_a != comp_b { - break; - } - - lcp_len += 1 + if component_count > MCC { + return Err(InvalidPathError::TooManyComponents); } - self.create_prefix(lcp_len) - } + let mut buf = + BytesMut::with_capacity(((component_count + 1) * size_of::()) + total_length); + buf.extend_from_slice(&(component_count.to_ne_bytes())[..]); - /// Return the least path which is greater than `self`, or return `None` if `self` is the greatest possible path. - fn successor(&self) -> Option { - if self.component_count() == 0 { - let new_component = Self::Component::new(&[]).ok()?; - return Self::new(&[new_component]).ok(); - } + // Fill up the accumulated component lengths with dummy values. + buf.put_bytes(0, component_count * size_of::()); + + let mut accumulated_component_length = 0; + for (i, comp) in iter.enumerate() { + // Overwrite the dummy accumulated component length for this component with the actual value. + accumulated_component_length += comp.len(); + let start = (1 + i) * size_of::(); + let end = start + size_of::(); + buf.as_mut()[start..end] + .copy_from_slice(&accumulated_component_length.to_ne_bytes()[..]); - // Try and add an empty component. - if let Ok(path) = self.append(Self::Component::empty()) { - return Some(path); + // Append the component to the path. + buf.extend_from_slice(comp.as_ref()); } - for (i, component) in self.components().enumerate().rev() { - // Try and do the *next* simplest thing (add a 0 byte to the component). - if let Some(component) = component.try_append_zero_byte() { - if let Ok(path) = self.create_prefix(i).append(component) { - return Some(path); - } - } - - // Otherwise we need to increment the component fixed-width style! - if let Some(incremented_component) = component.try_increment_fixed_width() { - // We can unwrap here because neither the max path length, component count, or component length has changed. - return Some(self.create_prefix(i).append(incremented_component).unwrap()); - } + if accumulated_component_length != total_length { + panic!("Tried to construct a path of total length {}, but got components whose accumulated length was {}.", total_length, accumulated_component_length); } - None + return Ok(Path { + data: HeapEncoding(buf.freeze()), + number_of_components: component_count, + }); } - /// Return the least path that is greater than `self` and which is not prefixed by `self`, or `None` if `self` is the empty path *or* if `self` is the greatest path. - fn successor_of_prefix(&self) -> Option { - for (i, component) in self.components().enumerate().rev() { - if let Some(successor_comp) = component.try_append_zero_byte() { - if let Ok(path) = self.create_prefix(i).append(successor_comp) { - return Some(path); - } - } - - if let Some(successor_comp) = component.prefix_successor() { - return self.create_prefix(i).append(successor_comp).ok(); - } + /// Construct a path of from a slice of components. + /// + /// Copies the bytes of the components into an owned allocation on the heap. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the path in bytes. Performs a single allocation of `O(n)` bytes. + pub fn new_from_slice<'a>(components: &[Component<'a, MCL>]) -> Result { + let mut total_length = 0; + for comp in components { + total_length += comp.len(); } - None + return Self::new_from_iter( + total_length, + &mut components.iter().map(|comp_ref| *comp_ref), + ); } -} -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct PathComponentBox(Box<[u8]>); + /// Construct a new path by appending a component to this one. + /// + /// Creates a fully separate copy of the new data on the heap; this function is not more efficient than constructing the new path from scratch. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the new path in bytes. Performs a single allocation of `O(n)` bytes. + pub fn append<'a>(&self, comp: Component<'a, MCL>) -> Result { + let total_length = self.get_path_length() + comp.len(); + return Self::new_from_iter( + total_length, + &mut ExactLengthChain::new(self.components(), iter::once(comp)), + ); + } -/// An implementation of [`PathComponent`] for [`PathComponentBox`]. -/// -/// ## Type parameters -/// -/// - `MCL`: A [`usize`] used as [`PathComponent::MAX_COMPONENT_LENGTH`]. -impl PathComponent for PathComponentBox { - const MAX_COMPONENT_LENGTH: usize = MCL; - - /// Create a new component by cloning and appending all bytes from the slice into a [`Vec`], or return a [`ComponentTooLongError`] if the bytelength of the slice exceeds [`PathComponent::MAX_COMPONENT_LENGTH`]. - fn new(bytes: &[u8]) -> Result { - if bytes.len() > Self::MAX_COMPONENT_LENGTH { - return Err(ComponentTooLongError); + /// Construct a new path by appending a slice of components to this one. + /// + /// Creates a fully separate copy of the new data on the heap; this function is not more efficient than constructing the new path from scratch. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the new path in bytes. Performs a single allocation of `O(n)` bytes. + pub fn append_slice<'a>( + &self, + components: &[Component<'a, MCL>], + ) -> Result { + let mut total_length = self.get_path_length(); + for comp in components { + total_length += comp.len(); } - Ok(Self(bytes.into())) + return Self::new_from_iter( + total_length, + &mut ExactLengthChain::new( + self.components(), + components.iter().map(|comp_ref| *comp_ref), + ), + ); } - fn len(&self) -> usize { - self.0.len() + /// Get the number of components in this path. + /// + /// Guaranteed to be at most `MCC`. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn get_component_count(&self) -> usize { + return self.number_of_components; } - fn is_empty(&self) -> bool { - self.0.is_empty() + /// Return whether this path has zero components. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn is_empty(&self) -> bool { + self.get_component_count() == 0 } - fn set_byte(&self, i: usize, value: u8) -> Self { - let mut new_component = self.clone(); - - new_component.0[i] = value; + /// Get the sum of the lengths of all components in this path. + /// + /// Guaranteed to be at most `MCC`. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn get_path_length(&self) -> usize { + if self.number_of_components == 0 { + return 0; + } else { + return self + .data + .get_sum_of_lengths_for_component(self.number_of_components - 1) + .unwrap(); + } + } - new_component + /// Get the `i`-th [`Component`] of this path. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn get_component<'s>(&'s self, i: usize) -> Option> { + return self.data.get_component(i); } -} -impl AsRef<[u8]> for PathComponentBox { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() + /// Create an iterator over the components of this path. + /// + /// Stepping the iterator takes `O(1)` time and performs no memory allocations. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn components<'s>( + &'s self, + ) -> impl DoubleEndedIterator> + ExactSizeIterator> + { + (0..self.get_component_count()).map(|i| { + self.get_component(i).unwrap() // Only `None` if `i >= self.get_component_count()` + }) } } -impl<'a, const MCL: usize> Arbitrary<'a> for PathComponentBox { - fn arbitrary(u: &mut Unstructured<'a>) -> Result { - let boxx: Box<[u8]> = Arbitrary::arbitrary(u)?; - Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) +/// Efficient, heap-allocated storage of a path encoding: +/// +/// - First, a usize that gives the total number of path components. +/// - Second, that many usizes, where the i-th one gives the sum of the lengths of the first i components. +/// - Third, the concatenation of all components. +/// +/// Note that these are not guaranteed to fulfil alignment requirements of usize, so we need to be careful in how we access these. +/// Always use the methods on this struct for that reason. +struct HeapEncoding(Bytes); + +// All offsets are in bytes, unless otherwise specified. +// Arguments named `i` are the index of a component in the string, *not* a byte offset. +impl HeapEncoding { + fn get_component_count(&self) -> usize { + self.get_usize_at_offset(0).unwrap() // We always store at least 8byte for the component count. } - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - as Arbitrary<'a>>::size_hint(depth) + // None if i is outside the slice. + fn get_sum_of_lengths_for_component(&self, i: usize) -> Option { + let start_offset_in_bytes = Self::start_offset_of_sum_of_lengths_for_component(i); + self.get_usize_at_offset(start_offset_in_bytes) } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -/// A cheaply cloneable [`Path`] using a `Rc<[PathComponentBox]>`. -/// While cloning is cheap, operations which return modified forms of the path (e.g. [`Path::append`]) are not, as they have to clone and adjust the contents of the underlying [`Rc`]. -pub struct PathRc( - Rc<[PathComponentBox]>, -); - -impl Path for PathRc { - type Component = PathComponentBox; - - const MAX_COMPONENT_COUNT: usize = MCC; - const MAX_PATH_LENGTH: usize = MPL; - - fn new(components: &[Self::Component]) -> Result { - if components.len() > Self::MAX_COMPONENT_COUNT { - return Err(InvalidPathError::TooManyComponents); - }; - let mut path_vec = Vec::new(); - let mut total_length = 0; - - for component in components { - total_length += component.len(); - - if total_length > Self::MAX_PATH_LENGTH { - return Err(InvalidPathError::PathTooLong); - } else { - path_vec.push(component.clone()); - } - } - - Ok(PathRc(path_vec.into())) + fn get_component<'s>(&'s self, i: usize) -> Option> { + let start = self.start_offset_of_component(i)?; + let end = self.end_offset_of_component(i)?; + return Some(unsafe { Component::new_unchecked(&self.0[start..end]) }); } - fn empty() -> Self { - PathRc(Vec::new().into()) + fn start_offset_of_sum_of_lengths_for_component(i: usize) -> usize { + // First usize is the number of components, then the i usizes storing the lengths; hence at i + 1. + size_of::() * (i + 1) } - fn create_prefix(&self, length: usize) -> Self { - if length == 0 { - return Self::empty(); + fn start_offset_of_component(&self, i: usize) -> Option { + let metadata_length = (self.get_component_count() + 1) * size_of::(); + if i == 0 { + return Some(metadata_length); + } else { + return self + .get_sum_of_lengths_for_component(i - 1) // Length of everything up until the previous component. + .map(|length| length + metadata_length); } - - let until = core::cmp::min(length, self.0.len()); - let slice = &self.0[0..until]; - - Path::new(slice).unwrap() } - fn append(&self, component: Self::Component) -> Result { - let total_component_count = self.0.len(); - - if total_component_count + 1 > MCC { - return Err(InvalidPathError::TooManyComponents); - } - - let total_path_length = self.0.iter().fold(0, |acc, item| acc + item.0.len()); + fn end_offset_of_component(&self, i: usize) -> Option { + let metadata_length = (self.get_component_count() + 1) * size_of::(); + return self + .get_sum_of_lengths_for_component(i) + .map(|length| length + metadata_length); + } - if total_path_length + component.as_ref().len() > MPL { - return Err(InvalidPathError::PathTooLong); - } + fn get_usize_at_offset(&self, offset_in_bytes: usize) -> Option { + let end = offset_in_bytes + size_of::(); - let mut new_path_vec = Vec::new(); + // We cannot interpret the memory in the slice as a usize directly, because the alignment might not match. + // So we first copy the bytes onto the heap, then construct a usize from it. + let mut usize_bytes = [0u8; size_of::()]; - for component in self.components() { - new_path_vec.push(component.clone()) + if self.0.len() <= end { + return None; + } else { + usize_bytes.copy_from_slice(&self.0[offset_in_bytes..end]); + return Some(usize::from_ne_bytes(usize_bytes)); } - - new_path_vec.push(component); - - Ok(PathRc(new_path_vec.into())) - } - - fn components( - &self, - ) -> impl DoubleEndedIterator + ExactSizeIterator - { - return self.0.iter(); - } - - fn component_count(&self) -> usize { - self.0.len() } } -impl Ord for PathRc { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - for (my_comp, your_comp) in self.components().zip(other.components()) { - let comparison = my_comp.cmp(your_comp); - - match comparison { - std::cmp::Ordering::Equal => { /* Continue */ } - _ => return comparison, - } - } - - self.component_count().cmp(&other.component_count()) - } +// #[derive(Debug)] +// /// An error indicating a [`PathComponent`'s bytestring is too long. +// pub struct ComponentTooLongError; + +// /// A bytestring representing an individual component of a [`Path`]. Provides access to the raw bytes via the [`AsRef`] trait. +// /// +// /// ## Implementation notes +// /// +// /// - This trait provides an immutable interface, so cloning of a [`PathComponent`] implementation is expected to be cheap. +// /// - Implementations of the [`Ord`] trait must correspond to lexicographically comparing the AsRef bytes. +// pub trait PathComponent: Eq + AsRef<[u8]> + Clone + PartialOrd + Ord { +// /// The maximum bytelength of a path component, corresponding to Willow's [`max_component_length`](https://willowprotocol.org/specs/data-model/index.html#max_component_length) parameter. +// const MAX_COMPONENT_LENGTH: usize; + +// /// Construct a new [`PathComponent`] from the provided slice, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. +// fn new(components: &[u8]) -> Result; + +// /// Construct a new [`PathComponent`] from the concatenation of `head` and the `tail`, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. +// /// +// /// This operation occurs when computing prefix successors, and the default implementation needs to perform an allocation. Implementers of this trait can override this with something more efficient if possible. +// fn new_with_tail(head: &[u8], tail: u8) -> Result { +// let mut vec = Vec::with_capacity(head.len() + 1); +// vec.extend_from_slice(head); +// vec.push(tail); + +// Self::new(&vec) +// } + +// /// Return a new [`PathComponent`] which corresponds to the empty string. +// fn empty() -> Self { +// Self::new(&[]).unwrap() +// } + +// /// The length of the component's `as_ref` bytes. +// fn len(&self) -> usize { +// self.as_ref().len() +// } + +// /// Whether the component is an empty bytestring or not. +// fn is_empty(&self) -> bool { +// self.len() == 0 +// } + +// /// Try to append a zero byte to the end of the component. +// /// Return `None` if the resulting component would be too long. +// fn try_append_zero_byte(&self) -> Option { +// if self.len() == Self::MAX_COMPONENT_LENGTH { +// return None; +// } + +// let mut new_component_vec = Vec::with_capacity(self.len() + 1); + +// new_component_vec.extend_from_slice(self.as_ref()); +// new_component_vec.push(0); + +// Some(Self::new(&new_component_vec).unwrap()) +// } + +// /// Create a new copy which differs at index `i`. +// /// Implementers may panic if `i` is out of bound. +// fn set_byte(&self, i: usize, value: u8) -> Self; + +// /// Interpret the component as a binary number, and increment that number by 1. +// /// If doing so would increase the bytelength of the component, return `None`. +// fn try_increment_fixed_width(&self) -> Option { +// // Wish we could avoid this allocation somehow. +// let mut new_component = self.clone(); + +// for i in (0..self.len()).rev() { +// let byte = self.as_ref()[i]; + +// if byte == 255 { +// new_component = new_component.set_byte(i, 0); +// } else { +// return Some(new_component.set_byte(i, byte + 1)); +// } +// } + +// None +// } + +// /// Return the least component which is greater than `self` but which is not prefixed by `self`. +// fn prefix_successor(&self) -> Option { +// for i in (0..self.len()).rev() { +// if self.as_ref()[i] != 255 { +// // Since we are not adjusting the length of the component this will always succeed. +// return Some( +// Self::new_with_tail(&self.as_ref()[0..i], self.as_ref()[i] + 1).unwrap(), +// ); +// } +// } + +// None +// } +// } + +// /// A sequence of [`PathComponent`], used to name and query entries in [Willow's data model](https://willowprotocol.org/specs/data-model/index.html#data_model). +// /// +// pub trait Path: PartialEq + Eq + PartialOrd + Ord + Clone { +// type Component: PathComponent; + +// /// The maximum number of [`PathComponent`] a [`Path`] may have, corresponding to Willow's [`max_component_count`](https://willowprotocol.org/specs/data-model/index.html#max_component_count) parameter +// const MAX_COMPONENT_COUNT: usize; +// /// The maximum total number of bytes a [`Path`] may have, corresponding to Willow's [`max_path_length`](https://willowprotocol.org/specs/data-model/index.html#max_path_length) parameter +// const MAX_PATH_LENGTH: usize; + +// /// Contruct a new [`Path`] from a slice of [`PathComponent`], or return a [`InvalidPathError`] if the resulting path would be too long or have too many components. +// fn new(components: &[Self::Component]) -> Result; + +// /// Construct a new [`Path`] with no components. +// fn empty() -> Self; + +// /// Return a new [`Path`] with the components of this path suffixed with the given [`PathComponent`](PathComponent), or return [`InvalidPathError`] if the resulting path would be invalid in some way. +// fn append(&self, component: Self::Component) -> Result; + +// /// Return an iterator of all this path's components. +// /// +// /// The `.len` method of the returned [`ExactSizeIterator`] must coincide with [`Self::component_count`]. +// fn components( +// &self, +// ) -> impl DoubleEndedIterator + ExactSizeIterator; + +// /// Return the number of [`PathComponent`] in the path. +// fn component_count(&self) -> usize; + +// /// Return whether the path has any [`PathComponent`] or not. +// fn is_empty(&self) -> bool { +// self.component_count() == 0 +// } + +// /// Create a new [`Path`] by taking the first `length` components of this path. +// fn create_prefix(&self, length: usize) -> Self; + +// /// Return all possible prefixes of a path, including the empty path and the path itself. +// fn all_prefixes(&self) -> impl Iterator { +// let self_len = self.components().count(); + +// (0..=self_len).map(|i| self.create_prefix(i)) +// } + +// /// Test whether this path is a prefix of the given path. +// /// Paths are always a prefix of themselves. +// fn is_prefix_of(&self, other: &Self) -> bool { +// for (comp_a, comp_b) in self.components().zip(other.components()) { +// if comp_a != comp_b { +// return false; +// } +// } + +// self.component_count() <= other.component_count() +// } + +// /// Test whether this path is prefixed by the given path. +// /// Paths are always a prefix of themselves. +// fn is_prefixed_by(&self, other: &Self) -> bool { +// other.is_prefix_of(self) +// } + +// /// Return the longest common prefix of this path and the given path. +// fn longest_common_prefix(&self, other: &Self) -> Self { +// let mut lcp_len = 0; + +// for (comp_a, comp_b) in self.components().zip(other.components()) { +// if comp_a != comp_b { +// break; +// } + +// lcp_len += 1 +// } + +// self.create_prefix(lcp_len) +// } + +// /// Return the least path which is greater than `self`, or return `None` if `self` is the greatest possible path. +// fn successor(&self) -> Option { +// if self.component_count() == 0 { +// let new_component = Self::Component::new(&[]).ok()?; +// return Self::new(&[new_component]).ok(); +// } + +// // Try and add an empty component. +// if let Ok(path) = self.append(Self::Component::empty()) { +// return Some(path); +// } + +// for (i, component) in self.components().enumerate().rev() { +// // Try and do the *next* simplest thing (add a 0 byte to the component). +// if let Some(component) = component.try_append_zero_byte() { +// if let Ok(path) = self.create_prefix(i).append(component) { +// return Some(path); +// } +// } + +// // Otherwise we need to increment the component fixed-width style! +// if let Some(incremented_component) = component.try_increment_fixed_width() { +// // We can unwrap here because neither the max path length, component count, or component length has changed. +// return Some(self.create_prefix(i).append(incremented_component).unwrap()); +// } +// } + +// None +// } + +// /// Return the least path that is greater than `self` and which is not prefixed by `self`, or `None` if `self` is the empty path *or* if `self` is the greatest path. +// fn successor_of_prefix(&self) -> Option { +// for (i, component) in self.components().enumerate().rev() { +// if let Some(successor_comp) = component.try_append_zero_byte() { +// if let Ok(path) = self.create_prefix(i).append(successor_comp) { +// return Some(path); +// } +// } + +// if let Some(successor_comp) = component.prefix_successor() { +// return self.create_prefix(i).append(successor_comp).ok(); +// } +// } + +// None +// } +// } + +// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +// pub struct PathComponentBox(Box<[u8]>); + +// /// An implementation of [`PathComponent`] for [`PathComponentBox`]. +// /// +// /// ## Type parameters +// /// +// /// - `MCL`: A [`usize`] used as [`PathComponent::MAX_COMPONENT_LENGTH`]. +// impl PathComponent for PathComponentBox { +// const MAX_COMPONENT_LENGTH: usize = MCL; + +// /// Create a new component by cloning and appending all bytes from the slice into a [`Vec`], or return a [`ComponentTooLongError`] if the bytelength of the slice exceeds [`PathComponent::MAX_COMPONENT_LENGTH`]. +// fn new(bytes: &[u8]) -> Result { +// if bytes.len() > Self::MAX_COMPONENT_LENGTH { +// return Err(ComponentTooLongError); +// } + +// Ok(Self(bytes.into())) +// } + +// fn len(&self) -> usize { +// self.0.len() +// } + +// fn is_empty(&self) -> bool { +// self.0.is_empty() +// } + +// fn set_byte(&self, i: usize, value: u8) -> Self { +// let mut new_component = self.clone(); + +// new_component.0[i] = value; + +// new_component +// } +// } + +// impl AsRef<[u8]> for PathComponentBox { +// fn as_ref(&self) -> &[u8] { +// self.0.as_ref() +// } +// } + +// impl<'a, const MCL: usize> Arbitrary<'a> for PathComponentBox { +// fn arbitrary(u: &mut Unstructured<'a>) -> Result { +// let boxx: Box<[u8]> = Arbitrary::arbitrary(u)?; +// Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) +// } + +// #[inline] +// fn size_hint(depth: usize) -> (usize, Option) { +// as Arbitrary<'a>>::size_hint(depth) +// } +// } + +// #[derive(Debug, PartialEq, Eq, Clone)] +// /// A cheaply cloneable [`Path`] using a `Rc<[PathComponentBox]>`. +// /// While cloning is cheap, operations which return modified forms of the path (e.g. [`Path::append`]) are not, as they have to clone and adjust the contents of the underlying [`Rc`]. +// pub struct PathRc( +// Rc<[PathComponentBox]>, +// ); + +// impl Path for PathRc { +// type Component = PathComponentBox; + +// const MAX_COMPONENT_COUNT: usize = MCC; +// const MAX_PATH_LENGTH: usize = MPL; + +// fn new(components: &[Self::Component]) -> Result { +// if components.len() > Self::MAX_COMPONENT_COUNT { +// return Err(InvalidPathError::TooManyComponents); +// }; + +// let mut path_vec = Vec::new(); +// let mut total_length = 0; + +// for component in components { +// total_length += component.len(); + +// if total_length > Self::MAX_PATH_LENGTH { +// return Err(InvalidPathError::PathTooLong); +// } else { +// path_vec.push(component.clone()); +// } +// } + +// Ok(PathRc(path_vec.into())) +// } + +// fn empty() -> Self { +// PathRc(Vec::new().into()) +// } + +// fn create_prefix(&self, length: usize) -> Self { +// if length == 0 { +// return Self::empty(); +// } + +// let until = core::cmp::min(length, self.0.len()); +// let slice = &self.0[0..until]; + +// Path::new(slice).unwrap() +// } + +// fn append(&self, component: Self::Component) -> Result { +// let total_component_count = self.0.len(); + +// if total_component_count + 1 > MCC { +// return Err(InvalidPathError::TooManyComponents); +// } + +// let total_path_length = self.0.iter().fold(0, |acc, item| acc + item.0.len()); + +// if total_path_length + component.as_ref().len() > MPL { +// return Err(InvalidPathError::PathTooLong); +// } + +// let mut new_path_vec = Vec::new(); + +// for component in self.components() { +// new_path_vec.push(component.clone()) +// } + +// new_path_vec.push(component); + +// Ok(PathRc(new_path_vec.into())) +// } + +// fn components( +// &self, +// ) -> impl DoubleEndedIterator + ExactSizeIterator +// { +// return self.0.iter(); +// } + +// fn component_count(&self) -> usize { +// self.0.len() +// } +// } + +// impl Ord for PathRc { +// fn cmp(&self, other: &Self) -> std::cmp::Ordering { +// for (my_comp, your_comp) in self.components().zip(other.components()) { +// let comparison = my_comp.cmp(your_comp); + +// match comparison { +// std::cmp::Ordering::Equal => { /* Continue */ } +// _ => return comparison, +// } +// } + +// self.component_count().cmp(&other.component_count()) +// } +// } + +// impl PartialOrd for PathRc { +// fn partial_cmp(&self, other: &Self) -> Option { +// Some(self.cmp(other)) +// } +// } + +// impl<'a, const MCL: usize, const MCC: usize, const MPL: usize> Arbitrary<'a> +// for PathRc +// { +// fn arbitrary(u: &mut Unstructured<'a>) -> Result { +// let boxx: Box<[PathComponentBox]> = Arbitrary::arbitrary(u)?; +// Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) +// } + +// #[inline] +// fn size_hint(depth: usize) -> (usize, Option) { +// as Arbitrary<'a>>::size_hint(depth) +// } +// } + +// #[cfg(test)] +// mod tests { +// use std::cmp::Ordering::Less; + +// use super::*; + +// const MCL: usize = 8; +// const MCC: usize = 4; +// const MPL: usize = 16; + +// #[test] +// fn empty() { +// let empty_path = PathRc::::empty(); + +// assert_eq!(empty_path.components().count(), 0); +// } + +// #[test] +// fn new() { +// let component_too_long = +// PathComponentBox::::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'z']); + +// assert!(matches!(component_too_long, Err(ComponentTooLongError))); + +// let too_many_components = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'z']).unwrap(), +// ]); + +// assert!(matches!( +// too_many_components, +// Err(InvalidPathError::TooManyComponents) +// )); + +// let path_too_long = PathRc::::new(&[ +// PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), +// PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), +// PathComponentBox::new(&[b'z']).unwrap(), +// ]); + +// assert!(matches!(path_too_long, Err(InvalidPathError::PathTooLong))); +// } + +// #[test] +// fn append() { +// let path = PathRc::::empty(); + +// let r1 = path.append(PathComponentBox::new(&[b'a']).unwrap()); +// assert!(r1.is_ok()); +// let p1 = r1.unwrap(); +// assert_eq!(p1.components().count(), 1); + +// let r2 = p1.append(PathComponentBox::new(&[b'b']).unwrap()); +// assert!(r2.is_ok()); +// let p2 = r2.unwrap(); +// assert_eq!(p2.components().count(), 2); + +// let r3 = p2.append(PathComponentBox::new(&[b'c']).unwrap()); +// assert!(r3.is_ok()); +// let p3 = r3.unwrap(); +// assert_eq!(p3.components().count(), 3); + +// let r4 = p3.append(PathComponentBox::new(&[b'd']).unwrap()); +// assert!(r4.is_ok()); +// let p4 = r4.unwrap(); +// assert_eq!(p4.components().count(), 4); + +// let r5 = p4.append(PathComponentBox::new(&[b'z']).unwrap()); +// assert!(r5.is_err()); + +// let collected = p4 +// .components() +// .map(|comp| comp.as_ref()) +// .collect::>(); + +// assert_eq!(collected, vec![[b'a'], [b'b'], [b'c'], [b'd'],]) +// } + +// #[test] +// fn prefix() { +// let path = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap(), +// ]) +// .unwrap(); + +// let prefix0 = path.create_prefix(0); + +// assert_eq!(prefix0, PathRc::empty()); + +// let prefix1 = path.create_prefix(1); + +// assert_eq!( +// prefix1, +// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() +// ); + +// let prefix2 = path.create_prefix(2); + +// assert_eq!( +// prefix2, +// PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap() +// ]) +// .unwrap() +// ); + +// let prefix3 = path.create_prefix(3); + +// assert_eq!( +// prefix3, +// PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap() +// ]) +// .unwrap() +// ); + +// let prefix4 = path.create_prefix(4); + +// assert_eq!( +// prefix4, +// PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap() +// ]) +// .unwrap() +// ) +// } + +// #[test] +// fn prefixes() { +// let path = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap(), +// ]) +// .unwrap(); + +// let prefixes: Vec> = path.all_prefixes().collect(); + +// assert_eq!( +// prefixes, +// vec![ +// PathRc::::new(&[]).unwrap(), +// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), +// PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap() +// ]) +// .unwrap(), +// PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap() +// ]) +// .unwrap(), +// ] +// ) +// } + +// #[test] +// fn is_prefix_of() { +// let path_a = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// ]) +// .unwrap(); + +// let path_b = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap(), +// ]) +// .unwrap(); + +// let path_c = PathRc::::new(&[ +// PathComponentBox::new(&[b'x']).unwrap(), +// PathComponentBox::new(&[b'y']).unwrap(), +// PathComponentBox::new(&[b'z']).unwrap(), +// ]) +// .unwrap(); + +// assert!(path_a.is_prefix_of(&path_b)); +// assert!(!path_a.is_prefix_of(&path_c)); + +// let path_d = PathRc::::new(&[PathComponentBox::new(&[]).unwrap()]).unwrap(); +// let path_e = PathRc::::new(&[PathComponentBox::new(&[0]).unwrap()]).unwrap(); + +// assert!(!path_d.is_prefix_of(&path_e)); + +// let empty_path = PathRc::empty(); + +// assert!(empty_path.is_prefix_of(&path_d)); +// } + +// #[test] +// fn is_prefixed_by() { +// let path_a = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// ]) +// .unwrap(); + +// let path_b = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap(), +// ]) +// .unwrap(); + +// let path_c = PathRc::::new(&[ +// PathComponentBox::new(&[b'x']).unwrap(), +// PathComponentBox::new(&[b'y']).unwrap(), +// PathComponentBox::new(&[b'z']).unwrap(), +// ]) +// .unwrap(); + +// assert!(path_b.is_prefixed_by(&path_a)); +// assert!(!path_c.is_prefixed_by(&path_a)); +// } + +// #[test] +// fn longest_common_prefix() { +// let path_a = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'x']).unwrap(), +// ]) +// .unwrap(); + +// let path_b = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'b']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap(), +// ]) +// .unwrap(); + +// let path_c = PathRc::::new(&[ +// PathComponentBox::new(&[b'x']).unwrap(), +// PathComponentBox::new(&[b'y']).unwrap(), +// PathComponentBox::new(&[b'z']).unwrap(), +// ]) +// .unwrap(); + +// let lcp_a_b = path_a.longest_common_prefix(&path_b); + +// assert_eq!( +// lcp_a_b, +// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() +// ); + +// let lcp_b_a = path_b.longest_common_prefix(&path_a); + +// assert_eq!(lcp_b_a, lcp_a_b); + +// let lcp_a_x = path_a.longest_common_prefix(&path_c); + +// assert_eq!(lcp_a_x, PathRc::empty()); + +// let path_d = PathRc::::new(&[ +// PathComponentBox::new(&[b'a']).unwrap(), +// PathComponentBox::new(&[b'x']).unwrap(), +// PathComponentBox::new(&[b'c']).unwrap(), +// ]) +// .unwrap(); + +// let lcp_b_d = path_b.longest_common_prefix(&path_d); + +// assert_eq!( +// lcp_b_d, +// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() +// ) +// } + +// fn make_test_path( +// vector: &Vec>, +// ) -> PathRc { +// let components: Vec<_> = vector +// .iter() +// .map(|bytes_vec| PathComponentBox::new(bytes_vec).expect("the component was too long")) +// .collect(); + +// PathRc::new(&components).expect("the path was invalid") +// } + +// #[test] +// fn ordering() { +// let test_vector = vec![(vec![vec![0, 0], vec![]], vec![vec![0, 0, 0]], Less)]; + +// for (a, b, expected) in test_vector { +// let path_a: PathRc<3, 3, 3> = make_test_path(&a); +// let path_b: PathRc<3, 3, 3> = make_test_path(&b); + +// let ordering = path_a.cmp(&path_b); + +// if ordering != expected { +// println!("a: {:?}", a); +// println!("b: {:?}", b); + +// assert_eq!(ordering, expected); +// } +// } +// } +// } + +/// Like core::iter::Chain, but implements ExactSizeIter if both components implement it. Panics if the resulting length overflows. +/// +/// Code liberally copy-pasted from the standard library. +pub struct ExactLengthChain { + a: Option, + b: Option, } -impl PartialOrd for PathRc { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) +impl ExactLengthChain { + fn new(a: A, b: B) -> ExactLengthChain { + ExactLengthChain { + a: Some(a), + b: Some(b), + } } } -impl<'a, const MCL: usize, const MCC: usize, const MPL: usize> Arbitrary<'a> - for PathRc +impl Iterator for ExactLengthChain +where + A: Iterator, + B: Iterator, { - fn arbitrary(u: &mut Unstructured<'a>) -> Result { - let boxx: Box<[PathComponentBox]> = Arbitrary::arbitrary(u)?; - Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) - } + type Item = A::Item; #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - as Arbitrary<'a>>::size_hint(depth) - } -} - -#[cfg(test)] -mod tests { - use std::cmp::Ordering::Less; - - use super::*; - - const MCL: usize = 8; - const MCC: usize = 4; - const MPL: usize = 16; - - #[test] - fn empty() { - let empty_path = PathRc::::empty(); - - assert_eq!(empty_path.components().count(), 0); - } - - #[test] - fn new() { - let component_too_long = - PathComponentBox::::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'z']); - - assert!(matches!(component_too_long, Err(ComponentTooLongError))); - - let too_many_components = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'z']).unwrap(), - ]); - - assert!(matches!( - too_many_components, - Err(InvalidPathError::TooManyComponents) - )); - - let path_too_long = PathRc::::new(&[ - PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), - PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), - PathComponentBox::new(&[b'z']).unwrap(), - ]); - - assert!(matches!(path_too_long, Err(InvalidPathError::PathTooLong))); - } - - #[test] - fn append() { - let path = PathRc::::empty(); - - let r1 = path.append(PathComponentBox::new(&[b'a']).unwrap()); - assert!(r1.is_ok()); - let p1 = r1.unwrap(); - assert_eq!(p1.components().count(), 1); - - let r2 = p1.append(PathComponentBox::new(&[b'b']).unwrap()); - assert!(r2.is_ok()); - let p2 = r2.unwrap(); - assert_eq!(p2.components().count(), 2); - - let r3 = p2.append(PathComponentBox::new(&[b'c']).unwrap()); - assert!(r3.is_ok()); - let p3 = r3.unwrap(); - assert_eq!(p3.components().count(), 3); - - let r4 = p3.append(PathComponentBox::new(&[b'd']).unwrap()); - assert!(r4.is_ok()); - let p4 = r4.unwrap(); - assert_eq!(p4.components().count(), 4); - - let r5 = p4.append(PathComponentBox::new(&[b'z']).unwrap()); - assert!(r5.is_err()); - - let collected = p4 - .components() - .map(|comp| comp.as_ref()) - .collect::>(); - - assert_eq!(collected, vec![[b'a'], [b'b'], [b'c'], [b'd'],]) - } - - #[test] - fn prefix() { - let path = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap(), - ]) - .unwrap(); - - let prefix0 = path.create_prefix(0); - - assert_eq!(prefix0, PathRc::empty()); - - let prefix1 = path.create_prefix(1); - - assert_eq!( - prefix1, - PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() - ); - - let prefix2 = path.create_prefix(2); - - assert_eq!( - prefix2, - PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap() - ]) - .unwrap() - ); - - let prefix3 = path.create_prefix(3); - - assert_eq!( - prefix3, - PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap() - ]) - .unwrap() - ); - - let prefix4 = path.create_prefix(4); - - assert_eq!( - prefix4, - PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap() - ]) - .unwrap() - ) - } - - #[test] - fn prefixes() { - let path = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap(), - ]) - .unwrap(); - - let prefixes: Vec> = path.all_prefixes().collect(); - - assert_eq!( - prefixes, - vec![ - PathRc::::new(&[]).unwrap(), - PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), - PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap() - ]) - .unwrap(), - PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap() - ]) - .unwrap(), - ] - ) - } - - #[test] - fn is_prefix_of() { - let path_a = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - ]) - .unwrap(); - - let path_b = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap(), - ]) - .unwrap(); - - let path_c = PathRc::::new(&[ - PathComponentBox::new(&[b'x']).unwrap(), - PathComponentBox::new(&[b'y']).unwrap(), - PathComponentBox::new(&[b'z']).unwrap(), - ]) - .unwrap(); - - assert!(path_a.is_prefix_of(&path_b)); - assert!(!path_a.is_prefix_of(&path_c)); - - let path_d = PathRc::::new(&[PathComponentBox::new(&[]).unwrap()]).unwrap(); - let path_e = PathRc::::new(&[PathComponentBox::new(&[0]).unwrap()]).unwrap(); - - assert!(!path_d.is_prefix_of(&path_e)); - - let empty_path = PathRc::empty(); - - assert!(empty_path.is_prefix_of(&path_d)); - } - - #[test] - fn is_prefixed_by() { - let path_a = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - ]) - .unwrap(); - - let path_b = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap(), - ]) - .unwrap(); - - let path_c = PathRc::::new(&[ - PathComponentBox::new(&[b'x']).unwrap(), - PathComponentBox::new(&[b'y']).unwrap(), - PathComponentBox::new(&[b'z']).unwrap(), - ]) - .unwrap(); - - assert!(path_b.is_prefixed_by(&path_a)); - assert!(!path_c.is_prefixed_by(&path_a)); + fn next(&mut self) -> Option { + and_then_or_clear(&mut self.a, Iterator::next).or_else(|| self.b.as_mut()?.next()) } - #[test] - fn longest_common_prefix() { - let path_a = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'x']).unwrap(), - ]) - .unwrap(); - - let path_b = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'b']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap(), - ]) - .unwrap(); - - let path_c = PathRc::::new(&[ - PathComponentBox::new(&[b'x']).unwrap(), - PathComponentBox::new(&[b'y']).unwrap(), - PathComponentBox::new(&[b'z']).unwrap(), - ]) - .unwrap(); - - let lcp_a_b = path_a.longest_common_prefix(&path_b); - - assert_eq!( - lcp_a_b, - PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() - ); - - let lcp_b_a = path_b.longest_common_prefix(&path_a); - - assert_eq!(lcp_b_a, lcp_a_b); - - let lcp_a_x = path_a.longest_common_prefix(&path_c); - - assert_eq!(lcp_a_x, PathRc::empty()); - - let path_d = PathRc::::new(&[ - PathComponentBox::new(&[b'a']).unwrap(), - PathComponentBox::new(&[b'x']).unwrap(), - PathComponentBox::new(&[b'c']).unwrap(), - ]) - .unwrap(); + fn size_hint(&self) -> (usize, Option) { + let (lower_a, higher_a) = self.a.as_ref().map_or((0, None), |a| a.size_hint()); + let (lower_b, higher_b) = self.b.as_ref().map_or((0, None), |b| b.size_hint()); - let lcp_b_d = path_b.longest_common_prefix(&path_d); + let higher = match (higher_a, higher_b) { + (Some(a), Some(b)) => Some( + a.checked_add(b) + .expect("Some of lengths of two iterators must not overflow."), + ), + _ => None, + }; - assert_eq!( - lcp_b_d, - PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() - ) + return (lower_a + lower_b, higher); } +} - fn make_test_path( - vector: &Vec>, - ) -> PathRc { - let components: Vec<_> = vector - .iter() - .map(|bytes_vec| PathComponentBox::new(bytes_vec).expect("the component was too long")) - .collect(); - - PathRc::new(&components).expect("the path was invalid") +impl DoubleEndedIterator for ExactLengthChain +where + A: DoubleEndedIterator, + B: DoubleEndedIterator, +{ + #[inline] + fn next_back(&mut self) -> Option { + and_then_or_clear(&mut self.b, |b| b.next_back()).or_else(|| self.a.as_mut()?.next_back()) } +} - #[test] - fn ordering() { - let test_vector = vec![(vec![vec![0, 0], vec![]], vec![vec![0, 0, 0]], Less)]; - - for (a, b, expected) in test_vector { - let path_a: PathRc<3, 3, 3> = make_test_path(&a); - let path_b: PathRc<3, 3, 3> = make_test_path(&b); - - let ordering = path_a.cmp(&path_b); - - if ordering != expected { - println!("a: {:?}", a); - println!("b: {:?}", b); +impl ExactSizeIterator for ExactLengthChain +where + A: ExactSizeIterator, + B: ExactSizeIterator, +{ +} - assert_eq!(ordering, expected); - } - } +#[inline] +fn and_then_or_clear(opt: &mut Option, f: impl FnOnce(&mut T) -> Option) -> Option { + let x = f(opt.as_mut()?); + if x.is_none() { + *opt = None; } + x } From 8de3cd00fbb9aedcd22a391697340477bd09f757 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Wed, 10 Jul 2024 18:03:04 +0200 Subject: [PATCH 02/14] Add prefix functionality --- data-model/src/path.rs | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/data-model/src/path.rs b/data-model/src/path.rs index 751a2e5..b9fc9a7 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -62,6 +62,7 @@ pub enum InvalidPathError { /// An immutable Willow [path](https://willowprotocol.org/specs/data-model/index.html#Path). Thread-safe, cheap to clone, cheap to take prefixes of. /// /// Enforces that each component has a length of at most `MCL` ([**m**ax\_**c**omponent\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_component_length)), that each path has at most `MCC` ([**m**ax\_**c**omponent\_**c**count](https://willowprotocol.org/specs/data-model/index.html#max_component_count)) components, and that the total size in bytes of all components is at most `MPL` ([**m**ax\_**p**ath\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_path_length)). +// TODO trait impls pub struct Path { /// The data of the underlying path. data: HeapEncoding, @@ -278,6 +279,86 @@ impl Path { self.get_component(i).unwrap() // Only `None` if `i >= self.get_component_count()` }) } + + /// Create a new path that consists of the first `length` components. More efficient than creating a new [`Path`] from scratch. + /// + /// Returns `None` if `length` is greater than `self.get_component_count()`. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn create_prefix(&self, length: usize) -> Option { + if length > self.get_component_count() { + return None; + } else { + return Some(Self { + data: self.data.clone(), + number_of_components: length, + }); + } + } + + /// Create a new path that consists of the first `length` components. More efficient than creating a new [`Path`] from scratch. + /// + /// Undefined behaviour if `length` is greater than `self.get_component_count()`. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub unsafe fn create_prefix_unchecked(&self, length: usize) -> Self { + Self { + data: self.data.clone(), + number_of_components: length, + } + } + + /// Create an iterator over all prefixes of this path (including th empty path and the path itself). + /// + /// Stepping the iterator takes `O(1)` time and performs no memory allocations. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn all_prefixes(&self) -> impl DoubleEndedIterator + '_ { + (0..=self.get_component_count()).map(|i| { + unsafe { + self.create_prefix_unchecked(i) // safe to call for i <= self.get_component_count() + } + }) + } + + /// Test whether this path is a prefix of the given path. + /// Paths are always a prefix of themselves, and the empty path is a prefix of every path. + pub fn is_prefix_of(&self, other: &Self) -> bool { + for (comp_a, comp_b) in self.components().zip(other.components()) { + if comp_a != comp_b { + return false; + } + } + + self.get_component_count() <= other.get_component_count() + } + + /// Test whether this path is prefixed by the given path. + /// Paths are always a prefix of themselves. + pub fn is_prefixed_by(&self, other: &Self) -> bool { + other.is_prefix_of(self) + } + + /// Return the longest common prefix of this path and the given path. + pub fn longest_common_prefix(&self, other: &Self) -> Self { + let mut lcp_len = 0; + + for (comp_a, comp_b) in self.components().zip(other.components()) { + if comp_a != comp_b { + break; + } + + lcp_len += 1 + } + + self.create_prefix(lcp_len).unwrap() // zip ensures that lcp_len self.get_component_count() + } } /// Efficient, heap-allocated storage of a path encoding: @@ -288,6 +369,7 @@ impl Path { /// /// Note that these are not guaranteed to fulfil alignment requirements of usize, so we need to be careful in how we access these. /// Always use the methods on this struct for that reason. +#[derive(Clone)] struct HeapEncoding(Bytes); // All offsets are in bytes, unless otherwise specified. From 68a2c4a0aff518651cdb4eedb2855230e4186bdf Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Fri, 12 Jul 2024 17:37:16 +0200 Subject: [PATCH 03/14] Start implementing tests --- data-model/src/lib.rs | 2 + data-model/src/path.rs | 794 ++-------------- fuzz/Cargo.toml | 9 +- fuzz/fuzz_targets/path.rs | 33 + fuzz/fuzz_targets/path_successor.rs | 4 +- fuzz/fuzz_targets/path_successor_even_more.rs | 4 +- fuzz/fuzz_targets/path_successor_more.rs | 4 +- fuzz/fuzz_targets/successor_of_prefix.rs | 4 +- .../successor_of_prefix_even_more.rs | 4 +- fuzz/fuzz_targets/successor_of_prefix_more.rs | 4 +- fuzz/src/lib.rs | 95 +- fuzz/src/path.rs | 860 ++++++++++++++++++ 12 files changed, 986 insertions(+), 831 deletions(-) create mode 100644 fuzz/fuzz_targets/path.rs create mode 100644 fuzz/src/path.rs diff --git a/data-model/src/lib.rs b/data-model/src/lib.rs index c2df1a4..4239f81 100644 --- a/data-model/src/lib.rs +++ b/data-model/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(debug_closure_helpers)] + // pub mod entry; // pub mod grouping; // pub mod parameters; diff --git a/data-model/src/path.rs b/data-model/src/path.rs index b9fc9a7..ddb8337 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -1,11 +1,13 @@ use core::borrow::Borrow; use core::convert::AsRef; +use core::fmt::Debug; +use core::hash::Hash; use core::iter; use core::mem::size_of; use core::ops::Deref; -use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured}; -use bytes::{Buf, BufMut, Bytes, BytesMut}; +// use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured}; +use bytes::{BufMut, Bytes, BytesMut}; /// A [component](https://willowprotocol.org/specs/data-model/index.html#Component) of a Willow Path. /// @@ -27,6 +29,10 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Component<'a, MAX_COMPONENT_LENGTH> pub unsafe fn new_unchecked(slice: &'a [u8]) -> Self { Self(slice) } + + pub fn into_inner(self) -> &'a [u8] { + return self.0; + } } impl<'a, const MAX_COMPONENT_LENGTH: usize> Deref for Component<'a, MAX_COMPONENT_LENGTH> { @@ -57,12 +63,11 @@ pub enum InvalidPathError { /// The path has too many components. TooManyComponents, } -// TODO trait impls -/// An immutable Willow [path](https://willowprotocol.org/specs/data-model/index.html#Path). Thread-safe, cheap to clone, cheap to take prefixes of. +/// An immutable Willow [path](https://willowprotocol.org/specs/data-model/index.html#Path). Thread-safe, cheap to clone, cheap to take prefixes of, expensive to append to. /// /// Enforces that each component has a length of at most `MCL` ([**m**ax\_**c**omponent\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_component_length)), that each path has at most `MCC` ([**m**ax\_**c**omponent\_**c**count](https://willowprotocol.org/specs/data-model/index.html#max_component_count)) components, and that the total size in bytes of all components is at most `MPL` ([**m**ax\_**p**ath\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_path_length)). -// TODO trait impls +#[derive(Clone)] pub struct Path { /// The data of the underlying path. data: HeapEncoding, @@ -93,16 +98,22 @@ impl Path { /// #### Complexity /// /// Runs in `O(n)`, where `n` is the length of the component. Performs a single allocation of `O(n)` bytes. - pub fn new_singleton<'a>(comp: Component<'a, MCL>) -> Self { - let mut buf = BytesMut::with_capacity((2 * size_of::()) + comp.len()); - buf.extend_from_slice(&(1usize.to_ne_bytes())[..]); - buf.extend_from_slice(&(comp.len().to_ne_bytes())[..]); - buf.extend_from_slice(comp.as_ref()); + pub fn new_singleton<'a>(comp: Component<'a, MCL>) -> Result { + if 1 > MCC { + return Err(InvalidPathError::TooManyComponents); + } else if comp.len() > MPL { + return Err(InvalidPathError::PathTooLong); + } else { + let mut buf = BytesMut::with_capacity((2 * size_of::()) + comp.len()); + buf.extend_from_slice(&(1usize.to_ne_bytes())[..]); + buf.extend_from_slice(&(comp.len().to_ne_bytes())[..]); + buf.extend_from_slice(comp.as_ref()); - return Path { - data: HeapEncoding(buf.freeze()), - number_of_components: 1, - }; + return Ok(Path { + data: HeapEncoding(buf.freeze()), + number_of_components: 1, + }); + } } /// Construct a path of known total length from an [`ExactSizeIterator`][core::iter::ExactSizeIterator] of components. @@ -357,7 +368,50 @@ impl Path { lcp_len += 1 } - self.create_prefix(lcp_len).unwrap() // zip ensures that lcp_len self.get_component_count() + self.create_prefix(lcp_len).unwrap() // zip ensures that lcp_len <= self.get_component_count() + } +} + +impl PartialEq for Path { + fn eq(&self, other: &Self) -> bool { + if self.number_of_components != other.number_of_components { + return false; + } else { + return self.components().eq(other.components()); + } + } +} + +impl Eq for Path {} + +impl Hash for Path { + fn hash(&self, state: &mut H) { + self.number_of_components.hash(state); + + for comp in self.components() { + comp.hash(state); + } + } +} + +impl PartialOrd for Path { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Compare paths lexicogrphically, since that is the path ordering that the Willow spec always uses. +impl Ord for Path { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + return self.components().cmp(other.components()); + } +} + +impl Debug for Path { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let data_vec: Vec<_> = self.components().collect(); + + f.debug_tuple("Path").field(&data_vec).finish() } } @@ -421,7 +475,7 @@ impl HeapEncoding { // So we first copy the bytes onto the heap, then construct a usize from it. let mut usize_bytes = [0u8; size_of::()]; - if self.0.len() <= end { + if self.0.len() < end { return None; } else { usize_bytes.copy_from_slice(&self.0[offset_in_bytes..end]); @@ -430,714 +484,6 @@ impl HeapEncoding { } } -// #[derive(Debug)] -// /// An error indicating a [`PathComponent`'s bytestring is too long. -// pub struct ComponentTooLongError; - -// /// A bytestring representing an individual component of a [`Path`]. Provides access to the raw bytes via the [`AsRef`] trait. -// /// -// /// ## Implementation notes -// /// -// /// - This trait provides an immutable interface, so cloning of a [`PathComponent`] implementation is expected to be cheap. -// /// - Implementations of the [`Ord`] trait must correspond to lexicographically comparing the AsRef bytes. -// pub trait PathComponent: Eq + AsRef<[u8]> + Clone + PartialOrd + Ord { -// /// The maximum bytelength of a path component, corresponding to Willow's [`max_component_length`](https://willowprotocol.org/specs/data-model/index.html#max_component_length) parameter. -// const MAX_COMPONENT_LENGTH: usize; - -// /// Construct a new [`PathComponent`] from the provided slice, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. -// fn new(components: &[u8]) -> Result; - -// /// Construct a new [`PathComponent`] from the concatenation of `head` and the `tail`, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. -// /// -// /// This operation occurs when computing prefix successors, and the default implementation needs to perform an allocation. Implementers of this trait can override this with something more efficient if possible. -// fn new_with_tail(head: &[u8], tail: u8) -> Result { -// let mut vec = Vec::with_capacity(head.len() + 1); -// vec.extend_from_slice(head); -// vec.push(tail); - -// Self::new(&vec) -// } - -// /// Return a new [`PathComponent`] which corresponds to the empty string. -// fn empty() -> Self { -// Self::new(&[]).unwrap() -// } - -// /// The length of the component's `as_ref` bytes. -// fn len(&self) -> usize { -// self.as_ref().len() -// } - -// /// Whether the component is an empty bytestring or not. -// fn is_empty(&self) -> bool { -// self.len() == 0 -// } - -// /// Try to append a zero byte to the end of the component. -// /// Return `None` if the resulting component would be too long. -// fn try_append_zero_byte(&self) -> Option { -// if self.len() == Self::MAX_COMPONENT_LENGTH { -// return None; -// } - -// let mut new_component_vec = Vec::with_capacity(self.len() + 1); - -// new_component_vec.extend_from_slice(self.as_ref()); -// new_component_vec.push(0); - -// Some(Self::new(&new_component_vec).unwrap()) -// } - -// /// Create a new copy which differs at index `i`. -// /// Implementers may panic if `i` is out of bound. -// fn set_byte(&self, i: usize, value: u8) -> Self; - -// /// Interpret the component as a binary number, and increment that number by 1. -// /// If doing so would increase the bytelength of the component, return `None`. -// fn try_increment_fixed_width(&self) -> Option { -// // Wish we could avoid this allocation somehow. -// let mut new_component = self.clone(); - -// for i in (0..self.len()).rev() { -// let byte = self.as_ref()[i]; - -// if byte == 255 { -// new_component = new_component.set_byte(i, 0); -// } else { -// return Some(new_component.set_byte(i, byte + 1)); -// } -// } - -// None -// } - -// /// Return the least component which is greater than `self` but which is not prefixed by `self`. -// fn prefix_successor(&self) -> Option { -// for i in (0..self.len()).rev() { -// if self.as_ref()[i] != 255 { -// // Since we are not adjusting the length of the component this will always succeed. -// return Some( -// Self::new_with_tail(&self.as_ref()[0..i], self.as_ref()[i] + 1).unwrap(), -// ); -// } -// } - -// None -// } -// } - -// /// A sequence of [`PathComponent`], used to name and query entries in [Willow's data model](https://willowprotocol.org/specs/data-model/index.html#data_model). -// /// -// pub trait Path: PartialEq + Eq + PartialOrd + Ord + Clone { -// type Component: PathComponent; - -// /// The maximum number of [`PathComponent`] a [`Path`] may have, corresponding to Willow's [`max_component_count`](https://willowprotocol.org/specs/data-model/index.html#max_component_count) parameter -// const MAX_COMPONENT_COUNT: usize; -// /// The maximum total number of bytes a [`Path`] may have, corresponding to Willow's [`max_path_length`](https://willowprotocol.org/specs/data-model/index.html#max_path_length) parameter -// const MAX_PATH_LENGTH: usize; - -// /// Contruct a new [`Path`] from a slice of [`PathComponent`], or return a [`InvalidPathError`] if the resulting path would be too long or have too many components. -// fn new(components: &[Self::Component]) -> Result; - -// /// Construct a new [`Path`] with no components. -// fn empty() -> Self; - -// /// Return a new [`Path`] with the components of this path suffixed with the given [`PathComponent`](PathComponent), or return [`InvalidPathError`] if the resulting path would be invalid in some way. -// fn append(&self, component: Self::Component) -> Result; - -// /// Return an iterator of all this path's components. -// /// -// /// The `.len` method of the returned [`ExactSizeIterator`] must coincide with [`Self::component_count`]. -// fn components( -// &self, -// ) -> impl DoubleEndedIterator + ExactSizeIterator; - -// /// Return the number of [`PathComponent`] in the path. -// fn component_count(&self) -> usize; - -// /// Return whether the path has any [`PathComponent`] or not. -// fn is_empty(&self) -> bool { -// self.component_count() == 0 -// } - -// /// Create a new [`Path`] by taking the first `length` components of this path. -// fn create_prefix(&self, length: usize) -> Self; - -// /// Return all possible prefixes of a path, including the empty path and the path itself. -// fn all_prefixes(&self) -> impl Iterator { -// let self_len = self.components().count(); - -// (0..=self_len).map(|i| self.create_prefix(i)) -// } - -// /// Test whether this path is a prefix of the given path. -// /// Paths are always a prefix of themselves. -// fn is_prefix_of(&self, other: &Self) -> bool { -// for (comp_a, comp_b) in self.components().zip(other.components()) { -// if comp_a != comp_b { -// return false; -// } -// } - -// self.component_count() <= other.component_count() -// } - -// /// Test whether this path is prefixed by the given path. -// /// Paths are always a prefix of themselves. -// fn is_prefixed_by(&self, other: &Self) -> bool { -// other.is_prefix_of(self) -// } - -// /// Return the longest common prefix of this path and the given path. -// fn longest_common_prefix(&self, other: &Self) -> Self { -// let mut lcp_len = 0; - -// for (comp_a, comp_b) in self.components().zip(other.components()) { -// if comp_a != comp_b { -// break; -// } - -// lcp_len += 1 -// } - -// self.create_prefix(lcp_len) -// } - -// /// Return the least path which is greater than `self`, or return `None` if `self` is the greatest possible path. -// fn successor(&self) -> Option { -// if self.component_count() == 0 { -// let new_component = Self::Component::new(&[]).ok()?; -// return Self::new(&[new_component]).ok(); -// } - -// // Try and add an empty component. -// if let Ok(path) = self.append(Self::Component::empty()) { -// return Some(path); -// } - -// for (i, component) in self.components().enumerate().rev() { -// // Try and do the *next* simplest thing (add a 0 byte to the component). -// if let Some(component) = component.try_append_zero_byte() { -// if let Ok(path) = self.create_prefix(i).append(component) { -// return Some(path); -// } -// } - -// // Otherwise we need to increment the component fixed-width style! -// if let Some(incremented_component) = component.try_increment_fixed_width() { -// // We can unwrap here because neither the max path length, component count, or component length has changed. -// return Some(self.create_prefix(i).append(incremented_component).unwrap()); -// } -// } - -// None -// } - -// /// Return the least path that is greater than `self` and which is not prefixed by `self`, or `None` if `self` is the empty path *or* if `self` is the greatest path. -// fn successor_of_prefix(&self) -> Option { -// for (i, component) in self.components().enumerate().rev() { -// if let Some(successor_comp) = component.try_append_zero_byte() { -// if let Ok(path) = self.create_prefix(i).append(successor_comp) { -// return Some(path); -// } -// } - -// if let Some(successor_comp) = component.prefix_successor() { -// return self.create_prefix(i).append(successor_comp).ok(); -// } -// } - -// None -// } -// } - -// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -// pub struct PathComponentBox(Box<[u8]>); - -// /// An implementation of [`PathComponent`] for [`PathComponentBox`]. -// /// -// /// ## Type parameters -// /// -// /// - `MCL`: A [`usize`] used as [`PathComponent::MAX_COMPONENT_LENGTH`]. -// impl PathComponent for PathComponentBox { -// const MAX_COMPONENT_LENGTH: usize = MCL; - -// /// Create a new component by cloning and appending all bytes from the slice into a [`Vec`], or return a [`ComponentTooLongError`] if the bytelength of the slice exceeds [`PathComponent::MAX_COMPONENT_LENGTH`]. -// fn new(bytes: &[u8]) -> Result { -// if bytes.len() > Self::MAX_COMPONENT_LENGTH { -// return Err(ComponentTooLongError); -// } - -// Ok(Self(bytes.into())) -// } - -// fn len(&self) -> usize { -// self.0.len() -// } - -// fn is_empty(&self) -> bool { -// self.0.is_empty() -// } - -// fn set_byte(&self, i: usize, value: u8) -> Self { -// let mut new_component = self.clone(); - -// new_component.0[i] = value; - -// new_component -// } -// } - -// impl AsRef<[u8]> for PathComponentBox { -// fn as_ref(&self) -> &[u8] { -// self.0.as_ref() -// } -// } - -// impl<'a, const MCL: usize> Arbitrary<'a> for PathComponentBox { -// fn arbitrary(u: &mut Unstructured<'a>) -> Result { -// let boxx: Box<[u8]> = Arbitrary::arbitrary(u)?; -// Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) -// } - -// #[inline] -// fn size_hint(depth: usize) -> (usize, Option) { -// as Arbitrary<'a>>::size_hint(depth) -// } -// } - -// #[derive(Debug, PartialEq, Eq, Clone)] -// /// A cheaply cloneable [`Path`] using a `Rc<[PathComponentBox]>`. -// /// While cloning is cheap, operations which return modified forms of the path (e.g. [`Path::append`]) are not, as they have to clone and adjust the contents of the underlying [`Rc`]. -// pub struct PathRc( -// Rc<[PathComponentBox]>, -// ); - -// impl Path for PathRc { -// type Component = PathComponentBox; - -// const MAX_COMPONENT_COUNT: usize = MCC; -// const MAX_PATH_LENGTH: usize = MPL; - -// fn new(components: &[Self::Component]) -> Result { -// if components.len() > Self::MAX_COMPONENT_COUNT { -// return Err(InvalidPathError::TooManyComponents); -// }; - -// let mut path_vec = Vec::new(); -// let mut total_length = 0; - -// for component in components { -// total_length += component.len(); - -// if total_length > Self::MAX_PATH_LENGTH { -// return Err(InvalidPathError::PathTooLong); -// } else { -// path_vec.push(component.clone()); -// } -// } - -// Ok(PathRc(path_vec.into())) -// } - -// fn empty() -> Self { -// PathRc(Vec::new().into()) -// } - -// fn create_prefix(&self, length: usize) -> Self { -// if length == 0 { -// return Self::empty(); -// } - -// let until = core::cmp::min(length, self.0.len()); -// let slice = &self.0[0..until]; - -// Path::new(slice).unwrap() -// } - -// fn append(&self, component: Self::Component) -> Result { -// let total_component_count = self.0.len(); - -// if total_component_count + 1 > MCC { -// return Err(InvalidPathError::TooManyComponents); -// } - -// let total_path_length = self.0.iter().fold(0, |acc, item| acc + item.0.len()); - -// if total_path_length + component.as_ref().len() > MPL { -// return Err(InvalidPathError::PathTooLong); -// } - -// let mut new_path_vec = Vec::new(); - -// for component in self.components() { -// new_path_vec.push(component.clone()) -// } - -// new_path_vec.push(component); - -// Ok(PathRc(new_path_vec.into())) -// } - -// fn components( -// &self, -// ) -> impl DoubleEndedIterator + ExactSizeIterator -// { -// return self.0.iter(); -// } - -// fn component_count(&self) -> usize { -// self.0.len() -// } -// } - -// impl Ord for PathRc { -// fn cmp(&self, other: &Self) -> std::cmp::Ordering { -// for (my_comp, your_comp) in self.components().zip(other.components()) { -// let comparison = my_comp.cmp(your_comp); - -// match comparison { -// std::cmp::Ordering::Equal => { /* Continue */ } -// _ => return comparison, -// } -// } - -// self.component_count().cmp(&other.component_count()) -// } -// } - -// impl PartialOrd for PathRc { -// fn partial_cmp(&self, other: &Self) -> Option { -// Some(self.cmp(other)) -// } -// } - -// impl<'a, const MCL: usize, const MCC: usize, const MPL: usize> Arbitrary<'a> -// for PathRc -// { -// fn arbitrary(u: &mut Unstructured<'a>) -> Result { -// let boxx: Box<[PathComponentBox]> = Arbitrary::arbitrary(u)?; -// Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) -// } - -// #[inline] -// fn size_hint(depth: usize) -> (usize, Option) { -// as Arbitrary<'a>>::size_hint(depth) -// } -// } - -// #[cfg(test)] -// mod tests { -// use std::cmp::Ordering::Less; - -// use super::*; - -// const MCL: usize = 8; -// const MCC: usize = 4; -// const MPL: usize = 16; - -// #[test] -// fn empty() { -// let empty_path = PathRc::::empty(); - -// assert_eq!(empty_path.components().count(), 0); -// } - -// #[test] -// fn new() { -// let component_too_long = -// PathComponentBox::::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'z']); - -// assert!(matches!(component_too_long, Err(ComponentTooLongError))); - -// let too_many_components = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'z']).unwrap(), -// ]); - -// assert!(matches!( -// too_many_components, -// Err(InvalidPathError::TooManyComponents) -// )); - -// let path_too_long = PathRc::::new(&[ -// PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), -// PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), -// PathComponentBox::new(&[b'z']).unwrap(), -// ]); - -// assert!(matches!(path_too_long, Err(InvalidPathError::PathTooLong))); -// } - -// #[test] -// fn append() { -// let path = PathRc::::empty(); - -// let r1 = path.append(PathComponentBox::new(&[b'a']).unwrap()); -// assert!(r1.is_ok()); -// let p1 = r1.unwrap(); -// assert_eq!(p1.components().count(), 1); - -// let r2 = p1.append(PathComponentBox::new(&[b'b']).unwrap()); -// assert!(r2.is_ok()); -// let p2 = r2.unwrap(); -// assert_eq!(p2.components().count(), 2); - -// let r3 = p2.append(PathComponentBox::new(&[b'c']).unwrap()); -// assert!(r3.is_ok()); -// let p3 = r3.unwrap(); -// assert_eq!(p3.components().count(), 3); - -// let r4 = p3.append(PathComponentBox::new(&[b'd']).unwrap()); -// assert!(r4.is_ok()); -// let p4 = r4.unwrap(); -// assert_eq!(p4.components().count(), 4); - -// let r5 = p4.append(PathComponentBox::new(&[b'z']).unwrap()); -// assert!(r5.is_err()); - -// let collected = p4 -// .components() -// .map(|comp| comp.as_ref()) -// .collect::>(); - -// assert_eq!(collected, vec![[b'a'], [b'b'], [b'c'], [b'd'],]) -// } - -// #[test] -// fn prefix() { -// let path = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap(), -// ]) -// .unwrap(); - -// let prefix0 = path.create_prefix(0); - -// assert_eq!(prefix0, PathRc::empty()); - -// let prefix1 = path.create_prefix(1); - -// assert_eq!( -// prefix1, -// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() -// ); - -// let prefix2 = path.create_prefix(2); - -// assert_eq!( -// prefix2, -// PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap() -// ]) -// .unwrap() -// ); - -// let prefix3 = path.create_prefix(3); - -// assert_eq!( -// prefix3, -// PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap() -// ]) -// .unwrap() -// ); - -// let prefix4 = path.create_prefix(4); - -// assert_eq!( -// prefix4, -// PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap() -// ]) -// .unwrap() -// ) -// } - -// #[test] -// fn prefixes() { -// let path = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap(), -// ]) -// .unwrap(); - -// let prefixes: Vec> = path.all_prefixes().collect(); - -// assert_eq!( -// prefixes, -// vec![ -// PathRc::::new(&[]).unwrap(), -// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), -// PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap() -// ]) -// .unwrap(), -// PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap() -// ]) -// .unwrap(), -// ] -// ) -// } - -// #[test] -// fn is_prefix_of() { -// let path_a = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// ]) -// .unwrap(); - -// let path_b = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap(), -// ]) -// .unwrap(); - -// let path_c = PathRc::::new(&[ -// PathComponentBox::new(&[b'x']).unwrap(), -// PathComponentBox::new(&[b'y']).unwrap(), -// PathComponentBox::new(&[b'z']).unwrap(), -// ]) -// .unwrap(); - -// assert!(path_a.is_prefix_of(&path_b)); -// assert!(!path_a.is_prefix_of(&path_c)); - -// let path_d = PathRc::::new(&[PathComponentBox::new(&[]).unwrap()]).unwrap(); -// let path_e = PathRc::::new(&[PathComponentBox::new(&[0]).unwrap()]).unwrap(); - -// assert!(!path_d.is_prefix_of(&path_e)); - -// let empty_path = PathRc::empty(); - -// assert!(empty_path.is_prefix_of(&path_d)); -// } - -// #[test] -// fn is_prefixed_by() { -// let path_a = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// ]) -// .unwrap(); - -// let path_b = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap(), -// ]) -// .unwrap(); - -// let path_c = PathRc::::new(&[ -// PathComponentBox::new(&[b'x']).unwrap(), -// PathComponentBox::new(&[b'y']).unwrap(), -// PathComponentBox::new(&[b'z']).unwrap(), -// ]) -// .unwrap(); - -// assert!(path_b.is_prefixed_by(&path_a)); -// assert!(!path_c.is_prefixed_by(&path_a)); -// } - -// #[test] -// fn longest_common_prefix() { -// let path_a = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'x']).unwrap(), -// ]) -// .unwrap(); - -// let path_b = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'b']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap(), -// ]) -// .unwrap(); - -// let path_c = PathRc::::new(&[ -// PathComponentBox::new(&[b'x']).unwrap(), -// PathComponentBox::new(&[b'y']).unwrap(), -// PathComponentBox::new(&[b'z']).unwrap(), -// ]) -// .unwrap(); - -// let lcp_a_b = path_a.longest_common_prefix(&path_b); - -// assert_eq!( -// lcp_a_b, -// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() -// ); - -// let lcp_b_a = path_b.longest_common_prefix(&path_a); - -// assert_eq!(lcp_b_a, lcp_a_b); - -// let lcp_a_x = path_a.longest_common_prefix(&path_c); - -// assert_eq!(lcp_a_x, PathRc::empty()); - -// let path_d = PathRc::::new(&[ -// PathComponentBox::new(&[b'a']).unwrap(), -// PathComponentBox::new(&[b'x']).unwrap(), -// PathComponentBox::new(&[b'c']).unwrap(), -// ]) -// .unwrap(); - -// let lcp_b_d = path_b.longest_common_prefix(&path_d); - -// assert_eq!( -// lcp_b_d, -// PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() -// ) -// } - -// fn make_test_path( -// vector: &Vec>, -// ) -> PathRc { -// let components: Vec<_> = vector -// .iter() -// .map(|bytes_vec| PathComponentBox::new(bytes_vec).expect("the component was too long")) -// .collect(); - -// PathRc::new(&components).expect("the path was invalid") -// } - -// #[test] -// fn ordering() { -// let test_vector = vec![(vec![vec![0, 0], vec![]], vec![vec![0, 0, 0]], Less)]; - -// for (a, b, expected) in test_vector { -// let path_a: PathRc<3, 3, 3> = make_test_path(&a); -// let path_b: PathRc<3, 3, 3> = make_test_path(&b); - -// let ordering = path_a.cmp(&path_b); - -// if ordering != expected { -// println!("a: {:?}", a); -// println!("b: {:?}", b); - -// assert_eq!(ordering, expected); -// } -// } -// } -// } - /// Like core::iter::Chain, but implements ExactSizeIter if both components implement it. Panics if the resulting length overflows. /// /// Code liberally copy-pasted from the standard library. diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 1bf7ea0..4a49005 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -8,11 +8,18 @@ edition = "2021" cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.4" +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } [dependencies.willow-data-model] path = "../data-model" +[[bin]] +name = "path" +path = "fuzz_targets/path.rs" +test = false +doc = false +bench = false + [[bin]] name = "path_successor" path = "fuzz_targets/path_successor.rs" diff --git a/fuzz/fuzz_targets/path.rs b/fuzz/fuzz_targets/path.rs new file mode 100644 index 0000000..f5568d6 --- /dev/null +++ b/fuzz/fuzz_targets/path.rs @@ -0,0 +1,33 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; + +fuzz_target!(|data: (CreatePath, CreatePath)| { + let (cp1, cp2) = data; + + let ctrl1 = create_path_rc::<100, 100, 100>(&cp1); + let ctrl2 = create_path_rc::<100, 100, 100>(&cp2); + + let p1 = create_path::<100, 100, 100>(&cp1); + let p2 = create_path::<100, 100, 100>(&cp2); + + match (ctrl1, p1) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl1), Ok(p1)) => { + match (ctrl2, p2) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl2), Ok(p2)) => { + assert_isomorphic_paths(&ctrl1, &ctrl2, &p1, &p2); + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } +}); diff --git a/fuzz/fuzz_targets/path_successor.rs b/fuzz/fuzz_targets/path_successor.rs index 88e4cf7..b9b2cbd 100644 --- a/fuzz/fuzz_targets/path_successor.rs +++ b/fuzz/fuzz_targets/path_successor.rs @@ -1,8 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use willow_data_model::path::*; -use willow_data_model_fuzz::test_successor; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; // MCL, MCC, MPL fuzz_target!(|data: (PathRc<3, 3, 3>, PathRc<3, 3, 3>)| { diff --git a/fuzz/fuzz_targets/path_successor_even_more.rs b/fuzz/fuzz_targets/path_successor_even_more.rs index 0217af3..40bdaa5 100644 --- a/fuzz/fuzz_targets/path_successor_even_more.rs +++ b/fuzz/fuzz_targets/path_successor_even_more.rs @@ -1,8 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use willow_data_model::path::*; -use willow_data_model_fuzz::test_successor; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; // MCL, MCC, MPL fuzz_target!(|data: (PathRc<4, 4, 16>, PathRc<4, 4, 16>)| { diff --git a/fuzz/fuzz_targets/path_successor_more.rs b/fuzz/fuzz_targets/path_successor_more.rs index 3caaf71..96d97a2 100644 --- a/fuzz/fuzz_targets/path_successor_more.rs +++ b/fuzz/fuzz_targets/path_successor_more.rs @@ -1,8 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use willow_data_model::path::*; -use willow_data_model_fuzz::test_successor; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; // MCL, MCC, MPL fuzz_target!(|data: (PathRc<3, 3, 3>, PathRc<3, 3, 3>)| { diff --git a/fuzz/fuzz_targets/successor_of_prefix.rs b/fuzz/fuzz_targets/successor_of_prefix.rs index 21d60db..11599b4 100644 --- a/fuzz/fuzz_targets/successor_of_prefix.rs +++ b/fuzz/fuzz_targets/successor_of_prefix.rs @@ -1,8 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use willow_data_model::path::*; -use willow_data_model_fuzz::test_successor_of_prefix; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; // MCL, MCC, MPL fuzz_target!(|data: (PathRc<2, 3, 3>, PathRc<2, 3, 3>)| { diff --git a/fuzz/fuzz_targets/successor_of_prefix_even_more.rs b/fuzz/fuzz_targets/successor_of_prefix_even_more.rs index e042c81..1c36dc4 100644 --- a/fuzz/fuzz_targets/successor_of_prefix_even_more.rs +++ b/fuzz/fuzz_targets/successor_of_prefix_even_more.rs @@ -1,8 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use willow_data_model::path::*; -use willow_data_model_fuzz::test_successor_of_prefix; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; // MCL, MCC, MPL fuzz_target!(|data: (PathRc<4, 4, 16>, PathRc<4, 4, 16>)| { diff --git a/fuzz/fuzz_targets/successor_of_prefix_more.rs b/fuzz/fuzz_targets/successor_of_prefix_more.rs index 1ad99d4..8db5e90 100644 --- a/fuzz/fuzz_targets/successor_of_prefix_more.rs +++ b/fuzz/fuzz_targets/successor_of_prefix_more.rs @@ -1,8 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use willow_data_model::path::*; -use willow_data_model_fuzz::test_successor_of_prefix; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; // MCL, MCC, MPL fuzz_target!(|data: (PathRc<3, 3, 3>, PathRc<3, 3, 3>)| { diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index 94fb6bc..4da9789 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -1,94 +1 @@ -use willow_data_model::path::*; - -pub fn test_successor( - baseline: PathRc, - candidate: PathRc, - max_path: PathRc, -) { - let successor = baseline.successor(); - - match successor { - None => { - if baseline != max_path { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", successor); - println!("candidate: {:?}", candidate); - println!("\n\n\n"); - panic!("returned None when the path was NOT the greatest path! BoooOOOoo") - } - } - Some(successor) => { - if successor <= baseline { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", successor); - println!("candidate: {:?}", candidate); - println!("\n\n\n"); - - panic!("successor was not greater than the path it was derived from! BooooOoooOOo") - } - - if candidate < successor && candidate > baseline { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", successor); - println!("candidate: {:?}", candidate); - println!("\n\n\n"); - - panic!("the successor generated was NOT the immediate successor! BooooOOOOo!") - } - } - } -} - -pub fn test_successor_of_prefix( - baseline: PathRc, - candidate: PathRc, - unsucceedable: &[PathRc], -) { - let prefix_successor = baseline.successor_of_prefix(); - - match prefix_successor { - None => { - if !unsucceedable.iter().any(|unsuc| unsuc == &baseline) { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); - println!("candidate: {:?}", candidate); - panic!("returned None when the path was NOT the greatest path! BoooOOOoo\n\n\n\n"); - } - } - Some(prefix_successor) => { - if prefix_successor <= baseline { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); - println!("candidate: {:?}", candidate); - panic!("the successor is meant to be greater than the baseline, but wasn't!! BOOOOOOOOO\n\n\n\n"); - } - - if prefix_successor.is_prefixed_by(&baseline) { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); - println!("candidate: {:?}", candidate); - panic!("successor was prefixed by the path it was derived from! BoooOOooOOooOo\n\n\n\n"); - } - - if !baseline.is_prefix_of(&candidate) - && candidate < prefix_successor - && candidate > baseline - { - println!("\n\n\n"); - println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); - println!("candidate: {:?}", candidate); - - panic!( - "the successor generated was NOT the immediate prefix successor! BooooOOOOo!\n\n\n\n" - ); - } - } - } -} +pub mod path; diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs new file mode 100644 index 0000000..bb16d4b --- /dev/null +++ b/fuzz/src/path.rs @@ -0,0 +1,860 @@ +use std::rc::Rc; + +use libfuzzer_sys::arbitrary::{self, Arbitrary, Error as ArbitraryError, Unstructured}; +use willow_data_model::path::{Component, InvalidPathError, Path}; + +/* +* A known-good, simple implementation of paths. Used in testing to compare the behaviour of the optimised imlpementation against it. +*/ + +#[derive(Debug)] +/// An error indicating a [`PathComponent`'s bytestring is too long. +pub struct ComponentTooLongError; + +/// A bytestring representing an individual component of a [`Path`]. Provides access to the raw bytes via the [`AsRef`] trait. +/// +/// ## Implementation notes +/// +/// - This trait provides an immutable interface, so cloning of a [`PathComponent`] implementation is expected to be cheap. +/// - Implementations of the [`Ord`] trait must correspond to lexicographically comparing the AsRef bytes. +pub trait PathComponent: Eq + AsRef<[u8]> + Clone + PartialOrd + Ord { + /// The maximum bytelength of a path component, corresponding to Willow's [`max_component_length`](https://willowprotocol.org/specs/data-model/index.html#max_component_length) parameter. + const MAX_COMPONENT_LENGTH: usize; + + /// Construct a new [`PathComponent`] from the provided slice, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. + fn new(components: &[u8]) -> Result; + + /// Construct a new [`PathComponent`] from the concatenation of `head` and the `tail`, or return a [`ComponentTooLongError`] if the resulting component would be longer than [`PathComponent::MAX_COMPONENT_LENGTH`]. + /// + /// This operation occurs when computing prefix successors, and the default implementation needs to perform an allocation. Implementers of this trait can override this with something more efficient if possible. + fn new_with_tail(head: &[u8], tail: u8) -> Result { + let mut vec = Vec::with_capacity(head.len() + 1); + vec.extend_from_slice(head); + vec.push(tail); + + Self::new(&vec) + } + + /// Return a new [`PathComponent`] which corresponds to the empty string. + fn empty() -> Self { + Self::new(&[]).unwrap() + } + + /// The length of the component's `as_ref` bytes. + fn len(&self) -> usize { + self.as_ref().len() + } + + /// Whether the component is an empty bytestring or not. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Try to append a zero byte to the end of the component. + /// Return `None` if the resulting component would be too long. + fn try_append_zero_byte(&self) -> Option { + if self.len() == Self::MAX_COMPONENT_LENGTH { + return None; + } + + let mut new_component_vec = Vec::with_capacity(self.len() + 1); + + new_component_vec.extend_from_slice(self.as_ref()); + new_component_vec.push(0); + + Some(Self::new(&new_component_vec).unwrap()) + } + + /// Create a new copy which differs at index `i`. + /// Implementers may panic if `i` is out of bound. + fn set_byte(&self, i: usize, value: u8) -> Self; + + /// Interpret the component as a binary number, and increment that number by 1. + /// If doing so would increase the bytelength of the component, return `None`. + fn try_increment_fixed_width(&self) -> Option { + // Wish we could avoid this allocation somehow. + let mut new_component = self.clone(); + + for i in (0..self.len()).rev() { + let byte = self.as_ref()[i]; + + if byte == 255 { + new_component = new_component.set_byte(i, 0); + } else { + return Some(new_component.set_byte(i, byte + 1)); + } + } + + None + } + + /// Return the least component which is greater than `self` but which is not prefixed by `self`. + fn prefix_successor(&self) -> Option { + for i in (0..self.len()).rev() { + if self.as_ref()[i] != 255 { + // Since we are not adjusting the length of the component this will always succeed. + return Some( + Self::new_with_tail(&self.as_ref()[0..i], self.as_ref()[i] + 1).unwrap(), + ); + } + } + + None + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PathComponentBox(Box<[u8]>); + +/// An implementation of [`PathComponent`] for [`PathComponentBox`]. +/// +/// ## Type parameters +/// +/// - `MCL`: A [`usize`] used as [`PathComponent::MAX_COMPONENT_LENGTH`]. +impl PathComponent for PathComponentBox { + const MAX_COMPONENT_LENGTH: usize = MCL; + + /// Create a new component by cloning and appending all bytes from the slice into a [`Vec`], or return a [`ComponentTooLongError`] if the bytelength of the slice exceeds [`PathComponent::MAX_COMPONENT_LENGTH`]. + fn new(bytes: &[u8]) -> Result { + if bytes.len() > Self::MAX_COMPONENT_LENGTH { + return Err(ComponentTooLongError); + } + + Ok(Self(bytes.into())) + } + + fn len(&self) -> usize { + self.0.len() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + + fn set_byte(&self, i: usize, value: u8) -> Self { + let mut new_component = self.clone(); + + new_component.0[i] = value; + + new_component + } +} + +impl AsRef<[u8]> for PathComponentBox { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl<'a, const MCL: usize> Arbitrary<'a> for PathComponentBox { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let boxx: Box<[u8]> = Arbitrary::arbitrary(u)?; + Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) + } + + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + as Arbitrary<'a>>::size_hint(depth) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +/// A cheaply cloneable [`Path`] using a `Rc<[PathComponentBox]>`. +/// While cloning is cheap, operations which return modified forms of the path (e.g. [`Path::append`]) are not, as they have to clone and adjust the contents of the underlying [`Rc`]. +pub struct PathRc( + Rc<[PathComponentBox]>, +); + +impl PathRc { + pub fn new(components: &[PathComponentBox]) -> Result { + if components.len() > MCC { + return Err(InvalidPathError::TooManyComponents); + }; + + let mut path_vec = Vec::new(); + let mut total_length = 0; + + for component in components { + total_length += component.len(); + + if total_length > MPL { + return Err(InvalidPathError::PathTooLong); + } else { + path_vec.push(component.clone()); + } + } + + Ok(PathRc(path_vec.into())) + } + + pub fn empty() -> Self { + PathRc(Vec::new().into()) + } + + pub fn create_prefix(&self, length: usize) -> Self { + if length == 0 { + return Self::empty(); + } + + let until = core::cmp::min(length, self.0.len()); + let slice = &self.0[0..until]; + + Self::new(slice).unwrap() + } + + pub fn append(&self, component: PathComponentBox) -> Result { + let total_component_count = self.0.len(); + + if total_component_count + 1 > MCC { + return Err(InvalidPathError::TooManyComponents); + } + + let total_path_length = self.0.iter().fold(0, |acc, item| acc + item.0.len()); + + if total_path_length + component.as_ref().len() > MPL { + return Err(InvalidPathError::PathTooLong); + } + + let mut new_path_vec = Vec::new(); + + for component in self.components() { + new_path_vec.push(component.clone()) + } + + new_path_vec.push(component); + + Ok(PathRc(new_path_vec.into())) + } + + pub fn components( + &self, + ) -> impl DoubleEndedIterator> + + ExactSizeIterator> { + return self.0.iter(); + } + + pub fn component_count(&self) -> usize { + self.0.len() + } + + pub fn get_component<'s>(&'s self, i: usize) -> Option<&PathComponentBox> { + return self.0.get(i); + } + + /// Return all possible prefixes of a path, including the empty path and the path itself. + pub fn all_prefixes(&self) -> impl Iterator + '_ { + let self_len = self.components().count(); + + (0..=self_len).map(|i| self.create_prefix(i)) + } + + /// Test whether this path is a prefix of the given path. + /// Paths are always a prefix of themselves. + pub fn is_prefix_of(&self, other: &Self) -> bool { + for (comp_a, comp_b) in self.components().zip(other.components()) { + if comp_a != comp_b { + return false; + } + } + + self.component_count() <= other.component_count() + } + + /// Test whether this path is prefixed by the given path. + /// Paths are always a prefix of themselves. + pub fn is_prefixed_by(&self, other: &Self) -> bool { + other.is_prefix_of(self) + } + + /// Return the longest common prefix of this path and the given path. + pub fn longest_common_prefix(&self, other: &Self) -> Self { + let mut lcp_len = 0; + + for (comp_a, comp_b) in self.components().zip(other.components()) { + if comp_a != comp_b { + break; + } + + lcp_len += 1 + } + + self.create_prefix(lcp_len) + } + + /// Return the least path which is greater than `self`, or return `None` if `self` is the greatest possible path. + pub fn successor(&self) -> Option { + if self.component_count() == 0 { + let new_component = PathComponentBox::::new(&[]).ok()?; + return Self::new(&[new_component]).ok(); + } + + // Try and add an empty component. + if let Ok(path) = self.append(PathComponentBox::::empty()) { + return Some(path); + } + + for (i, component) in self.components().enumerate().rev() { + // Try and do the *next* simplest thing (add a 0 byte to the component). + if let Some(component) = component.try_append_zero_byte() { + if let Ok(path) = self.create_prefix(i).append(component) { + return Some(path); + } + } + + // Otherwise we need to increment the component fixed-width style! + if let Some(incremented_component) = component.try_increment_fixed_width() { + // We can unwrap here because neither the max path length, component count, or component length has changed. + return Some(self.create_prefix(i).append(incremented_component).unwrap()); + } + } + + None + } + + /// Return the least path that is greater than `self` and which is not prefixed by `self`, or `None` if `self` is the empty path *or* if `self` is the greatest path. + pub fn successor_of_prefix(&self) -> Option { + for (i, component) in self.components().enumerate().rev() { + if let Some(successor_comp) = component.try_append_zero_byte() { + if let Ok(path) = self.create_prefix(i).append(successor_comp) { + return Some(path); + } + } + + if let Some(successor_comp) = component.prefix_successor() { + return self.create_prefix(i).append(successor_comp).ok(); + } + } + + None + } +} + +impl Ord for PathRc { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + for (my_comp, your_comp) in self.components().zip(other.components()) { + let comparison = my_comp.cmp(your_comp); + + match comparison { + std::cmp::Ordering::Equal => { /* Continue */ } + _ => return comparison, + } + } + + self.component_count().cmp(&other.component_count()) + } +} + +impl PartialOrd for PathRc { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl<'a, const MCL: usize, const MCC: usize, const MPL: usize> Arbitrary<'a> + for PathRc +{ + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let boxx: Box<[PathComponentBox]> = Arbitrary::arbitrary(u)?; + Self::new(&boxx).map_err(|_| ArbitraryError::IncorrectFormat) + } + + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + as Arbitrary<'a>>::size_hint(depth) + } +} + +#[cfg(test)] +mod tests { + use std::cmp::Ordering::Less; + + use super::*; + + const MCL: usize = 8; + const MCC: usize = 4; + const MPL: usize = 16; + + #[test] + fn empty() { + let empty_path = PathRc::::empty(); + + assert_eq!(empty_path.components().count(), 0); + } + + #[test] + fn new() { + let component_too_long = + PathComponentBox::::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'z']); + + assert!(matches!(component_too_long, Err(ComponentTooLongError))); + + let too_many_components = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'z']).unwrap(), + ]); + + assert!(matches!( + too_many_components, + Err(InvalidPathError::TooManyComponents) + )); + + let path_too_long = PathRc::::new(&[ + PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), + PathComponentBox::new(&[b'a', b'a', b'a', b'a', b'a', b'a', b'a', b'a']).unwrap(), + PathComponentBox::new(&[b'z']).unwrap(), + ]); + + assert!(matches!(path_too_long, Err(InvalidPathError::PathTooLong))); + } + + #[test] + fn append() { + let path = PathRc::::empty(); + + let r1 = path.append(PathComponentBox::new(&[b'a']).unwrap()); + assert!(r1.is_ok()); + let p1 = r1.unwrap(); + assert_eq!(p1.components().count(), 1); + + let r2 = p1.append(PathComponentBox::new(&[b'b']).unwrap()); + assert!(r2.is_ok()); + let p2 = r2.unwrap(); + assert_eq!(p2.components().count(), 2); + + let r3 = p2.append(PathComponentBox::new(&[b'c']).unwrap()); + assert!(r3.is_ok()); + let p3 = r3.unwrap(); + assert_eq!(p3.components().count(), 3); + + let r4 = p3.append(PathComponentBox::new(&[b'd']).unwrap()); + assert!(r4.is_ok()); + let p4 = r4.unwrap(); + assert_eq!(p4.components().count(), 4); + + let r5 = p4.append(PathComponentBox::new(&[b'z']).unwrap()); + assert!(r5.is_err()); + + let collected = p4 + .components() + .map(|comp| comp.as_ref()) + .collect::>(); + + assert_eq!(collected, vec![[b'a'], [b'b'], [b'c'], [b'd'],]) + } + + #[test] + fn prefix() { + let path = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap(), + ]) + .unwrap(); + + let prefix0 = path.create_prefix(0); + + assert_eq!(prefix0, PathRc::empty()); + + let prefix1 = path.create_prefix(1); + + assert_eq!( + prefix1, + PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() + ); + + let prefix2 = path.create_prefix(2); + + assert_eq!( + prefix2, + PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap() + ]) + .unwrap() + ); + + let prefix3 = path.create_prefix(3); + + assert_eq!( + prefix3, + PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap() + ]) + .unwrap() + ); + + let prefix4 = path.create_prefix(4); + + assert_eq!( + prefix4, + PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap() + ]) + .unwrap() + ) + } + + #[test] + fn prefixes() { + let path = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap(), + ]) + .unwrap(); + + let prefixes: Vec> = path.all_prefixes().collect(); + + assert_eq!( + prefixes, + vec![ + PathRc::::new(&[]).unwrap(), + PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap() + ]) + .unwrap(), + PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap() + ]) + .unwrap(), + ] + ) + } + + #[test] + fn is_prefix_of() { + let path_a = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + ]) + .unwrap(); + + let path_b = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap(), + ]) + .unwrap(); + + let path_c = PathRc::::new(&[ + PathComponentBox::new(&[b'x']).unwrap(), + PathComponentBox::new(&[b'y']).unwrap(), + PathComponentBox::new(&[b'z']).unwrap(), + ]) + .unwrap(); + + assert!(path_a.is_prefix_of(&path_b)); + assert!(!path_a.is_prefix_of(&path_c)); + + let path_d = PathRc::::new(&[PathComponentBox::new(&[]).unwrap()]).unwrap(); + let path_e = PathRc::::new(&[PathComponentBox::new(&[0]).unwrap()]).unwrap(); + + assert!(!path_d.is_prefix_of(&path_e)); + + let empty_path = PathRc::empty(); + + assert!(empty_path.is_prefix_of(&path_d)); + } + + #[test] + fn is_prefixed_by() { + let path_a = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + ]) + .unwrap(); + + let path_b = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap(), + ]) + .unwrap(); + + let path_c = PathRc::::new(&[ + PathComponentBox::new(&[b'x']).unwrap(), + PathComponentBox::new(&[b'y']).unwrap(), + PathComponentBox::new(&[b'z']).unwrap(), + ]) + .unwrap(); + + assert!(path_b.is_prefixed_by(&path_a)); + assert!(!path_c.is_prefixed_by(&path_a)); + } + + #[test] + fn longest_common_prefix() { + let path_a = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'x']).unwrap(), + ]) + .unwrap(); + + let path_b = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'b']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap(), + ]) + .unwrap(); + + let path_c = PathRc::::new(&[ + PathComponentBox::new(&[b'x']).unwrap(), + PathComponentBox::new(&[b'y']).unwrap(), + PathComponentBox::new(&[b'z']).unwrap(), + ]) + .unwrap(); + + let lcp_a_b = path_a.longest_common_prefix(&path_b); + + assert_eq!( + lcp_a_b, + PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() + ); + + let lcp_b_a = path_b.longest_common_prefix(&path_a); + + assert_eq!(lcp_b_a, lcp_a_b); + + let lcp_a_x = path_a.longest_common_prefix(&path_c); + + assert_eq!(lcp_a_x, PathRc::empty()); + + let path_d = PathRc::::new(&[ + PathComponentBox::new(&[b'a']).unwrap(), + PathComponentBox::new(&[b'x']).unwrap(), + PathComponentBox::new(&[b'c']).unwrap(), + ]) + .unwrap(); + + let lcp_b_d = path_b.longest_common_prefix(&path_d); + + assert_eq!( + lcp_b_d, + PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap() + ) + } + + fn make_test_path( + vector: &Vec>, + ) -> PathRc { + let components: Vec<_> = vector + .iter() + .map(|bytes_vec| PathComponentBox::new(bytes_vec).expect("the component was too long")) + .collect(); + + PathRc::new(&components).expect("the path was invalid") + } + + #[test] + fn ordering() { + let test_vector = vec![(vec![vec![0, 0], vec![]], vec![vec![0, 0, 0]], Less)]; + + for (a, b, expected) in test_vector { + let path_a: PathRc<3, 3, 3> = make_test_path(&a); + let path_b: PathRc<3, 3, 3> = make_test_path(&b); + + let ordering = path_a.cmp(&path_b); + + if ordering != expected { + println!("a: {:?}", a); + println!("b: {:?}", b); + + assert_eq!(ordering, expected); + } + } + } +} + +/* +* Utilities for testing the correctness of path successor and path prefix successors via fuzz testing. +*/ + +pub fn test_successor( + // A path whose successor was computed. + baseline: PathRc, + // The computed successor of the baseline. + candidate: PathRc, + // The maximal path for the given choice of MCL, MCC, MPL (we could compute it, but we were too lazy to implement that). + max_path: PathRc, +) { + let successor = baseline.successor(); + + match successor { + None => { + if baseline != max_path { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", successor); + println!("candidate: {:?}", candidate); + println!("\n\n\n"); + panic!("returned None when the path was NOT the greatest path! BoooOOOoo") + } + } + Some(successor) => { + if successor <= baseline { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", successor); + println!("candidate: {:?}", candidate); + println!("\n\n\n"); + + panic!("successor was not greater than the path it was derived from! BooooOoooOOo") + } + + if candidate < successor && candidate > baseline { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", successor); + println!("candidate: {:?}", candidate); + println!("\n\n\n"); + + panic!("the successor generated was NOT the immediate successor! BooooOOOOo!") + } + } + } +} + +pub fn test_successor_of_prefix( + baseline: PathRc, + candidate: PathRc, + unsucceedable: &[PathRc], +) { + let prefix_successor = baseline.successor_of_prefix(); + + match prefix_successor { + None => { + if !unsucceedable.iter().any(|unsuc| unsuc == &baseline) { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", prefix_successor); + println!("candidate: {:?}", candidate); + panic!("returned None when the path was NOT the greatest path! BoooOOOoo\n\n\n\n"); + } + } + Some(prefix_successor) => { + if prefix_successor <= baseline { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", prefix_successor); + println!("candidate: {:?}", candidate); + panic!("the successor is meant to be greater than the baseline, but wasn't!! BOOOOOOOOO\n\n\n\n"); + } + + if prefix_successor.is_prefixed_by(&baseline) { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", prefix_successor); + println!("candidate: {:?}", candidate); + panic!("successor was prefixed by the path it was derived from! BoooOOooOOooOo\n\n\n\n"); + } + + if !baseline.is_prefix_of(&candidate) + && candidate < prefix_successor + && candidate > baseline + { + println!("\n\n\n"); + println!("baseline: {:?}", baseline); + println!("successor: {:?}", prefix_successor); + println!("candidate: {:?}", candidate); + + panic!( + "the successor generated was NOT the immediate prefix successor! BooooOOOOo!\n\n\n\n" + ); + } + } + } +} + +/* +Instructions for how to create paths; for fuzz testing. +*/ + +// TODO complete this +#[derive(Debug, Arbitrary)] +pub enum CreatePath { + Empty, + Singleton(Vec), +} + +pub fn create_path_rc( + cp: &CreatePath, +) -> Result, Option> { + match cp { + CreatePath::Empty => Ok(PathRc::empty()), + CreatePath::Singleton(comp) => match PathComponentBox::new(&comp) { + Err(_) => return Err(None), + Ok(comp) => { + return PathRc::new(&[comp]).map_err(|err| Some(err)); + } + }, + } +} + +pub fn create_path( + cp: &CreatePath, +) -> Result, Option> { + match cp { + CreatePath::Empty => Ok(Path::new_empty()), + CreatePath::Singleton(comp) => match Component::new(comp) { + None => return Err(None), + Some(comp) => { + return Path::new_singleton(comp).map_err(|err| Some(err)); + } + }, + } +} + +/* +Check that the two `Path`s behave just like `PathRc`s. For fuzz testing. +*/ +pub fn assert_isomorphic_paths( + ctrl1: &PathRc, + ctrl2: &PathRc, + p1: &Path, + p2: &Path, +) { + assert_eq!(ctrl1.component_count(), p1.get_component_count()); + + assert_eq!(ctrl1.component_count() == 0, p1.is_empty()); + + assert_eq!( + ctrl1.components().fold(0, |acc, comp| acc + comp.len()), + p1.get_path_length() + ); + + for i in 0..ctrl1.component_count() { + assert_eq!( + ctrl1.get_component(i).map(|comp| comp.as_ref()), + p1.get_component(i).map(|comp| comp.into_inner()) + ); + } + + assert!(ctrl1 + .components() + .map(|comp| comp.as_ref()) + .eq(p1.components().map(|comp| comp.into_inner()))); + + assert_eq!(ctrl1.all_prefixes().count(), p1.all_prefixes().count()); + + for (ctrl_prefix, p_prefix) in ctrl1.all_prefixes().zip(p1.all_prefixes()) { + assert!(ctrl_prefix + .components() + .map(|comp| comp.as_ref()) + .eq(p_prefix.components().map(|comp| comp.into_inner()))); + } + + assert_eq!(ctrl1 == ctrl2, p1 == p2); + assert_eq!(ctrl1.cmp(ctrl2), p1.cmp(p2)); + // TODO complete this +} From 6f593359941352bfa8f771267a93ed647cc66f43 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 13 Jul 2024 09:18:51 +0200 Subject: [PATCH 04/14] Finish isomorphism checks --- fuzz/src/path.rs | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs index bb16d4b..0a47912 100644 --- a/fuzz/src/path.rs +++ b/fuzz/src/path.rs @@ -1,3 +1,4 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; use std::rc::Rc; use libfuzzer_sys::arbitrary::{self, Arbitrary, Error as ArbitraryError, Unstructured}; @@ -848,13 +849,40 @@ pub fn assert_isomorphic_paths( + ctrl: &PathRc, + p: &Path, +) { + assert!(ctrl + .components() + .map(|comp| comp.as_ref()) + .eq(p.components().map(|comp| comp.into_inner()))); } From 427bb490ee37b4a9178dcbeb2094ee628075af50 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 13 Jul 2024 10:17:59 +0200 Subject: [PATCH 05/14] Finish basic Path tests --- data-model/src/path.rs | 7 +-- fuzz/fuzz_targets/path.rs | 8 +-- fuzz/src/path.rs | 120 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 9 deletions(-) diff --git a/data-model/src/path.rs b/data-model/src/path.rs index ddb8337..989ead1 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -144,7 +144,7 @@ impl Path { buf.extend_from_slice(&(component_count.to_ne_bytes())[..]); // Fill up the accumulated component lengths with dummy values. - buf.put_bytes(0, component_count * size_of::()); + buf.put_bytes(0, component_count * size_of::()); let mut accumulated_component_length = 0; for (i, comp) in iter.enumerate() { @@ -302,10 +302,7 @@ impl Path { if length > self.get_component_count() { return None; } else { - return Some(Self { - data: self.data.clone(), - number_of_components: length, - }); + return Some(unsafe { self.create_prefix_unchecked(length) }); } } diff --git a/fuzz/fuzz_targets/path.rs b/fuzz/fuzz_targets/path.rs index f5568d6..83469e5 100644 --- a/fuzz/fuzz_targets/path.rs +++ b/fuzz/fuzz_targets/path.rs @@ -7,11 +7,11 @@ use willow_data_model_fuzz::path::*; fuzz_target!(|data: (CreatePath, CreatePath)| { let (cp1, cp2) = data; - let ctrl1 = create_path_rc::<100, 100, 100>(&cp1); - let ctrl2 = create_path_rc::<100, 100, 100>(&cp2); + let ctrl1 = create_path_rc::<300, 300, 300>(&cp1); + let ctrl2 = create_path_rc::<300, 300, 300>(&cp2); - let p1 = create_path::<100, 100, 100>(&cp1); - let p2 = create_path::<100, 100, 100>(&cp2); + let p1 = create_path::<300, 300, 300>(&cp1); + let p2 = create_path::<300, 300, 300>(&cp2); match (ctrl1, p1) { (Err(_), Err(_)) => { /* no-op */} diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs index 0a47912..ad557f9 100644 --- a/fuzz/src/path.rs +++ b/fuzz/src/path.rs @@ -786,6 +786,11 @@ Instructions for how to create paths; for fuzz testing. pub enum CreatePath { Empty, Singleton(Vec), + FromIter(Vec>), + FromSlice(Vec>), + Append(Box, Vec), + AppendSlice(Box, Vec>), + CreatePrefix(Box, usize), } pub fn create_path_rc( @@ -799,6 +804,59 @@ pub fn create_path_rc( return PathRc::new(&[comp]).map_err(|err| Some(err)); } }, + CreatePath::FromIter(raw_material) | CreatePath::FromSlice(raw_material) => { + let mut p = PathRc::empty(); + + for comp in raw_material { + match PathComponentBox::new(&comp) { + Ok(comp) => match p.append(comp) { + Err(err) => { + return Err(Some(err)); + } + Ok(yay) => p = yay, + }, + Err(_) => return Err(None), + } + } + + return Ok(p); + } + CreatePath::Append(rec, comp) => { + let base = create_path_rc(rec)?; + + match PathComponentBox::new(&comp) { + Err(_) => return Err(None), + Ok(comp) => { + return base.append(comp).map_err(|err| Some(err)); + } + } + } + CreatePath::AppendSlice(rec, comps) => { + let mut base = create_path_rc(rec)?; + + for comp in comps { + match PathComponentBox::new(&comp) { + Ok(comp) => match base.append(comp) { + Err(err) => { + return Err(Some(err)); + } + Ok(yay) => base = yay, + }, + Err(_) => return Err(None), + } + } + + return Ok(base); + } + CreatePath::CreatePrefix(rec, len) => { + let base = create_path_rc(rec)?; + + if *len > base.component_count() { + return Err(None); + } else { + return Ok(base.create_prefix(*len)); + } + } } } @@ -813,6 +871,68 @@ pub fn create_path( return Path::new_singleton(comp).map_err(|err| Some(err)); } }, + CreatePath::FromIter(raw_material) => { + let mut comps = vec![]; + let mut total_length = 0; + for comp in raw_material.iter() { + match Component::new(comp) { + Some(yay) => { + total_length += yay.len(); + comps.push(yay); + } + None => return Err(None), + } + } + + return Path::new_from_iter(total_length, &mut comps.into_iter()) + .map_err(|err| Some(err)); + } + CreatePath::FromSlice(raw_material) => { + let mut comps = vec![]; + for comp in raw_material.iter() { + match Component::new(comp) { + Some(yay) => { + comps.push(yay); + } + None => return Err(None), + } + } + + return Path::new_from_slice(&comps).map_err(|err| Some(err)); + } + CreatePath::Append(rec, comp) => { + let base = create_path(rec)?; + + match Component::new(&comp) { + None => return Err(None), + Some(comp) => { + return base.append(comp).map_err(|err| Some(err)); + } + } + } + CreatePath::AppendSlice(rec, raw_material) => { + let base = create_path(rec)?; + + let mut comps = vec![]; + for comp in raw_material.iter() { + match Component::new(comp) { + Some(yay) => { + comps.push(yay); + } + None => return Err(None), + } + } + + return base.append_slice(&comps).map_err(|err| Some(err)); + } + CreatePath::CreatePrefix(rec, len) => { + let base = create_path(rec)?; + + match base.create_prefix(*len) { + Some(yay) => return Ok(yay), + None => return Err(None), + } + } } } From fc8ca87abfa069a57c1ea149419051890b609a12 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 13 Jul 2024 10:22:37 +0200 Subject: [PATCH 06/14] Fix path_successor test --- fuzz/fuzz_targets/path_successor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/fuzz_targets/path_successor.rs b/fuzz/fuzz_targets/path_successor.rs index b9b2cbd..0b1758c 100644 --- a/fuzz/fuzz_targets/path_successor.rs +++ b/fuzz/fuzz_targets/path_successor.rs @@ -5,7 +5,7 @@ use libfuzzer_sys::fuzz_target; use willow_data_model_fuzz::path::*; // MCL, MCC, MPL -fuzz_target!(|data: (PathRc<3, 3, 3>, PathRc<3, 3, 3>)| { +fuzz_target!(|data: (PathRc<2, 3, 3>, PathRc<2, 3, 3>)| { let (baseline, candidate) = data; let max_path = PathRc::new(&[ PathComponentBox::new(&[255, 255]).unwrap(), From d7161960eec66c88a096c723902acbb117b4f9fc Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 13 Jul 2024 10:38:09 +0200 Subject: [PATCH 07/14] Test further combinations of path parameters --- data-model/src/path.rs | 3 ++- fuzz/Cargo.toml | 14 ++++++++++++++ fuzz/fuzz_targets/path2.rs | 33 +++++++++++++++++++++++++++++++++ fuzz/fuzz_targets/path3.rs | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 fuzz/fuzz_targets/path2.rs create mode 100644 fuzz/fuzz_targets/path3.rs diff --git a/data-model/src/path.rs b/data-model/src/path.rs index 989ead1..1e286aa 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -1,3 +1,5 @@ +// This struct is tested in `fuzz/path.rs`, `fuzz/path2.rs`, `fuzz/path3.rs`. + use core::borrow::Borrow; use core::convert::AsRef; use core::fmt::Debug; @@ -6,7 +8,6 @@ use core::iter; use core::mem::size_of; use core::ops::Deref; -// use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured}; use bytes::{BufMut, Bytes, BytesMut}; /// A [component](https://willowprotocol.org/specs/data-model/index.html#Component) of a Willow Path. diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 4a49005..2df2508 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,6 +20,20 @@ test = false doc = false bench = false +[[bin]] +name = "path2" +path = "fuzz_targets/path2.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "path3" +path = "fuzz_targets/path3.rs" +test = false +doc = false +bench = false + [[bin]] name = "path_successor" path = "fuzz_targets/path_successor.rs" diff --git a/fuzz/fuzz_targets/path2.rs b/fuzz/fuzz_targets/path2.rs new file mode 100644 index 0000000..7ceacd4 --- /dev/null +++ b/fuzz/fuzz_targets/path2.rs @@ -0,0 +1,33 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; + +fuzz_target!(|data: (CreatePath, CreatePath)| { + let (cp1, cp2) = data; + + let ctrl1 = create_path_rc::<2, 3, 3>(&cp1); + let ctrl2 = create_path_rc::<2, 3, 3>(&cp2); + + let p1 = create_path::<2, 3, 3>(&cp1); + let p2 = create_path::<2, 3, 3>(&cp2); + + match (ctrl1, p1) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl1), Ok(p1)) => { + match (ctrl2, p2) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl2), Ok(p2)) => { + assert_isomorphic_paths(&ctrl1, &ctrl2, &p1, &p2); + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } +}); diff --git a/fuzz/fuzz_targets/path3.rs b/fuzz/fuzz_targets/path3.rs new file mode 100644 index 0000000..5cc9019 --- /dev/null +++ b/fuzz/fuzz_targets/path3.rs @@ -0,0 +1,33 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; + +fuzz_target!(|data: (CreatePath, CreatePath)| { + let (cp1, cp2) = data; + + let ctrl1 = create_path_rc::<4, 4, 16>(&cp1); + let ctrl2 = create_path_rc::<4, 4, 16>(&cp2); + + let p1 = create_path::<4, 4, 16>(&cp1); + let p2 = create_path::<4, 4, 16>(&cp2); + + match (ctrl1, p1) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl1), Ok(p1)) => { + match (ctrl2, p2) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl2), Ok(p2)) => { + assert_isomorphic_paths(&ctrl1, &ctrl2, &p1, &p2); + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } +}); From 911a1cb3c10bafdd3ad6b8a8b54deca0cbc7b49c Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sun, 14 Jul 2024 00:45:52 +0200 Subject: [PATCH 08/14] Implement and test path successors --- data-model/src/path.rs | 239 ++++++++++++++++++++++++++++++++----- fuzz/Cargo.toml | 8 ++ fuzz/fuzz_targets/path4.rs | 33 +++++ fuzz/src/path.rs | 18 ++- 4 files changed, 259 insertions(+), 39 deletions(-) create mode 100644 fuzz/fuzz_targets/path4.rs diff --git a/data-model/src/path.rs b/data-model/src/path.rs index 1e286aa..5df3d0e 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -1,4 +1,5 @@ // This struct is tested in `fuzz/path.rs`, `fuzz/path2.rs`, `fuzz/path3.rs`. +// Further, successors and prefix_successors are tested in `fuzz/path_successor.rs` and friends, and `fuzz/path_successor_of_prefix.rs` and friends. use core::borrow::Borrow; use core::convert::AsRef; @@ -31,6 +32,11 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Component<'a, MAX_COMPONENT_LENGTH> Self(slice) } + /// Create an empty component. + pub fn new_empty() -> Self { + return Self(&[]); + } + pub fn into_inner(self) -> &'a [u8] { return self.0; } @@ -73,7 +79,7 @@ pub struct Path { /// The data of the underlying path. data: HeapEncoding, /// Number of components of the `data` to consider for this particular path. Must be less than or equal to the total number of components. - number_of_components: usize, + component_count: usize, } impl Path { @@ -88,7 +94,7 @@ impl Path { data: HeapEncoding(Bytes::from_static(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ])), - number_of_components: 0, + component_count: 0, }; } @@ -112,7 +118,7 @@ impl Path { return Ok(Path { data: HeapEncoding(buf.freeze()), - number_of_components: 1, + component_count: 1, }); } } @@ -166,7 +172,7 @@ impl Path { return Ok(Path { data: HeapEncoding(buf.freeze()), - number_of_components: component_count, + component_count, }); } @@ -237,7 +243,7 @@ impl Path { /// /// Runs in `O(1)`, performs no allocations. pub fn get_component_count(&self) -> usize { - return self.number_of_components; + return self.component_count; } /// Return whether this path has zero components. @@ -257,13 +263,14 @@ impl Path { /// /// Runs in `O(1)`, performs no allocations. pub fn get_path_length(&self) -> usize { - if self.number_of_components == 0 { + if self.component_count == 0 { return 0; } else { - return self - .data - .get_sum_of_lengths_for_component(self.number_of_components - 1) - .unwrap(); + return HeapEncoding::::get_sum_of_lengths_for_component( + self.data.as_ref(), + self.component_count - 1, + ) + .unwrap(); } } @@ -273,7 +280,7 @@ impl Path { /// /// Runs in `O(1)`, performs no allocations. pub fn get_component<'s>(&'s self, i: usize) -> Option> { - return self.data.get_component(i); + return HeapEncoding::::get_component(self.data.as_ref(), i); } /// Create an iterator over the components of this path. @@ -317,7 +324,7 @@ impl Path { pub unsafe fn create_prefix_unchecked(&self, length: usize) -> Self { Self { data: self.data.clone(), - number_of_components: length, + component_count: length, } } @@ -338,6 +345,10 @@ impl Path { /// Test whether this path is a prefix of the given path. /// Paths are always a prefix of themselves, and the empty path is a prefix of every path. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the shorter of the two paths. Performs no allocations. pub fn is_prefix_of(&self, other: &Self) -> bool { for (comp_a, comp_b) in self.components().zip(other.components()) { if comp_a != comp_b { @@ -350,11 +361,19 @@ impl Path { /// Test whether this path is prefixed by the given path. /// Paths are always a prefix of themselves. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the shorter of the two paths. Performs no allocations. pub fn is_prefixed_by(&self, other: &Self) -> bool { other.is_prefix_of(self) } /// Return the longest common prefix of this path and the given path. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the shorter of the two paths. Performs a single allocation to create the return value. pub fn longest_common_prefix(&self, other: &Self) -> Self { let mut lcp_len = 0; @@ -368,11 +387,142 @@ impl Path { self.create_prefix(lcp_len).unwrap() // zip ensures that lcp_len <= self.get_component_count() } + + /// Return the least path which is strictly greater than `self`, or return `None` if `self` is the greatest possible path. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the shorter of the two paths. Performs a single allocation to create the return value. + pub fn successor(&self) -> Option { + // If it is possible to append an empty component, then doing so yields the successor. + if let Ok(path) = self.append(Component::new_empty()) { + return Some(path); + } + + // Otherwise, we try incrementing the final component. If that fails, + // we try to increment the second-to-final component, and so on. + // All components that come after the incremented component are discarded. + // If *no* component can be incremented, `self` is the maximal path and we return `None`. + + for (i, component) in self.components().enumerate().rev() { + // It would be nice to call a `try_increment_component` function, but in order to avoid + // memory allocations, we write some lower-level but more efficient code. + + // If it is possible to append a zero byte to a component, then doing so yields its successor. + if component.len() < MCL + && HeapEncoding::::get_sum_of_lengths_for_component(self.data.as_ref(), i) + .unwrap() // i < self.component_count + < MPL + { + // We now know how to construct the path successor of `self`: + // Take the first `i` components (this *excludes* the current `component`), + // then append `component` with an additinoal zero byte at the end. + + // To minimise allocations, we implement this construction explicitly rather than using + // our general-purpose functions. In particular, there is no need to explicitly + // construct `component + zero_byte` on the heap. + + // First, we compute the total length of the allocation we need, then allocate it. + let successor_path_length = + HeapEncoding::::get_sum_of_lengths_for_component(self.data.as_ref(), i) + .unwrap() + + 1; // Can unwrap because `i < self.component_count`. + let buf_capacity = size_of::() * (i + 2) + successor_path_length; + let mut buf = BytesMut::with_capacity(buf_capacity); + + // Write the length of the successor path as the first usize. + buf.extend_from_slice(&((i + 1) as usize).to_ne_bytes()); + + // Next, copy the total path lengths for the first i prefixes. + buf.extend_from_slice( + &self.data.0[size_of::()..size_of::() * (i + 2)], + ); + + // Now, write the length of the final component, which is one greater than before. + buf_set_final_component_length(buf.as_mut(), i, successor_path_length); + + // Finally, copy the raw bytes of the first i+1 components... + buf.extend_from_slice( + &self.data.0[HeapEncoding::::start_offset_of_component( + &self.data.as_ref(), + 0, + ) + .unwrap() + ..HeapEncoding::::start_offset_of_component( + &self.data.as_ref(), + i + 1, + ) + .unwrap()], + ); + + // ... and append a zero byte. + buf.put_u8(0); + + return Some(Self::from_buffer_and_component_count(buf.freeze(), i + 1)); + } + + // We **cannot** append a zero byte, so instead we check whether we can treat the component as a fixed-width integer and increment it. The only failure case is if that component consists of 255-bytes only. + let can_increment = !component.iter().all(|byte| *byte == 255); + + // If we cannot increment, we go to the next iteration of the loop. But if we can, we can create a copy of the + // prefix on the first `i + 1` components, and mutate its backing memory in-place. + if can_increment { + // First, we compute the total length of the allocation we need, then allocate it. + let successor_path_length = + HeapEncoding::::get_sum_of_lengths_for_component(self.data.as_ref(), i) + .unwrap(); // Can unwrap because `i < self.component_count`. + let buf_capacity = size_of::() * (i + 2) + successor_path_length; + let mut buf = BytesMut::with_capacity(buf_capacity); + + // Write the length of the successor path as the first usize. + buf.extend_from_slice(&((i + 1) as usize).to_ne_bytes()); + + // Next, copy the total path lengths for the first i+1 prefixes. + buf.extend_from_slice( + &self.data.0[size_of::()..size_of::() * (i + 2)], + ); + + // Finally, copy the raw bytes of the first i+1 components. + buf.extend_from_slice( + &self.data.0[HeapEncoding::::start_offset_of_component( + &self.data.as_ref(), + 0, + ) + .unwrap() + ..HeapEncoding::::start_offset_of_component( + &self.data.as_ref(), + i + 1, + ) + .unwrap()], + ); + + let start_component_offset = + HeapEncoding::::start_offset_of_component(buf.as_ref(), i).unwrap(); // i < self.component_count + let end_component_offset = + HeapEncoding::::end_offset_of_component(buf.as_ref(), i).unwrap(); // i < self.component_count + fixed_width_increment( + &mut buf.as_mut()[start_component_offset..end_component_offset], + ); + + return Some(Self::from_buffer_and_component_count(buf.freeze(), i + 1)); + } + } + + // Failed to increment any component, so `self` is the maximal path. + None + } + + fn from_buffer_and_component_count(buf: Bytes, component_count: usize) -> Self { + Path { + data: HeapEncoding(buf), + component_count, + } + } } impl PartialEq for Path { fn eq(&self, other: &Self) -> bool { - if self.number_of_components != other.number_of_components { + if self.component_count != other.component_count { return false; } else { return self.components().eq(other.components()); @@ -384,7 +534,7 @@ impl Eq for Path Hash for Path { fn hash(&self, state: &mut H) { - self.number_of_components.hash(state); + self.component_count.hash(state); for comp in self.components() { comp.hash(state); @@ -427,20 +577,20 @@ struct HeapEncoding(Bytes); // All offsets are in bytes, unless otherwise specified. // Arguments named `i` are the index of a component in the string, *not* a byte offset. impl HeapEncoding { - fn get_component_count(&self) -> usize { - self.get_usize_at_offset(0).unwrap() // We always store at least 8byte for the component count. + fn get_component_count(buf: &[u8]) -> usize { + Self::get_usize_at_offset(buf, 0).unwrap() // We always store at least 8byte for the component count. } // None if i is outside the slice. - fn get_sum_of_lengths_for_component(&self, i: usize) -> Option { + fn get_sum_of_lengths_for_component(buf: &[u8], i: usize) -> Option { let start_offset_in_bytes = Self::start_offset_of_sum_of_lengths_for_component(i); - self.get_usize_at_offset(start_offset_in_bytes) + Self::get_usize_at_offset(buf, start_offset_in_bytes) } - fn get_component<'s>(&'s self, i: usize) -> Option> { - let start = self.start_offset_of_component(i)?; - let end = self.end_offset_of_component(i)?; - return Some(unsafe { Component::new_unchecked(&self.0[start..end]) }); + fn get_component<'s>(buf: &'s [u8], i: usize) -> Option> { + let start = Self::start_offset_of_component(buf, i)?; + let end = Self::end_offset_of_component(buf, i)?; + return Some(unsafe { Component::new_unchecked(&buf[start..end]) }); } fn start_offset_of_sum_of_lengths_for_component(i: usize) -> usize { @@ -448,40 +598,44 @@ impl HeapEncoding { size_of::() * (i + 1) } - fn start_offset_of_component(&self, i: usize) -> Option { - let metadata_length = (self.get_component_count() + 1) * size_of::(); + fn start_offset_of_component(buf: &[u8], i: usize) -> Option { + let metadata_length = (Self::get_component_count(buf) + 1) * size_of::(); if i == 0 { return Some(metadata_length); } else { - return self - .get_sum_of_lengths_for_component(i - 1) // Length of everything up until the previous component. + return Self::get_sum_of_lengths_for_component(buf, i - 1) // Length of everything up until the previous component. .map(|length| length + metadata_length); } } - fn end_offset_of_component(&self, i: usize) -> Option { - let metadata_length = (self.get_component_count() + 1) * size_of::(); - return self - .get_sum_of_lengths_for_component(i) + fn end_offset_of_component(buf: &[u8], i: usize) -> Option { + let metadata_length = (Self::get_component_count(buf) + 1) * size_of::(); + return Self::get_sum_of_lengths_for_component(buf, i) .map(|length| length + metadata_length); } - fn get_usize_at_offset(&self, offset_in_bytes: usize) -> Option { + fn get_usize_at_offset(buf: &[u8], offset_in_bytes: usize) -> Option { let end = offset_in_bytes + size_of::(); // We cannot interpret the memory in the slice as a usize directly, because the alignment might not match. // So we first copy the bytes onto the heap, then construct a usize from it. let mut usize_bytes = [0u8; size_of::()]; - if self.0.len() < end { + if buf.len() < end { return None; } else { - usize_bytes.copy_from_slice(&self.0[offset_in_bytes..end]); + usize_bytes.copy_from_slice(&buf[offset_in_bytes..end]); return Some(usize::from_ne_bytes(usize_bytes)); } } } +impl AsRef<[u8]> for HeapEncoding { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + /// Like core::iter::Chain, but implements ExactSizeIter if both components implement it. Panics if the resulting length overflows. /// /// Code liberally copy-pasted from the standard library. @@ -553,3 +707,22 @@ fn and_then_or_clear(opt: &mut Option, f: impl FnOnce(&mut T) -> Option } x } + +// In a buffer that stores a path on the heap, set the sum of all component lengths for the i-th component, which must be the final component. +fn buf_set_final_component_length(buf: &mut [u8], i: usize, new_sum_of_lengths: usize) { + let comp_len_start = (1 + i) * size_of::(); + let comp_len_end = comp_len_start + size_of::(); + buf[comp_len_start..comp_len_end].copy_from_slice(&new_sum_of_lengths.to_ne_bytes()[..]); +} + +// Overflows to all zeroes if all bytes are 255. +fn fixed_width_increment(buf: &mut [u8]) { + for byte_ref in buf.iter_mut().rev() { + if *byte_ref == 255 { + *byte_ref = 0; + } else { + *byte_ref += 1; + return; + } + } +} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2df2508..21f0b86 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -34,6 +34,14 @@ test = false doc = false bench = false + +[[bin]] +name = "path4" +path = "fuzz_targets/path4.rs" +test = false +doc = false +bench = false + [[bin]] name = "path_successor" path = "fuzz_targets/path_successor.rs" diff --git a/fuzz/fuzz_targets/path4.rs b/fuzz/fuzz_targets/path4.rs new file mode 100644 index 0000000..e2658eb --- /dev/null +++ b/fuzz/fuzz_targets/path4.rs @@ -0,0 +1,33 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +// use willow_data_model::path::*; +use willow_data_model_fuzz::path::*; + +fuzz_target!(|data: (CreatePath, CreatePath)| { + let (cp1, cp2) = data; + + let ctrl1 = create_path_rc::<3, 3, 3>(&cp1); + let ctrl2 = create_path_rc::<3, 3, 3>(&cp2); + + let p1 = create_path::<3, 3, 3>(&cp1); + let p2 = create_path::<3, 3, 3>(&cp2); + + match (ctrl1, p1) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl1), Ok(p1)) => { + match (ctrl2, p2) { + (Err(_), Err(_)) => { /* no-op */} + (Ok(ctrl2), Ok(p2)) => { + assert_isomorphic_paths(&ctrl1, &ctrl2, &p1, &p2); + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } + } + _ => { + panic!("Create_path_rc and create_path must either both succeeed or both fail."); + } + } +}); diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs index ad557f9..21f5c0f 100644 --- a/fuzz/src/path.rs +++ b/fuzz/src/path.rs @@ -284,11 +284,6 @@ impl PathRc /// Return the least path which is greater than `self`, or return `None` if `self` is the greatest possible path. pub fn successor(&self) -> Option { - if self.component_count() == 0 { - let new_component = PathComponentBox::::new(&[]).ok()?; - return Self::new(&[new_component]).ok(); - } - // Try and add an empty component. if let Ok(path) = self.append(PathComponentBox::::empty()) { return Some(path); @@ -979,6 +974,17 @@ pub fn assert_isomorphic_paths {} + (Some(succ_ctrl1), Some(succ_p1)) => { + assert_paths_are_equal(&succ_ctrl1, &succ_p1); + } + _ => { + panic!("Not good (successor)"); + } + } + + assert_eq!(ctrl1 == ctrl2, p1 == p2); if ctrl1 == ctrl2 { @@ -1004,5 +1010,5 @@ fn assert_paths_are_equal( assert!(ctrl .components() .map(|comp| comp.as_ref()) - .eq(p.components().map(|comp| comp.into_inner()))); + .eq(p.components().map(|comp| comp.into_inner())), "Unequal paths.\nctrl: {:?}\np: {:?}", ctrl, p); } From 28fa6549b84d6a61ff29d42373fe111e2cc095d9 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Mon, 15 Jul 2024 16:28:50 +0200 Subject: [PATCH 09/14] Clarifying renamings and comments --- data-model/src/path.rs | 170 ++++++++++-------- fuzz/fuzz_targets/successor_of_prefix.rs | 2 +- .../successor_of_prefix_even_more.rs | 2 +- fuzz/fuzz_targets/successor_of_prefix_more.rs | 2 +- fuzz/src/path.rs | 38 ++-- 5 files changed, 126 insertions(+), 88 deletions(-) diff --git a/data-model/src/path.rs b/data-model/src/path.rs index 5df3d0e..b8b013c 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -1,5 +1,5 @@ -// This struct is tested in `fuzz/path.rs`, `fuzz/path2.rs`, `fuzz/path3.rs`. -// Further, successors and prefix_successors are tested in `fuzz/path_successor.rs` and friends, and `fuzz/path_successor_of_prefix.rs` and friends. +// This struct is tested in `fuzz/path.rs`, `fuzz/path2.rs`, `fuzz/path3.rs`, `fuzz/path3.rs` by comparing against a non-optimised reference implementation. +// Further, the `successor` and `greater_but_not_prefixed` methods of that reference implementation are tested in `fuzz/path_successor.rs` and friends, and `fuzz/path_successor_of_prefix.rs` and friends. use core::borrow::Borrow; use core::convert::AsRef; @@ -63,7 +63,7 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Borrow<[u8]> for Component<'a, MAX_C } #[derive(Debug)] -/// An error arising from trying to construct a invalid [`Path`] from valid [`PathComponent`]. +/// An error arising from trying to construct a invalid [`Path`] from valid components. pub enum InvalidPathError { /// The path's total length in bytes is too large. PathTooLong, @@ -79,6 +79,7 @@ pub struct Path { /// The data of the underlying path. data: HeapEncoding, /// Number of components of the `data` to consider for this particular path. Must be less than or equal to the total number of components. + /// This field enables cheap prefix creation by cloning the heap data and adjusting the `component_count`. component_count: usize, } @@ -176,7 +177,7 @@ impl Path { }); } - /// Construct a path of from a slice of components. + /// Construct a path from a slice of components. /// /// Copies the bytes of the components into an owned allocation on the heap. /// @@ -417,45 +418,7 @@ impl Path { // We now know how to construct the path successor of `self`: // Take the first `i` components (this *excludes* the current `component`), // then append `component` with an additinoal zero byte at the end. - - // To minimise allocations, we implement this construction explicitly rather than using - // our general-purpose functions. In particular, there is no need to explicitly - // construct `component + zero_byte` on the heap. - - // First, we compute the total length of the allocation we need, then allocate it. - let successor_path_length = - HeapEncoding::::get_sum_of_lengths_for_component(self.data.as_ref(), i) - .unwrap() - + 1; // Can unwrap because `i < self.component_count`. - let buf_capacity = size_of::() * (i + 2) + successor_path_length; - let mut buf = BytesMut::with_capacity(buf_capacity); - - // Write the length of the successor path as the first usize. - buf.extend_from_slice(&((i + 1) as usize).to_ne_bytes()); - - // Next, copy the total path lengths for the first i prefixes. - buf.extend_from_slice( - &self.data.0[size_of::()..size_of::() * (i + 2)], - ); - - // Now, write the length of the final component, which is one greater than before. - buf_set_final_component_length(buf.as_mut(), i, successor_path_length); - - // Finally, copy the raw bytes of the first i+1 components... - buf.extend_from_slice( - &self.data.0[HeapEncoding::::start_offset_of_component( - &self.data.as_ref(), - 0, - ) - .unwrap() - ..HeapEncoding::::start_offset_of_component( - &self.data.as_ref(), - i + 1, - ) - .unwrap()], - ); - - // ... and append a zero byte. + let mut buf = clone_prefix_and_lengthen_final_component(self, i, 1); buf.put_u8(0); return Some(Self::from_buffer_and_component_count(buf.freeze(), i + 1)); @@ -467,34 +430,7 @@ impl Path { // If we cannot increment, we go to the next iteration of the loop. But if we can, we can create a copy of the // prefix on the first `i + 1` components, and mutate its backing memory in-place. if can_increment { - // First, we compute the total length of the allocation we need, then allocate it. - let successor_path_length = - HeapEncoding::::get_sum_of_lengths_for_component(self.data.as_ref(), i) - .unwrap(); // Can unwrap because `i < self.component_count`. - let buf_capacity = size_of::() * (i + 2) + successor_path_length; - let mut buf = BytesMut::with_capacity(buf_capacity); - - // Write the length of the successor path as the first usize. - buf.extend_from_slice(&((i + 1) as usize).to_ne_bytes()); - - // Next, copy the total path lengths for the first i+1 prefixes. - buf.extend_from_slice( - &self.data.0[size_of::()..size_of::() * (i + 2)], - ); - - // Finally, copy the raw bytes of the first i+1 components. - buf.extend_from_slice( - &self.data.0[HeapEncoding::::start_offset_of_component( - &self.data.as_ref(), - 0, - ) - .unwrap() - ..HeapEncoding::::start_offset_of_component( - &self.data.as_ref(), - i + 1, - ) - .unwrap()], - ); + let mut buf = clone_prefix_and_lengthen_final_component(self, i, 0); let start_component_offset = HeapEncoding::::start_offset_of_component(buf.as_ref(), i).unwrap(); // i < self.component_count @@ -512,6 +448,63 @@ impl Path { None } + /// Return the least path that is strictly greater than `self` and which is not prefixed by `self`, or `None` if no such path exists. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the total length of the shorter of the two paths. Performs a single allocation to create the return value. + pub fn greater_but_not_prefixed(&self) -> Option { + // We iterate through all components in reverse order. For each component, we check whether we can replace it by another cmponent that is strictly greater but not prefixed by the original component. If that is possible, we do replace it with the least such component and drop all later components. If that is impossible, we try again with the previous component. If this impossible for all components, then this functino returns `None`. + + for (i, component) in self.components().enumerate().rev() { + // If it is possible to append a zero byte to a component, then doing so yields its successor. + if component.len() < MCL + && HeapEncoding::::get_sum_of_lengths_for_component(self.data.as_ref(), i) + .unwrap() // i < self.component_count + < MPL + { + let mut buf = clone_prefix_and_lengthen_final_component(self, i, 1); + buf.put_u8(0); + + return Some(Self::from_buffer_and_component_count(buf.freeze(), i + 1)); + } + + // Next, we check whether the i-th component can be changed into the least component that is greater but not prefixed by the original. If so, do that and cut off all later components. + let mut next_component_length = None; + for (j, comp_byte) in component.iter().enumerate().rev() { + if *comp_byte < 255 { + next_component_length = Some(j + 1); + break; + } + } + + if let Some(next_component_length) = next_component_length { + // Yay, we can replace the i-th comopnent and then we are done. + + let mut buf = clone_prefix_and_lengthen_final_component(self, i, 0); + let length_of_prefix = HeapEncoding::::get_sum_of_lengths_for_component(&buf, i).unwrap(); + + // Update the length of the final component. + buf_set_final_component_length( + buf.as_mut(), + i, + length_of_prefix - (component.len() - next_component_length), + ); + + // Increment the byte at position `next_component_length` of the final component. + let offset = HeapEncoding::::start_offset_of_component(buf.as_ref(), i) + .unwrap() + + next_component_length - 1; + let byte = buf.as_ref()[offset]; // guaranteed < 255... + buf.as_mut()[offset] = byte + 1; // ... hence no overflow here. + + return Some(Self::from_buffer_and_component_count(buf.freeze(), i + 1)); + } + } + + None + } + fn from_buffer_and_component_count(buf: Bytes, component_count: usize) -> Self { Path { data: HeapEncoding(buf), @@ -726,3 +719,38 @@ fn fixed_width_increment(buf: &mut [u8]) { } } } + +/// Create a new BufMut that stores the heap encoding of the first i components of `original`, but increasing the length of the final component by `extra_capacity`. No data to fill that extra capacity is written into the buffer. +fn clone_prefix_and_lengthen_final_component< + const MCL: usize, + const MCC: usize, + const MPL: usize, +>( + original: &Path, + i: usize, + extra_capacity: usize, +) -> BytesMut { + let original_slice = original.data.as_ref(); + let successor_path_length = + HeapEncoding::::get_sum_of_lengths_for_component(original_slice, i).unwrap() + + extra_capacity; + let buf_capacity = size_of::() * (i + 2) + successor_path_length; + let mut buf = BytesMut::with_capacity(buf_capacity); + + // Write the length of the successor path as the first usize. + buf.extend_from_slice(&((i + 1) as usize).to_ne_bytes()); + + // Next, copy the total path lengths for the first i prefixes. + buf.extend_from_slice(&original_slice[size_of::()..size_of::() * (i + 2)]); + + // Now, write the length of the final component, which is one greater than before. + buf_set_final_component_length(buf.as_mut(), i, successor_path_length); + + // Finally, copy the raw bytes of the first i+1 components. + buf.extend_from_slice( + &original_slice[HeapEncoding::::start_offset_of_component(original_slice, 0).unwrap() + ..HeapEncoding::::start_offset_of_component(original_slice, i + 1).unwrap()], + ); + + return buf; +} diff --git a/fuzz/fuzz_targets/successor_of_prefix.rs b/fuzz/fuzz_targets/successor_of_prefix.rs index 11599b4..12a56ac 100644 --- a/fuzz/fuzz_targets/successor_of_prefix.rs +++ b/fuzz/fuzz_targets/successor_of_prefix.rs @@ -23,5 +23,5 @@ fuzz_target!(|data: (PathRc<2, 3, 3>, PathRc<2, 3, 3>)| { .unwrap(), ]; - test_successor_of_prefix(baseline, candidate, &unsucceedables); + test_greater_but_not_prefixed(baseline, candidate, &unsucceedables); }); diff --git a/fuzz/fuzz_targets/successor_of_prefix_even_more.rs b/fuzz/fuzz_targets/successor_of_prefix_even_more.rs index 1c36dc4..4e00ea1 100644 --- a/fuzz/fuzz_targets/successor_of_prefix_even_more.rs +++ b/fuzz/fuzz_targets/successor_of_prefix_even_more.rs @@ -30,5 +30,5 @@ fuzz_target!(|data: (PathRc<4, 4, 16>, PathRc<4, 4, 16>)| { .unwrap(), ]; - test_successor_of_prefix(baseline, candidate, &unsucceedables); + test_greater_but_not_prefixed(baseline, candidate, &unsucceedables); }); diff --git a/fuzz/fuzz_targets/successor_of_prefix_more.rs b/fuzz/fuzz_targets/successor_of_prefix_more.rs index 8db5e90..61837d8 100644 --- a/fuzz/fuzz_targets/successor_of_prefix_more.rs +++ b/fuzz/fuzz_targets/successor_of_prefix_more.rs @@ -23,5 +23,5 @@ fuzz_target!(|data: (PathRc<3, 3, 3>, PathRc<3, 3, 3>)| { .unwrap(), ]; - test_successor_of_prefix(baseline, candidate, &unsucceedables); + test_greater_but_not_prefixed(baseline, candidate, &unsucceedables); }); diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs index 21f5c0f..d92cd10 100644 --- a/fuzz/src/path.rs +++ b/fuzz/src/path.rs @@ -90,7 +90,7 @@ pub trait PathComponent: Eq + AsRef<[u8]> + Clone + PartialOrd + Ord { } /// Return the least component which is greater than `self` but which is not prefixed by `self`. - fn prefix_successor(&self) -> Option { + fn greater_but_not_prefixed(&self) -> Option { for i in (0..self.len()).rev() { if self.as_ref()[i] != 255 { // Since we are not adjusting the length of the component this will always succeed. @@ -308,7 +308,7 @@ impl PathRc } /// Return the least path that is greater than `self` and which is not prefixed by `self`, or `None` if `self` is the empty path *or* if `self` is the greatest path. - pub fn successor_of_prefix(&self) -> Option { + pub fn greater_but_not_prefixed(&self) -> Option { for (i, component) in self.components().enumerate().rev() { if let Some(successor_comp) = component.try_append_zero_byte() { if let Ok(path) = self.create_prefix(i).append(successor_comp) { @@ -316,7 +316,7 @@ impl PathRc } } - if let Some(successor_comp) = component.prefix_successor() { + if let Some(successor_comp) = component.greater_but_not_prefixed() { return self.create_prefix(i).append(successor_comp).ok(); } } @@ -721,47 +721,47 @@ pub fn test_successor( } } -pub fn test_successor_of_prefix( +pub fn test_greater_but_not_prefixed( baseline: PathRc, candidate: PathRc, unsucceedable: &[PathRc], ) { - let prefix_successor = baseline.successor_of_prefix(); + let greater_but_not_prefixed = baseline.greater_but_not_prefixed(); - match prefix_successor { + match greater_but_not_prefixed { None => { if !unsucceedable.iter().any(|unsuc| unsuc == &baseline) { println!("\n\n\n"); println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); + println!("successor: {:?}", greater_but_not_prefixed); println!("candidate: {:?}", candidate); panic!("returned None when the path was NOT the greatest path! BoooOOOoo\n\n\n\n"); } } - Some(prefix_successor) => { - if prefix_successor <= baseline { + Some(greater_but_not_prefixed) => { + if greater_but_not_prefixed <= baseline { println!("\n\n\n"); println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); + println!("successor: {:?}", greater_but_not_prefixed); println!("candidate: {:?}", candidate); panic!("the successor is meant to be greater than the baseline, but wasn't!! BOOOOOOOOO\n\n\n\n"); } - if prefix_successor.is_prefixed_by(&baseline) { + if greater_but_not_prefixed.is_prefixed_by(&baseline) { println!("\n\n\n"); println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); + println!("successor: {:?}", greater_but_not_prefixed); println!("candidate: {:?}", candidate); panic!("successor was prefixed by the path it was derived from! BoooOOooOOooOo\n\n\n\n"); } if !baseline.is_prefix_of(&candidate) - && candidate < prefix_successor + && candidate < greater_but_not_prefixed && candidate > baseline { println!("\n\n\n"); println!("baseline: {:?}", baseline); - println!("successor: {:?}", prefix_successor); + println!("successor: {:?}", greater_but_not_prefixed); println!("candidate: {:?}", candidate); panic!( @@ -984,6 +984,16 @@ pub fn assert_isomorphic_paths {} + (Some(succ_ctrl1), Some(succ_p1)) => { + assert_paths_are_equal(&succ_ctrl1, &succ_p1); + } + _ => { + panic!("Not good (greater_but_not_prefixed)"); + } + } + assert_eq!(ctrl1 == ctrl2, p1 == p2); From baa52e6a1d689164f2af969bc93d5876b4cb0375 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Mon, 15 Jul 2024 17:02:11 +0200 Subject: [PATCH 10/14] Add OwnedComponent struct --- data-model/src/path.rs | 97 ++++++++++++++++++++++++++++++++++++++++-- fuzz/src/path.rs | 23 +++++++--- 2 files changed, 110 insertions(+), 10 deletions(-) diff --git a/data-model/src/path.rs b/data-model/src/path.rs index b8b013c..d446dec 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -62,6 +62,65 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Borrow<[u8]> for Component<'a, MAX_C } } +/// An owned [component](https://willowprotocol.org/specs/data-model/index.html#Component) of a Willow Path that uses reference counting for cheap cloning. +/// +/// This type enforces a const-generic [maximum component length](https://willowprotocol.org/specs/data-model/index.html#max_component_length). Use the `AsRef`, `DeRef`, or `Borrow` implementation to access the immutable byte slice. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct OwnedComponent(Bytes); + +impl OwnedComponent { + /// Create an `OwnedComponent` by copying data from a byte slice. Return `None` if the slice is longer than `MaxComponentLength`. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the length of the slice. Performs a single allocation of `O(n)` bytes. + pub fn new(data: &[u8]) -> Option { + if data.len() <= MAX_COMPONENT_LENGTH { + return Some(unsafe { Self::new_unchecked(data) }); // Safe because we just checked the length. + } else { + return None; + } + } + + /// Create an `OwnedComponent` by copying data from a byte slice, without verifying its length. + /// + /// #### Complexity + /// + /// Runs in `O(n)`, where `n` is the length of the slice. Performs a single allocation of `O(n)` bytes. + pub unsafe fn new_unchecked(data: &[u8]) -> Self { + Self(Bytes::copy_from_slice(data)) + } + + /// Create an empty component. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn new_empty() -> Self { + return Self(Bytes::new()); + } +} + +impl Deref for OwnedComponent { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl AsRef<[u8]> for OwnedComponent { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl Borrow<[u8]> for OwnedComponent { + fn borrow(&self) -> &[u8] { + self.0.borrow() + } +} + #[derive(Debug)] /// An error arising from trying to construct a invalid [`Path`] from valid components. pub enum InvalidPathError { @@ -79,7 +138,7 @@ pub struct Path { /// The data of the underlying path. data: HeapEncoding, /// Number of components of the `data` to consider for this particular path. Must be less than or equal to the total number of components. - /// This field enables cheap prefix creation by cloning the heap data and adjusting the `component_count`. + /// This field enables cheap prefix creation by cloning the heap data and adjusting the `component_count`. component_count: usize, } @@ -284,6 +343,17 @@ impl Path { return HeapEncoding::::get_component(self.data.as_ref(), i); } + /// Get an owned handle to the `i`-th [`Component`] of this path. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn get_owned_component(&self, i: usize) -> Option> { + let start = HeapEncoding::::start_offset_of_component(self.data.0.as_ref(), i)?; + let end = HeapEncoding::::end_offset_of_component(self.data.0.as_ref(), i)?; + return Some(OwnedComponent(self.data.0.slice(start..end))); + } + /// Create an iterator over the components of this path. /// /// Stepping the iterator takes `O(1)` time and performs no memory allocations. @@ -300,6 +370,23 @@ impl Path { }) } + /// Create an iterator over owned handles to the components of this path. + /// + /// Stepping the iterator takes `O(1)` time and performs no memory allocations. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn owned_components( + &self, + ) -> impl DoubleEndedIterator> + + ExactSizeIterator> + + '_ { + (0..self.get_component_count()).map(|i| { + self.get_owned_component(i).unwrap() // Only `None` if `i >= self.get_component_count()` + }) + } + /// Create a new path that consists of the first `length` components. More efficient than creating a new [`Path`] from scratch. /// /// Returns `None` if `length` is greater than `self.get_component_count()`. @@ -480,9 +567,10 @@ impl Path { if let Some(next_component_length) = next_component_length { // Yay, we can replace the i-th comopnent and then we are done. - + let mut buf = clone_prefix_and_lengthen_final_component(self, i, 0); - let length_of_prefix = HeapEncoding::::get_sum_of_lengths_for_component(&buf, i).unwrap(); + let length_of_prefix = + HeapEncoding::::get_sum_of_lengths_for_component(&buf, i).unwrap(); // Update the length of the final component. buf_set_final_component_length( @@ -494,7 +582,8 @@ impl Path { // Increment the byte at position `next_component_length` of the final component. let offset = HeapEncoding::::start_offset_of_component(buf.as_ref(), i) .unwrap() - + next_component_length - 1; + + next_component_length + - 1; let byte = buf.as_ref()[offset]; // guaranteed < 255... buf.as_mut()[offset] = byte + 1; // ... hence no overflow here. diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs index d92cd10..aa9c087 100644 --- a/fuzz/src/path.rs +++ b/fuzz/src/path.rs @@ -954,6 +954,11 @@ pub fn assert_isomorphic_paths assert_eq!(ctrl1.get_component(i), None), + Some(comp) => assert_eq!(ctrl1.get_component(i).unwrap().as_ref(), comp.as_ref()), + } } assert!(ctrl1 @@ -984,7 +989,10 @@ pub fn assert_isomorphic_paths {} (Some(succ_ctrl1), Some(succ_p1)) => { assert_paths_are_equal(&succ_ctrl1, &succ_p1); @@ -994,7 +1002,6 @@ pub fn assert_isomorphic_paths( ctrl: &PathRc, p: &Path, ) { - assert!(ctrl - .components() - .map(|comp| comp.as_ref()) - .eq(p.components().map(|comp| comp.into_inner())), "Unequal paths.\nctrl: {:?}\np: {:?}", ctrl, p); + assert!( + ctrl.components() + .map(|comp| comp.as_ref()) + .eq(p.components().map(|comp| comp.into_inner())), + "Unequal paths.\nctrl: {:?}\np: {:?}", + ctrl, + p + ); } From 42aecf6c6c27bcb80c9382738ec1907cf19a970b Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Mon, 15 Jul 2024 17:06:28 +0200 Subject: [PATCH 11/14] Implement Error trait for InvalidPathError --- data-model/src/path.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data-model/src/path.rs b/data-model/src/path.rs index d446dec..dfa5c99 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -130,6 +130,27 @@ pub enum InvalidPathError { TooManyComponents, } +impl core::fmt::Display for InvalidPathError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InvalidPathError::PathTooLong => { + write!( + f, + "Total length of a path in bytes exceeded the maximum path length" + ) + } + InvalidPathError::TooManyComponents => { + write!( + f, + "Number of components of a path exceeded the maximum component count" + ) + } + } + } +} + +impl core::error::Error for InvalidPathError {} + /// An immutable Willow [path](https://willowprotocol.org/specs/data-model/index.html#Path). Thread-safe, cheap to clone, cheap to take prefixes of, expensive to append to. /// /// Enforces that each component has a length of at most `MCL` ([**m**ax\_**c**omponent\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_component_length)), that each path has at most `MCC` ([**m**ax\_**c**omponent\_**c**count](https://willowprotocol.org/specs/data-model/index.html#max_component_count)) components, and that the total size in bytes of all components is at most `MPL` ([**m**ax\_**p**ath\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_path_length)). From 049b6050fc91278d54b880f939cc9b3ddbb86570 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Mon, 15 Jul 2024 21:51:48 +0200 Subject: [PATCH 12/14] Add suffix iterators --- data-model/src/path.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/data-model/src/path.rs b/data-model/src/path.rs index dfa5c99..6e6bffe 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -386,7 +386,22 @@ impl Path { &'s self, ) -> impl DoubleEndedIterator> + ExactSizeIterator> { - (0..self.get_component_count()).map(|i| { + self.suffix_components(0) + } + + /// Create an iterator over the components of this path, starting at the `i`-th component. If `i` is greater than or equal to the number of components, the iterator yields zero items. + /// + /// Stepping the iterator takes `O(1)` time and performs no memory allocations. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn suffix_components<'s>( + &'s self, + i: usize, + ) -> impl DoubleEndedIterator> + ExactSizeIterator> + { + (i..self.get_component_count()).map(|i| { self.get_component(i).unwrap() // Only `None` if `i >= self.get_component_count()` }) } @@ -403,7 +418,23 @@ impl Path { ) -> impl DoubleEndedIterator> + ExactSizeIterator> + '_ { - (0..self.get_component_count()).map(|i| { + self.suffix_owned_components(0) + } + + /// Create an iterator over owned handles to the components of this path, starting at the `i`-th component. If `i` is greater than or equal to the number of components, the iterator yields zero items. + /// + /// Stepping the iterator takes `O(1)` time and performs no memory allocations. + /// + /// #### Complexity + /// + /// Runs in `O(1)`, performs no allocations. + pub fn suffix_owned_components( + &self, + i: usize, + ) -> impl DoubleEndedIterator> + + ExactSizeIterator> + + '_ { + (i..self.get_component_count()).map(|i| { self.get_owned_component(i).unwrap() // Only `None` if `i >= self.get_component_count()` }) } From 891f0901a9c0a5e073c70263bb9b8fa401117128 Mon Sep 17 00:00:00 2001 From: Sam Gwilym Date: Fri, 19 Jul 2024 17:15:05 +0100 Subject: [PATCH 13/14] Update entry, grouping, parameters for new Path + Obey clippy --- data-model/src/entry.rs | 44 ++++---- data-model/src/grouping/area.rs | 59 ++++++----- data-model/src/grouping/area_of_interest.rs | 15 ++- data-model/src/grouping/range_3d.rs | 108 +++++++++++--------- data-model/src/lib.rs | 7 +- data-model/src/parameters.rs | 13 ++- data-model/src/path.rs | 96 ++++++++--------- fuzz/src/path.rs | 61 +++++------ 8 files changed, 205 insertions(+), 198 deletions(-) diff --git a/data-model/src/entry.rs b/data-model/src/entry.rs index 64bce32..55979c1 100644 --- a/data-model/src/entry.rs +++ b/data-model/src/entry.rs @@ -1,4 +1,4 @@ -use super::{ +use crate::{ parameters::{IsAuthorisedWrite, NamespaceId, PayloadDigest, SubspaceId}, path::Path, }; @@ -18,11 +18,10 @@ pub type Timestamp = u64; /// - `P` - The type used for [`Path`]s. /// - `PD` - The type used for [`PayloadDigest`]. #[derive(Debug, PartialEq, Eq, Clone)] -pub struct Entry +pub struct Entry where N: NamespaceId, S: SubspaceId, - P: Path, PD: PayloadDigest, { /// The identifier of the namespace to which the [`Entry`] belongs. @@ -30,7 +29,7 @@ where /// The identifier of the subspace to which the [`Entry`] belongs. pub subspace_id: S, /// The [`Path`] to which the [`Entry`] was written. - pub path: P, + pub path: Path, /// The claimed creation time of the [`Entry`]. pub timestamp: Timestamp, /// The length of the Payload in bytes. @@ -39,11 +38,10 @@ where pub payload_digest: PD, } -impl Entry +impl Entry where N: NamespaceId, S: SubspaceId, - P: Path, PD: PayloadDigest, { /// Return if this [`Entry`] is newer than another using their timestamps. @@ -72,23 +70,25 @@ pub struct UnauthorisedWriteError; /// - `P` - The type used for [`Path`]s. /// - `PD` - The type used for [`PayloadDigest`]. /// - `AT` - The type used for the [`AuthorisationToken` (willowprotocol.org)](https://willowprotocol.org/specs/data-model/index.html#AuthorisationToken). -pub struct AuthorisedEntry(pub Entry, pub AT) +pub struct AuthorisedEntry( + pub Entry, + pub AT, +) where N: NamespaceId, S: SubspaceId, - P: Path, PD: PayloadDigest; -impl AuthorisedEntry +impl + AuthorisedEntry where N: NamespaceId, S: SubspaceId, - P: Path, PD: PayloadDigest, { /// Construct an [`AuthorisedEntry`] if the token permits the writing of this entry, otherwise return an [`UnauthorisedWriteError`] - pub fn new>( - entry: Entry, + pub fn new>( + entry: Entry, token: AT, is_authorised_write: IAW, ) -> Result { @@ -102,7 +102,7 @@ where #[cfg(test)] mod tests { - use crate::path::{PathComponent, PathComponentBox, PathRc}; + use crate::path::Component; use super::*; @@ -131,7 +131,8 @@ mod tests { let e_a1 = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId::default(), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), payload_digest: FakePayloadDigest::default(), payload_length: 0, timestamp: 20, @@ -140,7 +141,8 @@ mod tests { let e_a2 = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId::default(), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), payload_digest: FakePayloadDigest::default(), payload_length: 0, timestamp: 10, @@ -151,7 +153,8 @@ mod tests { let e_b1 = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId::default(), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), payload_digest: FakePayloadDigest(2), payload_length: 0, timestamp: 10, @@ -160,7 +163,8 @@ mod tests { let e_b2 = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId::default(), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), payload_digest: FakePayloadDigest(1), payload_length: 0, timestamp: 10, @@ -171,7 +175,8 @@ mod tests { let e_c1 = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId::default(), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), payload_digest: FakePayloadDigest::default(), payload_length: 2, timestamp: 20, @@ -180,7 +185,8 @@ mod tests { let e_c2 = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId::default(), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), payload_digest: FakePayloadDigest::default(), payload_length: 1, timestamp: 20, diff --git a/data-model/src/grouping/area.rs b/data-model/src/grouping/area.rs index 44f8e80..0b73c4b 100644 --- a/data-model/src/grouping/area.rs +++ b/data-model/src/grouping/area.rs @@ -39,22 +39,22 @@ impl AreaSubspace { /// A grouping of entries. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#areas). #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Area { +pub struct Area { /// To be included in this [`Area`], an [`Entry`]’s `subspace_id` must be equal to the subspace_id, unless it is any. pub subspace: AreaSubspace, /// To be included in this [`Area`], an [`Entry`]’s `path` must be prefixed by the path. - pub path: P, + pub path: Path, /// To be included in this [`Area`], an [`Entry`]’s `timestamp` must be included in the times. pub times: Range, } -impl Area { +impl Area { /// Return an [`Area`] which includes all entries. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#full_area). pub fn full() -> Self { Self { subspace: AreaSubspace::Any, - path: P::empty(), + path: Path::new_empty(), times: Range::new_open(0), } } @@ -64,7 +64,7 @@ impl Area { pub fn subspace(sub: S) -> Self { Self { subspace: AreaSubspace::Id(sub), - path: P::empty(), + path: Path::new_empty(), times: Range::new_open(0), } } @@ -72,7 +72,7 @@ impl Area { /// Return whether an [`Area`] [includes](https://willowprotocol.org/specs/grouping-entries/index.html#area_include) an [`Entry`]. pub fn includes_entry( &self, - entry: &Entry, + entry: &Entry, ) -> bool { self.subspace.includes(&entry.subspace_id) && self.path.is_prefix_of(&entry.path) @@ -81,7 +81,7 @@ impl Area { /// Return the intersection of this [`Area`] with another. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#area_intersection). - pub fn intersection(&self, other: &Area) -> Option { + pub fn intersection(&self, other: &Area) -> Option { let subspace_id = self.subspace.intersection(&other.subspace)?; let path = if self.path.is_prefix_of(&other.path) { Some(other.path.clone()) @@ -102,7 +102,7 @@ impl Area { #[cfg(test)] mod tests { - use crate::path::{PathComponent, PathComponentBox, PathRc}; + use crate::path::Component; use super::*; @@ -171,13 +171,13 @@ mod tests { #[test] fn area_full() { - let full_area = Area::>::full(); + let full_area = Area::::full(); assert_eq!( full_area, Area { subspace: AreaSubspace::Any, - path: PathRc::empty(), + path: Path::new_empty(), times: Range::new_open(0) } ) @@ -185,14 +185,13 @@ mod tests { #[test] fn area_subspace() { - let subspace_area = - Area::>::subspace(FakeSubspaceId(7)); + let subspace_area = Area::::subspace(FakeSubspaceId(7)); assert_eq!( subspace_area, Area { subspace: AreaSubspace::Id(FakeSubspaceId(7)), - path: PathRc::empty(), + path: Path::new_empty(), times: Range::new_open(0) } ) @@ -200,55 +199,55 @@ mod tests { #[test] fn area_intersects() { - let empty_intersection_subspace = Area::> { + let empty_intersection_subspace = Area:: { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::empty(), + path: Path::new_empty(), times: Range::new_open(0), } .intersection(&Area { subspace: AreaSubspace::Id(FakeSubspaceId(2)), - path: PathRc::empty(), + path: Path::new_empty(), times: Range::new_open(0), }); assert!(empty_intersection_subspace.is_none()); - let empty_intersection_path = Area::> { + let empty_intersection_path = Area:: { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), + path: Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), times: Range::new_open(0), } .intersection(&Area { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + path: Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), times: Range::new_open(0), }); assert!(empty_intersection_path.is_none()); - let empty_intersection_time = Area::> { + let empty_intersection_time = Area:: { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::empty(), + path: Path::new_empty(), times: Range::new_closed(0, 1).unwrap(), } .intersection(&Area { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::empty(), + path: Path::new_empty(), times: Range::new_closed(2, 3).unwrap(), }); assert!(empty_intersection_time.is_none()); - let intersection = Area::> { + let intersection = Area:: { subspace: AreaSubspace::Any, - path: PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + path: Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), times: Range::new_closed(0, 10).unwrap(), } .intersection(&Area { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::new(&[ - PathComponentBox::new(&[b'1']).unwrap(), - PathComponentBox::new(&[b'2']).unwrap(), + path: Path::new_from_slice(&[ + Component::new(&[b'1']).unwrap(), + Component::new(&[b'2']).unwrap(), ]) .unwrap(), times: Range::new_closed(5, 15).unwrap(), @@ -260,9 +259,9 @@ mod tests { intersection.unwrap(), Area { subspace: AreaSubspace::Id(FakeSubspaceId(1)), - path: PathRc::new(&[ - PathComponentBox::new(&[b'1']).unwrap(), - PathComponentBox::new(&[b'2']).unwrap(), + path: Path::new_from_slice(&[ + Component::new(&[b'1']).unwrap(), + Component::new(&[b'2']).unwrap(), ]) .unwrap(), times: Range::new_closed(5, 10).unwrap(), diff --git a/data-model/src/grouping/area_of_interest.rs b/data-model/src/grouping/area_of_interest.rs index 335db19..c84b006 100644 --- a/data-model/src/grouping/area_of_interest.rs +++ b/data-model/src/grouping/area_of_interest.rs @@ -1,20 +1,25 @@ -use crate::{grouping::area::Area, parameters::SubspaceId, path::Path}; +use crate::{grouping::area::Area, parameters::SubspaceId}; /// A grouping of [`Entry`]s that are among the newest in some [`Store`]. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#aois). -pub struct AreaOfInterest { +pub struct AreaOfInterest { /// To be included in this [`AreaOfInterest`], an [`Entry`] must be included in the [`Area`]. - pub area: Area, + pub area: Area, /// To be included in this AreaOfInterest, an Entry’s timestamp must be among the max_count greatest Timestamps, unless max_count is zero. pub max_count: u64, /// The total payload_lengths of all included Entries is at most max_size, unless max_size is zero. pub max_size: u64, } -impl AreaOfInterest { +impl + AreaOfInterest +{ /// Return the intersection of this [`AreaOfInterest`] with another. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#aoi_intersection). - pub fn intersection(&self, other: AreaOfInterest) -> Option> { + pub fn intersection( + &self, + other: AreaOfInterest, + ) -> Option> { match self.area.intersection(&other.area) { None => None, Some(area_intersection) => Some(Self { diff --git a/data-model/src/grouping/range_3d.rs b/data-model/src/grouping/range_3d.rs index 4f70932..e1b3125 100644 --- a/data-model/src/grouping/range_3d.rs +++ b/data-model/src/grouping/range_3d.rs @@ -8,23 +8,25 @@ use crate::{ /// A three-dimensional range that includes every Entry included in all three of its ranges. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#D3Range). #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Range3d { +pub struct Range3d { /// A range of [`SubspaceId`]s. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#SubspaceRange). pub subspaces: Range, /// A range of [`Path`]s. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#PathRange). - pub paths: Range

, + pub paths: Range>, /// A range of [`Timestamp`]s. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#TimeRange). pub times: Range, } -impl Range3d { +impl + Range3d +{ /// Return whether an [`Entry`] is [included](https://willowprotocol.org/specs/grouping-entries/index.html#d3_range_include) by this 3d range. pub fn includes_entry( &self, - entry: &Entry, + entry: &Entry, ) -> bool { self.subspaces.includes(&entry.subspace_id) && self.paths.includes(&entry.path) @@ -32,7 +34,7 @@ impl Range3d { } /// Return the intersection between this [`Range3d`] and another. - pub fn intersection(&self, other: &Range3d) -> Option { + pub fn intersection(&self, other: &Range3d) -> Option { let paths = self.paths.intersection(&other.paths)?; let times = self.times.intersection(&other.times)?; let subspaces = self.subspaces.intersection(&other.subspaces)?; @@ -44,26 +46,31 @@ impl Range3d { } } -impl Default for Range3d { +impl Default + for Range3d +{ /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#default_3d_range). fn default() -> Self { Self { subspaces: Range::::default(), - paths: Range::new_open(P::empty()), + paths: Range::new_open(Path::new_empty()), times: Range::new_open(0), } } } -impl From> for Range3d { - fn from(_value: Area) -> Self { +impl + From> for Range3d +{ + fn from(_value: Area) -> Self { todo!("Need to add successor fns to SubspaceId and Paths first.") } } #[cfg(test)] mod tests { - use crate::path::{PathComponent, PathComponentBox, PathRc}; + + use crate::path::Component; use super::*; @@ -92,7 +99,8 @@ mod tests { let entry = Entry { namespace_id: FakeNamespaceId::default(), subspace_id: FakeSubspaceId(10), - path: PathRc::::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(), + path: Path::::new_from_slice(&[Component::new(&[b'a']).unwrap()]) + .unwrap(), timestamp: 500, payload_length: 10, payload_digest: FakePayloadDigest::default(), @@ -101,9 +109,9 @@ mod tests { let range_including = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(9), FakeSubspaceId(11)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'b']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'b']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(400, 600).unwrap(), @@ -114,9 +122,9 @@ mod tests { let range_subspaces_excluding = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(11), FakeSubspaceId(13)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'b']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'b']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(400, 600).unwrap(), @@ -127,9 +135,9 @@ mod tests { let range_paths_excluding = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(9), FakeSubspaceId(11)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(400, 600).unwrap(), @@ -140,9 +148,9 @@ mod tests { let range_times_excluding = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(9), FakeSubspaceId(11)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'b']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'b']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(100, 200).unwrap(), @@ -156,9 +164,9 @@ mod tests { let empty_intersection_subspace = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(11), FakeSubspaceId(13)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(0, 100).unwrap(), @@ -166,9 +174,9 @@ mod tests { .intersection(&Range3d { subspaces: Range::::new_closed(FakeSubspaceId(0), FakeSubspaceId(3)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(0, 100).unwrap(), @@ -179,9 +187,9 @@ mod tests { let empty_intersection_path = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(0), FakeSubspaceId(3)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(0, 100).unwrap(), @@ -189,9 +197,9 @@ mod tests { .intersection(&Range3d { subspaces: Range::::new_closed(FakeSubspaceId(0), FakeSubspaceId(3)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'4']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'6']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'4']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'6']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(0, 100).unwrap(), @@ -202,9 +210,9 @@ mod tests { let empty_intersection_time = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(0), FakeSubspaceId(3)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(0, 100).unwrap(), @@ -212,9 +220,9 @@ mod tests { .intersection(&Range3d { subspaces: Range::::new_closed(FakeSubspaceId(0), FakeSubspaceId(3)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(200, 300).unwrap(), @@ -225,9 +233,9 @@ mod tests { let intersection = Range3d { subspaces: Range::::new_closed(FakeSubspaceId(0), FakeSubspaceId(3)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'6']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'6']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(0, 100).unwrap(), @@ -235,9 +243,9 @@ mod tests { .intersection(&Range3d { subspaces: Range::::new_closed(FakeSubspaceId(2), FakeSubspaceId(4)) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'3']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'9']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'3']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'9']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(50, 150).unwrap(), @@ -253,9 +261,9 @@ mod tests { FakeSubspaceId(3) ) .unwrap(), - paths: Range::>::new_closed( - PathRc::new(&[PathComponentBox::new(&[b'3']).unwrap()]).unwrap(), - PathRc::new(&[PathComponentBox::new(&[b'6']).unwrap()]).unwrap(), + paths: Range::>::new_closed( + Path::new_from_slice(&[Component::new(&[b'3']).unwrap()]).unwrap(), + Path::new_from_slice(&[Component::new(&[b'6']).unwrap()]).unwrap(), ) .unwrap(), times: Range::::new_closed(50, 100).unwrap(), diff --git a/data-model/src/lib.rs b/data-model/src/lib.rs index 4239f81..67163d2 100644 --- a/data-model/src/lib.rs +++ b/data-model/src/lib.rs @@ -1,6 +1,7 @@ #![feature(debug_closure_helpers)] +#![feature(error_in_core)] -// pub mod entry; -// pub mod grouping; -// pub mod parameters; +pub mod entry; +pub mod grouping; +pub mod parameters; pub mod path; diff --git a/data-model/src/parameters.rs b/data-model/src/parameters.rs index 676892d..d94397f 100644 --- a/data-model/src/parameters.rs +++ b/data-model/src/parameters.rs @@ -1,4 +1,4 @@ -use crate::{entry::Entry, path::Path}; +use crate::entry::Entry; /// A type for identifying [namespaces](https://willowprotocol.org/specs/data-model/index.html#namespace). /// [Definition](https://willowprotocol.org/specs/data-model/index.html#NamespaceId). @@ -31,7 +31,14 @@ pub trait PayloadDigest: Ord + Default + Clone {} /// - `P` - The type used for [`Path`]s. /// - `PD` - The type used for [`PayloadDigest`]. /// - `AT` - The type used for the [`AuthorisationToken` (willowprotocol.org)](https://willowprotocol.org/specs/data-model/index.html#AuthorisationToken). -pub trait IsAuthorisedWrite: - Fn(&Entry, &AT) -> bool +pub trait IsAuthorisedWrite< + const MCL: usize, + const MCC: usize, + const MPL: usize, + N: NamespaceId, + S: SubspaceId, + PD: PayloadDigest, + AT, +>: Fn(&Entry, &AT) -> bool { } diff --git a/data-model/src/path.rs b/data-model/src/path.rs index 6e6bffe..f7cd528 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -23,7 +23,7 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Component<'a, MAX_COMPONENT_LENGTH> if slice.len() <= MAX_COMPONENT_LENGTH { return Some(unsafe { Self::new_unchecked(slice) }); // Safe because we just checked the length. } else { - return None; + None } } @@ -34,11 +34,11 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Component<'a, MAX_COMPONENT_LENGTH> /// Create an empty component. pub fn new_empty() -> Self { - return Self(&[]); + Self(&[]) } pub fn into_inner(self) -> &'a [u8] { - return self.0; + self.0 } } @@ -46,19 +46,19 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Deref for Component<'a, MAX_COMPONEN type Target = [u8]; fn deref(&self) -> &Self::Target { - &self.0 + self.0 } } impl<'a, const MAX_COMPONENT_LENGTH: usize> AsRef<[u8]> for Component<'a, MAX_COMPONENT_LENGTH> { fn as_ref(&self) -> &[u8] { - &self.0 + self.0 } } impl<'a, const MAX_COMPONENT_LENGTH: usize> Borrow<[u8]> for Component<'a, MAX_COMPONENT_LENGTH> { fn borrow(&self) -> &[u8] { - &self.0 + self.0 } } @@ -76,9 +76,9 @@ impl OwnedComponent { /// Runs in `O(n)`, where `n` is the length of the slice. Performs a single allocation of `O(n)` bytes. pub fn new(data: &[u8]) -> Option { if data.len() <= MAX_COMPONENT_LENGTH { - return Some(unsafe { Self::new_unchecked(data) }); // Safe because we just checked the length. + Some(unsafe { Self::new_unchecked(data) }) // Safe because we just checked the length. } else { - return None; + None } } @@ -97,7 +97,7 @@ impl OwnedComponent { /// /// Runs in `O(1)`, performs no allocations. pub fn new_empty() -> Self { - return Self(Bytes::new()); + Self(Bytes::new()) } } @@ -170,13 +170,13 @@ impl Path { /// /// Runs in `O(1)`, performs no allocations. pub fn new_empty() -> Self { - return Path { + Path { // 16 zero bytes, to work even on platforms on which `usize` has a size of 16. data: HeapEncoding(Bytes::from_static(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ])), component_count: 0, - }; + } } /// Construct a singleton path, i.e., a path of exactly one component. @@ -186,9 +186,9 @@ impl Path { /// #### Complexity /// /// Runs in `O(n)`, where `n` is the length of the component. Performs a single allocation of `O(n)` bytes. - pub fn new_singleton<'a>(comp: Component<'a, MCL>) -> Result { + pub fn new_singleton(comp: Component) -> Result { if 1 > MCC { - return Err(InvalidPathError::TooManyComponents); + Err(InvalidPathError::TooManyComponents) } else if comp.len() > MPL { return Err(InvalidPathError::PathTooLong); } else { @@ -251,10 +251,10 @@ impl Path { panic!("Tried to construct a path of total length {}, but got components whose accumulated length was {}.", total_length, accumulated_component_length); } - return Ok(Path { + Ok(Path { data: HeapEncoding(buf.freeze()), component_count, - }); + }) } /// Construct a path from a slice of components. @@ -264,16 +264,13 @@ impl Path { /// #### Complexity /// /// Runs in `O(n)`, where `n` is the total length of the path in bytes. Performs a single allocation of `O(n)` bytes. - pub fn new_from_slice<'a>(components: &[Component<'a, MCL>]) -> Result { + pub fn new_from_slice(components: &[Component]) -> Result { let mut total_length = 0; for comp in components { total_length += comp.len(); } - return Self::new_from_iter( - total_length, - &mut components.iter().map(|comp_ref| *comp_ref), - ); + return Self::new_from_iter(total_length, &mut components.iter().copied()); } /// Construct a new path by appending a component to this one. @@ -283,7 +280,7 @@ impl Path { /// #### Complexity /// /// Runs in `O(n)`, where `n` is the total length of the new path in bytes. Performs a single allocation of `O(n)` bytes. - pub fn append<'a>(&self, comp: Component<'a, MCL>) -> Result { + pub fn append(&self, comp: Component) -> Result { let total_length = self.get_path_length() + comp.len(); return Self::new_from_iter( total_length, @@ -298,10 +295,7 @@ impl Path { /// #### Complexity /// /// Runs in `O(n)`, where `n` is the total length of the new path in bytes. Performs a single allocation of `O(n)` bytes. - pub fn append_slice<'a>( - &self, - components: &[Component<'a, MCL>], - ) -> Result { + pub fn append_slice(&self, components: &[Component]) -> Result { let mut total_length = self.get_path_length(); for comp in components { total_length += comp.len(); @@ -309,10 +303,7 @@ impl Path { return Self::new_from_iter( total_length, - &mut ExactLengthChain::new( - self.components(), - components.iter().map(|comp_ref| *comp_ref), - ), + &mut ExactLengthChain::new(self.components(), components.iter().copied()), ); } @@ -324,7 +315,7 @@ impl Path { /// /// Runs in `O(1)`, performs no allocations. pub fn get_component_count(&self) -> usize { - return self.component_count; + self.component_count } /// Return whether this path has zero components. @@ -345,7 +336,7 @@ impl Path { /// Runs in `O(1)`, performs no allocations. pub fn get_path_length(&self) -> usize { if self.component_count == 0 { - return 0; + 0 } else { return HeapEncoding::::get_sum_of_lengths_for_component( self.data.as_ref(), @@ -360,7 +351,7 @@ impl Path { /// #### Complexity /// /// Runs in `O(1)`, performs no allocations. - pub fn get_component<'s>(&'s self, i: usize) -> Option> { + pub fn get_component(&self, i: usize) -> Option> { return HeapEncoding::::get_component(self.data.as_ref(), i); } @@ -372,7 +363,7 @@ impl Path { pub fn get_owned_component(&self, i: usize) -> Option> { let start = HeapEncoding::::start_offset_of_component(self.data.0.as_ref(), i)?; let end = HeapEncoding::::end_offset_of_component(self.data.0.as_ref(), i)?; - return Some(OwnedComponent(self.data.0.slice(start..end))); + Some(OwnedComponent(self.data.0.slice(start..end))) } /// Create an iterator over the components of this path. @@ -382,9 +373,9 @@ impl Path { /// #### Complexity /// /// Runs in `O(1)`, performs no allocations. - pub fn components<'s>( - &'s self, - ) -> impl DoubleEndedIterator> + ExactSizeIterator> + pub fn components( + &self, + ) -> impl DoubleEndedIterator> + ExactSizeIterator> { self.suffix_components(0) } @@ -396,10 +387,10 @@ impl Path { /// #### Complexity /// /// Runs in `O(1)`, performs no allocations. - pub fn suffix_components<'s>( - &'s self, + pub fn suffix_components( + &self, i: usize, - ) -> impl DoubleEndedIterator> + ExactSizeIterator> + ) -> impl DoubleEndedIterator> + ExactSizeIterator> { (i..self.get_component_count()).map(|i| { self.get_component(i).unwrap() // Only `None` if `i >= self.get_component_count()` @@ -448,9 +439,9 @@ impl Path { /// Runs in `O(1)`, performs no allocations. pub fn create_prefix(&self, length: usize) -> Option { if length > self.get_component_count() { - return None; + None } else { - return Some(unsafe { self.create_prefix_unchecked(length) }); + Some(unsafe { self.create_prefix_unchecked(length) }) } } @@ -657,7 +648,7 @@ impl Path { impl PartialEq for Path { fn eq(&self, other: &Self) -> bool { if self.component_count != other.component_count { - return false; + false } else { return self.components().eq(other.components()); } @@ -721,7 +712,7 @@ impl HeapEncoding { Self::get_usize_at_offset(buf, start_offset_in_bytes) } - fn get_component<'s>(buf: &'s [u8], i: usize) -> Option> { + fn get_component(buf: &[u8], i: usize) -> Option> { let start = Self::start_offset_of_component(buf, i)?; let end = Self::end_offset_of_component(buf, i)?; return Some(unsafe { Component::new_unchecked(&buf[start..end]) }); @@ -735,17 +726,16 @@ impl HeapEncoding { fn start_offset_of_component(buf: &[u8], i: usize) -> Option { let metadata_length = (Self::get_component_count(buf) + 1) * size_of::(); if i == 0 { - return Some(metadata_length); + Some(metadata_length) } else { - return Self::get_sum_of_lengths_for_component(buf, i - 1) // Length of everything up until the previous component. - .map(|length| length + metadata_length); + Self::get_sum_of_lengths_for_component(buf, i - 1) // Length of everything up until the previous component. + .map(|length| length + metadata_length) } } fn end_offset_of_component(buf: &[u8], i: usize) -> Option { let metadata_length = (Self::get_component_count(buf) + 1) * size_of::(); - return Self::get_sum_of_lengths_for_component(buf, i) - .map(|length| length + metadata_length); + Self::get_sum_of_lengths_for_component(buf, i).map(|length| length + metadata_length) } fn get_usize_at_offset(buf: &[u8], offset_in_bytes: usize) -> Option { @@ -756,10 +746,10 @@ impl HeapEncoding { let mut usize_bytes = [0u8; size_of::()]; if buf.len() < end { - return None; + None } else { usize_bytes.copy_from_slice(&buf[offset_in_bytes..end]); - return Some(usize::from_ne_bytes(usize_bytes)); + Some(usize::from_ne_bytes(usize_bytes)) } } } @@ -811,7 +801,7 @@ where _ => None, }; - return (lower_a + lower_b, higher); + (lower_a + lower_b, higher) } } @@ -879,7 +869,7 @@ fn clone_prefix_and_lengthen_final_component< let mut buf = BytesMut::with_capacity(buf_capacity); // Write the length of the successor path as the first usize. - buf.extend_from_slice(&((i + 1) as usize).to_ne_bytes()); + buf.extend_from_slice(&(i + 1).to_ne_bytes()); // Next, copy the total path lengths for the first i prefixes. buf.extend_from_slice(&original_slice[size_of::()..size_of::() * (i + 2)]); @@ -893,5 +883,5 @@ fn clone_prefix_and_lengthen_final_component< ..HeapEncoding::::start_offset_of_component(original_slice, i + 1).unwrap()], ); - return buf; + buf } diff --git a/fuzz/src/path.rs b/fuzz/src/path.rs index aa9c087..308864b 100644 --- a/fuzz/src/path.rs +++ b/fuzz/src/path.rs @@ -238,7 +238,7 @@ impl PathRc self.0.len() } - pub fn get_component<'s>(&'s self, i: usize) -> Option<&PathComponentBox> { + pub fn get_component(&self, i: usize) -> Option<&PathComponentBox> { return self.0.get(i); } @@ -642,7 +642,7 @@ mod tests { } fn make_test_path( - vector: &Vec>, + vector: &[Vec], ) -> PathRc { let components: Vec<_> = vector .iter() @@ -793,17 +793,15 @@ pub fn create_path_rc( ) -> Result, Option> { match cp { CreatePath::Empty => Ok(PathRc::empty()), - CreatePath::Singleton(comp) => match PathComponentBox::new(&comp) { - Err(_) => return Err(None), - Ok(comp) => { - return PathRc::new(&[comp]).map_err(|err| Some(err)); - } + CreatePath::Singleton(comp) => match PathComponentBox::new(comp) { + Err(_) => Err(None), + Ok(comp) => PathRc::new(&[comp]).map_err(Some), }, CreatePath::FromIter(raw_material) | CreatePath::FromSlice(raw_material) => { let mut p = PathRc::empty(); for comp in raw_material { - match PathComponentBox::new(&comp) { + match PathComponentBox::new(comp) { Ok(comp) => match p.append(comp) { Err(err) => { return Err(Some(err)); @@ -814,23 +812,21 @@ pub fn create_path_rc( } } - return Ok(p); + Ok(p) } CreatePath::Append(rec, comp) => { let base = create_path_rc(rec)?; - match PathComponentBox::new(&comp) { - Err(_) => return Err(None), - Ok(comp) => { - return base.append(comp).map_err(|err| Some(err)); - } + match PathComponentBox::new(comp) { + Err(_) => Err(None), + Ok(comp) => base.append(comp).map_err(Some), } } CreatePath::AppendSlice(rec, comps) => { let mut base = create_path_rc(rec)?; for comp in comps { - match PathComponentBox::new(&comp) { + match PathComponentBox::new(comp) { Ok(comp) => match base.append(comp) { Err(err) => { return Err(Some(err)); @@ -841,15 +837,15 @@ pub fn create_path_rc( } } - return Ok(base); + Ok(base) } CreatePath::CreatePrefix(rec, len) => { let base = create_path_rc(rec)?; if *len > base.component_count() { - return Err(None); + Err(None) } else { - return Ok(base.create_prefix(*len)); + Ok(base.create_prefix(*len)) } } } @@ -861,10 +857,8 @@ pub fn create_path( match cp { CreatePath::Empty => Ok(Path::new_empty()), CreatePath::Singleton(comp) => match Component::new(comp) { - None => return Err(None), - Some(comp) => { - return Path::new_singleton(comp).map_err(|err| Some(err)); - } + None => Err(None), + Some(comp) => Path::new_singleton(comp).map_err(Some), }, CreatePath::FromIter(raw_material) => { let mut comps = vec![]; @@ -879,8 +873,7 @@ pub fn create_path( } } - return Path::new_from_iter(total_length, &mut comps.into_iter()) - .map_err(|err| Some(err)); + Path::new_from_iter(total_length, &mut comps.into_iter()).map_err(Some) } CreatePath::FromSlice(raw_material) => { let mut comps = vec![]; @@ -893,16 +886,14 @@ pub fn create_path( } } - return Path::new_from_slice(&comps).map_err(|err| Some(err)); + Path::new_from_slice(&comps).map_err(Some) } CreatePath::Append(rec, comp) => { let base = create_path(rec)?; - match Component::new(&comp) { - None => return Err(None), - Some(comp) => { - return base.append(comp).map_err(|err| Some(err)); - } + match Component::new(comp) { + None => Err(None), + Some(comp) => base.append(comp).map_err(Some), } } CreatePath::AppendSlice(rec, raw_material) => { @@ -918,14 +909,14 @@ pub fn create_path( } } - return base.append_slice(&comps).map_err(|err| Some(err)); + base.append_slice(&comps).map_err(Some) } CreatePath::CreatePrefix(rec, len) => { let base = create_path(rec)?; match base.create_prefix(*len) { - Some(yay) => return Ok(yay), - None => return Err(None), + Some(yay) => Ok(yay), + None => Err(None), } } } @@ -975,8 +966,8 @@ pub fn assert_isomorphic_paths Date: Sat, 20 Jul 2024 08:35:50 +0200 Subject: [PATCH 14/14] Make clippy happy --- data-model/src/lib.rs | 1 - data-model/src/path.rs | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/data-model/src/lib.rs b/data-model/src/lib.rs index 67163d2..43c5fc4 100644 --- a/data-model/src/lib.rs +++ b/data-model/src/lib.rs @@ -1,5 +1,4 @@ #![feature(debug_closure_helpers)] -#![feature(error_in_core)] pub mod entry; pub mod grouping; diff --git a/data-model/src/path.rs b/data-model/src/path.rs index f7cd528..3e07fb4 100644 --- a/data-model/src/path.rs +++ b/data-model/src/path.rs @@ -28,6 +28,11 @@ impl<'a, const MAX_COMPONENT_LENGTH: usize> Component<'a, MAX_COMPONENT_LENGTH> } /// Create a `Component` from a byte slice, without verifying its length. + /// + /// #### Safety + /// + /// Supplying a slice of length strictly greater than `MAX_COMPONENT_LENGTH` may trigger undefined behavior, + /// either immediately, or on any subsequent function invocation that operates on the resulting [`Component`]. pub unsafe fn new_unchecked(slice: &'a [u8]) -> Self { Self(slice) } @@ -83,6 +88,11 @@ impl OwnedComponent { } /// Create an `OwnedComponent` by copying data from a byte slice, without verifying its length. + /// + /// #### Safety + /// + /// Supplying a slice of length strictly greater than `MAX_COMPONENT_LENGTH` may trigger undefined behavior, + /// either immediately, or on any subsequent function invocation that operates on the resulting [`OwnedComponent`]. /// /// #### Complexity /// @@ -447,7 +457,10 @@ impl Path { /// Create a new path that consists of the first `length` components. More efficient than creating a new [`Path`] from scratch. /// - /// Undefined behaviour if `length` is greater than `self.get_component_count()`. + /// #### Safety + /// + /// Undefined behaviour if `length` is greater than `self.get_component_count()`. May manifest directly, or at any later + /// function invocation that operates on the resulting [`Path`]. /// /// #### Complexity ///