From 28b2a51537a8798be78db4c6a2d492518c5d933a Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 20 Sep 2026 14:45:59 +0200 Subject: [PATCH 1/4] refactor: simpler raw --- src/lib.rs | 76 +++++++++++++++++++++++++++------------------- src/rawsmallvec.rs | 64 +++++++++++++++----------------------- 2 files changed, 69 insertions(+), 71 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a33fb34b..58a60bbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,7 +154,8 @@ where R: core::ops::RangeBounds { #[repr(C)] pub struct SmallVec { len: TaggedLen, - raw: RawSmallVec + raw: RawSmallVec, + allocator: A } unsafe impl Send for SmallVec {} @@ -264,7 +265,8 @@ pub struct IntoIter { // `end` decides whether the data lives on the heap or not // // The members from begin..end are initialized - raw: RawSmallVec, + raw: RawSmallVec, + allocator: A, begin: usize, end: TaggedLen } @@ -383,7 +385,8 @@ impl SmallVec { // SAFETY: all the members in 0..S are initialized Self { len: TaggedLen::new(S, false), - raw: RawSmallVec::new_inline(buf, Global) + raw: RawSmallVec::new_inline(buf), + allocator: Global } } @@ -393,7 +396,8 @@ impl SmallVec { // SAFETY: all the members in 0..len are initialized let mut vec = Self { len: TaggedLen::new(len, false), - raw: RawSmallVec::new_inline(MaybeUninit::new(buf), Global) + raw: RawSmallVec::new_inline(MaybeUninit::new(buf)), + allocator: Global }; // Deallocate the remaining elements so no memory is leaked. unsafe { @@ -439,7 +443,8 @@ impl SmallVec { debug_assert!(len <= N); Self { len: TaggedLen::new(len, false), - raw: RawSmallVec::new_inline(buf, Global) + raw: RawSmallVec::new_inline(buf), + allocator: Global } } @@ -464,7 +469,8 @@ impl SmallVec { unsafe { vec.set_len(0) }; Self { len: TaggedLen::new(len, false), - raw: RawSmallVec::new(Global) + raw: RawSmallVec::new(), + allocator: Global } } else { let mut vec = ManuallyDrop::new(vec); @@ -476,7 +482,8 @@ impl SmallVec { Self { len: TaggedLen::new(len, true), - raw: RawSmallVec::new_heap(ptr, cap, Global) + raw: RawSmallVec::new_heap(ptr, cap), + allocator: Global } } } @@ -568,7 +575,8 @@ impl SmallVec { SmallVec { len: TaggedLen::new(length, true), - raw: RawSmallVec::new_heap(ptr, capacity, Global) + raw: RawSmallVec::new_heap(ptr, capacity), + allocator: Global } } } @@ -635,7 +643,7 @@ impl SmallVec { #[inline] pub const fn inline_size() -> usize { - RawSmallVec::::INLINE_CAP + RawSmallVec::::INLINE_CAP } #[inline] @@ -896,7 +904,7 @@ impl SmallVec { if new_capacity > Self::inline_size() { // SAFETY: we checked all the preconditions - let result = unsafe { self.raw.try_grow_raw(self.len, new_capacity) }; + let result = unsafe { self.raw.try_grow_raw(self.len, new_capacity, &self.allocator) }; if result.is_ok() { // SAFETY: the allocation succeeded, so self.raw.heap is now @@ -909,7 +917,7 @@ impl SmallVec { if on_heap { unsafe { // SAFETY: heap member is active - let (ptr, old_cap) = self.raw.inner.heap; + let (ptr, old_cap) = self.raw.heap; // inline member is now active // SAFETY: len <= new_capacity <= Self::inline_size() @@ -919,7 +927,7 @@ impl SmallVec { ptr: ptr.cast(), size_bytes: old_cap * size_of::(), align: align_of::(), - alloc: &self.raw.alloc + alloc: &self.allocator }); self.set_inline(); } @@ -991,10 +999,10 @@ impl SmallVec { if len <= Self::inline_size() { // SAFETY: on_heap is true, so we're on the heap unsafe { - let (ptr, capacity) = self.raw.inner.heap; + let (ptr, capacity) = self.raw.heap; copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), len); self.set_inline(); - self.raw.alloc.deallocate( + self.allocator.deallocate( ptr.cast(), Layout::from_size_align_unchecked(capacity * size_of::(), align_of::()) ); @@ -1003,7 +1011,7 @@ impl SmallVec { // SAFETY: len > Self::inline_size() >= 0 // so new capacity is non zero, it is equal to the length // T can't be a ZST because SmallVec is never spilled. - unsafe { infallible(self.raw.try_grow_raw(self.len, len)) }; + unsafe { infallible(self.raw.try_grow_raw(self.len, len, &self.allocator)) }; } } @@ -1014,16 +1022,16 @@ impl SmallVec { return; } // SAFETY: the vector is on the heap - let capacity = unsafe { self.raw.inner.heap.1 }; + let capacity = unsafe { self.raw.heap.1 }; if capacity > min_capacity { let target = core::cmp::max(len, min_capacity); if target <= Self::inline_size() { // SAFETY: on_heap is true, so we're on the heap unsafe { - let (ptr, capacity) = self.raw.inner.heap; + let (ptr, capacity) = self.raw.heap; copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), len); self.set_inline(); - self.raw.alloc.deallocate( + self.allocator.deallocate( ptr.cast(), Layout::from_size_align_unchecked( capacity * size_of::(), @@ -1035,7 +1043,7 @@ impl SmallVec { // SAFETY: len > Self::inline_size() >= 0 // so new capacity is non zero, it is equal to the length // T can't be a ZST because SmallVec is never spilled. - unsafe { infallible(self.raw.try_grow_raw(self.len, target)) }; + unsafe { infallible(self.raw.try_grow_raw(self.len, target, &self.allocator)) }; } } } @@ -1229,7 +1237,7 @@ impl SmallVec { // - the first `len` entries are proper `T`-values // - the allocation is not larger than `isize::MAX` unsafe { - let (ptr, cap) = this.raw.inner.heap; + let (ptr, cap) = this.raw.heap; Vec::from_raw_parts(ptr.as_ptr(), len, cap) } } @@ -1580,7 +1588,8 @@ impl SmallVec { pub const fn new_in(alloc: A) -> SmallVec { Self { len: TaggedLen::new(0, false), - raw: RawSmallVec::new(alloc) + raw: RawSmallVec::new(), + allocator: alloc } } @@ -1588,7 +1597,7 @@ impl SmallVec { let mut this = Self::new_in(alloc); if capacity > Self::inline_size() && !Self::IS_ZST { // SAFETY: we checked all the preconditions - unsafe { this.raw.try_grow_raw(TaggedLen::new(0, false), capacity) }?; + unsafe { this.raw.try_grow_raw(TaggedLen::new(0, false), capacity, &this.allocator) }?; // SAFETY: the allocation succeeded, so self.raw.heap is now active unsafe { this.set_on_heap() }; @@ -1674,7 +1683,7 @@ impl SmallVec { assert!(at <= len); let other_len = len - at; - let mut other = Self::with_capacity_in(other_len, self.raw.alloc.clone()); + let mut other = Self::with_capacity_in(other_len, self.allocator.clone()); // Unsafely `set_len` and copy items to `other`. unsafe { @@ -1730,12 +1739,12 @@ unsafe impl<#[may_dangle] T, const N: usize, A: Allocator> Drop for SmallVec(), align: align_of::(), - alloc: &self.raw.alloc + alloc: &self.allocator }) } else { None @@ -1754,12 +1763,12 @@ impl Drop for SmallVec { // SAFETY: see above unsafe { let _drop_dealloc = if on_heap { - let capacity = self.raw.inner.heap.1; + let capacity = self.raw.heap.1; Some(DropDealloc { ptr: NonNull::new_unchecked(ptr as *mut u8), size_bytes: capacity * size_of::(), align: align_of::(), - alloc: &self.raw.alloc + alloc: &self.allocator }) } else { None @@ -1777,12 +1786,12 @@ impl Drop for IntoIter { let begin = self.begin; let ptr = self.raw.as_mut_ptr(on_heap); let _drop_dealloc = if on_heap { - let capacity = self.raw.inner.heap.1; + let capacity = self.raw.heap.1; Some(DropDealloc { ptr: NonNull::new_unchecked(ptr as *mut u8), size_bytes: capacity * size_of::(), align: align_of::(), - alloc: &self.raw.alloc + alloc: &self.allocator }) } else { None @@ -1990,7 +1999,8 @@ impl Clone for SmallVec fn clone(&self) -> SmallVec { let mut vec = SmallVec { len: TaggedLen::new(0, false), - raw: RawSmallVec::new(self.raw.alloc.clone()) + raw: RawSmallVec::new(), + allocator: self.allocator.clone() }; vec.extend(self); @@ -2017,7 +2027,8 @@ impl Clone for IntoIter fn clone(&self) -> IntoIter { let mut vec = SmallVec { len: TaggedLen::new(0, false), - raw: RawSmallVec::new(self.raw.alloc.clone()) + raw: RawSmallVec::new(), + allocator: self.allocator.clone() }; vec.extend(self.as_slice()); @@ -2083,7 +2094,8 @@ impl IntoIterator for SmallVec { // the elements let this = ManuallyDrop::new(self); IntoIter { - raw: (&this.raw as *const RawSmallVec).read(), + raw: (&raw const this.raw).read(), + allocator: (&raw const this.allocator).read(), begin: 0, end: this.len } diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index 9a359af7..b79144f6 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -18,47 +18,31 @@ use { }; #[repr(C)] -pub(crate) union RawSmallVecInner { - pub(crate) inline: ManuallyDrop>, - pub(crate) heap: (NonNull, usize) +pub union RawSmallVec { + pub inline: ManuallyDrop>, + pub heap: (NonNull, usize) } -/// Either a stack array with `length <= N` or a heap array -/// whose pointer and capacity are stored here. -/// -/// We store a `NonNull` instead of a `*mut T` so that type is covariant -/// with respect to `T`, and since the heap pointer is never null. -pub struct RawSmallVec { - pub(crate) inner: RawSmallVecInner, - pub(crate) alloc: A -} - -impl RawSmallVec { +impl RawSmallVec { pub const INLINE_CAP: usize = if Self::IS_ZST { usize::MAX } else { N }; const IS_ZST: bool = size_of::() == 0; #[inline] - pub const fn new(alloc: A) -> Self { - Self::new_inline(MaybeUninit::uninit(), alloc) + pub const fn new() -> Self { + Self::new_inline(MaybeUninit::uninit()) } #[inline] - pub const fn new_inline(inline: MaybeUninit<[T; N]>, alloc: A) -> Self { + pub const fn new_inline(inline: MaybeUninit<[T; N]>) -> Self { Self { - inner: RawSmallVecInner { - inline: ManuallyDrop::new(inline) - }, - alloc + inline: ManuallyDrop::new(inline) } } #[inline] - pub const fn new_heap(ptr: NonNull, capacity: usize, alloc: A) -> Self { + pub const fn new_heap(ptr: NonNull, capacity: usize) -> Self { Self { - inner: RawSmallVecInner { - heap: (ptr, capacity) - }, - alloc + heap: (ptr, capacity) } } @@ -68,14 +52,14 @@ impl RawSmallVec { // a reference to it. reading it would be UB potentially, but // for that downstream unsafe is required #[allow(unused_unsafe, reason = "Unsafe in MSRV")] - (unsafe { &raw const self.inner.inline }).cast() + (unsafe { &raw const self.inline }).cast() } #[inline] pub const fn as_mut_ptr_inline(&mut self) -> *mut T { // SAFETY: same as above #[allow(unused_unsafe, reason = "Unsafe in MSRV")] - (unsafe { &raw mut self.inner.inline }).cast() + (unsafe { &raw mut self.inline }).cast() } /// # Safety @@ -84,7 +68,7 @@ impl RawSmallVec { #[inline(always)] pub const unsafe fn as_ptr(&self, on_heap: bool) -> *const T { if on_heap { - unsafe { self.inner.heap.0.as_ptr() } + unsafe { self.heap.0.as_ptr() } } else { self.as_ptr_inline() } @@ -96,7 +80,7 @@ impl RawSmallVec { #[inline(always)] pub const unsafe fn as_mut_ptr(&mut self, on_heap: bool) -> *mut T { if on_heap { - unsafe { self.inner.heap.0.as_ptr() } + unsafe { self.heap.0.as_ptr() } } else { self.as_mut_ptr_inline() } @@ -108,7 +92,7 @@ impl RawSmallVec { #[inline(always)] pub const unsafe fn capacity(&self, on_heap: bool) -> usize { if on_heap { - unsafe { self.inner.heap.1 } + unsafe { self.heap.1 } } else { Self::INLINE_CAP } @@ -118,10 +102,13 @@ impl RawSmallVec { /// /// `new_capacity` must be non zero, and greater or equal to the length. /// T must not be a ZST. - pub unsafe fn try_grow_raw( + /// + /// the allocator must be the same one the data was allocated with + pub unsafe fn try_grow_raw( &mut self, len: TaggedLen, - new_capacity: usize + new_capacity: usize, + allocator: &A ) -> Result<(), CollectionAllocErr> { let (len, was_on_heap) = len.parts(); debug_assert!(!Self::IS_ZST); @@ -139,8 +126,7 @@ impl RawSmallVec { let new_ptr = if !was_on_heap { // get a fresh allocation // `new_layout` has nonzero size. - let new_ptr = self - .alloc + let new_ptr = allocator .allocate(new_layout) .map_err(|_| CollectionAllocErr::AllocErr { layout: new_layout @@ -155,7 +141,7 @@ impl RawSmallVec { // layout during the previous allocation let old_layout = unsafe { Layout::from_size_align_unchecked( - self.inner.heap.1 * size_of::(), + self.heap.1 * size_of::(), align_of::() ) }; @@ -167,12 +153,12 @@ impl RawSmallVec { // alignment. since it was constructed // with Layout::array unsafe { - (if self.inner.heap.1 < new_capacity { + (if self.heap.1 < new_capacity { A::grow } else { A::shrink })( - &self.alloc, + allocator, NonNull::new(ptr as *mut u8).unwrap(), old_layout, new_layout @@ -183,7 +169,7 @@ impl RawSmallVec { })? .cast() }; - self.inner.heap = (new_ptr, new_capacity); + self.heap = (new_ptr, new_capacity); Ok(()) } } From dbd5b43c9362fd6d7e66acb13ffdabdbed85e500 Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 20 Sep 2026 14:49:03 +0200 Subject: [PATCH 2/4] style: formatting --- src/lib.rs | 10 ++++++++-- src/rawsmallvec.rs | 7 ++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 58a60bbe..353e347c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -904,7 +904,10 @@ impl SmallVec { if new_capacity > Self::inline_size() { // SAFETY: we checked all the preconditions - let result = unsafe { self.raw.try_grow_raw(self.len, new_capacity, &self.allocator) }; + let result = unsafe { + self.raw + .try_grow_raw(self.len, new_capacity, &self.allocator) + }; if result.is_ok() { // SAFETY: the allocation succeeded, so self.raw.heap is now @@ -1597,7 +1600,10 @@ impl SmallVec { let mut this = Self::new_in(alloc); if capacity > Self::inline_size() && !Self::IS_ZST { // SAFETY: we checked all the preconditions - unsafe { this.raw.try_grow_raw(TaggedLen::new(0, false), capacity, &this.allocator) }?; + unsafe { + this.raw + .try_grow_raw(TaggedLen::new(0, false), capacity, &this.allocator) + }?; // SAFETY: the allocation succeeded, so self.raw.heap is now active unsafe { this.set_on_heap() }; diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index b79144f6..f756c058 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -102,7 +102,7 @@ impl RawSmallVec { /// /// `new_capacity` must be non zero, and greater or equal to the length. /// T must not be a ZST. - /// + /// /// the allocator must be the same one the data was allocated with pub unsafe fn try_grow_raw( &mut self, @@ -140,10 +140,7 @@ impl RawSmallVec { // this can't overflow since we already constructed an equivalent // layout during the previous allocation let old_layout = unsafe { - Layout::from_size_align_unchecked( - self.heap.1 * size_of::(), - align_of::() - ) + Layout::from_size_align_unchecked(self.heap.1 * size_of::(), align_of::()) }; // SAFETY: ptr was allocated with this allocator From e6bd4c799807660981ebadcc2c6408d5c497a9b3 Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 20 Sep 2026 14:51:46 +0200 Subject: [PATCH 3/4] fix: add default for rawsmallvec --- src/rawsmallvec.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index f756c058..cb380d79 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -170,3 +170,9 @@ impl RawSmallVec { Ok(()) } } + +impl Default for RawSmallVec { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file From c514df2b2bc57ac3c3d941d53adb606bc479a4a0 Mon Sep 17 00:00:00 2001 From: Alejandro Vaz Date: Sun, 20 Sep 2026 14:52:35 +0200 Subject: [PATCH 4/4] style: formatting --- src/rawsmallvec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rawsmallvec.rs b/src/rawsmallvec.rs index cb380d79..b16b13b7 100644 --- a/src/rawsmallvec.rs +++ b/src/rawsmallvec.rs @@ -175,4 +175,4 @@ impl Default for RawSmallVec { fn default() -> Self { Self::new() } -} \ No newline at end of file +}