diff --git a/benches/bench.rs b/benches/bench.rs index 45c9979..4a0e143 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -4,7 +4,11 @@ extern crate test; -use flatcontainer::{Containerized, CopyOnto, FlatStack, ReserveItems}; +use flatcontainer::impls::tuple::TupleABCRegion; +use flatcontainer::{ + Containerized, CopyOnto, CopyRegion, FlatStack, MirrorRegion, Region, ReserveItems, + SliceRegion, StringRegion, +}; use test::Bencher; #[bench] @@ -24,6 +28,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 +54,56 @@ 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::< + SliceRegion, 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 +121,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 +164,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 +207,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 +248,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(); 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 3115d25..4ad472e 100644 --- a/src/impls/mod.rs +++ b/src/impls/mod.rs @@ -1,6 +1,9 @@ -pub(crate) mod mirror; -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 new file mode 100644 index 0000000..1ce2b26 --- /dev/null +++ b/src/impls/option.rs @@ -0,0 +1,92 @@ +//! A region that stores options. + +#[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. +/// +/// # 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: R, +} + +impl Region for OptionRegion { + 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 + TR: 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, TR> CopyOnto> for &'a Option +where + TR: 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, TR> ReserveItems> for &'a Option +where + TR: 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..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}; @@ -7,6 +9,22 @@ impl Containerized for Result { type Region = ResultRegion; } +/// 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 14cb308..cbf3e8e 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,15 +1,59 @@ +//! A region that stores slices. + 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. +/// +/// 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, } @@ -66,16 +110,21 @@ 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> { #[inline] fn clone(&self) -> Self { - Self(self.0, self.1) + *self } } @@ -90,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>); @@ -181,6 +231,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/slice_copy.rs b/src/impls/slice_copy.rs index 197a42c..db66276 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()) } } @@ -60,3 +78,23 @@ impl ReserveItems> for &[T] { target.slices.reserve(items.clone().map(|i| i.len()).sum()); } } + +impl CopyOnto> for &Vec +where + 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 76e0166..8b61982 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 { @@ -38,6 +57,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 { @@ -76,3 +99,12 @@ impl ReserveItems for &str { 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(|s| s.as_bytes())) + } +} 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 2049ba2..e8db03f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,67 @@ +//! 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}; -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; 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 +76,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 +92,25 @@ 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; } +// 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, +{ + 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 +118,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 +128,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 +145,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 +200,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::*;