diff --git a/README.md b/README.md index 7ae66bf2..78f2d7c8 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,10 @@ v.sort(); ## Feature List - `allocator-api` (nightly): uses the `allocator-api` backend - for the allocator API + for the allocator API - `allocator-api2`: uses the `allocator-api2` backend for the allocator API - `arbitrary`: implements `Arbitrary` for any `SmallVec` storing elements - that implement `Arbitrary` + that implement `Arbitrary` - `borsh`: implements `BorshSerialize`, `BorshDeserialize` and `BorshSchema` - `bytemuck`: implements `bytemuck::Zeroable` for `SmallVec` with the `Global` allocator - `bytes`: implements `BufMut` for SmallVec @@ -74,7 +74,7 @@ deserialization support implement `JsonSchema` - `serde`: implements serde's serialization and deserialization - `specialization` (nightly): enables specialization, - improving performance on some cases + improving performance on some cases - `std`: implements the `std::io::Write` type for `SmallVec` - `zeroize`: implements `zeroize::Zeroize`, erasing elements and storage @@ -83,4 +83,4 @@ deserialization support > - SmallVec without any features enabled does not make use of the standard library. > - The `rayon`, `proptest` and `schemars` features implicitly require `std`. > - The `allocator-api2` feature requires `allocator-api`, though it does not - require `nightly` +> require `nightly` diff --git a/src/allocator/boxed.rs b/src/allocator/boxed.rs index 7fa4cf9a..4d3ca1f7 100644 --- a/src/allocator/boxed.rs +++ b/src/allocator/boxed.rs @@ -2,18 +2,46 @@ use { super::{ Allocator, like::Like, - requirement::Requirement + vec::Vec }, - core::marker::PhantomData + core::{ + marker::PhantomData, + mem::MaybeUninit, + ptr::NonNull + } }; -pub struct Box(PhantomData<(Heap, Item)>); +pub struct Box(PhantomData<(Heap, Item)>); -impl Like for Box { +impl Like for Box<[Item], Heap> { + type Heap = Heap; + type Item = Item; #[cfg(not(feature = "allocator-api"))] - type Type = alloc::boxed::Box; + type Type = alloc::boxed::Box<[Item]>; #[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] - type Type = alloc::boxed::Box; + type Type = alloc::boxed::Box<[Item], Heap>; #[cfg(feature = "allocator-api2")] - type Type = allocator_api2::boxed::Box; + type Type = allocator_api2::boxed::Box<[Item], Heap>; + + #[inline] + fn destruct( + value: Self::Type + ) -> ( + Option]>>, + usize, + Self::Heap + ) { + as Like>::destruct(value.into_vec()) + } + + #[inline] + unsafe fn construct( + pointer: Option]>>, + length: usize, + allocator: Self::Heap + ) -> Self::Type { + unsafe { + as Like>::construct(pointer, length, allocator).into_boxed_slice() + } + } } diff --git a/src/allocator/implementations/base.rs b/src/allocator/implementations.rs similarity index 51% rename from src/allocator/implementations/base.rs rename to src/allocator/implementations.rs index 996586fd..b93bda7e 100644 --- a/src/allocator/implementations/base.rs +++ b/src/allocator/implementations.rs @@ -1,12 +1,16 @@ +#[cfg(not(feature = "allocator-api"))] use { - super::super::{ - Allocator, - global::Global - }, + super::global::Global, alloc::alloc::{ alloc, dealloc, realloc + } +}; +use { + super::{ + Allocator, + requirement::Requirement }, core::{ alloc::Layout, @@ -14,7 +18,11 @@ use { } }; -impl Allocator for Global { +#[cfg(not(feature = "allocator-api"))] +impl Requirement for Global {} + +#[cfg(not(feature = "allocator-api"))] +impl Allocator for Type { #[inline] fn allocate(&self, layout: Layout) -> Option> { Some(NonNull::slice_from_raw_parts( @@ -49,3 +57,31 @@ impl Allocator for Global { )) } } + +#[cfg(feature = "allocator-api")] +impl Allocator for Type { + #[inline] + fn allocate(&self, layout: Layout) -> Option> { + Requirement::allocate(&self, layout).ok() + } + + #[inline] + unsafe fn deallocate(&self, pointer: NonNull, layout: Layout) { + unsafe { Requirement::deallocate(&self, pointer, layout) } + } + + #[inline] + unsafe fn grow(&self, pointer: NonNull, old: Layout, new: Layout) -> Option> { + unsafe { Requirement::grow(&self, pointer, old, new).ok() } + } + + #[inline] + unsafe fn shrink( + &self, + pointer: NonNull, + old: Layout, + new: Layout + ) -> Option> { + unsafe { Requirement::shrink(&self, pointer, old, new).ok() } + } +} diff --git a/src/allocator/implementations/external.rs b/src/allocator/implementations/external.rs deleted file mode 100644 index 72083f23..00000000 --- a/src/allocator/implementations/external.rs +++ /dev/null @@ -1,35 +0,0 @@ -use { - super::super::Allocator, - allocator_api2::alloc::Allocator as External, - core::{ - alloc::Layout, - ptr::NonNull - } -}; - -impl Allocator for Type { - #[inline] - fn allocate(&self, layout: Layout) -> Option> { - External::allocate(&self, layout).ok() - } - - #[inline] - unsafe fn deallocate(&self, pointer: NonNull, layout: Layout) { - unsafe { External::deallocate(&self, pointer, layout) } - } - - #[inline] - unsafe fn grow(&self, pointer: NonNull, old: Layout, new: Layout) -> Option> { - unsafe { External::grow(&self, pointer, old, new).ok() } - } - - #[inline] - unsafe fn shrink( - &self, - pointer: NonNull, - old: Layout, - new: Layout - ) -> Option> { - unsafe { External::shrink(&self, pointer, old, new).ok() } - } -} diff --git a/src/allocator/implementations/mod.rs b/src/allocator/implementations/mod.rs deleted file mode 100644 index 59ab2e1e..00000000 --- a/src/allocator/implementations/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[cfg(not(feature = "allocator-api"))] -mod base; -#[cfg(feature = "allocator-api2")] -mod external; -#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] -mod native; diff --git a/src/allocator/implementations/native.rs b/src/allocator/implementations/native.rs deleted file mode 100644 index 70ef0866..00000000 --- a/src/allocator/implementations/native.rs +++ /dev/null @@ -1,35 +0,0 @@ -use { - super::super::Allocator, - alloc::alloc::Allocator as Native, - core::{ - alloc::Layout, - ptr::NonNull - } -}; - -impl Allocator for Type { - #[inline] - fn allocate(&self, layout: Layout) -> Option> { - Native::allocate(&self, layout).ok() - } - - #[inline] - unsafe fn deallocate(&self, pointer: NonNull, layout: Layout) { - unsafe { Native::deallocate(&self, pointer, layout) } - } - - #[inline] - unsafe fn grow(&self, pointer: NonNull, old: Layout, new: Layout) -> Option> { - unsafe { Native::grow(&self, pointer, old, new).ok() } - } - - #[inline] - unsafe fn shrink( - &self, - pointer: NonNull, - old: Layout, - new: Layout - ) -> Option> { - unsafe { Native::shrink(&self, pointer, old, new).ok() } - } -} diff --git a/src/allocator/like.rs b/src/allocator/like.rs index 593bdbef..898e0f98 100644 --- a/src/allocator/like.rs +++ b/src/allocator/like.rs @@ -1,3 +1,25 @@ +use { + super::Allocator, + core::{ + mem::MaybeUninit, + ptr::NonNull + } +}; + pub trait Like { type Type; + type Item; + type Heap: Allocator; + fn destruct( + value: Self::Type + ) -> ( + Option]>>, + usize, + Self::Heap + ); + unsafe fn construct( + pointer: Option]>>, + length: usize, + allocator: Self::Heap + ) -> Self::Type; } diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs index 074f7190..2e0b7b8a 100644 --- a/src/allocator/mod.rs +++ b/src/allocator/mod.rs @@ -1,17 +1,20 @@ pub mod boxed; pub mod global; -mod implementations; +pub mod implementations; pub mod like; pub mod proxy; -mod requirement; +pub mod requirement; pub mod vec; -use core::{ - alloc::Layout, - ptr::NonNull +use { + core::{ + alloc::Layout, + ptr::NonNull + }, + requirement::Requirement }; -pub trait Allocator { +pub trait Allocator: Requirement { fn allocate(&self, layout: Layout) -> Option>; unsafe fn deallocate(&self, pointer: NonNull, layout: Layout); unsafe fn grow(&self, pointer: NonNull, old: Layout, new: Layout) -> Option>; diff --git a/src/allocator/proxy.rs b/src/allocator/proxy.rs index 9628da89..57e52b0b 100644 --- a/src/allocator/proxy.rs +++ b/src/allocator/proxy.rs @@ -1,37 +1,87 @@ -use { - super::Allocator, - core::{ - alloc::Layout, - ptr::NonNull - } +use super::{ + Allocator, + requirement::Requirement +}; +#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] +use alloc::alloc::AllocError; +#[cfg(feature = "allocator-api2")] +use allocator_api2::alloc::AllocError; +#[cfg(feature = "allocator-api")] +use core::{ + alloc::Layout, + ptr::NonNull }; #[repr(transparent)] pub struct Proxy<'valid, Heap: Allocator>(pub &'valid Heap); -impl<'valid, Heap: Allocator> Allocator for Proxy<'valid, Heap> { +#[cfg(not(feature = "allocator-api"))] +impl<'valid, Heap: Allocator> Requirement for Proxy<'valid, Heap> {} +#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] +unsafe impl<'valid, Heap: Allocator> Requirement for Proxy<'valid, Heap> { + #[inline] + fn allocate(&self, layout: Layout) -> Result, AllocError> { + Requirement::allocate(&self, layout) + } + + #[inline] + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + unsafe { + Requirement::deallocate(&self, ptr, layout); + } + } + + #[inline] + unsafe fn grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout + ) -> Result, AllocError> { + unsafe { Requirement::grow(&self, ptr, old_layout, new_layout) } + } + #[inline] - fn allocate(&self, layout: Layout) -> Option> { - self.0.allocate(layout) + unsafe fn shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout + ) -> Result, AllocError> { + unsafe { Requirement::shrink(&self, ptr, old_layout, new_layout) } + } +} +#[cfg(feature = "allocator-api2")] +unsafe impl<'valid, Heap: Allocator> Requirement for Proxy<'valid, Heap> { + #[inline] + fn allocate(&self, layout: Layout) -> Result, AllocError> { + Requirement::allocate(&self, layout) } #[inline] - unsafe fn deallocate(&self, pointer: NonNull, layout: Layout) { - unsafe { self.0.deallocate(pointer, layout) }; + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + unsafe { + Requirement::deallocate(&self, ptr, layout); + } } #[inline] - unsafe fn grow(&self, pointer: NonNull, old: Layout, new: Layout) -> Option> { - unsafe { self.0.grow(pointer, old, new) } + unsafe fn grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout + ) -> Result, AllocError> { + unsafe { Requirement::grow(&self, ptr, old_layout, new_layout) } } #[inline] unsafe fn shrink( &self, - pointer: NonNull, - old: Layout, - new: Layout - ) -> Option> { - unsafe { self.0.shrink(pointer, old, new) } + ptr: NonNull, + old_layout: Layout, + new_layout: Layout + ) -> Result, AllocError> { + unsafe { Requirement::shrink(&self, ptr, old_layout, new_layout) } } } diff --git a/src/allocator/requirement.rs b/src/allocator/requirement.rs index 23529e2c..2ba895b0 100644 --- a/src/allocator/requirement.rs +++ b/src/allocator/requirement.rs @@ -3,4 +3,4 @@ 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; +pub trait Requirement {} diff --git a/src/allocator/vec.rs b/src/allocator/vec.rs index a7801449..1926f9ab 100644 --- a/src/allocator/vec.rs +++ b/src/allocator/vec.rs @@ -2,22 +2,170 @@ pub use alloc::vec; #[cfg(feature = "allocator-api2")] pub use allocator_api2::vec; +#[cfg(not(feature = "allocator-api2"))] +use core::mem::ManuallyDrop; use { super::{ Allocator, - like::Like, - requirement::Requirement + like::Like }, - core::marker::PhantomData + core::{ + marker::PhantomData, + mem::MaybeUninit, + num::NonZero, + ptr::{ + NonNull, + slice_from_raw_parts_mut + } + } }; -pub struct Vec(PhantomData<(Heap, Item)>); +pub struct Vec(PhantomData<(Heap, Item)>); -impl Like for Vec { +impl Like for Vec { + type Heap = Heap; + type Item = Item; #[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; + + #[cfg(not(feature = "allocator-api"))] + #[inline] + fn destruct( + value: Self::Type + ) -> ( + Option]>>, + usize, + Self::Heap + ) { + let mut data = ManuallyDrop::new(value); + ( + NonZero::new(data.capacity()).and_then(|capacity| { + NonNull::new(slice_from_raw_parts_mut( + data.as_mut_ptr().cast(), + capacity.get() + )) + }), + data.len(), + #[allow(clippy::uninit_assumed_init)] + unsafe { + MaybeUninit::uninit().assume_init() + } + ) + } + + #[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] + #[inline] + fn destruct( + mut value: Self::Type + ) -> ( + Option]>>, + usize, + Self::Heap + ) { + let (pointer, length, capacity, allocator) = value.into_raw_parts_with_allocator(); + ( + NonZero::new(capacity).and_then(|capacity| { + NonNull::new(slice_from_raw_parts_mut(pointer.cast(), capacity.get())) + }), + length, + allocator + ) + } + + #[cfg(feature = "allocator-api2")] + #[inline] + fn destruct( + value: Self::Type + ) -> ( + Option]>>, + usize, + Self::Heap + ) { + let (pointer, length, capacity, allocator) = value.into_raw_parts_with_alloc(); + ( + NonZero::new(capacity).and_then(|capacity| { + NonNull::new(slice_from_raw_parts_mut(pointer.cast(), capacity.get())) + }), + length, + allocator + ) + } + + #[cfg(not(feature = "allocator-api"))] + #[inline] + unsafe fn construct( + pointer: Option]>>, + length: usize, + _allocator: Self::Heap + ) -> Self::Type { + if let Some(pointer) = pointer { + unsafe { Self::Type::from_raw_parts(pointer.as_ptr().cast(), length, pointer.len()) } + } else { + let mut vector = Self::Type::new(); + if size_of::() == 0 { + // SAFETY: we can change the length freely as copying the item + // because it is a ZST + unsafe { vector.set_len(length) } + }; + vector + } + } + + #[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] + #[inline] + unsafe fn construct( + pointer: Option]>>, + length: usize, + allocator: Self::Heap + ) -> Self::Type { + if let Some(pointer) = pointer { + unsafe { + Self::Type::from_raw_parts_in( + pointer.as_ptr().cast(), + length, + pointer.len(), + allocator + ) + } + } else { + let mut vector = Self::Type::new_in(allocator); + if size_of::() == 0 { + // SAFETY: we can change the length freely as copying the item + // because it is a ZST + unsafe { vector.set_len(length) } + }; + vector + } + } + + #[cfg(feature = "allocator-api2")] + #[inline] + unsafe fn construct( + pointer: Option]>>, + length: usize, + allocator: Self::Heap + ) -> Self::Type { + if let Some(pointer) = pointer { + unsafe { + Self::Type::from_raw_parts_in( + pointer.as_ptr().cast(), + length, + pointer.len(), + allocator + ) + } + } else { + let mut vector = Self::Type::new_in(allocator); + if size_of::() == 0 { + // SAFETY: we can change the length freely as copying the item + // because it is a ZST + unsafe { vector.set_len(length) } + }; + vector + } + } } diff --git a/src/conversions.rs b/src/conversions.rs index 96b134e4..4ebe9b6f 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -13,6 +13,10 @@ use { locatedlength::LocatedLength, rawsmallvec::RawSmallVec }, + alloc::borrow::{ + Cow, + ToOwned + }, core::{ mem::ManuallyDrop, ptr::{ @@ -25,23 +29,20 @@ use { impl From<&[Item]> for SmallVec { #[inline] fn from(slice: &[Item]) -> Self { - if slice.len() > Self::inline_size() { - // Standard Rust vectors are already specialized. - as Like>::Type::from(slice).into() - } else { - // SAFETY: The precondition is checked in the initial comparison - // above. + if slice.len() <= Self::inline_size() { + // SAFETY: The precondition is already checked above unsafe { #[cfg(feature = "specialization")] { >::spec_from(slice) } - #[cfg(not(feature = "specialization"))] { Self::from_slice_fallback(slice) } } + } else { + as Like>::Type::from(slice).into() } } } @@ -53,136 +54,112 @@ impl From<&mut [Item]> for SmallVec From<&[Item; M]> +impl From<&[Item; LENGTH]> for SmallVec { #[inline] - fn from(slice: &[Item; M]) -> Self { + fn from(slice: &[Item; LENGTH]) -> Self { Self::from(slice as &[Item]) } } -impl From<&mut [Item; M]> +impl From<&mut [Item; LENGTH]> for SmallVec { #[inline] - fn from(slice: &mut [Item; M]) -> Self { + fn from(slice: &mut [Item; LENGTH]) -> Self { Self::from(slice as &[Item]) } } -impl From<[Item; M]> for SmallVec { - fn from(array: [Item; M]) -> Self { - if M > INLINE { - // If M > INLINE, we'd have to heap allocate anyway, - // so delegate for Vec for the allocation. - Self::from( as Like>::Type::from(array)) +impl From<[Item; LENGTH]> + for SmallVec +{ + fn from(array: [Item; LENGTH]) -> Self { + if const { LENGTH > INLINE } { + as Like>::Type::from(array).into() } else { - // M <= INLINE - let mut this = Self::new(); - debug_assert!(M <= this.capacity()); - let array = ManuallyDrop::new(array); - // SAFETY: M <= this.capacity() + let mut smallvec = Self::new(); + let data = ManuallyDrop::new(array); + // SAFETY: LENGTH <= INLINE, thus it fits unsafe { - copy_nonoverlapping(array.as_ptr(), this.as_mut_ptr(), M); - this.set_len(M); + copy_nonoverlapping(data.as_ptr(), smallvec.as_mut_ptr(), LENGTH); + smallvec.set_len(LENGTH); } - this + smallvec } } } - -impl - TryFrom> for [Item; M] +impl + TryFrom> for [Item; LENGTH] { type Error = SmallVec; #[inline] fn try_from( - mut this: SmallVec - ) -> Result<[Item; M], SmallVec> { - if this.len() != M { - Err(this) - } else { - // SAFETY: we release ownership of the elements we hold - unsafe { - this.set_len(0); - } - let pointer = this.as_ptr() as *const [Item; M]; - // SAFETY: these elements are initialized since the length was `M` - unsafe { Ok(pointer.read()) } - } + mut smallvec: SmallVec + ) -> Result<[Item; LENGTH], SmallVec> { + // SAFETY: we release ownership of the elements we hold + (smallvec.len() == LENGTH) + .then(|| unsafe { + smallvec.set_len(0); + smallvec.as_ptr().cast::<[Item; LENGTH]>().read() + }) + .ok_or(smallvec) } } -impl From< as Like>::Type> - for SmallVec +impl From< as Like>::Type> + for SmallVec { - fn from(array: as Like>::Type) -> Self { - if array.capacity() == 0 { - return Self::new(); - } - + fn from(vec: as Like>::Type) -> Self { + let (pointer, length, allocator) = as Like>::destruct(vec); 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 `capacity > INLINE`. - if vec.capacity() <= INLINE { - vec.reserve(INLINE + 1 - length); + allocator } - let capacity = vec.capacity(); - // SAFETY: vec.capacity is not `0` (checked above), so the pointer - // can not dangle and thus specifically cannot be null. - let pointer = unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) }; - + } else if let Some(pointer) = pointer { Self { length: LocatedLength::new(length, true), - raw: RawSmallVec::heap(pointer, capacity), - allocator: Global + raw: RawSmallVec::heap(pointer.cast(), pointer.len()), + allocator + } + } else { + Self { + length: LocatedLength::new(0, false), + raw: RawSmallVec::new(), + allocator } } } } - impl From> - for as Like>::Type + for as Like>::Type { - fn from(this: SmallVec) -> Self { - let (length, on_heap) = this.length.parts(); + fn from(smallvec: SmallVec) -> Self { + let (length, on_heap) = smallvec.length.parts(); + let smallvec = ManuallyDrop::new(smallvec); if !on_heap { - let mut vec = as Like>::Type::with_capacity(length); - let this = ManuallyDrop::new(this); + let mut vector = unsafe { + as Like>::construct( + None, + 0, + (&raw const smallvec.allocator).read() + ) + }; + vector.reserve(length); // SAFETY: we create a new vector with sufficient capacity, copy our // elements into it to transfer ownership and then set // the length we don't drop the elements we previously // held unsafe { - copy_nonoverlapping(this.raw.as_ptr_inline(), vec.as_mut_ptr(), length); - vec.set_len(length); + copy_nonoverlapping(smallvec.raw.as_ptr_inline(), vector.as_mut_ptr(), length); + vector.set_len(length); } - vec + vector } else { - let this = ManuallyDrop::new(this); // SAFETY: // - `pointer` was created with the SmallVec's allocator // - `pointer` was created with the appropriate alignment for `Item` @@ -192,11 +169,11 @@ impl From as Like>::Type::from_raw_parts( - pointer.as_ptr(), + let (ptr, cap) = smallvec.raw.heap; + Vec::construct( + Some(NonNull::slice_from_raw_parts(ptr.cast(), cap)), length, - capacity + (&raw const smallvec.allocator).read() ) } } @@ -204,15 +181,77 @@ impl From From> - for as Like>::Type + for as Like>::Type { - fn from(this: SmallVec) -> Self { - as Like>::Type::from(this).into_boxed_slice() + fn from(smallvec: SmallVec) -> Self { + as Like>::Type::from(smallvec).into_boxed_slice() } } - -impl From< as Like>::Type> for SmallVec { - fn from(boxed: as Like>::Type) -> Self { +impl From< as Like>::Type> + for SmallVec +{ + fn from(boxed: as Like>::Type) -> Self { boxed.into_vec().into() } } + +impl From> + for (LocatedLength, RawSmallVec) +{ + #[inline] + fn from(value: SmallVec) -> Self { + let (length, raw, Global) = value.into(); + (length, raw) + } +} + +impl From> + for (LocatedLength, RawSmallVec, Heap) +{ + #[inline] + fn from(value: SmallVec) -> Self { + let this = ManuallyDrop::new(value); + // SAFETY: `this` is never dropped or used again, so each field is moved + // out exactly once and `Drop for SmallVec` never runs. + ( + this.length, + unsafe { (&raw const this.raw).read() }, + unsafe { (&raw const this.allocator).read() } + ) + } +} + +impl From> for SmallVec { + #[inline] + fn from(value: Cow<'_, [Item]>) -> Self { + match value { + Cow::Owned(mut vector) => unsafe { + Self::parts( + LocatedLength::new(vector.len(), true), + RawSmallVec::heap( + NonNull::new(vector.as_mut_ptr()).unwrap_or_else(NonNull::dangling), + vector.capacity() + ) + ) + }, + Cow::Borrowed(slice) => slice.into() + } + } +} +impl From> for Cow<'_, [Item]> { + #[inline] + fn from(value: SmallVec) -> Self { + let (pointer, length, Global) = + as Like>::destruct( as Like>::Type::from(value)); + Cow::Owned(unsafe { + <[Item] as ToOwned>::Owned::from_raw_parts( + pointer + .map(NonNull::cast::) + .unwrap_or_else(NonNull::dangling) + .as_ptr(), + length, + pointer.map(NonNull::len).unwrap_or_default() + ) + }) + } +} diff --git a/src/iterators/intoiter.rs b/src/iterators/intoiter.rs index f77729aa..f8714499 100644 --- a/src/iterators/intoiter.rs +++ b/src/iterators/intoiter.rs @@ -214,7 +214,7 @@ impl IntoIterator for SmallVec Self::IntoIter { - let (raw, end, allocator) = self.into_parts_with_allocator(); + let (end, raw, allocator) = self.into(); IntoIter { raw, allocator, diff --git a/src/lib.rs b/src/lib.rs index 7cc8b0b1..928534d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,12 @@ use proptest::{ Strategy } }; +#[cfg(any( + feature = "allocator-api", + feature = "internals", + feature = "may-dangle" +))] +use smallvec_macro::syntax; #[cfg(feature = "specialization")] use specialization::{ SpecCloneFrom, @@ -147,8 +153,7 @@ use { from_raw_parts_mut } }, - newrange::NewRange, - smallvec_macro::syntax + newrange::NewRange }; pub use { error::SmallVecError, @@ -312,15 +317,14 @@ impl SmallVec { /// the `LocatedLength` says which member of `RawSmallVec` is active. /// /// The parts own the elements and (if spilled) the heap buffer. Dropping - /// them leaks both; pass them to [`from_parts`](Self::from_parts) to get + /// them leaks both; pass them to [`parts`](Self::parts) to get /// a SmallVec back. #[inline] #[must_use = "dropping the parts leaks the elements and the heap buffer"] #[cfg_attr(feature = "internals", syntax(pub))] #[deprecated(since = "2.0.0-beta.4", note = "")] - fn into_parts(self) -> (RawSmallVec, LocatedLength) { - let (raw, length, Global) = self.into_parts_with_allocator(); - (raw, length) + fn into_parts(self) -> (LocatedLength, RawSmallVec) { + self.into() } /// Rebuilds a `SmallVec` from the parts returned by @@ -338,9 +342,8 @@ impl SmallVec { /// - The parts are not used again afterwards. #[inline] #[cfg_attr(feature = "internals", syntax(pub))] - #[deprecated(since = "2.0.0-beta.4", note = "")] - unsafe fn from_parts(raw: RawSmallVec, length: LocatedLength) -> Self { - unsafe { Self::from_parts_with_allocator(raw, length, Global) } + unsafe fn parts(length: LocatedLength, raw: RawSmallVec) -> Self { + unsafe { Self::parts_in(length, raw, Global) } } } @@ -972,13 +975,13 @@ impl SmallVec { #[inline] #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] - pub fn into_vec(self) -> as Like>::Type { + 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) -> as Like>::Type { + pub fn into_boxed_slice(self) -> as Like>::Type { self.into() } @@ -1301,35 +1304,21 @@ impl SmallVec { #[must_use = "dropping the parts leaks the elements and the heap buffer"] #[cfg_attr(all(feature = "internals", feature = "allocator-api"), syntax(pub))] #[deprecated(since = "2.0.0-beta.4", note = "")] - fn into_parts_with_allocator(self) -> (RawSmallVec, LocatedLength, Heap) { - let this = ManuallyDrop::new(self); - // SAFETY: `this` is never dropped or used again, so each field is moved - // out exactly once and `Drop for SmallVec` never runs. - unsafe { - ( - (&raw const this.raw).read(), - this.length, - (&raw const this.allocator).read() - ) - } + fn into_parts_with_allocator(self) -> (LocatedLength, RawSmallVec, Heap) { + self.into() } /// # Safety /// - /// Same contract as [`SmallVec::from_parts`], except that a heap buffer + /// Same contract as [`SmallVec::parts`], except that a heap buffer /// must have been allocated by `allocator` (or an equivalent one). #[inline] #[cfg_attr(all(feature = "internals", feature = "allocator-api"), syntax(pub))] - #[deprecated(since = "2.0.0-beta.4", note = "")] - unsafe fn from_parts_with_allocator( - raw: RawSmallVec, + unsafe fn parts_in( length: LocatedLength, + raw: RawSmallVec, allocator: Heap ) -> Self { - let (len, on_heap) = length.parts(); - // SAFETY: the caller guarantees the tag matches the active member. - // `capacity` is only read by the debug assertions below. - let capacity = unsafe { raw.capacity(on_heap) }; Self { length, raw, diff --git a/tests/allocatorparts.rs b/tests/allocatorparts.rs index 5d927d25..f4648ab7 100644 --- a/tests/allocatorparts.rs +++ b/tests/allocatorparts.rs @@ -14,14 +14,12 @@ fn round_trip_with_allocator() { } assert_eq!(v.spilled(), len > 2); - let (raw, length, allocator) = v.into_parts_with_allocator(); + let (length, raw, allocator) = v.into(); assert_eq!(length.len(), len); assert_eq!(length.on_heap(), len > 2); // SAFETY: parts come straight from `into_parts_with_allocator`. - let rebuilt = unsafe { - SmallVec::::from_parts_with_allocator(raw, length, allocator) - }; + let rebuilt = unsafe { SmallVec::::parts_in(length, raw, allocator) }; assert_eq!(rebuilt.len(), len); assert_eq!(rebuilt[0], "0"); } diff --git a/tests/parts.rs b/tests/parts.rs index a5030484..5a4642ad 100644 --- a/tests/parts.rs +++ b/tests/parts.rs @@ -8,7 +8,7 @@ fn into_from_parts_spilled() { let v: SmallVec = SmallVec::from([-1, 0, 1]); assert!(v.spilled()); - let (raw, length) = v.into_parts(); + let (length, raw) = v.into(); assert!(length.on_heap()); assert_eq!(length.len(), 3); @@ -17,7 +17,7 @@ fn into_from_parts_spilled() { assert!(capacity > 1); // SAFETY: parts come straight from `into_parts`. - let rebuilt = unsafe { SmallVec::::from_parts(raw, length) }; + let rebuilt = unsafe { SmallVec::::parts(length, raw) }; assert_eq!(rebuilt, [-1, 0, 1]); assert_eq!(rebuilt.as_ptr(), pointer.as_ptr() as *const i32); } @@ -27,12 +27,12 @@ fn into_from_parts_inline() { let v: SmallVec = SmallVec::from([1, 2, 3]); assert!(!v.spilled()); - let (raw, length) = v.into_parts(); + let (length, raw) = v.into(); assert!(!length.on_heap()); assert_eq!(length.len(), 3); // SAFETY: parts come straight from `into_parts`. - let rebuilt = unsafe { SmallVec::::from_parts(raw, length) }; + let rebuilt = unsafe { SmallVec::::parts(length, raw) }; assert_eq!(rebuilt, [1, 2, 3]); } @@ -50,12 +50,12 @@ fn parts_do_not_drop_or_double_drop() { assert_eq!(v.spilled(), spilled); assert_eq!(Rc::strong_count(&rc), 1 + count); - let (raw, length) = v.into_parts(); + let (length, raw) = v.into(); // nothing was dropped by `into_parts` assert_eq!(Rc::strong_count(&rc), 1 + count); // SAFETY: parts come straight from `into_parts`. - let rebuilt = unsafe { SmallVec::, 2>::from_parts(raw, length) }; + let rebuilt = unsafe { SmallVec::, 2>::parts(length, raw) }; assert_eq!(Rc::strong_count(&rc), 1 + count); drop(rebuilt); @@ -68,12 +68,12 @@ fn parts_do_not_drop_or_double_drop() { fn into_from_parts_empty() { let v: SmallVec = SmallVec::new(); - let (raw, length) = v.into_parts(); + let (length, raw) = v.into(); assert_eq!(length.len(), 0); assert!(!length.on_heap()); // SAFETY: parts come straight from `into_parts`. - let rebuilt = unsafe { SmallVec::::from_parts(raw, length) }; + let rebuilt = unsafe { SmallVec::::parts(length, raw) }; assert!(rebuilt.is_empty()); } @@ -81,11 +81,11 @@ fn into_from_parts_empty() { fn into_from_parts_zst() { let v: SmallVec<(), 2> = SmallVec::from([(), (), ()]); - let (raw, length) = v.into_parts(); + let (length, raw) = v.into(); assert!(!length.on_heap()); assert_eq!(length.len(), 3); // SAFETY: parts come straight from `into_parts`. - let rebuilt = unsafe { SmallVec::<(), 2>::from_parts(raw, length) }; + let rebuilt = unsafe { SmallVec::<(), 2>::parts(length, raw) }; assert_eq!(rebuilt.len(), 3); }