diff --git a/benches/maps.rs b/benches/maps.rs index 0d3f3f4..b8c0726 100644 --- a/benches/maps.rs +++ b/benches/maps.rs @@ -55,7 +55,7 @@ macro_rules! removals { #[bench] fn $fnn(b: &mut Bencher) { let mut rng = SmallRng::seed_from_u64(0x5432_1012_3454_3210); - let mut pairs = coca::AllocVec::<(u32, u32), usize>::with_capacity($n); + let mut pairs = coca::collections::AllocVec::<(u32, u32), usize>::with_capacity($n); for _ in 0..$n { pairs.push((rng.next_u32(), rng.next_u32())); } @@ -77,7 +77,7 @@ macro_rules! removals { mod unordered { use super::*; - use coca::AllocVec; + use coca::collections::AllocVec; use std::collections::HashMap as StdHashMap; #[allow(unconditional_recursion)] // false positive! @@ -167,7 +167,7 @@ mod unordered { mod ordered { use super::*; - use coca::AllocVec; + use coca::collections::AllocVec; use std::collections::BTreeMap; impl Map for BTreeMap diff --git a/src/collections/list_map.rs b/src/collections/list_map.rs index a0f0a94..c5a4494 100644 --- a/src/collections/list_map.rs +++ b/src/collections/list_map.rs @@ -14,6 +14,8 @@ use self::Entry::{Occupied, Vacant}; /// The [`LayoutSpec`] for a [`ListMap`]. pub struct ListMapLayout(PhantomData<(K, V)>); impl LayoutSpec for ListMapLayout { + type Item = (K, V); + fn layout_with_capacity(items: usize) -> Result { let keys_array = Layout::array::(items)?; let values_array = Layout::array::(items)?; @@ -847,6 +849,8 @@ pub struct InlineStorage { } unsafe impl Storage> for InlineStorage { + const MIN_REPRESENTABLE: usize = N; + fn get_ptr(&self) -> *const u8 { (self as *const Self).cast() } diff --git a/src/collections/mod.rs b/src/collections/mod.rs index ce0ee4d..e206f36 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -596,6 +596,21 @@ pub type ArenaVec<'a, T, I = usize> = Vec>, I /// ``` pub type AllocVec = Vec>, I>; +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +/// A heap-allocated vector which automatically reallocates. +/// +/// # Examples +/// ``` +/// let mut vec = coca::collections::ReallocVec::::new(); +/// vec.push('a'); +/// vec.push('b'); +/// vec.push('c'); +/// assert_eq!(vec.len(), 3); +/// assert_eq!(vec.capacity(), 4); +/// ``` +pub type ReallocVec = Vec>, I>; + /// A vector using an inline array for storage. /// /// # Examples diff --git a/src/collections/pool/direct.rs b/src/collections/pool/direct.rs index b431f6b..9903447 100644 --- a/src/collections/pool/direct.rs +++ b/src/collections/pool/direct.rs @@ -21,6 +21,8 @@ union Slot { /// The [`LayoutSpec`] for a [`DirectPool`]. pub struct DirectPoolLayout(PhantomData<(T, H)>); impl LayoutSpec for DirectPoolLayout { + type Item = (T, H::Index, u32); + fn layout_with_capacity(items: usize) -> Result { let item_array = Layout::array::>(items)?; let gen_count_array = Layout::array::(items)?; @@ -1133,6 +1135,8 @@ pub struct InlineStorage { unsafe impl Storage> for InlineStorage { + const MIN_REPRESENTABLE: usize = N; + #[inline] fn get_ptr(&self) -> *const u8 { (self as *const Self).cast() diff --git a/src/collections/pool/packed.rs b/src/collections/pool/packed.rs index 1484515..075b9c3 100644 --- a/src/collections/pool/packed.rs +++ b/src/collections/pool/packed.rs @@ -17,6 +17,8 @@ use crate::storage::{Capacity, LayoutSpec, Storage}; /// The [`LayoutSpec`] for a [`PackedPool`]. pub struct PackedPoolLayout(PhantomData<(T, H)>); impl LayoutSpec for PackedPoolLayout { + type Item = (T, H, u32, H::Index); + fn layout_with_capacity(items: usize) -> Result { let values_array = Layout::array::(items)?; let handles_array = Layout::array::(items)?; @@ -1153,6 +1155,7 @@ pub struct InlineStorage { unsafe impl Storage> for InlineStorage { + const MIN_REPRESENTABLE: usize = N; fn get_ptr(&self) -> *const u8 { (self as *const Self).cast() } diff --git a/src/collections/vec.rs b/src/collections/vec.rs index 4025a93..82348bf 100644 --- a/src/collections/vec.rs +++ b/src/collections/vec.rs @@ -10,8 +10,8 @@ //! (c) 2019 by Daniel "Lokathor" Gee). use crate::storage::{ - buffer_too_large_for_index_type, mut_ptr_at_index, normalize_range, ptr_at_index, ArrayLayout, - Capacity, InlineStorage, Storage, + buffer_too_large_for_index_type, cast_capacity, mut_ptr_at_index, normalize_range, + ptr_at_index, ArrayLayout, Capacity, DefaultStorage, OwnedStorage, Storage, }; use crate::CapacityError; @@ -46,7 +46,7 @@ impl>, I: Capacity> From for Vec { } Vec { - len: I::from_usize(0), + len: I::ZERO, buf, elem: PhantomData, } @@ -368,7 +368,9 @@ impl>, I: Capacity> Vec { #[inline] pub fn try_push(&mut self, value: T) -> Result<(), T> { if self.is_full() { - return Err(value); + if let Err(_) = self.try_grow(None) { + return Err(value); + } } let len = self.len(); @@ -436,7 +438,7 @@ impl>, I: Capacity> Vec { /// Equivalent to `s.truncate(0)`. #[inline] pub fn clear(&mut self) { - self.truncate(I::from_usize(0)); + self.truncate(I::ZERO); } /// Swaps two elements in the vector. @@ -512,7 +514,7 @@ impl>, I: Capacity> Vec { #[cold] #[inline(never)] fn assert_failed() -> ! { - panic!("vector is already at capacity") + panic!("vector is at capacity and cannot be expanded") } let result = self.try_insert(index, element); @@ -550,7 +552,9 @@ impl>, I: Capacity> Vec { } if self.is_full() { - return Err(element); + if let Err(_) = self.try_grow(None) { + return Err(element); + } } let idx = index.as_usize(); @@ -855,6 +859,19 @@ impl>, I: Capacity> Vec { target_end: end, } } + + #[inline] + /// Try to expand the backing Storage if supported. + fn try_grow(&mut self, min_capacity: Option) -> Result<(), CapacityError> { + let mut buf = self.buf.try_grow::(min_capacity)?; + let src_ptr = self.as_ptr(); + let dst_ptr = buf.get_mut_ptr().cast::(); + unsafe { + ptr::copy_nonoverlapping(src_ptr, dst_ptr, self.len()); + } + self.buf = buf; // drops previous buffer + Ok(()) + } } impl>, I: Capacity> Vec { @@ -878,7 +895,7 @@ impl>, I: Capacity> Vec { pub fn try_extend_from_slice(&mut self, other: &[T]) -> crate::Result<()> { let new_len = self.len() + other.len(); if new_len > self.capacity() { - return CapacityError::new(); + self.try_grow(Some(new_len))?; } unsafe { @@ -936,7 +953,7 @@ impl>, I: Capacity> Vec { let count = src.len(); let new_len = self.len() + count; if new_len > self.capacity() { - return CapacityError::new(); + self.try_grow(Some(new_len))?; } let idx = idx.as_usize(); @@ -996,7 +1013,7 @@ impl>, I: Capacity> Vec { let count = end - start; let new_len = self.len() + count; if new_len > self.capacity() { - return CapacityError::new(); + self.try_grow(Some(new_len))?; } unsafe { @@ -1065,8 +1082,9 @@ impl>, I: Capacity> Vec { } } else { let extra_space_needed = src_count - dst_count; - if self.len() + extra_space_needed > self.capacity() { - return CapacityError::new(); + let min_cap = self.len() + extra_space_needed; + if min_cap > self.capacity() { + self.try_grow(Some(min_cap))?; } unsafe { @@ -1104,6 +1122,155 @@ impl>, I: Capacity> Vec { } } +impl>, I: Capacity> Vec { + const UNINIT: Self = Vec { + len: I::ZERO, + buf: S::UNINIT, + elem: PhantomData, + }; + + /// Constructs a new, empty `Vec`. + /// + /// # Panics + /// Panics if `C` cannot be represented as a value of type `I`. + /// + /// # Examples + /// ``` + /// let vec = coca::collections::InlineVec::::new(); + /// assert_eq!(vec.capacity(), 6); + /// assert_eq!(vec.len(), 0); + /// ``` + #[inline] + pub fn new() -> Self { + if S::MIN_REPRESENTABLE > I::MAX_REPRESENTABLE { + buffer_too_large_for_index_type::(); + } + Self::UNINIT + } +} + +impl>, I: Capacity> Vec { + /// Constructs a new, empty Vec with the specified capacity, returning + /// an error if the capacity exceeds the maximum supported by the backing + /// Storage or the Capacity type cannot fully index the backing Storage. + #[inline] + pub fn try_with_capacity(capacity: I) -> Result { + if S::MIN_REPRESENTABLE > I::MAX_REPRESENTABLE { + CapacityError::new() + } else { + Ok(Vec { + len: I::ZERO, + buf: S::try_with_capacity(capacity.as_usize())?, + elem: PhantomData, + }) + } + } + + /// Constructs a new, empty Vec with the specified capacity. + /// + /// # Panics + /// Panics if the Capacity type cannot fully index the backing Storage, + /// or if the capacity exceeds the maximum supported by the backing Storage. + #[inline] + pub fn with_capacity(capacity: I) -> Self { + if S::MIN_REPRESENTABLE > I::MAX_REPRESENTABLE { + buffer_too_large_for_index_type::(); + } + Vec { + len: I::ZERO, + buf: S::try_with_capacity(capacity.as_usize()) + .expect("exceeded maximum storage capacity"), + elem: PhantomData, + } + } +} + +impl>, I: Capacity> Default for Vec { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl>, I: Capacity> core::clone::Clone for Vec { + fn clone(&self) -> Self { + let mut ret = Self::with_capacity(self.len); + ret.clone_from(self); + ret + } + + fn clone_from(&mut self, source: &Self) { + self.clear(); + self.extend(source.iter()) + } +} + +impl>, I: Capacity> From<&[T]> for Vec { + fn from(source: &[T]) -> Self { + let cap = cast_capacity(source.len()); + let mut ret = Self::with_capacity(cap); + ret.extend(source.iter().cloned()); + ret + } +} + +impl>, I: Capacity> From<&mut [T]> for Vec { + fn from(source: &mut [T]) -> Self { + let cap = cast_capacity(source.len()); + let mut ret = Self::with_capacity(cap); + ret.extend(source.iter().cloned()); + ret + } +} + +impl>, I: Capacity, const N: usize> From<&[T; N]> + for Vec +{ + fn from(source: &[T; N]) -> Self { + let cap = cast_capacity(source.len()); + let mut ret = Self::with_capacity(cap); + ret.extend(source.iter().cloned()); + ret + } +} + +impl>, I: Capacity, const N: usize> From<&mut [T; N]> + for Vec +{ + fn from(source: &mut [T; N]) -> Self { + let cap = cast_capacity(source.len()); + let mut ret = Self::with_capacity(cap); + ret.extend(source.iter().cloned()); + ret + } +} + +impl>, I: Capacity, const N: usize> From<[T; N]> + for Vec +{ + fn from(source: [T; N]) -> Self { + let cap = cast_capacity(N); + let mut buf = Self::with_capacity(cap); + buf.extend(core::iter::IntoIterator::into_iter(source)); + buf + } +} + +impl>, I: Capacity> core::iter::FromIterator for Vec { + /// Creates a vector from an iterator. + /// + /// # Panics + /// Panics if the iterator yields more elements than the maximum storage capacity. + fn from_iter>(iter: It) -> Self { + let iter = iter.into_iter(); + let (min_len, max_len) = iter.size_hint(); + let cap = cast_capacity(max_len.unwrap_or(min_len)); + let mut ret = Self::with_capacity(cap); + ret.extend(iter); + ret + } +} + impl>, I: Capacity> core::ops::Deref for Vec { type Target = [T]; fn deref(&self) -> &[T] { @@ -1378,7 +1545,7 @@ impl>, I: Capacity> IntoIter for Vec { core::mem::forget(self); IntoIterator { - start: I::from_usize(0), + start: I::ZERO, end, buf, elems: PhantomData, @@ -1643,126 +1810,6 @@ impl crate::collections::SliceVec<'_, T, I> { } } -#[cfg(feature = "alloc")] -#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] -impl crate::collections::AllocVec { - /// Constructs a new, empty `AllocVec` with the specified capacity. - /// - /// # Panics - /// Panics if the specified capacity cannot be represented by a `usize`. - pub fn with_capacity(capacity: I) -> Self { - let cap = capacity.as_usize(); - if capacity != I::from_usize(cap) { - buffer_too_large_for_index_type::(); - } - - Vec { - len: I::from_usize(0), - buf: crate::storage::AllocStorage::with_capacity(cap), - elem: PhantomData, - } - } -} - -#[cfg(feature = "alloc")] -#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] -impl Clone for crate::collections::AllocVec { - fn clone(&self) -> Self { - let mut result = Self::with_capacity(I::from_usize(self.capacity())); - result.extend(self.iter().cloned()); - result - } -} - -impl Vec, I> { - /// Constructs a new, empty `Vec` backed by an inline array. - /// - /// # Panics - /// Panics if `C` cannot be represented as a value of type `I`. - /// - /// # Examples - /// ``` - /// let vec = coca::collections::InlineVec::::new(); - /// assert_eq!(vec.capacity(), 6); - /// assert_eq!(vec.len(), 0); - /// ``` - #[inline] - pub fn new() -> Self { - if C > I::MAX_REPRESENTABLE { - buffer_too_large_for_index_type::(); - } - - Vec { - len: I::from_usize(0), - buf: unsafe { MaybeUninit::uninit().assume_init() }, - elem: PhantomData, - } - } -} - -impl Default for Vec, I> { - fn default() -> Self { - Self::new() - } -} - -impl core::clone::Clone for Vec, I> { - fn clone(&self) -> Self { - let mut ret = Self::new(); - ret.clone_from(self); - ret - } - - fn clone_from(&mut self, source: &Self) { - self.clear(); - for next in source { - self.push(next.clone()); - } - } -} - -impl From<&[T]> for Vec, I> { - fn from(source: &[T]) -> Self { - if C > I::MAX_REPRESENTABLE { - buffer_too_large_for_index_type::(); - } - - assert!( - source.len() <= C, - "source should not have more than {} elements (has {})", - C, - source.len() - ); - - let mut ret = Self::new(); - for next in source { - ret.push(next.clone()); - } - ret - } -} - -impl From<&mut [T]> for Vec, I> { - fn from(source: &mut [T]) -> Self { - if C > I::MAX_REPRESENTABLE { - buffer_too_large_for_index_type::(); - } - - assert!( - source.len() <= C, - "source should not have more than {} elements (has {})", - C, - source.len() - ); - - let mut ret = Self::new(); - for next in source { - ret.push(next.clone()); - } - ret - } -} - impl PartialEq> for [V; N] where V: PartialEq, @@ -1787,20 +1834,6 @@ where } } -impl core::iter::FromIterator - for Vec, I> -{ - /// Creates a vector backed by an inline array from an iterator. - /// - /// # Panics - /// Panics if the iterator yields more than `N` elements. - fn from_iter>(iter: It) -> Self { - let mut result = Self::new(); - result.extend(iter); - result - } -} - #[cfg(test)] mod tests { use super::*; @@ -1910,4 +1943,58 @@ mod tests { } } } + + #[cfg(feature = "alloc")] + #[test] + fn alloc_vec() { + use crate::collections::AllocVec; + use core::iter::FromIterator; + + let mut v = AllocVec::::with_capacity(32); + v.extend([1, 2, 3, 4, 5, 6, 7, 8]); + assert_eq!(v.capacity(), 32); + + let v = AllocVec::::from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(v.capacity(), 10); + + let mut v = AllocVec::::with_capacity(1); + v.push(1); + assert_eq!(v.try_push(2), Err(2)); + + let v = AllocVec::::from_iter([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(v.capacity(), 10); + } + + #[cfg(feature = "alloc")] + #[test] + fn realloc_vec() { + use crate::collections::ReallocVec; + use core::iter::FromIterator; + + let _v1 = ReallocVec::::new(); + let _v2 = ReallocVec::::with_capacity(32); + + let mut v = ReallocVec::::default(); + v.extend([1, 2, 3, 4, 5, 6, 7, 8]); + assert_eq!(v.capacity(), 8); + v.push(9); + assert_eq!(v.capacity(), 16); + + let v = ReallocVec::::from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(v.capacity(), 10); + + let v = ReallocVec::::from_iter([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(v.capacity(), 10); + + // test initial capacities based on size_of + let mut v = ReallocVec::::new(); + v.push(1u8); + assert_eq!(v.capacity(), 8); + let mut v = ReallocVec::::new(); + v.push(1u32); + assert_eq!(v.capacity(), 4); + let mut v = ReallocVec::<[u8; 1025]>::new(); + v.push([1u8; 1025]); + assert_eq!(v.capacity(), 1); + } } diff --git a/src/storage.rs b/src/storage.rs index 8ba4088..f68a9bd 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -9,6 +9,8 @@ use core::mem::MaybeUninit; use core::ops::{Range, RangeBounds}; use core::ptr::NonNull; +use crate::CapacityError; + /// Types that can be used for indexing into array-like data structures. /// /// # Safety @@ -20,6 +22,8 @@ use core::ptr::NonNull; pub unsafe trait Capacity: Copy + Debug + Eq + Hash + Ord { /// The maximum `usize` value that can safely be represented by this type. const MAX_REPRESENTABLE: usize; + /// The zero value for this type. + const ZERO: Self; /// Convert a `usize` into `Self`. fn from_usize(i: usize) -> Self; /// Convert `self` into `usize`. @@ -36,6 +40,24 @@ pub(crate) fn buffer_too_large_for_index_type() { ); } +#[cold] +#[inline(never)] +#[track_caller] +pub(crate) fn usize_exceeds_max_capacity() { + panic!( + "maximum capacity of index type {} exceeds the requested capacity", + core::any::type_name::() + ); +} + +#[inline] +pub(crate) fn cast_capacity(capacity: usize) -> I { + if capacity > I::MAX_REPRESENTABLE { + usize_exceeds_max_capacity::(); + } + I::from_usize(capacity) +} + pub(crate) fn normalize_range>( range: R, max_end: usize, @@ -71,6 +93,7 @@ pub(crate) fn normalize_range>( #[allow(clippy::cast_possible_truncation)] unsafe impl Capacity for u8 { const MAX_REPRESENTABLE: usize = 0xFF; + const ZERO: Self = 0u8; #[inline] fn from_usize(i: usize) -> Self { debug_assert!(>::try_into(i).is_ok()); @@ -86,6 +109,7 @@ unsafe impl Capacity for u8 { #[allow(clippy::cast_possible_truncation)] unsafe impl Capacity for u16 { const MAX_REPRESENTABLE: usize = 0xFFFF; + const ZERO: Self = 0u16; #[inline] fn from_usize(i: usize) -> Self { debug_assert!(>::try_into(i).is_ok()); @@ -101,6 +125,7 @@ unsafe impl Capacity for u16 { #[allow(clippy::cast_possible_truncation)] unsafe impl Capacity for u32 { const MAX_REPRESENTABLE: usize = 0xFFFF_FFFF; + const ZERO: Self = 0u32; #[inline] fn from_usize(i: usize) -> Self { debug_assert!(>::try_into(i).is_ok()); @@ -117,6 +142,7 @@ unsafe impl Capacity for u32 { #[allow(clippy::cast_possible_truncation)] unsafe impl Capacity for u64 { const MAX_REPRESENTABLE: usize = usize::max_value(); + const ZERO: Self = 0u64; #[inline] fn from_usize(i: usize) -> Self { debug_assert!(>::try_into(i).is_ok()); @@ -132,6 +158,7 @@ unsafe impl Capacity for u64 { unsafe impl Capacity for usize { const MAX_REPRESENTABLE: usize = usize::max_value(); + const ZERO: Self = 0usize; #[inline] fn from_usize(i: usize) -> Self { i @@ -189,6 +216,7 @@ macro_rules! index_type { unsafe impl $crate::storage::Capacity for $name { const MAX_REPRESENTABLE: usize = <$repr as $crate::storage::Capacity>::MAX_REPRESENTABLE; + const ZERO: Self = Self(<$repr as $crate::storage::Capacity>::ZERO); #[inline] #[track_caller] fn from_usize(i: usize) -> Self { @@ -213,6 +241,9 @@ macro_rules! index_type { /// Types that specify a data structure's storage layout requirements. pub trait LayoutSpec { + /// A type representing the approximate size of a single item. + type Item: Sized; + /// Constructs a [`Layout`] for a memory block capable of holding the /// specified number of items. fn layout_with_capacity(items: usize) -> Result; @@ -221,6 +252,8 @@ pub trait LayoutSpec { /// Inconstructible type to mark data structures that require an array-like storage layout. pub struct ArrayLayout(PhantomData); impl LayoutSpec for ArrayLayout { + type Item = T; + fn layout_with_capacity(items: usize) -> Result { Layout::array::(items) } @@ -229,6 +262,8 @@ impl LayoutSpec for ArrayLayout { /// An interface to a contiguous memory block for use by data structures. #[allow(clippy::missing_safety_doc)] // individual methods _do_ have safety docs! pub unsafe trait Storage: Sized { + /// The minimum capacity which must be indexable for this storage type. + const MIN_REPRESENTABLE: usize = 0; /// Extracts a pointer to the beginning of the memory block. /// /// # Safety @@ -251,6 +286,26 @@ pub unsafe trait Storage: Sized { /// Implementors must ensure the same value is returned every time this /// method is called throughout the block's lifetime. fn capacity(&self) -> usize; + /// Try to grow a storage instance, reallocating when supported. + /// + /// # Safety + /// When supported, this method must return a new, non-overlapping + /// memory block without invalidating the current block. + fn try_grow(&self, _min_capacity: Option) -> Result { + CapacityError::new() + } +} + +/// An interface for storage types which are owned, not bound to an external buffer. +pub trait OwnedStorage: Storage { + /// Create a new storage buffer with a minimum capacity. + fn try_with_capacity(min_capacity: usize) -> Result; +} + +/// An interface for storage types which have a default initializer. +pub trait DefaultStorage: OwnedStorage { + /// An empty instance of the Storage. + const UNINIT: Self; } #[inline(always)] @@ -322,6 +377,8 @@ unsafe impl Storage for ArenaStorage<'_, R> { } unsafe impl> Storage for crate::arena::Box<'_, S> { + const MIN_REPRESENTABLE: usize = S::MIN_REPRESENTABLE; + #[inline] fn get_ptr(&self) -> *const u8 { (**self).get_ptr() @@ -337,6 +394,8 @@ unsafe impl> Storage for crate::arena::Box<'_, S } unsafe impl> Storage for &mut S { + const MIN_REPRESENTABLE: usize = S::MIN_REPRESENTABLE; + fn get_ptr(&self) -> *const u8 { (**self).get_ptr() } @@ -348,18 +407,80 @@ unsafe impl> Storage for &mut S { } } +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +/// Policy for heap storage (re)allocation. +pub unsafe trait AllocPolicy { + #[inline] + // FIXME add exact: bool parameter? + /// Try to grow an existing allocation. + fn try_grow( + _from_capacity: usize, + _min_capacity: Option, + ) -> Result<(NonNull, usize), CapacityError> { + CapacityError::new() + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +/// An allocation policy for constant capacity storage. +pub struct NoResize; + +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +unsafe impl AllocPolicy for NoResize {} + +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +/// An allocation policy using exponential growth. +pub struct ExpGrow; + +#[cfg(feature = "alloc")] +const fn min_non_zero_cap() -> usize { + if core::mem::size_of::() == 1 { + 8 + } else if core::mem::size_of::() <= 1024 { + 4 + } else { + 1 + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +unsafe impl AllocPolicy for ExpGrow { + #[inline] + fn try_grow( + from_capacity: usize, + min_capacity: Option, + ) -> Result<(NonNull, usize), CapacityError> { + let min_cap = min_capacity.unwrap_or(0).max(min_non_zero_cap::()); + let cap = min_cap.max((from_capacity.saturating_mul(2)).min(I::MAX_REPRESENTABLE)); + if cap <= from_capacity || cap < min_cap { + return CapacityError::new(); + } + + let layout = R::layout_with_capacity(cap).expect("realloc to invalid AllocStorage"); + let ptr = unsafe { alloc::alloc::alloc(layout) }; + NonNull::new(ptr).map(|ptr| (ptr, cap)).ok_or(CapacityError) + } +} + /// A fat pointer to a heap-allocated storage block conforming to a [`LayoutSpec`]. #[cfg(feature = "alloc")] #[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] -pub struct AllocStorage { +pub struct AllocStorage { ptr: NonNull, cap: usize, - spec: PhantomData, + spec: PhantomData<(R, A)>, } #[cfg(feature = "alloc")] #[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] -impl AllocStorage { +impl AllocStorage { /// Allocates a new storage block with the specified capacity with the /// global allocator. /// @@ -381,16 +502,19 @@ impl AllocStorage { #[cfg(feature = "alloc")] #[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] -impl Drop for AllocStorage { +impl Drop for AllocStorage { fn drop(&mut self) { - let layout = R::layout_with_capacity(self.cap).expect("dropped an invalid AllocStorage"); - unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), layout) }; + if self.cap != 0 { + let layout = + R::layout_with_capacity(self.cap).expect("dropped an invalid AllocStorage"); + unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), layout) }; + } } } #[cfg(feature = "alloc")] #[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] -unsafe impl Storage for AllocStorage { +unsafe impl Storage for AllocStorage { fn get_ptr(&self) -> *const u8 { self.ptr.as_ptr() as _ } @@ -400,8 +524,40 @@ unsafe impl Storage for AllocStorage { fn capacity(&self) -> usize { self.cap } + fn try_grow(&self, min_capacity: Option) -> Result { + let (ptr, cap) = A::try_grow::(self.cap, min_capacity)?; + Ok(AllocStorage { + ptr, + cap, + spec: PhantomData, + }) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +impl OwnedStorage for AllocStorage { + #[inline] + fn try_with_capacity(min_capacity: usize) -> Result { + Ok(Self::with_capacity(min_capacity)) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +impl DefaultStorage for AllocStorage { + const UNINIT: Self = AllocStorage { + ptr: NonNull::dangling(), + cap: 0, + spec: PhantomData, + }; } +#[cfg(feature = "alloc")] +#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] +/// Alias for a reallocating Storage type with the default behavior. +pub type ReallocStorage = AllocStorage; + #[cfg(feature = "alloc")] #[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))] unsafe impl> Storage for alloc::boxed::Box { @@ -414,12 +570,18 @@ unsafe impl> Storage for alloc::boxed::Box { fn capacity(&self) -> usize { (**self).capacity() } + fn try_grow(&self, min_capacity: Option) -> Result { + Ok(alloc::boxed::Box::new( + (**self).try_grow::(min_capacity)?, + )) + } } /// Shorthand for `[MaybeUninit; C]` for use with generic data structures. pub type InlineStorage = [MaybeUninit; C]; unsafe impl Storage> for InlineStorage { + const MIN_REPRESENTABLE: usize = C; fn get_ptr(&self) -> *const u8 { self.as_ptr().cast() } @@ -430,3 +592,18 @@ unsafe impl Storage> for InlineStorage { C } } + +impl OwnedStorage> for InlineStorage { + #[inline] + fn try_with_capacity(min_capacity: usize) -> Result { + if min_capacity > C { + CapacityError::new() + } else { + unsafe { MaybeUninit::uninit().assume_init() } + } + } +} + +impl DefaultStorage> for InlineStorage { + const UNINIT: Self = unsafe { MaybeUninit::uninit().assume_init() }; +}