diff --git a/src/impls/index.rs b/src/impls/index.rs index 229e889..daeb393 100644 --- a/src/impls/index.rs +++ b/src/impls/index.rs @@ -248,7 +248,11 @@ where /// Reserve space for `additional` elements. #[inline] pub fn reserve(&mut self, additional: usize) { - self.smol.reserve(additional); + if self.chonk.is_empty() { + self.smol.reserve(additional); + } else { + self.chonk.reserve(additional); + } } /// Remove all elements. @@ -299,6 +303,15 @@ where fn is_empty(&self) -> bool { self.is_empty() } + + #[inline] + fn capacity(&self) -> usize { + if self.chonk.is_empty() { + self.smol.capacity() + } else { + self.chonk.capacity() + } + } } impl IndexContainer for IndexList @@ -407,6 +420,16 @@ where fn heap_size(&self, callback: F) { self.spilled.heap_size(callback); } + + #[inline] + fn capacity(&self) -> usize { + if self.spilled.is_empty() { + // TODO: What else could we do here? `usize::MAX`? + 1 + } else { + self.spilled.capacity() + } + } } impl IndexContainer for IndexOptimized @@ -439,8 +462,25 @@ where where I::IntoIter: ExactSizeIterator, { - for item in iter { - self.push(item); + let mut iter = iter.into_iter(); + if !self.spilled.is_empty() { + // We certainly push into `spilled`, so reserve enough space. + let (lower, upper) = iter.size_hint(); + self.spilled.reserve(upper.unwrap_or(lower)); + } + while let Some(item) = iter.next() { + if self.spilled.is_empty() { + let inserted = self.strided.push(item); + if !inserted { + // This is a transition point from pushing into `strided` to pushing into + // `spilled`. + let (lower, upper) = iter.size_hint(); + self.spilled.reserve(upper.unwrap_or(lower)); + self.spilled.push(item); + } + } else { + self.spilled.push(item); + } } } diff --git a/src/impls/mirror.rs b/src/impls/mirror.rs index 664f1e0..b54be3e 100644 --- a/src/impls/mirror.rs +++ b/src/impls/mirror.rs @@ -6,7 +6,9 @@ use std::marker::PhantomData; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{Index, IntoOwned, Push, Region, RegionPreference, ReserveItems}; +use crate::{ + CanPush, Index, IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush, +}; /// A region for types where the read item type is equal to the index type. /// @@ -99,6 +101,25 @@ where } } +impl TryPush for MirrorRegion +where + for<'a> T: Index + IntoOwned<'a, Owned = T>, +{ + #[inline(always)] + fn try_push(&mut self, item: T) -> Result { + Ok(item) + } +} + +impl CanPush for MirrorRegion { + fn can_push(&self, _: I) -> bool + where + I: Iterator + Clone, + { + true + } +} + impl Push<&T> for MirrorRegion where for<'a> T: Index + IntoOwned<'a, Owned = T>, @@ -109,6 +130,25 @@ where } } +impl<'a, T> CanPush<&'a T> for MirrorRegion { + fn can_push(&self, _: I) -> bool + where + I: Iterator + Clone, + { + true + } +} + +impl<'b, T> TryPush<&'b T> for MirrorRegion +where + for<'a> T: Index + IntoOwned<'a, Owned = T>, +{ + #[inline(always)] + fn try_push(&mut self, item: &'b T) -> Result { + Ok(*item) + } +} + impl Push<&&T> for MirrorRegion where for<'a> T: Index + IntoOwned<'a, Owned = T>, @@ -119,6 +159,16 @@ where } } +impl<'b, 'c, T> TryPush<&'b &'c T> for MirrorRegion +where + for<'a> T: Index + IntoOwned<'a, Owned = T>, +{ + #[inline(always)] + fn try_push(&mut self, item: &'b &'c T) -> Result { + Ok(**item) + } +} + impl ReserveItems for MirrorRegion where for<'a> T: Index + IntoOwned<'a, Owned = T>, @@ -145,6 +195,14 @@ where } } +impl Reserve for MirrorRegion { + type Reserve = (); + + fn reserve(&mut self, (): &Self::Reserve) { + // No storage + } +} + macro_rules! implement_for { ($index_type:ty) => { impl RegionPreference for $index_type { diff --git a/src/impls/option.rs b/src/impls/option.rs index ec518b6..23d924c 100644 --- a/src/impls/option.rs +++ b/src/impls/option.rs @@ -3,7 +3,7 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; +use crate::{IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems}; impl RegionPreference for Option { type Owned = Option; @@ -167,6 +167,17 @@ where } } +impl Reserve for OptionRegion +where + R: Reserve, +{ + type Reserve = R::Reserve; + + fn reserve(&mut self, size: &Self::Reserve) { + self.inner.reserve(size); + } +} + #[cfg(test)] mod tests { use crate::{MirrorRegion, OwnedRegion, Region, ReserveItems}; diff --git a/src/impls/result.rs b/src/impls/result.rs index 99ea871..ccb2574 100644 --- a/src/impls/result.rs +++ b/src/impls/result.rs @@ -3,7 +3,7 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; +use crate::{IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems}; impl RegionPreference for Result { type Owned = Result; @@ -194,6 +194,19 @@ where } } +impl Reserve for ResultRegion +where + T: Reserve, + E: Reserve, +{ + type Reserve = (T::Reserve, E::Reserve); + + fn reserve(&mut self, (t, e): &Self::Reserve) { + self.oks.reserve(t); + self.errs.reserve(e); + } +} + #[cfg(test)] mod tests { use crate::{MirrorRegion, OwnedRegion, Region, ReserveItems}; diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 34dda42..9fa3f64 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -8,7 +8,7 @@ use std::ops::{Deref, Range}; use serde::{Deserialize, Serialize}; use crate::impls::index::IndexContainer; -use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; +use crate::{CanPush, IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush}; impl RegionPreference for Vec { type Owned = Vec; @@ -56,7 +56,7 @@ impl RegionPreference for [T; N] { /// ``` #[derive(Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct SliceRegion::Index>> { +pub struct SliceRegion::Index>> { /// Container of slices. slices: O, /// Inner region. @@ -416,6 +416,33 @@ where } } +impl<'a, C, T, O> TryPush<&'a [T]> for SliceRegion +where + C: Region + TryPush<&'a T> + CanPush<&'a T>, + O: IndexContainer, +{ + fn try_push(&mut self, item: &'a [T]) -> Result { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, C, T, O> CanPush<&'a [T]> for SliceRegion +where + C: Region + CanPush<&'a T>, + T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.inner.can_push(items.flatten()) + } +} + impl<'a, T, R, O> ReserveItems<&'a [T]> for SliceRegion where R: Region + ReserveItems<&'a T>, @@ -445,6 +472,33 @@ where } } +impl TryPush> for SliceRegion +where + C: Region + TryPush + for<'a> CanPush<&'a T>, + O: IndexContainer, +{ + fn try_push(&mut self, item: Vec) -> Result> { + if self.can_push(std::iter::once(&item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, C, T, O> CanPush<&'a Vec> for SliceRegion +where + C: Region + CanPush<&'a T>, + T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator> + Clone, + { + self.inner.can_push(items.flatten()) + } +} + impl Push<&Vec> for SliceRegion where for<'a> C: Region + Push<&'a T>, @@ -456,6 +510,33 @@ where } } +impl<'a, C, T, O> TryPush<&'a Vec> for SliceRegion +where + C: Region + for<'b> Push<&'b T> + CanPush<&'a T>, + O: IndexContainer, +{ + fn try_push(&mut self, item: &'a Vec) -> Result> { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, 'b, C, T, O> CanPush<&'a &'b Vec> for SliceRegion +where + C: Region + CanPush<&'b T>, + &'b T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator> + Clone, + { + self.inner.can_push(items.map(|x| *x).flatten()) + } +} + impl<'a, C, T, O> Push<&&'a Vec> for SliceRegion where C: Region + Push<&'a T>, @@ -467,6 +548,33 @@ where } } +impl TryPush<&&Vec> for SliceRegion +where + for<'b> C: Region + Push<&'b T> + CanPush<&'b T>, + O: IndexContainer, +{ + fn try_push<'a, 'b>(&mut self, item: &'a &'b Vec) -> Result> { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +// impl<'b, 'c, C, T, O> CanPush<&'b &'c Vec> for SliceRegion +// where +// C: Region + CanPush, +// { +// fn can_push<'a, I>(&self, items: I) -> bool +// where +// I: Iterator> + Clone, +// &'b &'c Vec: 'a, +// { +// self.inner.can_push(items.map(|x| **x).flatten()) +// } +// } + impl<'a, T, R, O> ReserveItems<&'a Vec> for SliceRegion where for<'b> R: Region + ReserveItems<&'b T>, @@ -502,6 +610,33 @@ where } } +impl<'a, C, O> TryPush> for SliceRegion +where + C: Region + Push<::ReadItem<'a>> + CanPush>, + O: IndexContainer, +{ + fn try_push(&mut self, item: ReadSlice<'a, C, O>) -> Result> { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'b, C, O> CanPush> for SliceRegion +where + C: Region + CanPush>, + O: IndexContainer, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator> + Clone, + { + self.inner.can_push(items.flatten()) + } +} + impl<'a, C, O> Push> for SliceRegion where C: Region + Push<::ReadItem<'a>>, @@ -522,12 +657,42 @@ where impl Push<[T; N]> for SliceRegion where - for<'a> R: Region + Push<&'a T>, + for<'a> R: Region + Push, O: IndexContainer, { #[inline] fn push(&mut self, item: [T; N]) -> as Region>::Index { - self.push(item.as_slice()) + let start = self.slices.len(); + self.slices + .extend(item.into_iter().map(|t| self.inner.push(t))); + (start, self.slices.len()) + } +} + +impl TryPush<[T; N]> for SliceRegion +where + for<'a> R: Region + Push + CanPush<&'a T>, + O: IndexContainer, +{ + fn try_push(&mut self, item: [T; N]) -> Result { + if self.can_push(std::iter::once(&item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, T, R, O, const N: usize> CanPush<&'a [T; N]> for SliceRegion +where + R: CanPush<&'a T>, + T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.inner.can_push(items.flatten()) } } @@ -542,6 +707,20 @@ where } } +impl<'a, T, R, O, const N: usize> TryPush<&'a [T; N]> for SliceRegion +where + R: Region + Push<&'a T> + CanPush<&'a T>, + O: IndexContainer, +{ + fn try_push(&mut self, item: &'a [T; N]) -> Result { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + impl<'a, T, R, O, const N: usize> Push<&&'a [T; N]> for SliceRegion where R: Region + Push<&'a T>, @@ -581,6 +760,19 @@ where } } +impl Reserve for SliceRegion +where + R: Reserve + Region, + O: IndexContainer, +{ + type Reserve = (usize, R::Reserve); + + fn reserve(&mut self, (items, inner): &Self::Reserve) { + self.slices.reserve(*items); + self.inner.reserve(inner); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/impls/slice_owned.rs b/src/impls/slice_owned.rs index 209e0e0..1227941 100644 --- a/src/impls/slice_owned.rs +++ b/src/impls/slice_owned.rs @@ -6,7 +6,7 @@ use std::marker::PhantomData; use serde::{Deserialize, Serialize}; use crate::impls::storage::{PushStorage, Storage}; -use crate::{Push, PushIter, Region, ReserveItems}; +use crate::{CanPush, Push, PushIter, Region, Reserve, ReserveItems, TryPush}; /// A container for owned types. /// @@ -128,6 +128,20 @@ where } } +impl<'a, T, S> CanPush<&'a [T]> for OwnedRegion +where + S: Storage, + T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + let required = items.map(|item| item.len()).sum(); + self.slices.capacity() - self.slices.len() >= required + } +} + impl Push<&[T; N]> for OwnedRegion where T: Clone, @@ -141,6 +155,36 @@ where } } +impl<'a, T, S, const N: usize> TryPush<&'a [T; N]> for OwnedRegion +where + T: Clone, + S: Storage + + for<'b> PushStorage<&'b [T]> + + std::ops::Index, Output = [T]>, +{ + fn try_push(&mut self, item: &'a [T; N]) -> Result { + if self.can_push(std::iter::once(item.as_slice())) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, T, S, const N: usize> CanPush<&'a [T; N]> for OwnedRegion +where + S: Storage, + T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + let required = items.count() * N; + self.slices.capacity() - self.slices.len() >= required + } +} + impl Push<&&[T; N]> for OwnedRegion where T: Clone, @@ -183,6 +227,22 @@ where } } +impl<'b, T, S> TryPush<&'b [T]> for OwnedRegion +where + T: Clone, + S: Storage + + for<'a> PushStorage<&'a [T]> + + std::ops::Index, Output = [T]>, +{ + fn try_push(&mut self, item: &'b [T]) -> Result { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + impl> Push<&&[T]> for OwnedRegion where for<'a> Self: Push<&'a [T]>, @@ -222,6 +282,22 @@ where } } +impl TryPush> for OwnedRegion +where + [T]: ToOwned, + S: Storage + + for<'a> PushStorage<&'a mut Vec> + + std::ops::Index, Output = [T]>, +{ + fn try_push(&mut self, item: Vec) -> Result> { + if self.can_push(std::iter::once(&item[..])) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + impl Push<&Vec> for OwnedRegion where T: Clone, @@ -235,6 +311,22 @@ where } } +impl<'b, T, S> TryPush<&'b Vec> for OwnedRegion +where + T: Clone, + S: Storage + + for<'a> PushStorage<&'a [T]> + + std::ops::Index, Output = [T]>, +{ + fn try_push(&mut self, item: &'b Vec) -> Result> { + if self.can_push(std::iter::once(&item[..])) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + impl<'a, T, S> ReserveItems<&'a Vec> for OwnedRegion where [T]: ToOwned, @@ -282,6 +374,19 @@ where } } +impl Reserve for OwnedRegion +where + S: Storage, +{ + type Reserve = usize; + + fn reserve(&mut self, size: &Self::Reserve) { + if self.slices.capacity() < *size { + self.slices.reserve(*size - self.slices.capacity()); + } + } +} + #[cfg(test)] mod tests { use crate::{Push, PushIter, Region, ReserveItems}; @@ -322,4 +427,16 @@ mod tests { let index = r.push(PushIter(iter)); assert_eq!([1, 1, 1, 1], r.index(index)); } + + #[test] + fn try_push() { + let mut r = >::default(); + assert!(!r.can_push(std::iter::once([1; 4].as_slice()))); + assert_eq!(r.try_push(&[1; 4]), Err(&[1; 4])); + r.reserve_items(std::iter::once(&[1; 4])); + assert!(r.can_push(std::iter::once([1; 4].as_slice()))); + let index = r.try_push(&[1; 4]); + assert_eq!(index, Ok((0, 4))); + assert_eq!([1, 1, 1, 1], r.index(index.unwrap())); + } } diff --git a/src/impls/storage.rs b/src/impls/storage.rs index 6e1b208..1831beb 100644 --- a/src/impls/storage.rs +++ b/src/impls/storage.rs @@ -48,6 +48,10 @@ pub trait Storage: Default { /// Returns `true` if empty, i.e., it doesn't contain any elements. #[must_use] fn is_empty(&self) -> bool; + + /// Returns the capacity of the storage. + #[must_use] + fn capacity(&self) -> usize; } impl Storage for Vec { @@ -83,6 +87,11 @@ impl Storage for Vec { fn is_empty(&self) -> bool { self.is_empty() } + + #[inline] + fn capacity(&self) -> usize { + self.capacity() + } } /// Push an item into storage. diff --git a/src/impls/string.rs b/src/impls/string.rs index e92e5bd..b452692 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::impls::slice_owned::OwnedRegion; -use crate::{Push, Region, RegionPreference, ReserveItems}; +use crate::{CanPush, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush}; /// A region to store strings and read `&str`. /// @@ -130,6 +130,37 @@ where } } +impl TryPush for StringRegion +where + for<'a> R: Region = &'a [u8]> + + TryPush<&'a [u8]> + + Push<&'a [u8]> + + CanPush<&'a [u8]> + + 'a, +{ + #[inline] + fn try_push(&mut self, item: String) -> Result { + if self.can_push(std::iter::once(item.as_str())) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, R> CanPush<&'a String> for StringRegion +where + R: CanPush<&'a [u8]>, +{ + #[inline] + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.inner.can_push(items.map(|item| item.as_bytes())) + } +} + impl<'b, R> ReserveItems<&'b String> for StringRegion where for<'a> R: Region = &'a [u8]> + ReserveItems<&'a [u8]> + 'a, @@ -153,6 +184,37 @@ where } } +impl TryPush<&str> for StringRegion +where + for<'a> R: Region = &'a [u8]> + + TryPush<&'a [u8]> + + Push<&'a [u8]> + + CanPush<&'a [u8]> + + 'a, +{ + #[inline] + fn try_push<'a>(&mut self, item: &'a str) -> Result { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, R> CanPush<&'a str> for StringRegion +where + R: CanPush<&'a [u8]>, +{ + #[inline] + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.inner.can_push(items.map(|item| item.as_bytes())) + } +} + impl Push<&&str> for StringRegion where for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, @@ -163,6 +225,38 @@ where } } +impl TryPush<&&str> for StringRegion +where + for<'a> R: Region = &'a [u8]> + + TryPush<&'a [u8]> + + Push<&'a [u8]> + + CanPush<&'a [u8]> + + 'a, +{ + #[inline] + fn try_push<'a, 'b>(&mut self, item: &'a &'b str) -> Result { + if self.can_push(std::iter::once(*item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } +} + +impl<'a, 'b, R> CanPush<&'a &'b str> for StringRegion +where + R: CanPush<&'b [u8]>, + 'b: 'a, +{ + #[inline] + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.inner.can_push(items.map(|item| item.as_bytes())) + } +} + impl<'b, R> ReserveItems<&'b str> for StringRegion where for<'a> R: Region = &'a [u8]> + ReserveItems<&'a [u8]> + 'a, @@ -189,6 +283,14 @@ where } } +impl Reserve for StringRegion { + type Reserve = R::Reserve; + + fn reserve(&mut self, size: &Self::Reserve) { + self.inner.reserve(size); + } +} + #[cfg(test)] mod tests { use crate::{IntoOwned, Push, Region, ReserveItems, StringRegion}; diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index 2348fb1..3376469 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -4,7 +4,9 @@ use paste::paste; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; +use crate::{ + CanPush, Index, IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush, +}; /// The macro creates the region implementation for tuples macro_rules! tuple_flatcontainer { @@ -26,7 +28,7 @@ macro_rules! tuple_flatcontainer { #[allow(non_snake_case)] impl<$($name: Region + Clone),*> Clone for []<$($name),*> where - $(<$name as Region>::Index: crate::Index),* + $(<$name as Region>::Index: Index),* { fn clone(&self) -> Self { Self { @@ -42,7 +44,7 @@ macro_rules! tuple_flatcontainer { #[allow(non_snake_case)] impl<$($name: Region),*> Region for []<$($name),*> where - $(<$name as Region>::Index: crate::Index),* + $(<$name as Region>::Index: Index),* { type Owned = ($($name::Owned,)*); type ReadItem<'a> = ($($name::ReadItem<'a>,)*) where Self: 'a; @@ -122,6 +124,57 @@ macro_rules! tuple_flatcontainer { } } + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + impl<$($name, [<$name _C>]),*> TryPush<($($name,)*)> for []<$([<$name _C>]),*> + where + $([<$name _C>]: Region + Push<$name> + for<'a> CanPush<&'a $name>),* + { + #[inline] + fn try_push(&mut self, item: ($($name,)*)) -> Result<<[]<$([<$name _C>]),*> as Region>::Index, ($($name,)*)> { + if self.can_push(std::iter::once(&item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } + } + + + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + impl<$($name, [<$name _C>]),*> TryPush<&($($name,)*)> for []<$([<$name _C>]),*> + where + $(for<'a> [<$name _C>]: Region + Push<&'a $name> + CanPush<&'a $name>),* + { + #[inline] + fn try_push<'a>(&mut self, item: &'a ($($name,)*)) -> Result<<[]<$([<$name _C>]),*> as Region>::Index, &'a ($($name,)*)> { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } + } + } + + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + impl<'a, $($name, [<$name _C>]),*> CanPush<&'a ($($name,)*)> for []<$([<$name _C>]),*> + where + $([<$name _C>]: Region + CanPush<&'a $name> + 'a),* + // $($name: 'a,)* + { + #[inline] + fn can_push(&self, items: It) -> bool + where + It: Iterator + Clone, + { + let can_push = true; + tuple_flatcontainer!(can_push can_push self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); + can_push + } + } + #[allow(non_camel_case_types)] #[allow(non_snake_case)] impl<'a, $($name),*> IntoOwned<'a> for ($($name,)*) @@ -165,7 +218,7 @@ macro_rules! tuple_flatcontainer { where It: Iterator + Clone, { - tuple_flatcontainer!(reserve_items self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); + tuple_flatcontainer!(reserve_items self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); } } @@ -180,11 +233,32 @@ macro_rules! tuple_flatcontainer { where It: Iterator + Clone, { - tuple_flatcontainer!(reserve_items_owned self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); + tuple_flatcontainer!(reserve_items_owned self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); + } + } + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + impl<$($name),*> Reserve for []<$($name),*> + where + $($name: Reserve),* + { + type Reserve = ($($name::Reserve,)*); + + #[inline] + fn reserve(&mut self, size: &Self::Reserve) { + let ($([<$name _size>],)*) = size; + $(self.[].reserve([<$name _size>]);)* } } } ); + (can_push $var:ident $self:ident $items:ident $name0:ident $($name:ident)* @ $num0:tt $($num:tt)*) => { + paste! { + let $var = $var && $self.[].can_push($items.clone().map(|i| &i.$num0)); + tuple_flatcontainer!(can_push $var $self $items $($name)* @ $($num)*); + } + }; + (can_push $var:ident $self:ident $items:ident @ $($num:tt)*) => {}; (reserve_items $self:ident $items:ident $name0:ident $($name:ident)* @ $num0:tt $($num:tt)*) => { paste! { $self.[].reserve_items($items.clone().map(|i| &i.$num0)); diff --git a/src/impls/vec.rs b/src/impls/vec.rs index 712a79d..029e3d3 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -1,6 +1,6 @@ //! Definitions to use `Vec` as a region. -use crate::{Push, Region, ReserveItems}; +use crate::{CanPush, Push, Region, Reserve, ReserveItems, TryPush}; impl Region for Vec { type Owned = T; @@ -50,6 +50,16 @@ impl Push for Vec { } } +impl TryPush for Vec { + fn try_push(&mut self, item: T) -> Result { + if self.can_push(std::iter::once(&item)) { + Ok(Push::push(self, item)) + } else { + Err(item) + } + } +} + impl Push<&T> for Vec { fn push(&mut self, item: &T) -> Self::Index { self.push(item.clone()); @@ -57,6 +67,28 @@ impl Push<&T> for Vec { } } +impl TryPush<&T> for Vec { + fn try_push<'a>(&mut self, item: &'a T) -> Result { + if self.can_push(std::iter::once(item)) { + Ok(Push::push(self, item)) + } else { + Err(item) + } + } +} + +impl<'a, T> CanPush<&'a T> for Vec +where + T: 'a, +{ + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.capacity() - self.len() >= items.count() + } +} + impl Push<&&T> for Vec { fn push(&mut self, item: &&T) -> Self::Index { self.push((*item).clone()); @@ -64,6 +96,29 @@ impl Push<&&T> for Vec { } } +impl TryPush<&&T> for Vec { + fn try_push<'a, 'b>(&mut self, item: &'a &'b T) -> Result { + if self.can_push(std::iter::once(*item)) { + Ok(Push::push(self, item)) + } else { + Err(item) + } + } +} + +impl<'a, 'b, T> CanPush<&'a &'b T> for Vec +where + &'b T: 'a, +{ + #[inline] + fn can_push(&self, items: I) -> bool + where + I: Iterator + Clone, + { + self.capacity() - self.len() >= items.count() + } +} + impl ReserveItems for Vec { fn reserve_items(&mut self, items: I) where @@ -73,6 +128,14 @@ impl ReserveItems for Vec { } } +impl Reserve for Vec { + type Reserve = usize; + + fn reserve(&mut self, size: &Self::Reserve) { + self.reserve(*size); + } +} + #[cfg(test)] mod tests { #[test] diff --git a/src/lib.rs b/src/lib.rs index 759a508..db7dbf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,8 @@ use serde::{Deserialize, Serialize}; pub mod impls; -use crate::impls::index::IndexContainer; pub use impls::columns::ColumnsRegion; +pub use impls::index::IndexContainer; pub use impls::mirror::MirrorRegion; pub use impls::option::OptionRegion; pub use impls::result::ResultRegion; @@ -112,6 +112,22 @@ pub trait Push: Region { fn push(&mut self, item: T) -> Self::Index; } +/// Push an item `T` into a region. +pub trait TryPush: Push { + /// Push `item` into self, returning an index that allows to look up the + /// corresponding read item. + fn try_push(&mut self, item: T) -> Result; +} + +/// Check if items can be pushed without reallocation +pub trait CanPush { + /// Test if an item can be pushed into the target without reallocation. + #[must_use] + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone; +} + /// Reserve space in the receiving region. /// /// Closely related to [`Push`], but separate because target type is likely different. @@ -122,6 +138,14 @@ pub trait ReserveItems: Region { I: Iterator + Clone; } +/// Preallocate space based on a description. +pub trait Reserve { + /// The type describing how to pre-size the region. + type Reserve; + /// Preallocate space for a size description. + fn reserve(&mut self, size: &Self::Reserve); +} + /// A reference type corresponding to an owned type, supporting conversion in each direction. /// /// This trait can be implemented by a GAT, and enables owned types to be borrowed as a GAT. @@ -226,6 +250,27 @@ impl::Index>> FlatStack { self.indices.push(index); } + /// Appends the element to the back of the stack, if there is sufficient capacity + #[inline] + pub fn try_push(&mut self, item: T) -> Result<(), T> + where + R: TryPush, + { + let index = self.region.try_push(item)?; + self.indices.push(index); + Ok(()) + } + + /// Appends the element to the back of the stack, if there is sufficient capacity + #[inline] + pub fn can_push(&mut self, item: &T) -> bool + where + R: for<'a> CanPush<&'a T>, + { + // TODO: Include `indices` in the check. + self.region.can_push(std::iter::once(item)) + } + /// Returns the element at the `index` position. #[inline] #[must_use] diff --git a/tests/reserve.rs b/tests/reserve.rs new file mode 100644 index 0000000..c1092e4 --- /dev/null +++ b/tests/reserve.rs @@ -0,0 +1,45 @@ +//! Tests that [`Reserve`](crate::Reserve) works as expected. + +use flatcontainer::impls::tuple::TupleABCRegion; +use flatcontainer::{MirrorRegion, OwnedRegion, Reserve, SliceRegion, StringRegion, TryPush}; + +#[test] +fn string_reserve() { + let mut r = ::default(); + assert_eq!(r.try_push("abc"), Err("abc")); + r.reserve(&64); + assert_eq!(r.try_push("abc"), Ok((0, 3))); +} + +#[test] +fn tuple_reserve() { + let mut r = , OwnedRegion, StringRegion>>::default(); + let item = (8, [1, 2, 3], "abc"); + assert_eq!(TryPush::try_push(&mut MirrorRegion::default(), &1u8), Ok(1)); + assert_eq!( + TryPush::try_push(&mut >::default(), &[1, 2, 3]), + Err(&[1, 2, 3]) + ); + assert_eq!( + TryPush::try_push(&mut ::default(), &"abc"), + Err(&"abc") + ); + , OwnedRegion, StringRegion> as TryPush<&( + u8, + [usize; 3], + &str, + )>>::try_push(&mut r, &item); + assert_eq!(TryPush::try_push(&mut r, &item), Err(&item)); + assert_eq!(r.try_push(&item), Err(&item)); + r.reserve(&((), 3, 3)); + assert!(r.try_push(&item).is_ok()); +} + +#[test] +fn slice_reserve() { + let mut r = >::default(); + let item = ["abc", "def", "ghi"]; + assert_eq!(r.try_push(&item), Err(&item)); + r.reserve(&(3, 9)); + assert!(r.try_push(&item).is_ok()); +}