Skip to content
Draft
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
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ fn test_with_inline_cap<const N: usize>(
std_vec = Vec::with_capacity(cap.0);
}
Op::FromVec => {
small_vec = SmallVec::from_vec(small_vec.into());
small_vec = SmallVec::from(Into::<Vec<_>>::into(small_vec));
// No-op on `Vec`
}
Op::FromSlice(data) => {
Expand Down
23 changes: 19 additions & 4 deletions src/allocator/boxed.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
#[cfg(not(feature = "allocator-api2"))]
pub use alloc::boxed::Box;
#[cfg(feature = "allocator-api2")]
pub use allocator_api2::boxed::Box;
use {
super::{
Allocator,
like::Like,
requirement::Requirement
},
core::marker::PhantomData
};

pub struct Box<Item: ?Sized, Heap: Allocator + Requirement>(PhantomData<(Heap, Item)>);

impl<Item: ?Sized, Heap: Allocator + Requirement> Like for Box<Item, Heap> {
#[cfg(not(feature = "allocator-api"))]
type Type = alloc::boxed::Box<Item>;
#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))]
type Type = alloc::boxed::Box<Item, Heap>;
#[cfg(feature = "allocator-api2")]
type Type = allocator_api2::boxed::Box<Item, Heap>;
}
3 changes: 3 additions & 0 deletions src/allocator/like.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub trait Like {
type Type;
}
Comment thread
alejandro-vaz marked this conversation as resolved.
2 changes: 2 additions & 0 deletions src/allocator/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod boxed;
pub mod global;
mod implementations;
pub mod like;
pub mod proxy;
mod requirement;
pub mod vec;

use core::{
Expand Down
6 changes: 6 additions & 0 deletions src/allocator/requirement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))]
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;
30 changes: 20 additions & 10 deletions src/allocator/vec.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
#[cfg(not(feature = "allocator-api2"))]
#[rustfmt::skip]
pub use alloc::{
vec,
vec::Vec
};

pub use alloc::vec;
#[cfg(feature = "allocator-api2")]
#[rustfmt::skip]
pub use allocator_api2::{
vec,
vec::Vec
pub use allocator_api2::vec;
use {
super::{
Allocator,
like::Like,
requirement::Requirement
},
core::marker::PhantomData
};

pub struct Vec<Item, Heap: Allocator + Requirement>(PhantomData<(Heap, Item)>);

impl<Item, Heap: Allocator + Requirement> Like for Vec<Item, Heap> {
#[cfg(not(feature = "allocator-api"))]
type Type = alloc::vec::Vec<Item>;
#[cfg(all(feature = "allocator-api", not(feature = "allocator-api2")))]
type Type = alloc::vec::Vec<Item, Heap>;
#[cfg(feature = "allocator-api2")]
type Type = allocator_api2::vec::Vec<Item, Heap>;
}
79 changes: 65 additions & 14 deletions src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ use {
Allocator,
Box,
Global,
LocatedLength,
RawSmallVec,
SmallVec,
Vec
Vec,
allocator::like::Like
},
core::{
mem::ManuallyDrop,
ptr::copy_nonoverlapping
ptr::{
NonNull,
copy_nonoverlapping
}
}
};

Expand All @@ -17,7 +23,7 @@ impl<Item: Clone, const INLINE: usize> From<&[Item]> for SmallVec<Item, INLINE,
fn from(slice: &[Item]) -> Self {
if slice.len() > Self::inline_size() {
// Standard Rust vectors are already specialized.
Vec::from(slice).into()
<Vec<Item, Global> as Like>::Type::from(slice).into()
} else {
// SAFETY: The precondition is checked in the initial comparison
// above.
Expand Down Expand Up @@ -66,7 +72,7 @@ impl<Item, const INLINE: usize, const M: usize> From<[Item; M]> for SmallVec<Ite
if M > INLINE {
// If M > INLINE, we'd have to heap allocate anyway,
// so delegate for Vec for the allocation.
Self::from(Vec::from(array))
Self::from(<Vec<Item, Global> as Like>::Type::from(array))
} else {
// M <= INLINE
let mut this = Self::new();
Expand Down Expand Up @@ -105,17 +111,62 @@ impl<Item, const INLINE: usize, const M: usize, Heap: Allocator>
}
}

impl<Item, const INLINE: usize> From<Vec<Item>> for SmallVec<Item, INLINE, Global> {
fn from(array: Vec<Item>) -> Self {
Self::from_vec(array)
impl<Item, const INLINE: usize> From<<Vec<Item, Global> as Like>::Type>
for SmallVec<Item, INLINE, Global>
{
fn from(array: <Vec<Item, Global> as Like>::Type) -> Self {
if array.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 = 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 `cap > INLINE`.
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::new_heap(ptr, cap),
allocator: Global
}
}
}
}

impl<Item, const INLINE: usize, Heap: Allocator> From<SmallVec<Item, INLINE, Heap>> for Vec<Item> {
impl<Item, const INLINE: usize, Heap: Allocator> From<SmallVec<Item, INLINE, Heap>>
for <Vec<Item, Global> as Like>::Type
{
fn from(this: SmallVec<Item, INLINE, Heap>) -> Self {
let (length, on_heap) = this.length.parts();
if !on_heap {
let mut vec = Vec::with_capacity(length);
let mut vec = <Vec<Item, Global> as Like>::Type::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
Expand All @@ -137,22 +188,22 @@ impl<Item, const INLINE: usize, Heap: Allocator> From<SmallVec<Item, INLINE, Hea
// - the allocation is not larger than `isize::MAX`
unsafe {
let (ptr, cap) = this.raw.heap;
Vec::from_raw_parts(ptr.as_ptr(), length, cap)
<Vec<Item, Global> as Like>::Type::from_raw_parts(ptr.as_ptr(), length, cap)
}
}
}
}

impl<Item, const INLINE: usize, Heap: Allocator> From<SmallVec<Item, INLINE, Heap>>
for Box<[Item]>
for <Box<[Item], Global> as Like>::Type
{
fn from(this: SmallVec<Item, INLINE, Heap>) -> Self {
Vec::from(this).into_boxed_slice()
<Vec<Item, Global> as Like>::Type::from(this).into_boxed_slice()
}
}

impl<T, const N: usize> From<Box<[T]>> for SmallVec<T, N> {
fn from(boxed: Box<[T]>) -> Self {
impl<T, const N: usize> From<<Box<[T], Global> as Like>::Type> for SmallVec<T, N> {
fn from(boxed: <Box<[T], Global> as Like>::Type) -> Self {
boxed.into_vec().into()
}
}
59 changes: 11 additions & 48 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ use {
Allocator,
boxed::Box,
global::Global,
like::Like,
vec::{
Vec,
vec
Expand Down Expand Up @@ -231,49 +232,8 @@ impl<Item, const INLINE: usize> SmallVec<Item, INLINE, Global> {

#[inline]
#[deprecated(since = "2.0.0-beta.3", note = "use `From::<Vec<T>>::from` instead")]
pub fn from_vec(vec: Vec<Item>) -> 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 > INLINE`.
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::new_heap(ptr, cap),
allocator: Global
}
}
pub fn from_vec(vec: <Vec<Item, Global> as Like>::Type) -> Self {
vec.into()
}

/// Creates a `SmallVec` directly from the raw components of another
Expand Down Expand Up @@ -772,7 +732,8 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {
let (ptr, capacity) = self.raw.heap;
copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length);
self.length.set_location::<false>();
self.allocator.deallocate(
Allocator::deallocate(
&self.allocator,
ptr.cast(),
Layout::from_size_align_unchecked(
capacity * size_of::<Item>(),
Expand Down Expand Up @@ -809,7 +770,8 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {
let (ptr, capacity) = self.raw.heap;
copy_nonoverlapping(ptr.as_ptr(), self.raw.as_mut_ptr_inline(), length);
self.length.set_location::<false>();
self.allocator.deallocate(
Allocator::deallocate(
&self.allocator,
ptr.cast(),
Layout::from_size_align_unchecked(
capacity * size_of::<Item>(),
Expand Down Expand Up @@ -998,13 +960,13 @@ impl<Item, const INLINE: usize, Heap: Allocator> SmallVec<Item, INLINE, Heap> {

#[inline]
#[deprecated(since = "2.0.0", note = "use `Into::<Vec<Item>>::into` instead")]
pub fn into_vec(self) -> Vec<Item> {
pub fn into_vec(self) -> <Vec<Item, Global> as Like>::Type {
self.into()
}

#[inline]
#[deprecated(since = "2.0.0", note = "use `Into::<Box<[Item]>>::into` instead")]
pub fn into_boxed_slice(self) -> Box<[Item]> {
pub fn into_boxed_slice(self) -> <Box<[Item], Global> as Like>::Type {
self.into()
}

Expand Down Expand Up @@ -1485,7 +1447,8 @@ impl<Heap: Allocator> Drop for DropDealloc<'_, Heap> {
fn drop(&mut self) {
unsafe {
if self.size_bytes > 0 {
self.allocator.deallocate(
Allocator::deallocate(
self.allocator,
self.ptr,
Layout::from_size_align_unchecked(self.size_bytes, self.align)
);
Expand Down
7 changes: 3 additions & 4 deletions src/rawsmallvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
let new_ptr = if !was_on_heap {
// get a fresh allocation
// `new_layout` has nonzero size.
let new_ptr = allocator
.allocate(new_layout)
let new_ptr = Allocator::allocate(allocator, new_layout)
.ok_or(SmallVecError::AllocationError(new_layout))?
.cast();
unsafe { copy_nonoverlapping(ptr, new_ptr.as_ptr(), length) };
Expand All @@ -152,9 +151,9 @@ impl<Item, const INLINE: usize> RawSmallVec<Item, INLINE> {
// with Layout::array
unsafe {
(if self.heap.1 < new_capacity {
Heap::grow
<Heap as Allocator>::grow
} else {
Heap::shrink
<Heap as Allocator>::shrink
})(
allocator,
NonNull::new(ptr as *mut u8).unwrap(),
Expand Down
14 changes: 7 additions & 7 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ fn shrink_to_fit_unspill() {

#[test]
fn shrink_after_from_empty_vec() {
let mut v = SmallVec::<u8, 2>::from_vec(Vec::new());
let mut v = SmallVec::<u8, 2>::from(Vec::new());
v.shrink_to_fit();
assert!(!v.spilled())
}
Expand Down Expand Up @@ -835,32 +835,32 @@ fn try_into_array() {
#[test]
fn from_vec() {
let vec = Vec::new();
let small_vec: SmallVec<u8, 3> = SmallVec::from_vec(vec);
let small_vec: SmallVec<u8, 3> = SmallVec::from(vec);
assert_eq!(&*small_vec, &[0u8; 0]);
drop(small_vec);

let vec = Vec::new();
let small_vec: SmallVec<u8, 1> = SmallVec::from_vec(vec);
let small_vec: SmallVec<u8, 1> = SmallVec::from(vec);
assert_eq!(&*small_vec, &[0u8; 0]);
drop(small_vec);

let vec = Vec::from([1]);
let small_vec: SmallVec<u8, 3> = SmallVec::from_vec(vec);
let small_vec: SmallVec<u8, 3> = SmallVec::from(vec);
assert_eq!(&*small_vec, &[1]);
drop(small_vec);

let vec = Vec::from([1, 2, 3]);
let small_vec: SmallVec<u8, 3> = SmallVec::from_vec(vec);
let small_vec: SmallVec<u8, 3> = SmallVec::from(vec);
assert_eq!(&*small_vec, &[1, 2, 3]);
drop(small_vec);

let vec = Vec::from([1, 2, 3, 4, 5]);
let small_vec: SmallVec<u8, 3> = SmallVec::from_vec(vec);
let small_vec: SmallVec<u8, 3> = SmallVec::from(vec);
assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]);
drop(small_vec);

let vec = Vec::from([1, 2, 3, 4, 5]);
let small_vec: SmallVec<u8, 1> = SmallVec::from_vec(vec);
let small_vec: SmallVec<u8, 1> = SmallVec::from(vec);
assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]);
drop(small_vec);
}
Expand Down
Loading