Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,16 @@ impl<Item, const INLINE: usize> From<<Vec<Item, Global> 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()) };

Self {
length: LocatedLength::new(length, true),
raw: RawSmallVec::heap(ptr, cap),
raw: RawSmallVec {
heap: NonNull::slice_from_raw_parts(
unsafe { NonNull::new_unchecked(vec.as_mut_ptr()).cast() },
vec.capacity()
)
},
allocator: Global
}
}
Expand Down Expand Up @@ -191,8 +193,12 @@ impl<Item, const INLINE: usize, Heap: Allocator> From<SmallVec<Item, INLINE, Hea
// - the first `length` entries are proper `Item`-values
// - the allocation is not larger than `isize::MAX`
unsafe {
let (ptr, cap) = this.raw.heap;
<Vec<Item, Global> as Like>::Type::from_raw_parts(ptr.as_ptr(), length, cap)
let pointer = this.raw.heap;
<Vec<Item, Global> as Like>::Type::from_raw_parts(
pointer.cast().as_ptr(),
length,
pointer.len()
)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/iterators/intoiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ impl<Item, const INLINE: usize, Heap: Allocator> Drop for IntoIter<Item, INLINE,
let begin = self.begin;
let ptr = self.raw.as_mut_ptr(on_heap);
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::<Item>(),
Expand Down
59 changes: 41 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,9 @@ impl<Item, const INLINE: usize> SmallVec<Item, INLINE, Global> {
// 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
}
}
Expand All @@ -240,7 +242,9 @@ impl<Item, const INLINE: usize> SmallVec<Item, INLINE, Global> {
// 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.
Expand Down Expand Up @@ -288,7 +292,9 @@ impl<Item, const INLINE: usize> SmallVec<Item, INLINE, Global> {
debug_assert!(length <= INLINE);
Self {
length: LocatedLength::new(length, false),
raw: RawSmallVec::inline(buf),
raw: RawSmallVec {
inline: ManuallyDrop::new(buf)
},
allocator: Global
}
}
Expand Down Expand Up @@ -365,7 +371,11 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {

#[inline]
pub const fn inline_size() -> usize {
RawSmallVec::<Item, INLINE>::INLINE_CAP
if size_of::<Item>() == 0 {
usize::MAX
} else {
INLINE
}
}

#[inline]
Expand Down Expand Up @@ -660,15 +670,20 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {
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::<Item>(),
ptr: pointer.cast(),
size_bytes: pointer.len() * size_of::<Item>(),
align: align_of::<Item>(),
allocator: &self.allocator
});
Expand Down Expand Up @@ -738,14 +753,18 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {
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::<false>();
Allocator::deallocate(
&self.allocator,
ptr.cast(),
pointer.cast(),
Layout::from_size_align_unchecked(
capacity * size_of::<Item>(),
pointer.len() * size_of::<Item>(),
align_of::<Item>()
)
);
Expand All @@ -770,20 +789,24 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {
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::<false>();
Allocator::deallocate(
&self.allocator,
ptr.cast(),
pointer.cast(),
Layout::from_size_align_unchecked(
capacity * size_of::<Item>(),
pointer.len() * size_of::<Item>(),
align_of::<Item>()
)
);
Expand Down Expand Up @@ -1472,7 +1495,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::<Item>(),
Expand Down
37 changes: 12 additions & 25 deletions src/rawsmallvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,14 @@ use {
#[repr(C)]
pub union RawSmallVec<Item, const INLINE: usize> {
pub inline: ManuallyDrop<[MaybeUninit<Item>; INLINE]>,
pub heap: (NonNull<Item>, usize)
pub heap: NonNull<[MaybeUninit<Item>]>
Comment thread
alejandro-vaz marked this conversation as resolved.
Resolved
}

impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
pub const INLINE_CAP: usize = if Self::IS_ZST { usize::MAX } else { INLINE };
const IS_ZST: bool = size_of::<Item>() == 0;

#[inline]
pub const fn new() -> Self {
Self::inline([const { MaybeUninit::uninit() }; INLINE])
}

#[inline]
pub const fn inline(inline: [MaybeUninit<Item>; INLINE]) -> Self {
Self {
inline: ManuallyDrop::new(inline)
}
}

#[inline]
pub const fn heap(ptr: NonNull<Item>, capacity: usize) -> Self {
Self {
heap: (ptr, capacity)
inline: ManuallyDrop::new([const { MaybeUninit::uninit() }; INLINE])
}
}

Expand All @@ -68,7 +53,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
#[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()
}
Expand All @@ -80,7 +65,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
#[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()
}
Expand All @@ -92,9 +77,11 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
#[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::<Item>() == 0 {
usize::MAX
} else {
Self::INLINE_CAP
INLINE
}
}

Expand All @@ -111,7 +98,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
allocator: &Heap
) -> Result<(), SmallVecError> {
let (length, was_on_heap) = length.parts();
debug_assert!(!Self::IS_ZST);
debug_assert!(size_of::<Item>() != 0);
debug_assert!(new_capacity > 0 && new_capacity >= length);

// SAFETY: the tag tells which member is active
Expand All @@ -138,7 +125,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
// layout during the previous allocation
let old_layout = unsafe {
Layout::from_size_align_unchecked(
self.heap.1 * size_of::<Item>(),
self.heap.len() * size_of::<Item>(),
align_of::<Item>()
)
};
Expand All @@ -150,7 +137,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
// alignment. since it was constructed
// with Layout::array
unsafe {
(if self.heap.1 < new_capacity {
(if self.heap.len() < new_capacity {
<Heap as Allocator>::grow
} else {
<Heap as Allocator>::shrink
Expand All @@ -164,7 +151,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
.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(())
}
}
Expand Down
6 changes: 3 additions & 3 deletions tests/parts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ 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 };
assert!(pointer.len() > 1);
Comment thread
alejandro-vaz marked this conversation as resolved.
Resolved

// SAFETY: parts come straight from `into_parts`.
let rebuilt = unsafe { SmallVec::<i32, 1>::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]
Expand Down
Loading