From ca703153f56776e982ce06a5c13340796215af95 Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 1 Nov 2026 07:58:09 +0000 Subject: [PATCH 1/3] refactor: simplify rawsmallvec --- src/conversions.rs | 19 +++++++++---- src/iterators/intoiter.rs | 2 +- src/lib.rs | 59 +++++++++++++++++++++++++++------------ src/rawsmallvec.rs | 39 +++++++++----------------- tests/parts.rs | 7 +++-- 5 files changed, 73 insertions(+), 53 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index fd6a41f2..47854923 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -150,14 +150,18 @@ impl From< as Like>::Type> 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()) }; + let pointer = NonNull::slice_from_raw_parts( + unsafe { NonNull::new_unchecked(vec.as_mut_ptr()).cast() }, + vec.capacity() + ); Self { length: LocatedLength::new(length, true), - raw: RawSmallVec::heap(ptr, cap), + raw: RawSmallVec { + heap: pointer + }, allocator: Global } } @@ -191,8 +195,13 @@ impl From as Like>::Type::from_raw_parts(ptr.as_ptr(), length, cap) + let pointer = this.raw.heap; + let capacity = pointer.len(); + as Like>::Type::from_raw_parts( + pointer.cast().as_ptr(), + length, + capacity + ) } } } diff --git a/src/iterators/intoiter.rs b/src/iterators/intoiter.rs index ec08ec90..9d9ee0f5 100644 --- a/src/iterators/intoiter.rs +++ b/src/iterators/intoiter.rs @@ -171,7 +171,7 @@ impl Drop for IntoIter(), diff --git a/src/lib.rs b/src/lib.rs index 2c6e4bcb..17b43523 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,7 +224,9 @@ impl SmallVec { // SAFETY: all the members in 0..S are initialized Self { length: LocatedLength::new(S, false), - raw: RawSmallVec::inline(buf), + raw: RawSmallVec { + inline: ManuallyDrop::new(buf) + }, allocator: Global } } @@ -235,7 +237,9 @@ impl SmallVec { // SAFETY: all the members in 0..length are initialized let mut vec = Self { length: LocatedLength::new(length, false), - raw: RawSmallVec::inline(buf.map(MaybeUninit::new)), + raw: RawSmallVec { + inline: ManuallyDrop::new(buf.map(MaybeUninit::new)) + }, allocator: Global }; // Deallocate the remaining elements so no memory is leaked. @@ -283,7 +287,9 @@ impl SmallVec { debug_assert!(length <= INLINE); Self { length: LocatedLength::new(length, false), - raw: RawSmallVec::inline(buf), + raw: RawSmallVec { + inline: ManuallyDrop::new(buf) + }, allocator: Global } } @@ -360,7 +366,11 @@ impl SmallVec { #[inline] pub const fn inline_size() -> usize { - RawSmallVec::::INLINE_CAP + if size_of::() == 0 { + usize::MAX + } else { + INLINE + } } #[inline] @@ -651,15 +661,20 @@ impl SmallVec { if on_heap { unsafe { // SAFETY: heap member is active - let (ptr, old_cap) = self.raw.heap; + let pointer = self.raw.heap; + // inline member is now active // SAFETY: length <= new_capacity <= Self::inline_size() // so the copy is within bounds of the inline member - copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length); + copy_nonoverlapping( + pointer.as_ptr().cast(), + self.raw.as_mut_ptr_inline(), + length + ); drop(DropDealloc { - ptr: ptr.cast(), - size_bytes: old_cap * size_of::(), + ptr: pointer.cast(), + size_bytes: pointer.len() * size_of::(), align: align_of::(), allocator: &self.allocator }); @@ -725,14 +740,18 @@ impl SmallVec { if length <= Self::inline_size() { // SAFETY: on_heap is true, so we're on the heap unsafe { - let (ptr, capacity) = self.raw.heap; - copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length); + let pointer = self.raw.heap; + copy_nonoverlapping( + pointer.as_ptr().cast(), + self.raw.as_mut_ptr_inline(), + length + ); self.length.set_location::(); Allocator::deallocate( &self.allocator, - ptr.cast(), + pointer.cast(), Layout::from_size_align_unchecked( - capacity * size_of::(), + pointer.len() * size_of::(), align_of::() ) ); @@ -757,20 +776,24 @@ impl SmallVec { return; } // SAFETY: the vector is on the heap - let capacity = unsafe { self.raw.heap.1 }; + let capacity = unsafe { self.raw.heap.len() }; if capacity > min_capacity { let target = length.max(min_capacity); if target <= Self::inline_size() { // SAFETY: on_heap is true, so we're on the heap unsafe { - let (ptr, capacity) = self.raw.heap; - copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length); + let pointer = self.raw.heap; + copy_nonoverlapping( + pointer.as_ptr().cast(), + self.raw.as_mut_ptr_inline(), + length + ); self.length.set_location::(); Allocator::deallocate( &self.allocator, - ptr.cast(), + pointer.cast(), Layout::from_size_align_unchecked( - capacity * size_of::(), + pointer.len() * size_of::(), align_of::() ) ); @@ -1445,7 +1468,7 @@ impl<#[cfg_attr(feature = "may-dangle", may_dangle)] Item, const INLINE: usize, // SAFETY: see above unsafe { let _drop_dealloc = if on_heap { - let capacity = self.raw.heap.1; + let capacity = self.raw.heap.len(); Some(DropDealloc { ptr: NonNull::new_unchecked(ptr as *mut u8), size_bytes: capacity * size_of::(), diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index 7c8d8f8e..1158ea3f 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -20,29 +20,14 @@ use { #[repr(C)] pub union RawSmallVec { pub inline: ManuallyDrop<[MaybeUninit; INLINE]>, - pub heap: (NonNull, usize) + pub heap: NonNull<[MaybeUninit]> } impl RawSmallVec { - pub const INLINE_CAP: usize = if Self::IS_ZST { usize::MAX } else { INLINE }; - const IS_ZST: bool = size_of::() == 0; - #[inline] pub const fn new() -> Self { - Self::inline([const { MaybeUninit::uninit() }; INLINE]) - } - - #[inline] - pub const fn inline(inline: [MaybeUninit; INLINE]) -> Self { - Self { - inline: ManuallyDrop::new(inline) - } - } - - #[inline] - pub const fn heap(ptr: NonNull, capacity: usize) -> Self { Self { - heap: (ptr, capacity) + inline: ManuallyDrop::new([const { MaybeUninit::uninit() }; INLINE]) } } @@ -68,7 +53,7 @@ impl RawSmallVec { #[inline(always)] pub const unsafe fn as_ptr(&self, on_heap: bool) -> *const Item { if on_heap { - unsafe { self.heap.0.as_ptr() } + unsafe { self.heap.as_ptr().cast_const().cast() } } else { self.as_ptr_inline() } @@ -80,7 +65,7 @@ impl RawSmallVec { #[inline(always)] pub const unsafe fn as_mut_ptr(&mut self, on_heap: bool) -> *mut Item { if on_heap { - unsafe { self.heap.0.as_ptr() } + unsafe { self.heap.as_ptr().cast() } } else { self.as_mut_ptr_inline() } @@ -89,12 +74,14 @@ impl RawSmallVec { /// # Safety /// /// `on_heap` must be true if and only if `self.heap` is the active member. - #[inline(always)] + //#[inline(always)] pub const unsafe fn capacity(&self, on_heap: bool) -> usize { if on_heap { - unsafe { self.heap.1 } + unsafe { self.heap.len() } + } else if size_of::() == 0 { + usize::MAX } else { - Self::INLINE_CAP + INLINE } } @@ -111,7 +98,7 @@ impl RawSmallVec { allocator: &Heap ) -> Result<(), SmallVecError> { let (length, was_on_heap) = length.parts(); - debug_assert!(!Self::IS_ZST); + debug_assert!(size_of::() != 0); debug_assert!(new_capacity > 0 && new_capacity >= length); // SAFETY: the tag tells which member is active @@ -138,7 +125,7 @@ impl RawSmallVec { // layout during the previous allocation let old_layout = unsafe { Layout::from_size_align_unchecked( - self.heap.1 * size_of::(), + self.heap.len() * size_of::(), align_of::() ) }; @@ -150,7 +137,7 @@ impl RawSmallVec { // alignment. since it was constructed // with Layout::array unsafe { - (if self.heap.1 < new_capacity { + (if self.heap.len() < new_capacity { ::grow } else { ::shrink @@ -164,7 +151,7 @@ impl RawSmallVec { .ok_or(SmallVecError::AllocationError(new_layout))? .cast() }; - self.heap = (new_ptr, new_capacity); + self.heap = NonNull::slice_from_raw_parts(new_ptr.cast(), new_capacity); Ok(()) } } diff --git a/tests/parts.rs b/tests/parts.rs index 24e50784..303957a4 100644 --- a/tests/parts.rs +++ b/tests/parts.rs @@ -13,13 +13,14 @@ fn into_from_parts_spilled() { assert_eq!(length.len(), 3); // SAFETY: `length` says on heap, so `heap` is the active member. - let (ptr, cap) = unsafe { raw.heap }; - assert!(cap > 1); + let pointer = unsafe { raw.heap }; + // let (ptr, cap) = unsafe { raw.heap }; + assert!(pointer.len() > 1); // SAFETY: parts come straight from `into_parts`. let rebuilt = unsafe { SmallVec::::from_parts(raw, length) }; assert_eq!(rebuilt, [-1, 0, 1]); - assert_eq!(rebuilt.as_ptr(), ptr.as_ptr() as *const i32); + assert_eq!(rebuilt.as_ptr(), pointer.as_ptr() as *const i32); } #[test] From 608e9b1eabc35fed1872e66d5841bf98d9c14251 Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 1 Nov 2026 08:35:29 +0000 Subject: [PATCH 2/3] fix: remove weird comments --- src/rawsmallvec.rs | 2 +- tests/parts.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index 1158ea3f..492633d1 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -74,7 +74,7 @@ impl RawSmallVec { /// # Safety /// /// `on_heap` must be true if and only if `self.heap` is the active member. - //#[inline(always)] + #[inline(always)] pub const unsafe fn capacity(&self, on_heap: bool) -> usize { if on_heap { unsafe { self.heap.len() } diff --git a/tests/parts.rs b/tests/parts.rs index 303957a4..1bc0fe01 100644 --- a/tests/parts.rs +++ b/tests/parts.rs @@ -14,7 +14,6 @@ fn into_from_parts_spilled() { // SAFETY: `length` says on heap, so `heap` is the active member. let pointer = unsafe { raw.heap }; - // let (ptr, cap) = unsafe { raw.heap }; assert!(pointer.len() > 1); // SAFETY: parts come straight from `into_parts`. From 73d830359690998adbe9866ca6c8af39267cdc54 Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 1 Nov 2026 09:15:25 +0000 Subject: [PATCH 3/3] refactor: simplify --- src/conversions.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index 47854923..6d3915e9 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -152,15 +152,13 @@ impl From< as Like>::Type> } // SAFETY: vec.capacity is not `0` (checked above), so the pointer // can not dangle and thus specifically cannot be null. - let pointer = NonNull::slice_from_raw_parts( - unsafe { NonNull::new_unchecked(vec.as_mut_ptr()).cast() }, - vec.capacity() - ); - Self { length: LocatedLength::new(length, true), raw: RawSmallVec { - heap: pointer + heap: NonNull::slice_from_raw_parts( + unsafe { NonNull::new_unchecked(vec.as_mut_ptr()).cast() }, + vec.capacity() + ) }, allocator: Global } @@ -196,11 +194,10 @@ impl From as Like>::Type::from_raw_parts( pointer.cast().as_ptr(), length, - capacity + pointer.len() ) } }