From abc0526074aecbaa9fdea5f6670cbf26c8e2dc0b Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Tue, 2 Jul 2024 22:13:25 -0400 Subject: [PATCH 1/5] TryPush trait to enable non-reallocating pushes Signed-off-by: Moritz Hoffmann --- src/impls/index.rs | 46 +++++++++++++++++++-- src/impls/slice_owned.rs | 86 +++++++++++++++++++++++++++++++++++++++- src/impls/storage.rs | 9 +++++ src/lib.rs | 32 +++++++++++++++ 4 files changed, 169 insertions(+), 4 deletions(-) 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/slice_owned.rs b/src/impls/slice_owned.rs index 209e0e0..2f34d9f 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::{Push, PushIter, Region, ReserveItems, TryPush}; /// A container for owned types. /// @@ -128,6 +128,30 @@ where } } +impl TryPush<[T; N]> for OwnedRegion +where + [T]: ToOwned, + S: Storage + + for<'a> PushStorage> + + std::ops::Index, Output = [T]>, +{ + #[inline] + fn try_push(&mut self, item: [T; N]) -> Result< as Region>::Index, [T; N]> { + if self.can_push(&item) { + let start = self.slices.len(); + self.slices.push_storage(PushIter(item)); + Ok((start, self.slices.len())) + } else { + Err(item) + } + } + + #[inline] + fn can_push(&self, item: &[T; N]) -> bool { + self.slices.capacity() - self.slices.len() >= item.len() + } +} + impl Push<&[T; N]> for OwnedRegion where T: Clone, @@ -141,6 +165,27 @@ where } } +impl TryPush<&[T; N]> for OwnedRegion +where + T: Clone, + S: Storage + + for<'a> PushStorage<&'a [T]> + + std::ops::Index, Output = [T]>, +{ + #[inline] + fn try_push<'a>( + &mut self, + item: &'a [T; N], + ) -> Result< as Region>::Index, &'a [T; N]> { + self.try_push(item.as_slice()).map_err(|_| item) + } + + #[inline] + fn can_push(&self, item: &&[T; N]) -> bool { + self.can_push(&item.as_slice()) + } +} + impl Push<&&[T; N]> for OwnedRegion where T: Clone, @@ -183,6 +228,33 @@ where } } +impl TryPush<&[T]> for OwnedRegion +where + T: Clone, + S: Storage + + for<'a> PushStorage<&'a [T]> + + std::ops::Index, Output = [T]>, +{ + #[inline] + fn try_push<'a>( + &mut self, + item: &'a [T], + ) -> Result< as Region>::Index, &'a [T]> { + if self.can_push(&item) { + let start = self.slices.len(); + self.slices.push_storage(item); + Ok((start, self.slices.len())) + } else { + Err(item) + } + } + + #[inline] + fn can_push(&self, item: &&[T]) -> bool { + self.slices.capacity() - self.slices.len() >= item.len() + } +} + impl> Push<&&[T]> for OwnedRegion where for<'a> Self: Push<&'a [T]>, @@ -322,4 +394,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(&[1; 4])); + assert_eq!(r.try_push(&[1; 4]), Err(&[1; 4])); + r.reserve_items(std::iter::once(&[1; 4])); + assert!(r.can_push(&[1; 4])); + 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/lib.rs b/src/lib.rs index 759a508..da6e7cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,6 +112,17 @@ pub trait Push: Region { fn push(&mut self, item: T) -> Self::Index; } +/// Push an item `T` into a region. +pub trait TryPush: Region { + /// Push `item` into self, returning an index that allows to look up the + /// corresponding read item. + fn try_push(&mut self, item: T) -> Result; + + /// Test if an item can be pushed into the region without reallocation. + #[must_use] + fn can_push(&self, item: &T) -> bool; +} + /// Reserve space in the receiving region. /// /// Closely related to [`Push`], but separate because target type is likely different. @@ -226,6 +237,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: TryPush, + { + // TODO: Include `indices` in the check. + self.region.can_push(item) + } + /// Returns the element at the `index` position. #[inline] #[must_use] From 4e4cbc23a5421b462d1ab5657f7da2b9522f97ab Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 3 Jul 2024 16:28:54 -0400 Subject: [PATCH 2/5] TryPush depends on Push Signed-off-by: Moritz Hoffmann --- src/impls/slice_owned.rs | 33 --------------------------------- src/lib.rs | 11 +++++++++-- 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/impls/slice_owned.rs b/src/impls/slice_owned.rs index 2f34d9f..209b7a8 100644 --- a/src/impls/slice_owned.rs +++ b/src/impls/slice_owned.rs @@ -135,17 +135,6 @@ where + for<'a> PushStorage> + std::ops::Index, Output = [T]>, { - #[inline] - fn try_push(&mut self, item: [T; N]) -> Result< as Region>::Index, [T; N]> { - if self.can_push(&item) { - let start = self.slices.len(); - self.slices.push_storage(PushIter(item)); - Ok((start, self.slices.len())) - } else { - Err(item) - } - } - #[inline] fn can_push(&self, item: &[T; N]) -> bool { self.slices.capacity() - self.slices.len() >= item.len() @@ -172,14 +161,6 @@ where + for<'a> PushStorage<&'a [T]> + std::ops::Index, Output = [T]>, { - #[inline] - fn try_push<'a>( - &mut self, - item: &'a [T; N], - ) -> Result< as Region>::Index, &'a [T; N]> { - self.try_push(item.as_slice()).map_err(|_| item) - } - #[inline] fn can_push(&self, item: &&[T; N]) -> bool { self.can_push(&item.as_slice()) @@ -235,20 +216,6 @@ where + for<'a> PushStorage<&'a [T]> + std::ops::Index, Output = [T]>, { - #[inline] - fn try_push<'a>( - &mut self, - item: &'a [T], - ) -> Result< as Region>::Index, &'a [T]> { - if self.can_push(&item) { - let start = self.slices.len(); - self.slices.push_storage(item); - Ok((start, self.slices.len())) - } else { - Err(item) - } - } - #[inline] fn can_push(&self, item: &&[T]) -> bool { self.slices.capacity() - self.slices.len() >= item.len() diff --git a/src/lib.rs b/src/lib.rs index da6e7cb..cb6b6f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,10 +113,17 @@ pub trait Push: Region { } /// Push an item `T` into a region. -pub trait TryPush: 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; + #[inline] + fn try_push(&mut self, item: T) -> Result { + if self.can_push(&item) { + Ok(self.push(item)) + } else { + Err(item) + } + } /// Test if an item can be pushed into the region without reallocation. #[must_use] From 17abcc479be0b6783d136515e581ec169f0da9a0 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 26 Jul 2024 11:41:18 -0400 Subject: [PATCH 3/5] wip Signed-off-by: Moritz Hoffmann --- src/impls/mirror.rs | 21 +++++++- src/impls/option.rs | 13 ++++- src/impls/result.rs | 15 +++++- src/impls/slice.rs | 27 +++++++++- src/impls/slice_owned.rs | 106 +++++++++++++++++++++++++++++++++------ src/impls/string.rs | 58 ++++++++++++++++++++- src/impls/tuple.rs | 42 +++++++++++++++- src/impls/vec.rs | 10 +++- src/lib.rs | 45 ++++++++++++----- 9 files changed, 301 insertions(+), 36 deletions(-) diff --git a/src/impls/mirror.rs b/src/impls/mirror.rs index 664f1e0..7fa306b 100644 --- a/src/impls/mirror.rs +++ b/src/impls/mirror.rs @@ -6,7 +6,7 @@ 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 +99,17 @@ where } } +impl CanPush for MirrorRegion +{ + fn can_push<'a, I>(&self, _: I) -> bool + where + I: Iterator + Clone, + T: 'a + { + true + } +} + impl Push<&T> for MirrorRegion where for<'a> T: Index + IntoOwned<'a, Owned = T>, @@ -145,6 +156,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..85f29db 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::{IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush}; impl RegionPreference for Vec { type Owned = Vec; @@ -416,6 +416,18 @@ where } } +// impl<'a, C, T, O> TryPush<&'a [T]> for SliceRegion +// where +// C: Region + TryPush<&'a T>, +// O: IndexContainer, +// { +// fn can_push(&self, item: &&'a [T]) -> bool { +// // TODO: Check `self.slices` for sufficient capacity. +// self.inner.can_push(item) +// } +// } + + impl<'a, T, R, O> ReserveItems<&'a [T]> for SliceRegion where R: Region + ReserveItems<&'a T>, @@ -581,6 +593,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 209b7a8..d965273 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, TryPush}; +use crate::{CanPush, Push, PushIter, Region, Reserve, ReserveItems, TryPush}; /// A container for owned types. /// @@ -128,16 +128,20 @@ where } } -impl TryPush<[T; N]> for OwnedRegion +impl CanPush<[T; N]> for OwnedRegion where [T]: ToOwned, S: Storage + for<'a> PushStorage> + std::ops::Index, Output = [T]>, { - #[inline] - fn can_push(&self, item: &[T; N]) -> bool { - self.slices.capacity() - self.slices.len() >= item.len() + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + [T; N]: 'a + { + let required = items.map(|item| item.len()).sum(); + self.slices.capacity() - self.slices.len() >= required } } @@ -154,17 +158,25 @@ where } } -impl TryPush<&[T; N]> for OwnedRegion +impl<'a, T, S, const N: usize> TryPush<&'a [T; N]> for OwnedRegion where T: Clone, S: Storage - + for<'a> PushStorage<&'a [T]> + + for<'b> PushStorage<&'b [T]> + std::ops::Index, Output = [T]>, { - #[inline] - fn can_push(&self, item: &&[T; N]) -> bool { - self.can_push(&item.as_slice()) + fn can_push<'b, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + &'a [T; N]: 'b + { + let required = items.map(|item| item.len()).sum(); + self.slices.capacity() - self.slices.len() >= required } + // #[inline] + // fn can_push(&self, item: &&[T; N]) -> bool { + // self.can_push(&item.as_slice()) + // } } impl Push<&&[T; N]> for OwnedRegion @@ -209,17 +221,24 @@ where } } -impl TryPush<&[T]> for OwnedRegion +impl<'b, T, S> TryPush<&'b [T]> for OwnedRegion where T: Clone, S: Storage + for<'a> PushStorage<&'a [T]> + std::ops::Index, Output = [T]>, { - #[inline] - fn can_push(&self, item: &&[T]) -> bool { - self.slices.capacity() - self.slices.len() >= item.len() + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + &'b [T]: 'a + { + todo!() } + // #[inline] + // fn can_push(&self, item: &&[T]) -> bool { + // self.slices.capacity() - self.slices.len() >= item.len() + // } } impl> Push<&&[T]> for OwnedRegion @@ -261,6 +280,27 @@ where } } +impl TryPush> for OwnedRegion +where + [T]: ToOwned, + S: Storage + + for<'a> PushStorage<&'a mut Vec> + + std::ops::Index, Output = [T]>, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator> + Clone, + Vec: 'a + { + let required = items.map(Vec::len).sum(); + self.slices.capacity() - self.slices.len() >= required + } + // #[inline] + // fn can_push(&self, item: &Vec) -> bool { + // self.slices.capacity() - self.slices.len() >= item.len() + // } +} + impl Push<&Vec> for OwnedRegion where T: Clone, @@ -274,6 +314,27 @@ 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 can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator> + Clone, + &'b Vec: 'a + { + let required = items.map(|item| item.len()).sum(); + self.slices.capacity() - self.slices.len() >= required + } + // #[inline] + // fn can_push(&self, item: &&Vec) -> bool { + // self.slices.capacity() - self.slices.len() >= item.len() + // } +} + impl<'a, T, S> ReserveItems<&'a Vec> for OwnedRegion where [T]: ToOwned, @@ -321,6 +382,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}; @@ -365,10 +439,10 @@ mod tests { #[test] fn try_push() { let mut r = >::default(); - assert!(!r.can_push(&[1; 4])); + assert!(!r.can_push(std::iter::once(&[1; 4]))); assert_eq!(r.try_push(&[1; 4]), Err(&[1; 4])); r.reserve_items(std::iter::once(&[1; 4])); - assert!(r.can_push(&[1; 4])); + assert!(r.can_push(std::iter::once(&[1; 4]))); 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/string.rs b/src/impls/string.rs index e92e5bd..fcc5671 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::{Push, Region, RegionPreference, Reserve, ReserveItems, TryPush}; /// A region to store strings and read `&str`. /// @@ -130,6 +130,22 @@ where } } +impl TryPush for StringRegion +where + for<'a> R: Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + 'a, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + String: 'a + { + todo!() + } + // fn can_push(&self, item: &String) -> bool { + // self.inner.can_push(&item.as_bytes()) + // } +} + impl<'b, R> ReserveItems<&'b String> for StringRegion where for<'a> R: Region = &'a [u8]> + ReserveItems<&'a [u8]> + 'a, @@ -153,6 +169,22 @@ where } } +impl<'b, R> TryPush<&'b str> for StringRegion +where + for<'a> R: Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + 'a, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + &'b str: 'a + { + todo!() + } + // fn can_push(&self, item: &&str) -> bool { + // self.inner.can_push(&item.as_bytes()) + // } +} + impl Push<&&str> for StringRegion where for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, @@ -163,6 +195,22 @@ where } } +impl<'b, R> TryPush<&'b &'b str> for StringRegion +where + for<'a> R: Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + 'a, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + &'b &'b str: 'a + { + todo!() + } + // fn can_push(&self, item: &&&str) -> bool { + // self.inner.can_push(&item.as_bytes()) + // } +} + impl<'b, R> ReserveItems<&'b str> for StringRegion where for<'a> R: Region = &'a [u8]> + ReserveItems<&'a [u8]> + 'a, @@ -189,6 +237,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..b18edf5 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -122,6 +122,23 @@ macro_rules! tuple_flatcontainer { } } + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + impl<$($name, [<$name _C>]),*> crate::CanPush<($($name,)*)> for []<$([<$name _C>]),*> + where + $([<$name _C>]: Region + crate::CanPush<$name>),* + { + #[inline] + fn can_push<'a, It>(&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 +182,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 +197,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),*> crate::Reserve for []<$($name),*> + where + $($name: crate::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..a81c4cd 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::{Push, Region, Reserve, ReserveItems}; impl Region for Vec { type Owned = T; @@ -73,6 +73,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 cb6b6f6..4af2357 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; @@ -116,20 +116,33 @@ pub trait Push: Region { pub trait TryPush: Push { /// Push `item` into self, returning an index that allows to look up the /// corresponding read item. - #[inline] - fn try_push(&mut self, item: T) -> Result { - if self.can_push(&item) { - Ok(self.push(item)) - } else { - Err(item) - } - } + // #[inline] + fn try_push(&mut self, item: T) -> Result; + // if self.can_push(std::iter::once(&item)) { + // Ok(self.push(item)) + // } else { + // Err(item) + // } + // } +} +/// Check if items can be pushed without reallocation +pub trait CanPush { /// Test if an item can be pushed into the region without reallocation. #[must_use] - fn can_push(&self, item: &T) -> bool; + fn can_push<'a, I>(&self, items: I) -> bool where I: Iterator + Clone, T: 'a; } +// impl<'c, T, C: CanPush> CanPush<&'c T> for C { +// fn can_push<'a, I>(&self, items: I) -> bool +// where +// I: Iterator + Clone, +// &'c T: 'a +// { +// C::can_push(items.map(|item| *item)) +// } +// } + /// Reserve space in the receiving region. /// /// Closely related to [`Push`], but separate because target type is likely different. @@ -140,6 +153,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. @@ -259,10 +280,10 @@ impl::Index>> FlatStack { #[inline] pub fn can_push(&mut self, item: &T) -> bool where - R: TryPush, + R: CanPush, { // TODO: Include `indices` in the check. - self.region.can_push(item) + self.region.can_push(std::iter::once(item)) } /// Returns the element at the `index` position. From b46d07284ad1c444833cfd6dab95c9a95faffd1e Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 26 Jul 2024 17:27:20 -0400 Subject: [PATCH 4/5] wip Signed-off-by: Moritz Hoffmann --- src/impls/mirror.rs | 39 +++++++- src/impls/slice.rs | 197 ++++++++++++++++++++++++++++++++++++--- src/impls/slice_owned.rs | 90 ++++++++---------- src/impls/string.rs | 85 ++++++++++++----- src/impls/tuple.rs | 50 ++++++++-- src/impls/vec.rs | 53 ++++++++++- src/lib.rs | 26 ++---- 7 files changed, 421 insertions(+), 119 deletions(-) diff --git a/src/impls/mirror.rs b/src/impls/mirror.rs index 7fa306b..e1b601a 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::{CanPush, Index, IntoOwned, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush}; +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,12 +101,21 @@ where } } -impl CanPush for MirrorRegion +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<'a, I>(&self, _: I) -> bool where - I: Iterator + Clone, - T: 'a + I: Iterator + Clone, + T: 'a, { true } @@ -120,6 +131,16 @@ where } } +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>, @@ -130,6 +151,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>, diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 85f29db..1bb5003 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, Reserve, ReserveItems, TryPush}; +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,17 +416,32 @@ where } } -// impl<'a, C, T, O> TryPush<&'a [T]> for SliceRegion -// where -// C: Region + TryPush<&'a T>, -// O: IndexContainer, -// { -// fn can_push(&self, item: &&'a [T]) -> bool { -// // TODO: Check `self.slices` for sufficient capacity. -// self.inner.can_push(item) -// } -// } +impl<'a, C, T, O> TryPush<&'a [T]> for SliceRegion +where + C: Region + TryPush<&'a T> + CanPush, + 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 CanPush<[T]> for SliceRegion +where + C: Region + CanPush, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + T: 'a, + { + self.inner.can_push(items.flatten()) + } +} impl<'a, T, R, O> ReserveItems<&'a [T]> for SliceRegion where @@ -457,6 +472,33 @@ where } } +impl<'a, C, T, O> TryPush> for SliceRegion +where + C: Region + TryPush + CanPush, + 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 CanPush> for SliceRegion +where + C: Region + CanPush, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator> + Clone, + T: 'a, + { + self.inner.can_push(items.flatten()) + } +} + impl Push<&Vec> for SliceRegion where for<'a> C: Region + Push<&'a T>, @@ -468,6 +510,34 @@ where } } +impl<'a, C, T, O> TryPush<&'a Vec> for SliceRegion +where + C: Region + for<'b> Push<&'b T> + CanPush, + 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<'b, C, T, O> CanPush<&'b Vec> for SliceRegion +where + C: Region + CanPush, + T: 'b, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator> + Clone, + &'b Vec: 'a, + { + 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>, @@ -479,6 +549,33 @@ where } } +impl TryPush<&&Vec> for SliceRegion +where + C: Region + for<'b> Push<&'b T> + CanPush, + 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>, @@ -514,6 +611,34 @@ 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<'a, I>(&self, items: I) -> bool + where + I: Iterator> + Clone, + ReadSlice<'b, C, O>: 'a, + { + self.inner.can_push(items.copied().flatten()) + } +} + impl<'a, C, O> Push> for SliceRegion where C: Region + Push<::ReadItem<'a>>, @@ -534,12 +659,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, + 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 CanPush<[T; N]> for SliceRegion +where + R: CanPush, +{ + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + [T; N]: 'a, + { + self.inner.can_push(items.flatten()) } } @@ -554,6 +709,20 @@ where } } +impl<'a, T, R, O, const N: usize> TryPush<&'a [T; N]> for SliceRegion +where + R: Region + Push<&'a T> + CanPush, + 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>, diff --git a/src/impls/slice_owned.rs b/src/impls/slice_owned.rs index d965273..21e33e0 100644 --- a/src/impls/slice_owned.rs +++ b/src/impls/slice_owned.rs @@ -128,17 +128,14 @@ where } } -impl CanPush<[T; N]> for OwnedRegion +impl CanPush<[T]> for OwnedRegion where - [T]: ToOwned, - S: Storage - + for<'a> PushStorage> - + std::ops::Index, Output = [T]>, + S: Storage, { fn can_push<'a, I>(&self, items: I) -> bool where - I: Iterator + Clone, - [T; N]: 'a + I: Iterator + Clone, + T: 'a, { let required = items.map(|item| item.len()).sum(); self.slices.capacity() - self.slices.len() >= required @@ -165,18 +162,27 @@ where + for<'b> PushStorage<&'b [T]> + std::ops::Index, Output = [T]>, { - fn can_push<'b, I>(&self, items: I) -> bool + 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 CanPush<[T; N]> for OwnedRegion +where + S: Storage, +{ + fn can_push<'a, I>(&self, items: I) -> bool where - I: Iterator + Clone, - &'a [T; N]: 'b + I: Iterator + Clone, + T: 'a, { - let required = items.map(|item| item.len()).sum(); + let required = items.count() * N; self.slices.capacity() - self.slices.len() >= required } - // #[inline] - // fn can_push(&self, item: &&[T; N]) -> bool { - // self.can_push(&item.as_slice()) - // } } impl Push<&&[T; N]> for OwnedRegion @@ -228,17 +234,13 @@ where + for<'a> PushStorage<&'a [T]> + std::ops::Index, Output = [T]>, { - fn can_push<'a, I>(&self, items: I) -> bool - where - I: Iterator + Clone, - &'b [T]: 'a - { - todo!() + fn try_push(&mut self, item: &'b [T]) -> Result { + if self.can_push(std::iter::once(item)) { + Ok(self.push(item)) + } else { + Err(item) + } } - // #[inline] - // fn can_push(&self, item: &&[T]) -> bool { - // self.slices.capacity() - self.slices.len() >= item.len() - // } } impl> Push<&&[T]> for OwnedRegion @@ -287,18 +289,13 @@ where + for<'a> PushStorage<&'a mut Vec> + std::ops::Index, Output = [T]>, { - fn can_push<'a, I>(&self, items: I) -> bool - where - I: Iterator> + Clone, - Vec: 'a - { - let required = items.map(Vec::len).sum(); - self.slices.capacity() - self.slices.len() >= required + fn try_push(&mut self, item: Vec) -> Result> { + if self.can_push(std::iter::once(&item[..])) { + Ok(self.push(item)) + } else { + Err(item) + } } - // #[inline] - // fn can_push(&self, item: &Vec) -> bool { - // self.slices.capacity() - self.slices.len() >= item.len() - // } } impl Push<&Vec> for OwnedRegion @@ -321,18 +318,13 @@ where + for<'a> PushStorage<&'a [T]> + std::ops::Index, Output = [T]>, { - fn can_push<'a, I>(&self, items: I) -> bool - where - I: Iterator> + Clone, - &'b Vec: 'a - { - let required = items.map(|item| item.len()).sum(); - self.slices.capacity() - self.slices.len() >= required + fn try_push(&mut self, item: &'b Vec) -> Result> { + if self.can_push(std::iter::once(&item[..])) { + Ok(self.push(item)) + } else { + Err(item) + } } - // #[inline] - // fn can_push(&self, item: &&Vec) -> bool { - // self.slices.capacity() - self.slices.len() >= item.len() - // } } impl<'a, T, S> ReserveItems<&'a Vec> for OwnedRegion @@ -439,10 +431,10 @@ mod tests { #[test] fn try_push() { let mut r = >::default(); - assert!(!r.can_push(std::iter::once(&[1; 4]))); + 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]))); + 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/string.rs b/src/impls/string.rs index fcc5671..db2b9ad 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, Reserve, ReserveItems, TryPush}; +use crate::{CanPush, Push, Region, RegionPreference, Reserve, ReserveItems, TryPush}; /// A region to store strings and read `&str`. /// @@ -132,18 +132,30 @@ where impl TryPush for StringRegion where - for<'a> R: Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + 'a, + for<'a> R: + Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + CanPush<[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 CanPush for StringRegion +where + R: CanPush<[u8]>, +{ + #[inline] fn can_push<'a, I>(&self, items: I) -> bool where - I: Iterator + Clone, - String: 'a + I: Iterator + Clone, { - todo!() + self.inner.can_push(items.map(|item| item.as_bytes())) } - // fn can_push(&self, item: &String) -> bool { - // self.inner.can_push(&item.as_bytes()) - // } } impl<'b, R> ReserveItems<&'b String> for StringRegion @@ -169,20 +181,32 @@ where } } -impl<'b, R> TryPush<&'b str> for StringRegion +impl TryPush<&str> for StringRegion where - for<'a> R: Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + 'a, + for<'a> R: + Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + CanPush<[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 CanPush for StringRegion +where + R: CanPush<[u8]>, +{ + #[inline] fn can_push<'a, I>(&self, items: I) -> bool where - I: Iterator + Clone, - &'b str: 'a + I: Iterator + Clone, { - todo!() + self.inner.can_push(items.map(|item| item.as_bytes())) } - // fn can_push(&self, item: &&str) -> bool { - // self.inner.can_push(&item.as_bytes()) - // } } impl Push<&&str> for StringRegion @@ -195,20 +219,33 @@ where } } -impl<'b, R> TryPush<&'b &'b str> for StringRegion +impl TryPush<&&str> for StringRegion +where + for<'a> R: + Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + CanPush<[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<'b, R> CanPush<&'b str> for StringRegion where - for<'a> R: Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + 'a, + R: CanPush<[u8]>, { + #[inline] fn can_push<'a, I>(&self, items: I) -> bool where - I: Iterator + Clone, - &'b &'b str: 'a + I: Iterator + Clone, + 'b: 'a, { - todo!() + self.inner.can_push(items.map(|item| item.as_bytes())) } - // fn can_push(&self, item: &&&str) -> bool { - // self.inner.can_push(&item.as_bytes()) - // } } impl<'b, R> ReserveItems<&'b str> for StringRegion diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index b18edf5..f8958d3 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; @@ -124,14 +126,48 @@ macro_rules! tuple_flatcontainer { #[allow(non_camel_case_types)] #[allow(non_snake_case)] - impl<$($name, [<$name _C>]),*> crate::CanPush<($($name,)*)> for []<$([<$name _C>]),*> + impl<$($name, [<$name _C>]),*> TryPush<($($name,)*)> for []<$([<$name _C>]),*> where - $([<$name _C>]: Region + crate::CanPush<$name>),* + $([<$name _C>]: Region + Push<$name> + CanPush<$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 + $([<$name _C>]: Region + for<'a> Push<&'a $name> + CanPush<$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<$($name, [<$name _C>]),*> CanPush<($($name,)*)> for []<$([<$name _C>]),*> + where + $([<$name _C>]: Region + CanPush<$name>),* { #[inline] fn can_push<'a, It>(&self, items: It) -> bool where It: Iterator + Clone, + $($name: 'a,)* { 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); @@ -202,9 +238,9 @@ macro_rules! tuple_flatcontainer { } #[allow(non_camel_case_types)] #[allow(non_snake_case)] - impl<$($name),*> crate::Reserve for []<$($name),*> + impl<$($name),*> Reserve for []<$($name),*> where - $($name: crate::Reserve),* + $($name: Reserve),* { type Reserve = ($($name::Reserve,)*); diff --git a/src/impls/vec.rs b/src/impls/vec.rs index a81c4cd..59060aa 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, Reserve, 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,26 @@ 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 CanPush for Vec { + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + T: 'a, + { + 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 +94,27 @@ 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<'b, T> CanPush<&'b T> for Vec { + #[inline] + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + &'b T: 'a, + { + self.capacity() - self.len() >= items.count() + } +} + impl ReserveItems for Vec { fn reserve_items(&mut self, items: I) where diff --git a/src/lib.rs b/src/lib.rs index 4af2357..b626b92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,33 +116,19 @@ pub trait Push: Region { pub trait TryPush: Push { /// Push `item` into self, returning an index that allows to look up the /// corresponding read item. - // #[inline] fn try_push(&mut self, item: T) -> Result; - // if self.can_push(std::iter::once(&item)) { - // Ok(self.push(item)) - // } else { - // Err(item) - // } - // } } /// Check if items can be pushed without reallocation -pub trait CanPush { - /// Test if an item can be pushed into the region 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, T: 'a; + fn can_push<'a, I>(&self, items: I) -> bool + where + I: Iterator + Clone, + T: 'a; } -// impl<'c, T, C: CanPush> CanPush<&'c T> for C { -// fn can_push<'a, I>(&self, items: I) -> bool -// where -// I: Iterator + Clone, -// &'c T: 'a -// { -// C::can_push(items.map(|item| *item)) -// } -// } - /// Reserve space in the receiving region. /// /// Closely related to [`Push`], but separate because target type is likely different. From f86067a7417ce0b3e464bc6f452f71d8cfbc7c9b Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 26 Jul 2024 21:51:44 -0400 Subject: [PATCH 5/5] Capture wip Signed-off-by: Moritz Hoffmann --- src/impls/mirror.rs | 14 +++++-- src/impls/slice.rs | 80 ++++++++++++++++++++-------------------- src/impls/slice_owned.rs | 12 +++--- src/impls/string.rs | 41 ++++++++++++-------- src/impls/tuple.rs | 12 +++--- src/impls/vec.rs | 16 +++++--- src/lib.rs | 7 ++-- tests/reserve.rs | 45 ++++++++++++++++++++++ 8 files changed, 145 insertions(+), 82 deletions(-) create mode 100644 tests/reserve.rs diff --git a/src/impls/mirror.rs b/src/impls/mirror.rs index e1b601a..b54be3e 100644 --- a/src/impls/mirror.rs +++ b/src/impls/mirror.rs @@ -112,10 +112,9 @@ where } impl CanPush for MirrorRegion { - fn can_push<'a, I>(&self, _: I) -> bool + fn can_push(&self, _: I) -> bool where - I: Iterator + Clone, - T: 'a, + I: Iterator + Clone, { true } @@ -131,6 +130,15 @@ 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>, diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 1bb5003..9fa3f64 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -418,7 +418,7 @@ where impl<'a, C, T, O> TryPush<&'a [T]> for SliceRegion where - C: Region + TryPush<&'a T> + CanPush, + C: Region + TryPush<&'a T> + CanPush<&'a T>, O: IndexContainer, { fn try_push(&mut self, item: &'a [T]) -> Result { @@ -430,14 +430,14 @@ where } } -impl CanPush<[T]> for SliceRegion +impl<'a, C, T, O> CanPush<&'a [T]> for SliceRegion where - C: Region + CanPush, + C: Region + CanPush<&'a T>, + T: 'a, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - T: 'a, { self.inner.can_push(items.flatten()) } @@ -472,9 +472,9 @@ where } } -impl<'a, C, T, O> TryPush> for SliceRegion +impl TryPush> for SliceRegion where - C: Region + TryPush + CanPush, + C: Region + TryPush + for<'a> CanPush<&'a T>, O: IndexContainer, { fn try_push(&mut self, item: Vec) -> Result> { @@ -486,14 +486,14 @@ where } } -impl CanPush> for SliceRegion +impl<'a, C, T, O> CanPush<&'a Vec> for SliceRegion where - C: Region + CanPush, + C: Region + CanPush<&'a T>, + T: 'a, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator> + Clone, - T: 'a, { self.inner.can_push(items.flatten()) } @@ -512,7 +512,7 @@ where impl<'a, C, T, O> TryPush<&'a Vec> for SliceRegion where - C: Region + for<'b> Push<&'b T> + CanPush, + C: Region + for<'b> Push<&'b T> + CanPush<&'a T>, O: IndexContainer, { fn try_push(&mut self, item: &'a Vec) -> Result> { @@ -524,15 +524,14 @@ where } } -impl<'b, C, T, O> CanPush<&'b Vec> for SliceRegion +impl<'a, 'b, C, T, O> CanPush<&'a &'b Vec> for SliceRegion where - C: Region + CanPush, - T: 'b, + C: Region + CanPush<&'b T>, + &'b T: 'a, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator> + Clone, - &'b Vec: 'a, { self.inner.can_push(items.map(|x| *x).flatten()) } @@ -551,7 +550,7 @@ where impl TryPush<&&Vec> for SliceRegion where - C: Region + for<'b> Push<&'b T> + CanPush, + for<'b> C: Region + Push<&'b T> + CanPush<&'b T>, O: IndexContainer, { fn try_push<'a, 'b>(&mut self, item: &'a &'b Vec) -> Result> { @@ -563,18 +562,18 @@ where } } -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<'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 @@ -617,7 +616,7 @@ where O: IndexContainer, { fn try_push(&mut self, item: ReadSlice<'a, C, O>) -> Result> { - if self.can_push(std::iter::once(&item)) { + if self.can_push(std::iter::once(item)) { Ok(self.push(item)) } else { Err(item) @@ -630,12 +629,11 @@ where C: Region + CanPush>, O: IndexContainer, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where - I: Iterator> + Clone, - ReadSlice<'b, C, O>: 'a, + I: Iterator> + Clone, { - self.inner.can_push(items.copied().flatten()) + self.inner.can_push(items.flatten()) } } @@ -673,7 +671,7 @@ where impl TryPush<[T; N]> for SliceRegion where - for<'a> R: Region + Push + CanPush, + for<'a> R: Region + Push + CanPush<&'a T>, O: IndexContainer, { fn try_push(&mut self, item: [T; N]) -> Result { @@ -685,14 +683,14 @@ where } } -impl CanPush<[T; N]> for SliceRegion +impl<'a, T, R, O, const N: usize> CanPush<&'a [T; N]> for SliceRegion where - R: CanPush, + R: CanPush<&'a T>, + T: 'a, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - [T; N]: 'a, { self.inner.can_push(items.flatten()) } @@ -711,7 +709,7 @@ where impl<'a, T, R, O, const N: usize> TryPush<&'a [T; N]> for SliceRegion where - R: Region + Push<&'a T> + CanPush, + R: Region + Push<&'a T> + CanPush<&'a T>, O: IndexContainer, { fn try_push(&mut self, item: &'a [T; N]) -> Result { diff --git a/src/impls/slice_owned.rs b/src/impls/slice_owned.rs index 21e33e0..1227941 100644 --- a/src/impls/slice_owned.rs +++ b/src/impls/slice_owned.rs @@ -128,14 +128,14 @@ where } } -impl CanPush<[T]> for OwnedRegion +impl<'a, T, S> CanPush<&'a [T]> for OwnedRegion where S: Storage, + T: 'a, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - T: 'a, { let required = items.map(|item| item.len()).sum(); self.slices.capacity() - self.slices.len() >= required @@ -171,14 +171,14 @@ where } } -impl CanPush<[T; N]> for OwnedRegion +impl<'a, T, S, const N: usize> CanPush<&'a [T; N]> for OwnedRegion where S: Storage, + T: 'a, { - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - T: 'a, { let required = items.count() * N; self.slices.capacity() - self.slices.len() >= required diff --git a/src/impls/string.rs b/src/impls/string.rs index db2b9ad..b452692 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -132,8 +132,11 @@ where impl TryPush for StringRegion where - for<'a> R: - Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + CanPush<[u8]> + 'a, + 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 { @@ -145,12 +148,12 @@ where } } -impl CanPush for StringRegion +impl<'a, R> CanPush<&'a String> for StringRegion where - R: CanPush<[u8]>, + R: CanPush<&'a [u8]>, { #[inline] - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, { @@ -183,8 +186,11 @@ where impl TryPush<&str> for StringRegion where - for<'a> R: - Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + CanPush<[u8]> + 'a, + 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 { @@ -196,12 +202,12 @@ where } } -impl CanPush for StringRegion +impl<'a, R> CanPush<&'a str> for StringRegion where - R: CanPush<[u8]>, + R: CanPush<&'a [u8]>, { #[inline] - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, { @@ -221,8 +227,11 @@ where impl TryPush<&&str> for StringRegion where - for<'a> R: - Region = &'a [u8]> + TryPush<&'a [u8]> + Push<&'a [u8]> + CanPush<[u8]> + 'a, + 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 { @@ -234,15 +243,15 @@ where } } -impl<'b, R> CanPush<&'b str> for StringRegion +impl<'a, 'b, R> CanPush<&'a &'b str> for StringRegion where - R: CanPush<[u8]>, + R: CanPush<&'b [u8]>, + 'b: 'a, { #[inline] - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - 'b: 'a, { self.inner.can_push(items.map(|item| item.as_bytes())) } diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index f8958d3..3376469 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -128,7 +128,7 @@ macro_rules! tuple_flatcontainer { #[allow(non_snake_case)] impl<$($name, [<$name _C>]),*> TryPush<($($name,)*)> for []<$([<$name _C>]),*> where - $([<$name _C>]: Region + Push<$name> + CanPush<$name>),* + $([<$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,)*)> { @@ -145,7 +145,7 @@ macro_rules! tuple_flatcontainer { #[allow(non_snake_case)] impl<$($name, [<$name _C>]),*> TryPush<&($($name,)*)> for []<$([<$name _C>]),*> where - $([<$name _C>]: Region + for<'a> Push<&'a $name> + CanPush<$name>),* + $(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,)*)> { @@ -159,15 +159,15 @@ macro_rules! tuple_flatcontainer { #[allow(non_camel_case_types)] #[allow(non_snake_case)] - impl<$($name, [<$name _C>]),*> CanPush<($($name,)*)> for []<$([<$name _C>]),*> + impl<'a, $($name, [<$name _C>]),*> CanPush<&'a ($($name,)*)> for []<$([<$name _C>]),*> where - $([<$name _C>]: Region + CanPush<$name>),* + $([<$name _C>]: Region + CanPush<&'a $name> + 'a),* + // $($name: 'a,)* { #[inline] - fn can_push<'a, It>(&self, items: It) -> bool + fn can_push(&self, items: It) -> bool where It: Iterator + Clone, - $($name: 'a,)* { 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); diff --git a/src/impls/vec.rs b/src/impls/vec.rs index 59060aa..029e3d3 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -77,11 +77,13 @@ impl TryPush<&T> for Vec { } } -impl CanPush for Vec { - fn can_push<'a, I>(&self, items: I) -> bool +impl<'a, T> CanPush<&'a T> for Vec +where + T: 'a, +{ + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - T: 'a, { self.capacity() - self.len() >= items.count() } @@ -104,12 +106,14 @@ impl TryPush<&&T> for Vec { } } -impl<'b, T> CanPush<&'b T> for Vec { +impl<'a, 'b, T> CanPush<&'a &'b T> for Vec +where + &'b T: 'a, +{ #[inline] - fn can_push<'a, I>(&self, items: I) -> bool + fn can_push(&self, items: I) -> bool where I: Iterator + Clone, - &'b T: 'a, { self.capacity() - self.len() >= items.count() } diff --git a/src/lib.rs b/src/lib.rs index b626b92..db7dbf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,13 +120,12 @@ pub trait TryPush: Push { } /// Check if items can be pushed without reallocation -pub trait CanPush { +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, - T: 'a; + I: Iterator + Clone; } /// Reserve space in the receiving region. @@ -266,7 +265,7 @@ impl::Index>> FlatStack { #[inline] pub fn can_push(&mut self, item: &T) -> bool where - R: CanPush, + R: for<'a> CanPush<&'a T>, { // TODO: Include `indices` in the check. self.region.can_push(std::iter::once(item)) 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()); +}