From fd17773a1ce5bb3b90bb087e29537d2d9840f484 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Tue, 6 Feb 2024 22:26:14 -0500 Subject: [PATCH 1/5] Add docs, utility features, option region Signed-off-by: Moritz Hoffmann --- src/impls/mod.rs | 1 + src/impls/option.rs | 78 ++++++++++++++++++++++++ src/impls/result.rs | 1 + src/impls/slice.rs | 48 ++++++++++++++- src/impls/string.rs | 4 ++ src/lib.rs | 142 +++++++++++++++++++++++++++++++++++++------- 6 files changed, 250 insertions(+), 24 deletions(-) create mode 100644 src/impls/option.rs diff --git a/src/impls/mod.rs b/src/impls/mod.rs index 3115d25..9c3d721 100644 --- a/src/impls/mod.rs +++ b/src/impls/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod mirror; +pub(crate) mod option; pub(crate) mod result; pub(crate) mod slice; pub(crate) mod slice_copy; diff --git a/src/impls/option.rs b/src/impls/option.rs new file mode 100644 index 0000000..917d177 --- /dev/null +++ b/src/impls/option.rs @@ -0,0 +1,78 @@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::{Containerized, CopyOnto, Region, ReserveItems}; + +impl Containerized for Option { + type Region = OptionRegion; +} + +/// A region to hold [`Option`]s. +#[derive(Default, Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct OptionRegion { + inner: T, +} + +impl Region for OptionRegion +where + T: Region, +{ + type ReadItem<'a> = Option> where Self: 'a; + type Index = Option; + + #[inline] + fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { + index.map(|t| self.inner.index(t)) + } + + #[inline] + fn reserve_regions<'a, I>(&mut self, regions: I) + where + Self: 'a, + I: Iterator + Clone, + { + self.inner + .reserve_regions(regions.clone().map(|r| &r.inner)); + } + + #[inline] + fn clear(&mut self) { + self.inner.clear(); + } +} + +impl CopyOnto> for Option +where + TC: Region, + T: CopyOnto, +{ + #[inline] + fn copy_onto(self, target: &mut OptionRegion) -> as Region>::Index { + self.map(|t| t.copy_onto(&mut target.inner)) + } +} + +impl<'a, T: 'a, TC> CopyOnto> for &'a Option +where + TC: Region, + &'a T: CopyOnto, +{ + #[inline] + fn copy_onto(self, target: &mut OptionRegion) -> as Region>::Index { + self.as_ref().map(|t| t.copy_onto(&mut target.inner)) + } +} + +impl<'a, T: 'a, TC> ReserveItems> for &'a Option +where + TC: Region, + &'a T: ReserveItems, +{ + fn reserve_items(target: &mut OptionRegion, items: I) + where + I: Iterator + Clone, + { + ReserveItems::reserve_items(&mut target.inner, items.clone().flat_map(|r| r.as_ref())); + } +} diff --git a/src/impls/result.rs b/src/impls/result.rs index 39b3df2..cfc140d 100644 --- a/src/impls/result.rs +++ b/src/impls/result.rs @@ -7,6 +7,7 @@ impl Containerized for Result { type Region = ResultRegion; } +/// A region to hold [`Result`]s. #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ResultRegion { diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 14cb308..b16a3b4 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -3,7 +3,19 @@ use std::ops::Deref; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{CopyOnto, Region, ReserveItems}; +use crate::{Containerized, CopyOnto, Region, ReserveItems}; + +impl Containerized for Vec { + type Region = SliceRegion; +} + +impl Containerized for [T] { + type Region = SliceRegion; +} + +impl Containerized for [T; N] { + type Region = SliceRegion; +} /// A container representing slices of data. #[derive(Debug, Clone)] @@ -75,7 +87,7 @@ impl<'a, C: Region> ReadSlice<'a, C> { impl<'a, C: Region> Clone for ReadSlice<'a, C> { #[inline] fn clone(&self) -> Self { - Self(self.0, self.1) + *self } } @@ -181,6 +193,38 @@ where } } +impl<'a, T, R: Region, const N: usize> CopyOnto> for &'a [T; N] +where + for<'b> &'b [T]: CopyOnto>, +{ + #[inline] + fn copy_onto(self, target: &mut SliceRegion) -> as Region>::Index { + self.as_slice().copy_onto(target) + } +} + +impl<'a, T: 'a, R: Region, const N: usize> ReserveItems> for &'a [T; N] +where + &'a T: ReserveItems, +{ + fn reserve_items(target: &mut SliceRegion, items: I) + where + I: Iterator + Clone, + { + ReserveItems::reserve_items(target, items.map(|item| item.as_slice())) + } +} + +impl CopyOnto> for [T; N] +where + for<'a> &'a [T]: CopyOnto>, +{ + #[inline] + fn copy_onto(self, target: &mut SliceRegion) -> as Region>::Index { + self.as_slice().copy_onto(target) + } +} + impl<'a, C: Region + 'a> ReserveItems> for &'a (C, &'a [C::Index]) where C::ReadItem<'a>: ReserveItems, diff --git a/src/impls/string.rs b/src/impls/string.rs index 76e0166..3e317b7 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -38,6 +38,10 @@ impl Containerized for String { type Region = StringRegion; } +impl<'a> Containerized for &'a str { + type Region = StringRegion; +} + impl CopyOnto for &String { #[inline] fn copy_onto(self, target: &mut StringRegion) -> ::Index { diff --git a/src/lib.rs b/src/lib.rs index 2049ba2..19d50e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,34 @@ +//! Flatcontainer provides abstractions to represent collections of data in a flat structure. +//! +//! This library contains types and implementations that allow one to collect types deconstructed +//! into their basic components, represented by few allocations. The catch is that the original +//! type may be lost, but the library instead provides an equivalent representation. +//! +//! # Safety +//! +//! This crate is safe to use, and all unsafe code can be explained locally. +//! At the moment, this is only for assuming that utf-8 data is correct, which is true by +//! construction. +//! +//! # Features +//! +//! The `serde` feature controls whether types implement support for serializing and deserializing +//! data. Enabled by default. +//! +//! # Examples +//! +//! The following example shows how to copy data into a [`FlatStack`]: +//! +//! ``` +//! use flatcontainer::*; +//! +//! let mut container = FlatStack::default_impl::>(); +//! container.copy(["Hello", "flatcontainer"]); +//! println!("Element 0: {:?}", container.get(0)); +//! ``` + +#![deny(missing_docs)] + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -9,17 +40,27 @@ pub use impls::slice::SliceRegion; pub use impls::slice_copy::CopyRegion; pub use impls::string::StringRegion; +/// An index into a region. Automatically implemented for relevant types. +/// +/// We require an index to be [`Copy`] and to support serde. #[cfg(feature = "serde")] pub trait Index: Copy + Serialize + for<'a> Deserialize<'a> {} #[cfg(feature = "serde")] impl Deserialize<'a>> Index for T {} +/// An index into a region. Automatically implemented for relevant types. +/// +/// We require an index to be [`Copy`]. #[cfg(not(feature = "serde"))] pub trait Index: Copy {} #[cfg(not(feature = "serde"))] impl Index for T {} -/// A region to store data. +/// A region to absorb presented data and present it as a type with a lifetime. +/// +/// This type absorbs data and provides an index to look up an equivalent representation +/// of this data at a later time. It is up to an implementation to select the appropriate +/// presentation of the data, and what data it can absorb. pub trait Region: Default { /// The type of the data that one gets out of the container. type ReadItem<'a>: CopyOnto @@ -34,12 +75,13 @@ pub trait Region: Default { /// pushing data into the container. fn index(&self, index: Self::Index) -> Self::ReadItem<'_>; - // Ensure that the region can absorb the items of `regions` without reallocation + /// Ensure that the region can absorb the items of `regions` without reallocation fn reserve_regions<'a, I>(&mut self, regions: I) where Self: 'a, I: Iterator + Clone; + /// Remove all elements from this region, but retain allocations if possible. fn clear(&mut self); } @@ -49,13 +91,23 @@ pub trait Containerized { type Region: Region; } -/// Push a type into a container. +/// A type that can write its contents into a region. pub trait CopyOnto { /// Copy self into the target container, returning an index that allows to /// look up the corresponding read item. fn copy_onto(self, target: &mut C) -> C::Index; } +impl CopyOnto for Box +where + for<'a> &'a T: CopyOnto, +{ + fn copy_onto(self, target: &mut R) -> R::Index { + self.as_ref().copy_onto(target) + } +} + +/// Reserve space in the receiving region. pub trait ReserveItems { /// Ensure that the region can absorb `items` without reallocation. fn reserve_items(target: &mut R, items: I) @@ -63,14 +115,7 @@ pub trait ReserveItems { I: Iterator + Clone; } -impl Containerized for Vec { - type Region = SliceRegion; -} - -impl Containerized for [T] { - type Region = SliceRegion; -} - +/// A container for indices into a region. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr( @@ -80,17 +125,12 @@ impl Containerized for [T] { ) )] pub struct FlatStack { + /// The indices, which we use to lookup items in the region. indices: Vec, + /// A region to index into. region: R, } -impl FlatStack { - #[inline] - pub fn default_impl>() -> Self { - Self::default() - } -} - impl Default for FlatStack { #[inline] fn default() -> Self { @@ -102,37 +142,54 @@ impl Default for FlatStack { } impl FlatStack { + /// Default implementation based on the preference of type `T`. + #[inline] + pub fn default_impl>() -> Self { + Self::default() + } + + /// Appends the element to the back of the stack. #[inline] pub fn copy(&mut self, item: impl CopyOnto) { let index = item.copy_onto(&mut self.region); self.indices.push(index); } + /// Returns the element at the `offset` position. #[inline] pub fn get(&self, offset: usize) -> R::ReadItem<'_> { self.region.index(self.indices[offset]) } - /// The length of the container. 0 if unspecified. + /// Returns the number of indices in the stack. #[inline] pub fn len(&self) -> usize { self.indices.len() } - /// Test whether the container is empty. True if length is not specified. - /// - /// Must return `true` if and only if [`Self::len`] returns 0. + /// Returns `true` if the stack contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.indices.is_empty() } + /// Returns the total number of indices the stack can hold without reallocation. + pub fn capacity(&self) -> usize { + self.indices.capacity() + } + + /// Reserves space to hold `additional` indices. + pub fn reserve(&mut self, additional: usize) { + self.indices.reserve(additional) + } + /// Remove all elements while possibly retaining allocations. pub fn clear(&mut self) { self.indices.clear(); self.region.clear(); } + /// Reserve space for the items returned by the iterator. pub fn reserve_items(&mut self, items: impl Iterator + Clone) where T: ReserveItems, @@ -140,14 +197,55 @@ impl FlatStack { ReserveItems::reserve_items(&mut self.region, items); } + /// Reserve space for the regions returned by the iterator. pub fn reserve_regions<'a>(&mut self, regions: impl Iterator + Clone) where R: 'a, { self.region.reserve_regions(regions) } + + /// Iterate the items in this stack. + pub fn iter(&self) -> Iter<'_, R> { + self.into_iter() + } +} + +impl<'a, R: Region> IntoIterator for &'a FlatStack { + type Item = R::ReadItem<'a>; + type IntoIter = Iter<'a, R>; + + fn into_iter(self) -> Self::IntoIter { + Iter { + inner: self.indices.iter(), + region: &self.region, + } + } +} + +/// An iterator over [`FlatStack`]. The iterator yields [`Region::ReadItem`] elements, which +/// it obtains by looking up indices. +pub struct Iter<'a, R: Region> { + /// Iterator over indices. + inner: std::slice::Iter<'a, R::Index>, + /// Region to map indices to read items. + region: &'a R, +} + +impl<'a, R: Region> Iterator for Iter<'a, R> { + type Item = R::ReadItem<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(|idx| self.region.index(*idx)) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } } +impl<'a, R: Region> ExactSizeIterator for Iter<'a, R> {} + #[cfg(test)] mod tests { use super::*; From b6554e329daf5da32dab88ef697c1ca42a12d10e Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Thu, 8 Feb 2024 09:53:50 -0500 Subject: [PATCH 2/5] Documentation and performance improvements Signed-off-by: Moritz Hoffmann --- src/impls/mirror.rs | 17 +++++++++++++ src/impls/mod.rs | 16 ++++++------ src/impls/option.rs | 54 ++++++++++++++++++++++++++--------------- src/impls/result.rs | 17 +++++++++++++ src/impls/slice.rs | 40 +++++++++++++++++++++++++++++- src/impls/slice_copy.rs | 20 ++++++++++++++- src/impls/string.rs | 19 +++++++++++++++ src/impls/tuple.rs | 4 ++- src/lib.rs | 5 +++- 9 files changed, 161 insertions(+), 31 deletions(-) diff --git a/src/impls/mirror.rs b/src/impls/mirror.rs index a171852..ecd16ab 100644 --- a/src/impls/mirror.rs +++ b/src/impls/mirror.rs @@ -1,3 +1,5 @@ +//! A region that copies its inputs. + use std::marker::PhantomData; #[cfg(feature = "serde")] @@ -6,6 +8,21 @@ use serde::{Deserialize, Serialize}; use crate::{Containerized, CopyOnto, Index, Region, ReserveItems}; /// A region for types where the read item type is equal to the index type. +/// +/// This region is useful where the type is not larger than roughly two `usize`s (or 1.5x with +/// some optimizations), or looking up the value is too costly. For larger copy types, the memory +/// required to store the copy type and an index is only marginally bigger, with the benefit +/// that the index remains compact. +/// +/// # Examples +/// +/// For [`MirrorRegion`]s, we can index with a copy type: +/// ``` +/// # use flatcontainer::{MirrorRegion, Region}; +/// let r = MirrorRegion::::default(); +/// let output: u8 = r.index(42); +/// assert_eq!(output, 42); +/// ``` #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MirrorRegion(PhantomData<*const T>); diff --git a/src/impls/mod.rs b/src/impls/mod.rs index 9c3d721..4ad472e 100644 --- a/src/impls/mod.rs +++ b/src/impls/mod.rs @@ -1,7 +1,9 @@ -pub(crate) mod mirror; -pub(crate) mod option; -pub(crate) mod result; -pub(crate) mod slice; -pub(crate) mod slice_copy; -pub(crate) mod string; -pub(crate) mod tuple; +//! Various region implementations. + +pub mod mirror; +pub mod option; +pub mod result; +pub mod slice; +pub mod slice_copy; +pub mod string; +pub mod tuple; diff --git a/src/impls/option.rs b/src/impls/option.rs index 917d177..1ce2b26 100644 --- a/src/impls/option.rs +++ b/src/impls/option.rs @@ -1,3 +1,5 @@ +//! A region that stores options. + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -8,18 +10,30 @@ impl Containerized for Option { } /// A region to hold [`Option`]s. +/// +/// # Examples +/// +/// The region can hold options: +/// ``` +/// # use flatcontainer::{Containerized, CopyOnto, OptionRegion, Region}; +/// let mut r = OptionRegion::<::Region>::default(); +/// +/// let some_index = Some(123).copy_onto(&mut r); +/// // Type annotations required for `None`: +/// let none_index = Option::::None.copy_onto(&mut r); +/// +/// assert_eq!(Some(123), r.index(some_index)); +/// assert_eq!(None, r.index(none_index)); +/// ``` #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct OptionRegion { - inner: T, +pub struct OptionRegion { + inner: R, } -impl Region for OptionRegion -where - T: Region, -{ - type ReadItem<'a> = Option> where Self: 'a; - type Index = Option; +impl Region for OptionRegion { + type ReadItem<'a> = Option> where Self: 'a; + type Index = Option; #[inline] fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { @@ -42,34 +56,34 @@ where } } -impl CopyOnto> for Option +impl CopyOnto> for Option where - TC: Region, - T: CopyOnto, + TR: Region, + T: CopyOnto, { #[inline] - fn copy_onto(self, target: &mut OptionRegion) -> as Region>::Index { + fn copy_onto(self, target: &mut OptionRegion) -> as Region>::Index { self.map(|t| t.copy_onto(&mut target.inner)) } } -impl<'a, T: 'a, TC> CopyOnto> for &'a Option +impl<'a, T: 'a, TR> CopyOnto> for &'a Option where - TC: Region, - &'a T: CopyOnto, + TR: Region, + &'a T: CopyOnto, { #[inline] - fn copy_onto(self, target: &mut OptionRegion) -> as Region>::Index { + fn copy_onto(self, target: &mut OptionRegion) -> as Region>::Index { self.as_ref().map(|t| t.copy_onto(&mut target.inner)) } } -impl<'a, T: 'a, TC> ReserveItems> for &'a Option +impl<'a, T: 'a, TR> ReserveItems> for &'a Option where - TC: Region, - &'a T: ReserveItems, + TR: Region, + &'a T: ReserveItems, { - fn reserve_items(target: &mut OptionRegion, items: I) + fn reserve_items(target: &mut OptionRegion, items: I) where I: Iterator + Clone, { diff --git a/src/impls/result.rs b/src/impls/result.rs index cfc140d..c400a60 100644 --- a/src/impls/result.rs +++ b/src/impls/result.rs @@ -1,3 +1,5 @@ +//! A region that stores results. + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -8,6 +10,21 @@ impl Containerized for Result { } /// A region to hold [`Result`]s. +/// +/// # Examples +/// +/// Add results to a result region: +/// ``` +/// use flatcontainer::{Containerized, CopyOnto, Region, ResultRegion}; +/// let mut r = +/// ResultRegion::<<() as Containerized>::Region, ::Region>::default(); +/// +/// let ok_index = Result::<(), String>::Ok(()).copy_onto(&mut r); +/// let err_index = Result::<(), String>::Err("Error".to_string()).copy_onto(&mut r); +/// +/// assert_eq!(Ok(()), r.index(ok_index)); +/// assert_eq!(Err("Error"), r.index(err_index)); +/// ``` #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ResultRegion { diff --git a/src/impls/slice.rs b/src/impls/slice.rs index b16a3b4..cbf3e8e 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,3 +1,5 @@ +//! A region that stores slices. + use std::ops::Deref; #[cfg(feature = "serde")] @@ -18,10 +20,40 @@ impl Containerized for [T; N] { } /// A container representing slices of data. +/// +/// Reading from this region is more involved than for others, because the data only exists in +/// an indexable representation. The read item is a [`ReadSlice`], which can be iterated or indexed. +/// However, it is not possible to represent the data as a slice, simply because the slice doesn't +/// exist. +/// +/// # Examples +/// +/// We fill some data into a slice region and use the [`ReadSlice`] to extract it later. +/// ``` +/// use flatcontainer::{Containerized, CopyOnto, Region, SliceRegion}; +/// let mut r = SliceRegion::<::Region>::default(); +/// +/// let panagram_en = "The quick fox jumps over the lazy dog" +/// .split(" ") +/// .collect::>(); +/// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich" +/// .split(" ") +/// .collect::>(); +/// +/// let en_index = (&panagram_en).copy_onto(&mut r); +/// let de_index = (&panagram_de).copy_onto(&mut r); +/// +/// assert!(panagram_de.into_iter().eq(r.index(de_index))); +/// assert!(panagram_en.into_iter().eq(r.index(en_index))); +/// +/// assert_eq!(r.index(de_index).get(2), "jagen"); +/// ``` #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SliceRegion { + /// Container of slices. slices: Vec, + /// Inner region. inner: C, } @@ -78,10 +110,15 @@ impl<'a, C: Region> ReadSlice<'a, C> { self.1.len() } - /// Test if this slice is empty. + /// Returns `true` if the slice is empty. pub fn is_empty(&self) -> bool { self.1.is_empty() } + + /// Returns an iterator over all contained items. + pub fn iter(&self) -> ::IntoIter { + self.into_iter() + } } impl<'a, C: Region> Clone for ReadSlice<'a, C> { @@ -102,6 +139,7 @@ impl<'a, C: Region> IntoIterator for ReadSlice<'a, C> { } } +/// An iterator over the items read from a slice region. #[derive(Debug, Clone)] pub struct ReadSliceIter<'a, C: Region>(&'a C, std::slice::Iter<'a, C::Index>); diff --git a/src/impls/slice_copy.rs b/src/impls/slice_copy.rs index 197a42c..05fba73 100644 --- a/src/impls/slice_copy.rs +++ b/src/impls/slice_copy.rs @@ -1,9 +1,27 @@ +//! A region that stores slices of copy types. + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{CopyOnto, Region, ReserveItems}; /// A container for [`Copy`] types. +/// +/// # Examples +/// +/// ``` +/// use flatcontainer::{CopyOnto, CopyRegion, Region}; +/// let mut r = CopyRegion::::default(); +/// +/// let panagram_en = "The quick fox jumps over the lazy dog"; +/// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich"; +/// +/// let en_index = panagram_en.as_bytes().copy_onto(&mut r); +/// let de_index = panagram_de.as_bytes().copy_onto(&mut r); +/// +/// assert_eq!(panagram_de.as_bytes(), r.index(de_index)); +/// assert_eq!(panagram_en.as_bytes(), r.index(en_index)); +/// ``` #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct CopyRegion { @@ -47,7 +65,7 @@ where { fn copy_onto(self, target: &mut CopyRegion) -> as Region>::Index { let start = target.slices.len(); - target.slices.extend(self); + target.slices.extend_from_slice(self); (start, target.slices.len()) } } diff --git a/src/impls/string.rs b/src/impls/string.rs index 3e317b7..bbde61b 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -1,3 +1,5 @@ +//! A region that stores strings. + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -5,6 +7,23 @@ use crate::impls::slice_copy::CopyRegion; use crate::{Containerized, CopyOnto, Region, ReserveItems}; /// A region to store strings and read `&str`. +/// +/// # Examples +/// +/// We fill some data into a string region and use extract it later. +/// ``` +/// use flatcontainer::{Containerized, CopyOnto, Region, StringRegion}; +/// let mut r = StringRegion::default(); +/// +/// let panagram_en = "The quick fox jumps over the lazy dog"; +/// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich"; +/// +/// let en_index = panagram_en.copy_onto(&mut r); +/// let de_index = panagram_de.copy_onto(&mut r); +/// +/// assert_eq!(panagram_de, r.index(de_index)); +/// assert_eq!(panagram_en, r.index(en_index)); +/// ``` #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct StringRegion { diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index 530b3a2..b4c7d6a 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -1,4 +1,5 @@ -/// Implementation for tuples. +//! Regions that stores tuples. + use paste::paste; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -13,6 +14,7 @@ macro_rules! tuple_flatcontainer { type Region = []<$($name::Region,)*>; } + /// A region for a tuple. #[allow(non_snake_case)] #[derive(Default, Clone, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/lib.rs b/src/lib.rs index 19d50e8..e8db03f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,9 +32,10 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -mod impls; +pub mod impls; pub use impls::mirror::MirrorRegion; +pub use impls::option::OptionRegion; pub use impls::result::ResultRegion; pub use impls::slice::SliceRegion; pub use impls::slice_copy::CopyRegion; @@ -98,6 +99,8 @@ pub trait CopyOnto { fn copy_onto(self, target: &mut C) -> C::Index; } +// Blanket implementation for `Box`. This might be a bad idea because it precludes blanket +// implementations. impl CopyOnto for Box where for<'a> &'a T: CopyOnto, From 30366aff1e9fe9926e63319a7fdf02039f72d5e5 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Thu, 8 Feb 2024 09:57:25 -0500 Subject: [PATCH 3/5] Benchmark updates Signed-off-by: Moritz Hoffmann --- benches/bench.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/benches/bench.rs b/benches/bench.rs index 45c9979..35a15a2 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -4,8 +4,9 @@ extern crate test; -use flatcontainer::{Containerized, CopyOnto, FlatStack, ReserveItems}; +use flatcontainer::{Containerized, CopyOnto, CopyRegion, FlatStack, MirrorRegion, Region, ReserveItems, SliceRegion, StringRegion}; use test::Bencher; +use flatcontainer::impls::tuple::TupleABCRegion; #[bench] fn empty_copy(bencher: &mut Bencher) { @@ -24,6 +25,10 @@ fn u8_u64_copy(bencher: &mut Bencher) { _bench_copy(bencher, vec![(0u8, 0u64); 512]); } #[bench] +fn str10_copy(bencher: &mut Bencher) { + _bench_copy(bencher, vec!["grawwwwrr!"; 1024]); +} +#[bench] fn string10_copy(bencher: &mut Bencher) { _bench_copy(bencher, vec![format!("grawwwwrr!"); 1024]); } @@ -46,6 +51,53 @@ fn vec_u_vn_s_copy(bencher: &mut Bencher) { ); } +#[bench] +fn empty_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec![(); 1024]); +} +#[bench] +fn u64_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec![0u64; 1024]); +} +#[bench] +fn u32x2_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec![(0u32, 0u32); 1024]); +} +#[bench] +fn u8_u64_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec![(0u8, 0u64); 512]); +} +#[bench] +fn str10_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec!["grawwwwrr!"; 1024]); +} +#[bench] +fn str100_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec!["grawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrr!!!!!!!!!grawwwwrr!"; 1024]); +} +#[bench] +fn string10_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec![format!("grawwwwrr!"); 1024]); +} +#[bench] +fn string20_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); +} +#[bench] +fn vec_u_s_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, _>( + bencher, + vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], + ); +} +#[bench] +fn vec_u_vn_s_copy_region(bencher: &mut Bencher) { + _bench_copy_region::, CopyRegion<_>, StringRegion>>>, _>( + bencher, + vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], + ); +} + #[bench] fn empty_clone(bencher: &mut Bencher) { _bench_clone(bencher, vec![(); 1024]); @@ -63,6 +115,10 @@ fn u8_u64_clone(bencher: &mut Bencher) { _bench_clone(bencher, vec![(0u8, 0u64); 512]); } #[bench] +fn str10_clone(bencher: &mut Bencher) { + _bench_clone(bencher, vec!["grawwwwrr!"; 1024]); +} +#[bench] fn string10_clone(bencher: &mut Bencher) { _bench_clone(bencher, vec![format!("grawwwwrr!"); 1024]); } @@ -102,6 +158,10 @@ fn u8_u64_realloc(bencher: &mut Bencher) { _bench_realloc(bencher, vec![(0u8, 0u64); 512]); } #[bench] +fn str10_realloc(bencher: &mut Bencher) { + _bench_realloc(bencher, vec!["grawwwwrr!"; 1024]); +} +#[bench] fn string10_realloc(bencher: &mut Bencher) { _bench_realloc(bencher, vec![format!("grawwwwrr!"); 1024]); } @@ -141,6 +201,10 @@ fn u8_u64_prealloc(bencher: &mut Bencher) { _bench_prealloc(bencher, vec![(0u8, 0u64); 512]); } #[bench] +fn str10_prealloc(bencher: &mut Bencher) { + _bench_prealloc(bencher, vec!["grawwwwrr!"; 1024]); +} +#[bench] fn string10_prealloc(bencher: &mut Bencher) { _bench_prealloc(bencher, vec![format!("grawwwwrr!"); 1024]); } @@ -178,6 +242,21 @@ where }); } +fn _bench_copy_region(bencher: &mut Bencher, record: T) +where + for<'a> &'a T: CopyOnto, +{ + // prepare encoded data for bencher.bytes + let mut arena = FlatStack::::default(); + + bencher.iter(|| { + arena.clear(); + for _ in 0..1024 { + arena.copy(&record); + } + }); +} + fn _bench_clone(bencher: &mut Bencher, record: T) { // prepare encoded data for bencher.bytes let mut arena = Vec::new(); From df7b24f7195a5d863151b4f94bc6f7f3f75e04e5 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Thu, 8 Feb 2024 09:59:02 -0500 Subject: [PATCH 4/5] More trait implementations Signed-off-by: Moritz Hoffmann --- src/impls/slice_copy.rs | 22 +++++++++++++++++++++- src/impls/string.rs | 11 ++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/impls/slice_copy.rs b/src/impls/slice_copy.rs index 05fba73..01d9b93 100644 --- a/src/impls/slice_copy.rs +++ b/src/impls/slice_copy.rs @@ -72,8 +72,28 @@ where impl ReserveItems> for &[T] { fn reserve_items(target: &mut CopyRegion, items: I) + where + I: Iterator + Clone, + { + target.slices.reserve(items.clone().map(|i| i.len()).sum()); + } +} + +impl CopyOnto> for &Vec where - I: Iterator + Clone, + T: Copy, +{ + fn copy_onto(self, target: &mut CopyRegion) -> as Region>::Index { + let start = target.slices.len(); + target.slices.extend_from_slice(self); + (start, target.slices.len()) + } +} + +impl ReserveItems> for &Vec { + fn reserve_items(target: &mut CopyRegion, items: I) + where + I: Iterator + Clone, { target.slices.reserve(items.clone().map(|i| i.len()).sum()); } diff --git a/src/impls/string.rs b/src/impls/string.rs index bbde61b..3e720a9 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -92,10 +92,19 @@ impl CopyOnto for &&str { } impl ReserveItems for &str { + fn reserve_items(target: &mut StringRegion, items: I) + where + I: Iterator + Clone, + { + ReserveItems::reserve_items(&mut target.inner, items.map(str::as_bytes)) + } +} + +impl ReserveItems for &&str { fn reserve_items(target: &mut StringRegion, items: I) where I: Iterator + Clone, { - ReserveItems::reserve_items(&mut target.inner, items.map(str::as_bytes)) + ReserveItems::reserve_items(&mut target.inner, items.map(|s| s.as_bytes())) } } From 5b62c23d975145f9004bc7ae35af2d51ef30ce0d Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Thu, 8 Feb 2024 09:59:44 -0500 Subject: [PATCH 5/5] Formatting Signed-off-by: Moritz Hoffmann --- benches/bench.rs | 12 +++++++++--- src/impls/slice_copy.rs | 12 ++++++------ src/impls/string.rs | 4 ++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 35a15a2..4a0e143 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -4,9 +4,12 @@ extern crate test; -use flatcontainer::{Containerized, CopyOnto, CopyRegion, FlatStack, MirrorRegion, Region, ReserveItems, SliceRegion, StringRegion}; -use test::Bencher; use flatcontainer::impls::tuple::TupleABCRegion; +use flatcontainer::{ + Containerized, CopyOnto, CopyRegion, FlatStack, MirrorRegion, Region, ReserveItems, + SliceRegion, StringRegion, +}; +use test::Bencher; #[bench] fn empty_copy(bencher: &mut Bencher) { @@ -92,7 +95,10 @@ fn vec_u_s_copy_region(bencher: &mut Bencher) { } #[bench] fn vec_u_vn_s_copy_region(bencher: &mut Bencher) { - _bench_copy_region::, CopyRegion<_>, StringRegion>>>, _>( + _bench_copy_region::< + SliceRegion, CopyRegion<_>, StringRegion>>>, + _, + >( bencher, vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], ); diff --git a/src/impls/slice_copy.rs b/src/impls/slice_copy.rs index 01d9b93..db66276 100644 --- a/src/impls/slice_copy.rs +++ b/src/impls/slice_copy.rs @@ -72,16 +72,16 @@ where impl ReserveItems> for &[T] { fn reserve_items(target: &mut CopyRegion, items: I) - where - I: Iterator + Clone, + where + I: Iterator + Clone, { target.slices.reserve(items.clone().map(|i| i.len()).sum()); } } impl CopyOnto> for &Vec - where - T: Copy, +where + T: Copy, { fn copy_onto(self, target: &mut CopyRegion) -> as Region>::Index { let start = target.slices.len(); @@ -92,8 +92,8 @@ impl CopyOnto> for &Vec impl ReserveItems> for &Vec { fn reserve_items(target: &mut CopyRegion, items: I) - where - I: Iterator + Clone, + where + I: Iterator + Clone, { target.slices.reserve(items.clone().map(|i| i.len()).sum()); } diff --git a/src/impls/string.rs b/src/impls/string.rs index 3e720a9..8b61982 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -93,8 +93,8 @@ impl CopyOnto for &&str { impl ReserveItems for &str { fn reserve_items(target: &mut StringRegion, items: I) - where - I: Iterator + Clone, + where + I: Iterator + Clone, { ReserveItems::reserve_items(&mut target.inner, items.map(str::as_bytes)) }