From 4c9c23bca41144f802fb50926487e72041567f49 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 13:07:53 +0100 Subject: [PATCH 1/9] Add custom allocator support to Vec/Box conversions --- src/allocator/mod.rs | 1 - src/conversions.rs | 148 ++++++++++++++++++++++++++++++++++++++----- src/lib.rs | 59 +---------------- 3 files changed, 135 insertions(+), 73 deletions(-) diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs index 2e212219..c5c7370b 100644 --- a/src/allocator/mod.rs +++ b/src/allocator/mod.rs @@ -14,7 +14,6 @@ use core::{ #[rustfmt::skip] pub use { alloc::{ - boxed::Box, vec::Vec, vec }, diff --git a/src/conversions.rs b/src/conversions.rs index 13cde585..17d2303f 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -1,14 +1,17 @@ use { crate::{ Allocator, - Box, Global, - SmallVec, - Vec + LocatedLength, + RawSmallVec, + SmallVec }, core::{ mem::ManuallyDrop, - ptr::copy_nonoverlapping + ptr::{ + NonNull, + copy_nonoverlapping + } } }; @@ -17,7 +20,7 @@ impl From<&[T]> for SmallVec { fn from(slice: &[T]) -> Self { if slice.len() > Self::inline_size() { // Standard Rust vectors are already specialized. - Self::from_vec(Vec::from(slice)) + alloc::vec::Vec::from(slice).into() } else { // SAFETY: The precondition is checked in the initial comparison // above. @@ -62,7 +65,7 @@ impl From<[T; M]> for SmallVec if M > N { // If M > N, we'd have to heap allocate anyway, // so delegate for Vec for the allocation. - Self::from(Vec::from(array)) + alloc::vec::Vec::from(array).into() } else { // M <= N let mut this = Self::new(); @@ -97,18 +100,122 @@ impl TryFrom> } } -impl From> for SmallVec { - fn from(array: Vec) -> Self { - Self::from_vec(array) +#[cfg(feature = "allocator-api2")] +use allocator_api2::alloc::Allocator as BaseAllocator; +#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] +use core::alloc::Allocator as BaseAllocator; + +#[cfg(feature = "allocator-api")] +impl From> for SmallVec { + fn from(vec: crate::Vec) -> Self { + use core::mem::MaybeUninit; + + #[cfg(feature = "allocator-api2")] + let (ptr, length, cap, allocator) = vec.into_raw_parts_with_alloc(); + #[cfg(not(feature = "allocator-api2"))] + let (ptr, length, cap, allocator) = vec.into_raw_parts_with_allocator(); + + if N < cap { + Self { + length: LocatedLength::new(length, true), + raw: RawSmallVec { + // SAFETY: A `Vec` always has a non-null pointer. + heap: (unsafe { NonNull::new_unchecked(ptr) }, cap) + }, + allocator + } + } else { + let mut inline = MaybeUninit::uninit(); + // SAFETY: vec.capacity() <= N + unsafe { copy_nonoverlapping(ptr, &raw mut inline as *mut T, length) }; + Self { + length: LocatedLength::new(length, false), + raw: RawSmallVec { + inline: ManuallyDrop::new(inline) + }, + allocator + } + } } } -impl From> for Vec { +#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] +impl From> for SmallVec { + fn from(vec: alloc::vec::Vec) -> Self { + use core::mem::MaybeUninit; + + let (ptr, length, cap) = vec.into_raw_parts(); + + if N < cap { + Self { + length: LocatedLength::new(length, true), + raw: RawSmallVec { + // SAFETY: A `Vec` always has a non-null pointer. + heap: (unsafe { NonNull::new_unchecked(ptr) }, cap) + }, + allocator: Global + } + } else { + let mut inline = MaybeUninit::uninit(); + // SAFETY: vec.capacity() <= N + unsafe { copy_nonoverlapping(ptr, &raw mut inline as *mut T, length) }; + Self { + length: LocatedLength::new(length, false), + raw: RawSmallVec { + inline: ManuallyDrop::new(inline) + }, + allocator: Global + } + } + } +} + +#[cfg(feature = "allocator-api")] +impl From> for crate::Vec { fn from(this: SmallVec) -> Self { let (length, on_heap) = this.length.parts(); + let this = ManuallyDrop::new(this); + if !on_heap { + unsafe { + let mut vec = + crate::Vec::with_capacity_in(length, core::ptr::read(&this.allocator)); + // 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 + copy_nonoverlapping(this.raw.as_ptr_inline(), vec.as_mut_ptr(), length); + vec.set_len(length); + + vec + } + } else { + // SAFETY: + // - `ptr` was created with the SmallVec's allocator + // - `ptr` was created with the appropriate alignment for `T` + // - the allocation pointed to by ptr is exactly cap * sizeof(T) + // - `length` is less than or equal to `cap` + // - the first `length` entries are proper `T`-values + // - the allocation is not larger than `isize::MAX` + unsafe { + let (ptr, cap) = this.raw.heap; + crate::Vec::from_raw_parts_in( + ptr.as_ptr(), + length, + cap, + core::ptr::read(&this.allocator) + ) + } + } + } +} + +#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] +impl From> for alloc::vec::Vec { + fn from(this: SmallVec) -> Self { + let (length, on_heap) = this.length.parts(); + let this = ManuallyDrop::new(this); if !on_heap { - let mut vec = Vec::with_capacity(length); - let this = ManuallyDrop::new(this); + let mut vec = alloc::vec::Vec::with_capacity(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 @@ -119,7 +226,6 @@ impl From> for Vec { } vec } else { - let this = ManuallyDrop::new(this); // SAFETY: // - `ptr` was created with the SmallVec's allocator // - `ptr` was created with the appropriate alignment for `T` @@ -129,14 +235,24 @@ impl From> for Vec { // - the allocation is not larger than `isize::MAX` unsafe { let (ptr, cap) = this.raw.heap; - Vec::from_raw_parts(ptr.as_ptr(), length, cap) + alloc::vec::Vec::from_raw_parts(ptr.as_ptr(), length, cap) } } } } -impl From> for Box<[T]> { +#[cfg(feature = "allocator-api")] +impl From> + for crate::allocator::Box<[T], A> +{ fn from(this: SmallVec) -> Self { - Vec::from(this).into_boxed_slice() + crate::Vec::from(this).into_boxed_slice() + } +} + +#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] +impl From> for alloc::boxed::Box<[T]> { + fn from(this: SmallVec) -> Self { + alloc::vec::Vec::from(this).into_boxed_slice() } } diff --git a/src/lib.rs b/src/lib.rs index 3e3a624d..1cf339d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,6 @@ use std::io; use { allocator::{ Allocator, - Box, Global, Vec, vec @@ -220,49 +219,9 @@ impl SmallVec { } #[inline] + #[deprecated(since = "2.0.0", 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 > N`. - if vec.capacity() <= N { - vec.reserve(N + 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 - } - } + vec.into() } /// Creates a `SmallVec` directly from the raw components of another @@ -978,18 +937,6 @@ impl SmallVec { unsafe { self.raw.as_mut_ptr(self.length.on_heap()) } } - #[inline] - #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] - pub fn into_vec(self) -> Vec { - self.into() - } - - #[inline] - #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] - pub fn into_boxed_slice(self) -> Box<[T]> { - self.into() - } - #[inline] #[deprecated( since = "2.0.0-alpha.13", @@ -1505,7 +1452,7 @@ impl Drop for SmallVec { pub fn from_elem(elem: T, n: usize) -> SmallVec { if n > SmallVec::::inline_size() { // Standard Rust vectors are already specialized. - SmallVec::from_vec(vec![elem; n]) + vec![elem; n].into() } else { #[cfg(feature = "specialization")] { From 63cbe5dab6d5f26d38dff3e28ddfbbbf8842db28 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 13:53:02 +0100 Subject: [PATCH 2/9] Actually deallocate vec when moving it inline --- src/conversions.rs | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index 17d2303f..017f3ff8 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -108,26 +108,39 @@ use core::alloc::Allocator as BaseAllocator; #[cfg(feature = "allocator-api")] impl From> for SmallVec { fn from(vec: crate::Vec) -> Self { - use core::mem::MaybeUninit; + use core::{ + alloc::Layout, + mem::MaybeUninit + }; #[cfg(feature = "allocator-api2")] let (ptr, length, cap, allocator) = vec.into_raw_parts_with_alloc(); #[cfg(not(feature = "allocator-api2"))] let (ptr, length, cap, allocator) = vec.into_raw_parts_with_allocator(); + // SAFETY: A `Vec` always has a non-null pointer. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + if N < cap { Self { - length: LocatedLength::new(length, true), + length: LocatedLength::new(length, !Self::IS_ZST), raw: RawSmallVec { - // SAFETY: A `Vec` always has a non-null pointer. - heap: (unsafe { NonNull::new_unchecked(ptr) }, cap) + heap: (ptr, cap) }, allocator } } else { let mut inline = MaybeUninit::uninit(); // SAFETY: vec.capacity() <= N - unsafe { copy_nonoverlapping(ptr, &raw mut inline as *mut T, length) }; + unsafe { + copy_nonoverlapping(ptr.as_ptr(), &raw mut inline as *mut T, length); + // We have to manually deallocate vec's memory, since we need to + // move its allocator out, meaning we can't drop it + allocator.deallocate( + ptr.cast(), + Layout::from_size_align_unchecked(cap * size_of::(), align_of::()) + ); + } Self { length: LocatedLength::new(length, false), raw: RawSmallVec { @@ -141,14 +154,14 @@ impl From> for SmallVec From> for SmallVec { - fn from(vec: alloc::vec::Vec) -> Self { + fn from(mut vec: alloc::vec::Vec) -> Self { use core::mem::MaybeUninit; - let (ptr, length, cap) = vec.into_raw_parts(); + if N < vec.capacity() { + let (ptr, length, cap) = vec.into_raw_parts(); - if N < cap { Self { - length: LocatedLength::new(length, true), + length: LocatedLength::new(length, !Self::IS_ZST), raw: RawSmallVec { // SAFETY: A `Vec` always has a non-null pointer. heap: (unsafe { NonNull::new_unchecked(ptr) }, cap) @@ -156,9 +169,13 @@ impl From> for SmallVec { allocator: Global } } else { + let length = vec.len(); let mut inline = MaybeUninit::uninit(); // SAFETY: vec.capacity() <= N - unsafe { copy_nonoverlapping(ptr, &raw mut inline as *mut T, length) }; + unsafe { + copy_nonoverlapping(vec.as_ptr(), &raw mut inline as *mut T, length); + vec.set_len(0); + } Self { length: LocatedLength::new(length, false), raw: RawSmallVec { From 481092d66635ac38c62fef4c6c8d0e048c2603a2 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 14:13:21 +0100 Subject: [PATCH 3/9] =?UTF-8?q?into=5Fraw=5Fparts=20isn't=20in=20MSRV?= =?UTF-8?q?=E2=80=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/conversions.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index 017f3ff8..1aab4d44 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -158,13 +158,16 @@ impl From> for SmallVec { use core::mem::MaybeUninit; if N < vec.capacity() { - let (ptr, length, cap) = vec.into_raw_parts(); + let mut vec = ManuallyDrop::new(vec); Self { - length: LocatedLength::new(length, !Self::IS_ZST), + length: LocatedLength::new(vec.len(), !Self::IS_ZST), raw: RawSmallVec { - // SAFETY: A `Vec` always has a non-null pointer. - heap: (unsafe { NonNull::new_unchecked(ptr) }, cap) + heap: ( + // SAFETY: A `Vec` always has a non-null pointer. + unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) }, + vec.capacity() + ) }, allocator: Global } From 727c235cab1986aef3b9de22d0213a6ad7782968 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 16:14:00 +0100 Subject: [PATCH 4/9] Draft internal Parts trait and vec conversion helpers --- src/conversions.rs | 185 ++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 101 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index 1aab4d44..1b4977d1 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -6,8 +6,13 @@ use { RawSmallVec, SmallVec }, + alloc::alloc::handle_alloc_error, core::{ - mem::ManuallyDrop, + alloc::Layout, + mem::{ + ManuallyDrop, + MaybeUninit + }, ptr::{ NonNull, copy_nonoverlapping @@ -100,23 +105,51 @@ impl TryFrom> } } +#[cfg(not(feature = "allocator-api"))] +use Allocator as BaseAllocator; #[cfg(feature = "allocator-api2")] use allocator_api2::alloc::Allocator as BaseAllocator; #[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] use core::alloc::Allocator as BaseAllocator; -#[cfg(feature = "allocator-api")] -impl From> for SmallVec { - fn from(vec: crate::Vec) -> Self { - use core::{ - alloc::Layout, - mem::MaybeUninit - }; +trait Parts { + fn into_parts(self) -> (*mut T, usize, usize, A); + unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, allocator: A) -> Self; +} +#[cfg(feature = "allocator-api")] +impl Parts for crate::Vec { + fn into_parts(self) -> (*mut T, usize, usize, A) { #[cfg(feature = "allocator-api2")] - let (ptr, length, cap, allocator) = vec.into_raw_parts_with_alloc(); + { + self.into_raw_parts_with_alloc() + } #[cfg(not(feature = "allocator-api2"))] - let (ptr, length, cap, allocator) = vec.into_raw_parts_with_allocator(); + { + self.into_raw_parts_with_allocator() + } + } + + unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, allocator: A) -> Self { + unsafe { crate::Vec::from_raw_parts_in(ptr, length, cap, allocator) } + } +} + +#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] +impl Parts for alloc::vec::Vec { + fn into_parts(self) -> (*mut T, usize, usize, Global) { + let (ptr, length, cap) = self.into_raw_parts(); + (ptr, length, cap, Global) + } + + unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, _: Global) -> Self { + unsafe { alloc::vec::Vec::from_raw_parts(ptr, length, cap) } + } +} + +impl SmallVec { + fn from_vec_helper(vec: impl Parts) -> Self { + let (ptr, length, cap, allocator) = vec.into_parts(); // SAFETY: A `Vec` always has a non-null pointer. let ptr = unsafe { NonNull::new_unchecked(ptr) }; @@ -150,114 +183,64 @@ impl From> for SmallVec From> for SmallVec { - fn from(mut vec: alloc::vec::Vec) -> Self { - use core::mem::MaybeUninit; + fn into_vec_helper>(self) -> P { + let (length, on_heap) = self.length.parts(); + let this = ManuallyDrop::new(self); + unsafe { + // SAFETY: we don't call any allocator-using methods on `this`, so + // it's fine to copy it out + let allocator = core::ptr::read(&this.allocator); + if !on_heap { + let layout = + Layout::from_size_align_unchecked(length * size_of::(), align_of::()); + let ptr = Allocator::allocate(&allocator, layout) + .unwrap_or_else(|| handle_alloc_error(layout)) + .as_ptr() as *mut T; - if N < vec.capacity() { - let mut vec = ManuallyDrop::new(vec); + copy_nonoverlapping(this.raw.as_ptr_inline(), ptr, length); - Self { - length: LocatedLength::new(vec.len(), !Self::IS_ZST), - raw: RawSmallVec { - heap: ( - // SAFETY: A `Vec` always has a non-null pointer. - unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) }, - vec.capacity() - ) - }, - allocator: Global - } - } else { - let length = vec.len(); - let mut inline = MaybeUninit::uninit(); - // SAFETY: vec.capacity() <= N - unsafe { - copy_nonoverlapping(vec.as_ptr(), &raw mut inline as *mut T, length); - vec.set_len(0); - } - Self { - length: LocatedLength::new(length, false), - raw: RawSmallVec { - inline: ManuallyDrop::new(inline) - }, - allocator: Global + P::from_parts(ptr, length, length, allocator) + } else { + // SAFETY: + // - `ptr` was created with the SmallVec's allocator + // - `ptr` was created with the appropriate alignment for `T` + // - the allocation pointed to by ptr is exactly cap * sizeof(T) + // - `length` is less than or equal to `cap` + // - the first `length` entries are proper `T`-values + // - the allocation is not larger than `isize::MAX` + let (ptr, cap) = this.raw.heap; + P::from_parts(ptr.as_ptr(), length, cap, allocator) } } } } +#[cfg(feature = "allocator-api")] +impl From> for SmallVec { + fn from(vec: crate::Vec) -> Self { + Self::from_vec_helper(vec) + } +} + +#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] +impl From> for SmallVec { + fn from(vec: alloc::vec::Vec) -> Self { + Self::from_vec_helper(vec) + } +} + #[cfg(feature = "allocator-api")] impl From> for crate::Vec { fn from(this: SmallVec) -> Self { - let (length, on_heap) = this.length.parts(); - let this = ManuallyDrop::new(this); - if !on_heap { - unsafe { - let mut vec = - crate::Vec::with_capacity_in(length, core::ptr::read(&this.allocator)); - // 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 - copy_nonoverlapping(this.raw.as_ptr_inline(), vec.as_mut_ptr(), length); - vec.set_len(length); - - vec - } - } else { - // SAFETY: - // - `ptr` was created with the SmallVec's allocator - // - `ptr` was created with the appropriate alignment for `T` - // - the allocation pointed to by ptr is exactly cap * sizeof(T) - // - `length` is less than or equal to `cap` - // - the first `length` entries are proper `T`-values - // - the allocation is not larger than `isize::MAX` - unsafe { - let (ptr, cap) = this.raw.heap; - crate::Vec::from_raw_parts_in( - ptr.as_ptr(), - length, - cap, - core::ptr::read(&this.allocator) - ) - } - } + this.into_vec_helper() } } #[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] impl From> for alloc::vec::Vec { fn from(this: SmallVec) -> Self { - let (length, on_heap) = this.length.parts(); - let this = ManuallyDrop::new(this); - if !on_heap { - let mut vec = alloc::vec::Vec::with_capacity(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); - } - vec - } else { - // SAFETY: - // - `ptr` was created with the SmallVec's allocator - // - `ptr` was created with the appropriate alignment for `T` - // - the allocation pointed to by ptr is exactly cap * sizeof(T) - // - `length` is less than or equal to `cap` - // - the first `length` entries are proper `T`-values - // - the allocation is not larger than `isize::MAX` - unsafe { - let (ptr, cap) = this.raw.heap; - alloc::vec::Vec::from_raw_parts(ptr.as_ptr(), length, cap) - } - } + this.into_vec_helper() } } From 41e4f7f467501512635b6044d51d22a73c4b5aec Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 16:16:26 +0100 Subject: [PATCH 5/9] Comment style --- src/conversions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversions.rs b/src/conversions.rs index 1b4977d1..0d34ea6c 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -189,7 +189,7 @@ impl SmallVec { let this = ManuallyDrop::new(self); unsafe { // SAFETY: we don't call any allocator-using methods on `this`, so - // it's fine to copy it out + // it's fine to copy it out let allocator = core::ptr::read(&this.allocator); if !on_heap { let layout = From 837416455d93b15df4bf442f27a08961b5a8212d Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 16:23:32 +0100 Subject: [PATCH 6/9] Don't try to deallocate a dangling pointer --- src/conversions.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/conversions.rs b/src/conversions.rs index 0d34ea6c..abe3b10d 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -151,6 +151,10 @@ impl SmallVec { fn from_vec_helper(vec: impl Parts) -> Self { let (ptr, length, cap, allocator) = vec.into_parts(); + if cap == 0 { + return Self::new_in(allocator); + } + // SAFETY: A `Vec` always has a non-null pointer. let ptr = unsafe { NonNull::new_unchecked(ptr) }; From 20a03c9bc18c72e30f6a9517b3ecc7b36dfcd85c Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Mon, 5 Oct 2026 16:33:50 +0100 Subject: [PATCH 7/9] Fix MSRV compatibility --- src/conversions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index abe3b10d..9998089c 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -138,8 +138,8 @@ impl Parts for crate::Vec { #[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] impl Parts for alloc::vec::Vec { fn into_parts(self) -> (*mut T, usize, usize, Global) { - let (ptr, length, cap) = self.into_raw_parts(); - (ptr, length, cap, Global) + let mut this = ManuallyDrop::new(self); + (this.as_mut_ptr(), this.len(), this.capacity(), Global) } unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, _: Global) -> Self { From d65d8574e57ceb0b46a5ea56d0daa79d2e86acab Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Tue, 6 Oct 2026 13:49:53 +0100 Subject: [PATCH 8/9] Correct deprecation version --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1cf339d4..b0724bcc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -219,7 +219,7 @@ impl SmallVec { } #[inline] - #[deprecated(since = "2.0.0", note = "use `From::>::from` instead")] + #[deprecated(since = "2.0.0-beta.3", note = "use `From::>::from` instead")] pub fn from_vec(vec: Vec) -> Self { vec.into() } From e4251250f7a45ae3069708095d0982c3cdc3aa09 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Thu, 8 Oct 2026 21:01:28 +0100 Subject: [PATCH 9/9] Remove all but deprecation --- src/allocator/mod.rs | 1 + src/conversions.rs | 245 ++++++++++++------------------------------- src/lib.rs | 56 +++++++++- 3 files changed, 125 insertions(+), 177 deletions(-) diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs index c5c7370b..2e212219 100644 --- a/src/allocator/mod.rs +++ b/src/allocator/mod.rs @@ -14,6 +14,7 @@ use core::{ #[rustfmt::skip] pub use { alloc::{ + boxed::Box, vec::Vec, vec }, diff --git a/src/conversions.rs b/src/conversions.rs index 9998089c..0c4579f4 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -1,38 +1,30 @@ use { crate::{ Allocator, + Box, Global, - LocatedLength, - RawSmallVec, - SmallVec + SmallVec, + Vec }, - alloc::alloc::handle_alloc_error, core::{ - alloc::Layout, - mem::{ - ManuallyDrop, - MaybeUninit - }, - ptr::{ - NonNull, - copy_nonoverlapping - } + mem::ManuallyDrop, + ptr::copy_nonoverlapping } }; -impl From<&[T]> for SmallVec { +impl From<&[Item]> for SmallVec { #[inline] - fn from(slice: &[T]) -> Self { + fn from(slice: &[Item]) -> Self { if slice.len() > Self::inline_size() { // Standard Rust vectors are already specialized. - alloc::vec::Vec::from(slice).into() + Vec::from(slice).into() } else { // SAFETY: The precondition is checked in the initial comparison // above. unsafe { #[cfg(feature = "specialization")] { - >::spec_from(slice) + >::spec_from(slice) } #[cfg(not(feature = "specialization"))] @@ -44,35 +36,39 @@ impl From<&[T]> for SmallVec { } } -impl From<&mut [T]> for SmallVec { +impl From<&mut [Item]> for SmallVec { #[inline] - fn from(slice: &mut [T]) -> Self { - Self::from(slice as &[T]) + fn from(slice: &mut [Item]) -> Self { + Self::from(slice as &[Item]) } } -impl From<&[T; M]> for SmallVec { +impl From<&[Item; M]> + for SmallVec +{ #[inline] - fn from(slice: &[T; M]) -> Self { - Self::from(slice as &[T]) + fn from(slice: &[Item; M]) -> Self { + Self::from(slice as &[Item]) } } -impl From<&mut [T; M]> for SmallVec { +impl From<&mut [Item; M]> + for SmallVec +{ #[inline] - fn from(slice: &mut [T; M]) -> Self { - Self::from(slice as &[T]) + fn from(slice: &mut [Item; M]) -> Self { + Self::from(slice as &[Item]) } } -impl From<[T; M]> for SmallVec { - fn from(array: [T; M]) -> Self { - if M > N { - // If M > N, we'd have to heap allocate anyway, +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. - alloc::vec::Vec::from(array).into() + Self::from(Vec::from(array)) } else { - // M <= N + // M <= INLINE let mut this = Self::new(); debug_assert!(M <= this.capacity()); let array = ManuallyDrop::new(array); @@ -86,11 +82,15 @@ impl From<[T; M]> for SmallVec } } -impl TryFrom> for [T; M] { - type Error = SmallVec; +impl + TryFrom> for [Item; M] +{ + type Error = SmallVec; #[inline] - fn try_from(mut this: SmallVec) -> Result<[T; M], SmallVec> { + fn try_from( + mut this: SmallVec + ) -> Result<[Item; M], SmallVec> { if this.len() != M { Err(this) } else { @@ -98,168 +98,61 @@ impl TryFrom> unsafe { this.set_len(0); } - let ptr = this.as_ptr() as *const [T; M]; + let ptr = this.as_ptr() as *const [Item; M]; // SAFETY: these elements are initialized since the length was `M` unsafe { Ok(ptr.read()) } } } } -#[cfg(not(feature = "allocator-api"))] -use Allocator as BaseAllocator; -#[cfg(feature = "allocator-api2")] -use allocator_api2::alloc::Allocator as BaseAllocator; -#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))] -use core::alloc::Allocator as BaseAllocator; - -trait Parts { - fn into_parts(self) -> (*mut T, usize, usize, A); - unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, allocator: A) -> Self; -} - -#[cfg(feature = "allocator-api")] -impl Parts for crate::Vec { - fn into_parts(self) -> (*mut T, usize, usize, A) { - #[cfg(feature = "allocator-api2")] - { - self.into_raw_parts_with_alloc() - } - #[cfg(not(feature = "allocator-api2"))] - { - self.into_raw_parts_with_allocator() - } - } - - unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, allocator: A) -> Self { - unsafe { crate::Vec::from_raw_parts_in(ptr, length, cap, allocator) } - } -} - -#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] -impl Parts for alloc::vec::Vec { - fn into_parts(self) -> (*mut T, usize, usize, Global) { - let mut this = ManuallyDrop::new(self); - (this.as_mut_ptr(), this.len(), this.capacity(), Global) - } - - unsafe fn from_parts(ptr: *mut T, length: usize, cap: usize, _: Global) -> Self { - unsafe { alloc::vec::Vec::from_raw_parts(ptr, length, cap) } +impl From> for SmallVec { + fn from(array: Vec) -> Self { + Self::from_vec(array) } } -impl SmallVec { - fn from_vec_helper(vec: impl Parts) -> Self { - let (ptr, length, cap, allocator) = vec.into_parts(); - - if cap == 0 { - return Self::new_in(allocator); - } - - // SAFETY: A `Vec` always has a non-null pointer. - let ptr = unsafe { NonNull::new_unchecked(ptr) }; - - if N < cap { - Self { - length: LocatedLength::new(length, !Self::IS_ZST), - raw: RawSmallVec { - heap: (ptr, cap) - }, - allocator +impl From> for Vec { + fn from(this: SmallVec) -> Self { + let (length, on_heap) = this.length.parts(); + if !on_heap { + let mut vec = Vec::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 + // 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); } + vec } else { - let mut inline = MaybeUninit::uninit(); - // SAFETY: vec.capacity() <= N + let this = ManuallyDrop::new(this); + // SAFETY: + // - `ptr` was created with the SmallVec's allocator + // - `ptr` was created with the appropriate alignment for `Item` + // - the allocation pointed to by ptr is exactly cap * sizeof(Item) + // - `length` is less than or equal to `cap` + // - the first `length` entries are proper `Item`-values + // - the allocation is not larger than `isize::MAX` unsafe { - copy_nonoverlapping(ptr.as_ptr(), &raw mut inline as *mut T, length); - // We have to manually deallocate vec's memory, since we need to - // move its allocator out, meaning we can't drop it - allocator.deallocate( - ptr.cast(), - Layout::from_size_align_unchecked(cap * size_of::(), align_of::()) - ); - } - Self { - length: LocatedLength::new(length, false), - raw: RawSmallVec { - inline: ManuallyDrop::new(inline) - }, - allocator - } - } - } - - fn into_vec_helper>(self) -> P { - let (length, on_heap) = self.length.parts(); - let this = ManuallyDrop::new(self); - unsafe { - // SAFETY: we don't call any allocator-using methods on `this`, so - // it's fine to copy it out - let allocator = core::ptr::read(&this.allocator); - if !on_heap { - let layout = - Layout::from_size_align_unchecked(length * size_of::(), align_of::()); - let ptr = Allocator::allocate(&allocator, layout) - .unwrap_or_else(|| handle_alloc_error(layout)) - .as_ptr() as *mut T; - - copy_nonoverlapping(this.raw.as_ptr_inline(), ptr, length); - - P::from_parts(ptr, length, length, allocator) - } else { - // SAFETY: - // - `ptr` was created with the SmallVec's allocator - // - `ptr` was created with the appropriate alignment for `T` - // - the allocation pointed to by ptr is exactly cap * sizeof(T) - // - `length` is less than or equal to `cap` - // - the first `length` entries are proper `T`-values - // - the allocation is not larger than `isize::MAX` let (ptr, cap) = this.raw.heap; - P::from_parts(ptr.as_ptr(), length, cap, allocator) + Vec::from_raw_parts(ptr.as_ptr(), length, cap) } } } } -#[cfg(feature = "allocator-api")] -impl From> for SmallVec { - fn from(vec: crate::Vec) -> Self { - Self::from_vec_helper(vec) - } -} - -#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] -impl From> for SmallVec { - fn from(vec: alloc::vec::Vec) -> Self { - Self::from_vec_helper(vec) - } -} - -#[cfg(feature = "allocator-api")] -impl From> for crate::Vec { - fn from(this: SmallVec) -> Self { - this.into_vec_helper() - } -} - -#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] -impl From> for alloc::vec::Vec { - fn from(this: SmallVec) -> Self { - this.into_vec_helper() - } -} - -#[cfg(feature = "allocator-api")] -impl From> - for crate::allocator::Box<[T], A> +impl From> + for Box<[Item]> { - fn from(this: SmallVec) -> Self { - crate::Vec::from(this).into_boxed_slice() + fn from(this: SmallVec) -> Self { + Vec::from(this).into_boxed_slice() } } -#[cfg(any(not(feature = "allocator-api"), feature = "allocator-api2"))] -impl From> for alloc::boxed::Box<[T]> { - fn from(this: SmallVec) -> Self { - alloc::vec::Vec::from(this).into_boxed_slice() +impl From> for SmallVec { + fn from(boxed: Box<[T]>) -> Self { + boxed.into_vec().into() } } diff --git a/src/lib.rs b/src/lib.rs index b0724bcc..186a3b10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,7 @@ use std::io; use { allocator::{ Allocator, + Box, Global, Vec, vec @@ -221,7 +222,48 @@ impl SmallVec { #[inline] #[deprecated(since = "2.0.0-beta.3", note = "use `From::>::from` instead")] pub fn from_vec(vec: Vec) -> Self { - vec.into() + 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() <= N { + vec.reserve(N + 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 + } + } } /// Creates a `SmallVec` directly from the raw components of another @@ -937,6 +979,18 @@ impl SmallVec { unsafe { self.raw.as_mut_ptr(self.length.on_heap()) } } + #[inline] + #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] + pub fn into_vec(self) -> Vec { + self.into() + } + + #[inline] + #[deprecated(since = "2.0.0", note = "use `Into::>::into` instead")] + pub fn into_boxed_slice(self) -> Box<[T]> { + self.into() + } + #[inline] #[deprecated( since = "2.0.0-alpha.13",