diff --git a/fuzz/fuzz_targets/main.rs b/fuzz/fuzz_targets/main.rs index 71fb83c9..9d60676f 100644 --- a/fuzz/fuzz_targets/main.rs +++ b/fuzz/fuzz_targets/main.rs @@ -162,7 +162,7 @@ fn test_with_inline_cap( std_vec = Vec::with_capacity(cap.0); } Op::FromVec => { - small_vec = SmallVec::from_vec(small_vec.into()); + small_vec = SmallVec::from(Into::>::into(small_vec)); // No-op on `Vec` } Op::FromSlice(data) => { diff --git a/src/allocator/boxed.rs b/src/allocator/boxed.rs index 9d27af95..7fa4cf9a 100644 --- a/src/allocator/boxed.rs +++ b/src/allocator/boxed.rs @@ -1,4 +1,19 @@ -#[cfg(not(feature = "allocator-api2"))] -pub use alloc::boxed::Box; -#[cfg(feature = "allocator-api2")] -pub use allocator_api2::boxed::Box; +use { + super::{ + Allocator, + like::Like, + requirement::Requirement + }, + core::marker::PhantomData +}; + +pub struct Box(PhantomData<(Heap, Item)>); + +impl Like for Box { + #[cfg(not(feature = "allocator-api"))] + type Type = alloc::boxed::Box; + #[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] + type Type = alloc::boxed::Box; + #[cfg(feature = "allocator-api2")] + type Type = allocator_api2::boxed::Box; +} diff --git a/src/allocator/like.rs b/src/allocator/like.rs new file mode 100644 index 00000000..593bdbef --- /dev/null +++ b/src/allocator/like.rs @@ -0,0 +1,3 @@ +pub trait Like { + type Type; +} diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs index 46927df0..074f7190 100644 --- a/src/allocator/mod.rs +++ b/src/allocator/mod.rs @@ -1,7 +1,9 @@ pub mod boxed; pub mod global; mod implementations; +pub mod like; pub mod proxy; +mod requirement; pub mod vec; use core::{ diff --git a/src/allocator/requirement.rs b/src/allocator/requirement.rs new file mode 100644 index 00000000..23529e2c --- /dev/null +++ b/src/allocator/requirement.rs @@ -0,0 +1,6 @@ +#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] +pub use alloc::alloc::Allocator as Requirement; +#[cfg(feature = "allocator-api2")] +pub use allocator_api2::alloc::Allocator as Requirement; +#[cfg(not(feature = "allocator-api"))] +pub use core::marker::Sized as Requirement; diff --git a/src/allocator/vec.rs b/src/allocator/vec.rs index a1181229..a7801449 100644 --- a/src/allocator/vec.rs +++ b/src/allocator/vec.rs @@ -1,13 +1,23 @@ #[cfg(not(feature = "allocator-api2"))] -#[rustfmt::skip] -pub use alloc::{ - vec, - vec::Vec -}; - +pub use alloc::vec; #[cfg(feature = "allocator-api2")] -#[rustfmt::skip] -pub use allocator_api2::{ - vec, - vec::Vec +pub use allocator_api2::vec; +use { + super::{ + Allocator, + like::Like, + requirement::Requirement + }, + core::marker::PhantomData }; + +pub struct Vec(PhantomData<(Heap, Item)>); + +impl Like for Vec { + #[cfg(not(feature = "allocator-api"))] + type Type = alloc::vec::Vec; + #[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] + type Type = alloc::vec::Vec; + #[cfg(feature = "allocator-api2")] + type Type = allocator_api2::vec::Vec; +} diff --git a/src/conversions.rs b/src/conversions.rs index 0c4579f4..672b95da 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -3,12 +3,18 @@ use { Allocator, Box, Global, + LocatedLength, + RawSmallVec, SmallVec, - Vec + Vec, + allocator::like::Like }, core::{ mem::ManuallyDrop, - ptr::copy_nonoverlapping + ptr::{ + NonNull, + copy_nonoverlapping + } } }; @@ -17,7 +23,7 @@ impl From<&[Item]> for SmallVec Self { if slice.len() > Self::inline_size() { // Standard Rust vectors are already specialized. - Vec::from(slice).into() + as Like>::Type::from(slice).into() } else { // SAFETY: The precondition is checked in the initial comparison // above. @@ -66,7 +72,7 @@ impl From<[Item; M]> for SmallVec INLINE { // If M > INLINE, we'd have to heap allocate anyway, // so delegate for Vec for the allocation. - Self::from(Vec::from(array)) + Self::from( as Like>::Type::from(array)) } else { // M <= INLINE let mut this = Self::new(); @@ -105,17 +111,62 @@ impl } } -impl From> for SmallVec { - fn from(array: Vec) -> Self { - Self::from_vec(array) +impl From< as Like>::Type> + for SmallVec +{ + fn from(array: as Like>::Type) -> Self { + if array.capacity() == 0 { + return Self::new(); + } + + if Self::IS_ZST { + // "Move" elements to stack buffer. They're ZST so we don't actually + // have to do anything. Just make sure they're not + // dropped. We don't wrap the vector in ManuallyDrop so + // that when it's dropped, the memory is deallocated, if + // it needs to be. + let mut vec = array; + let length = vec.len(); + + // SAFETY: `0` is less than the vector's capacity. + // old_len..new_len is an empty range. So there are no uninitialized + // elements + unsafe { vec.set_len(0) }; + Self { + length: LocatedLength::new(length, false), + raw: RawSmallVec::new(), + allocator: Global + } + } else { + let mut vec = ManuallyDrop::new(array); + let length = vec.len(); + + // A heap-allocated `SmallVec` must always observe the invariant + // that `cap > INLINE`. + if vec.capacity() <= INLINE { + vec.reserve(INLINE + 1 - length); + } + let cap = vec.capacity(); + // SAFETY: vec.capacity is not `0` (checked above), so the pointer + // can not dangle and thus specifically cannot be null. + let ptr = unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) }; + + Self { + length: LocatedLength::new(length, true), + raw: RawSmallVec::new_heap(ptr, cap), + allocator: Global + } + } } } -impl From> for Vec { +impl From> + for as Like>::Type +{ fn from(this: SmallVec) -> Self { let (length, on_heap) = this.length.parts(); if !on_heap { - let mut vec = Vec::with_capacity(length); + let mut vec = as Like>::Type::with_capacity(length); let this = ManuallyDrop::new(this); // SAFETY: we create a new vector with sufficient capacity, copy our // elements into it to transfer ownership and then set @@ -137,22 +188,22 @@ impl From as Like>::Type::from_raw_parts(ptr.as_ptr(), length, cap) } } } } impl From> - for Box<[Item]> + for as Like>::Type { fn from(this: SmallVec) -> Self { - Vec::from(this).into_boxed_slice() + as Like>::Type::from(this).into_boxed_slice() } } -impl From> for SmallVec { - fn from(boxed: Box<[T]>) -> Self { +impl From< as Like>::Type> for SmallVec { + fn from(boxed: as Like>::Type) -> Self { boxed.into_vec().into() } } diff --git a/src/lib.rs b/src/lib.rs index dc890e01..0dae9e38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ use { Allocator, boxed::Box, global::Global, + like::Like, vec::{ Vec, vec @@ -231,49 +232,8 @@ impl SmallVec { #[inline] #[deprecated(since = "2.0.0-beta.3", note = "use `From::>::from` instead")] - pub fn from_vec(vec: Vec) -> Self { - if vec.capacity() == 0 { - return Self::new(); - } - - if Self::IS_ZST { - // "Move" elements to stack buffer. They're ZST so we don't actually - // have to do anything. Just make sure they're not - // dropped. We don't wrap the vector in ManuallyDrop so - // that when it's dropped, the memory is deallocated, if - // it needs to be. - let mut vec = vec; - let length = vec.len(); - - // SAFETY: `0` is less than the vector's capacity. - // old_len..new_len is an empty range. So there are no uninitialized - // elements - unsafe { vec.set_len(0) }; - Self { - length: LocatedLength::new(length, false), - raw: RawSmallVec::new(), - allocator: Global - } - } else { - let mut vec = ManuallyDrop::new(vec); - let length = vec.len(); - - // A heap-allocated `SmallVec` must always observe the invariant - // that `cap > INLINE`. - if vec.capacity() <= INLINE { - vec.reserve(INLINE + 1 - length); - } - let cap = vec.capacity(); - // SAFETY: vec.capacity is not `0` (checked above), so the pointer - // can not dangle and thus specifically cannot be null. - let ptr = unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) }; - - Self { - length: LocatedLength::new(length, true), - raw: RawSmallVec::new_heap(ptr, cap), - allocator: Global - } - } + pub fn from_vec(vec: as Like>::Type) -> Self { + vec.into() } /// Creates a `SmallVec` directly from the raw components of another @@ -772,7 +732,8 @@ impl SmallVec { let (ptr, capacity) = self.raw.heap; copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length); self.length.set_location::(); - self.allocator.deallocate( + Allocator::deallocate( + &self.allocator, ptr.cast(), Layout::from_size_align_unchecked( capacity * size_of::(), @@ -809,7 +770,8 @@ impl SmallVec { let (ptr, capacity) = self.raw.heap; copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length); self.length.set_location::(); - self.allocator.deallocate( + Allocator::deallocate( + &self.allocator, ptr.cast(), Layout::from_size_align_unchecked( capacity * size_of::(), @@ -998,13 +960,13 @@ impl SmallVec { #[inline] #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] - pub fn into_vec(self) -> Vec { + pub fn into_vec(self) -> as Like>::Type { self.into() } #[inline] #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] - pub fn into_boxed_slice(self) -> Box<[Item]> { + pub fn into_boxed_slice(self) -> as Like>::Type { self.into() } @@ -1485,7 +1447,8 @@ impl Drop for DropDealloc<'_, Heap> { fn drop(&mut self) { unsafe { if self.size_bytes > 0 { - self.allocator.deallocate( + Allocator::deallocate( + self.allocator, self.ptr, Layout::from_size_align_unchecked(self.size_bytes, self.align) ); diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index b10b4845..90c593cb 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -126,8 +126,7 @@ impl RawSmallVec { let new_ptr = if !was_on_heap { // get a fresh allocation // `new_layout` has nonzero size. - let new_ptr = allocator - .allocate(new_layout) + let new_ptr = Allocator::allocate(allocator, new_layout) .ok_or(SmallVecError::AllocationError(new_layout))? .cast(); unsafe { copy_nonoverlapping(ptr, new_ptr.as_ptr(), length) }; @@ -152,9 +151,9 @@ impl RawSmallVec { // with Layout::array unsafe { (if self.heap.1 < new_capacity { - Heap::grow + ::grow } else { - Heap::shrink + ::shrink })( allocator, NonNull::new(ptr as *mut u8).unwrap(), diff --git a/tests/main.rs b/tests/main.rs index 22ecfb9f..7e6462d7 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -766,7 +766,7 @@ fn shrink_to_fit_unspill() { #[test] fn shrink_after_from_empty_vec() { - let mut v = SmallVec::::from_vec(Vec::new()); + let mut v = SmallVec::::from(Vec::new()); v.shrink_to_fit(); assert!(!v.spilled()) } @@ -835,32 +835,32 @@ fn try_into_array() { #[test] fn from_vec() { let vec = Vec::new(); - let small_vec: SmallVec = SmallVec::from_vec(vec); + let small_vec: SmallVec = SmallVec::from(vec); assert_eq!(&*small_vec, &[0u8; 0]); drop(small_vec); let vec = Vec::new(); - let small_vec: SmallVec = SmallVec::from_vec(vec); + let small_vec: SmallVec = SmallVec::from(vec); assert_eq!(&*small_vec, &[0u8; 0]); drop(small_vec); let vec = Vec::from([1]); - let small_vec: SmallVec = SmallVec::from_vec(vec); + let small_vec: SmallVec = SmallVec::from(vec); assert_eq!(&*small_vec, &[1]); drop(small_vec); let vec = Vec::from([1, 2, 3]); - let small_vec: SmallVec = SmallVec::from_vec(vec); + let small_vec: SmallVec = SmallVec::from(vec); assert_eq!(&*small_vec, &[1, 2, 3]); drop(small_vec); let vec = Vec::from([1, 2, 3, 4, 5]); - let small_vec: SmallVec = SmallVec::from_vec(vec); + let small_vec: SmallVec = SmallVec::from(vec); assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); drop(small_vec); let vec = Vec::from([1, 2, 3, 4, 5]); - let small_vec: SmallVec = SmallVec::from_vec(vec); + let small_vec: SmallVec = SmallVec::from(vec); assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); drop(small_vec); }