Skip to content

Add an "allocator_api" feature - #387

Open
TDecking wants to merge 6 commits into
servo:v2from
TDecking:allocator-api
Open

Add an "allocator_api" feature#387
TDecking wants to merge 6 commits into
servo:v2from
TDecking:allocator-api

Conversation

@TDecking

Copy link
Copy Markdown
Contributor

This should cross out the last item in the wishlist (#183).

If the feature is enabled, the SmallVec type gets another type parameter for the allocator and makes use of the nightly Rust "allocator_api" feature. Additional allocator-based functions also become available, including the non-standard from_slice_in
that mimics the to_vec_in function in Rust.

@TDecking

Copy link
Copy Markdown
Contributor Author

Was there ever a vision articulated for this feature? While I believe that the feature is supposed to behave as it does is in this PR, it also mandates a couple of compromises.

@mbrubeck

Copy link
Copy Markdown
Collaborator

Sorry for the delay in reviewing this. Just wanted to let you know this is still on my to-do list.

@dflemstr

dflemstr commented Nov 7, 2025

Copy link
Copy Markdown

Hey, I would love to see this merged, and additionally make it possible to use on stable by for example using this crate as a poly-fill if we're not on nightly: https://crates.io/crates/allocator-api2

Comment thread src/lib.rs
}

#[cfg(feature = "allocator_api")]
macro_rules! alloc_param {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this must be removed

Comment thread src/lib.rs
/// A macro that conditionally discards the last type parameter of its input depending on the "allocator_api" feature.
/// This is used to control the presence of the allocator parameter.
#[cfg(not(feature = "allocator_api"))]
macro_rules! alloc_param {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there has to be a better way for doing this that doesn't require a macro

Comment thread src/lib.rs

impl<T, const N: usize> RawSmallVec<T, N> {
#[inline(always)]
const fn is_zst<T>() -> bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is unnecessary and bloats the codebase

this can be done inline whenever it is needed

Comment thread src/lib.rs
/// The methods correspond to the `allocator_api` Rust nightly feature.
#[derive(Clone, Copy)]
#[cfg(not(feature = "allocator_api"))]
struct A;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done instead with allocator-api2

Comment thread src/lib.rs
}
}

pub struct RawSmallVec<T, const N: usize, #[cfg(feature = "allocator_api")] A: Allocator = Global> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, yeah... but I wonder if we can greatly simplify all this with allocator-api2

at what cost are we adding the allocator type param??

I'm not sure this is worth it done this way

Comment thread src/lib.rs
}
}

impl<T, const N: usize, #[cfg(feature = "allocator_api")] A: Allocator> alloc_param!(RawSmallVec<T, N, A>) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Comment thread src/lib.rs
Comment on lines +1426 to 1495

/// Creates a `SmallVec` directly from the raw components of another `SmallVec`.
///
/// # Safety
///
/// This is highly unsafe, due to the number of invariants that aren’t checked:
///
/// - `ptr` needs to have been previously allocated via `SmallVec` from its spilled storage (at least, it’s highly likely to be incorrect if it wasn’t).
/// - `ptr`’s `A::Item` type needs to be the same size and alignment that it was allocated with
/// - `length` needs to be less than or equal to `capacity`.
/// - `capacity` needs to be the capacity that the pointer was allocated with.
///
/// Violating these may cause problems like corrupting the allocator’s internal data structures.
///
/// Additionally, `capacity` must be greater than the amount of inline storage `A` has; that is, the new `SmallVec` must need to spill over into heap allocated storage. This condition is asserted against.
///
/// The ownership of `ptr` is effectively transferred to the `SmallVec` which may then deallocate, reallocate or change the contents of memory pointed to by the pointer at will. Ensure that nothing else uses the pointer after calling this function.
///
/// # Examples
///
/// ```
/// use smallvec::{SmallVec, smallvec};
///
/// let mut v: SmallVec<_, 1> = smallvec![1, 2, 3];
///
/// // Pull out the important parts of `v`.
/// let p = v.as_mut_ptr();
/// let len = v.len();
/// let cap = v.capacity();
/// let spilled = v.spilled();
///
/// unsafe {
/// // Forget all about `v`. The heap allocation that stored the
/// // three values won't be deallocated.
/// std::mem::forget(v);
///
/// // Overwrite memory with [4, 5, 6].
/// //
/// // This is only safe if `spilled` is true! Otherwise, we are
/// // writing into the old `SmallVec`'s inline storage on the
/// // stack.
/// assert!(spilled);
/// for i in 0..len {
/// std::ptr::write(p.add(i), 4 + i);
/// }
///
/// // Put everything back together into a SmallVec with a different
/// // amount of inline storage, but which is still less than `cap`.
/// let rebuilt = SmallVec::<_, 2>::from_raw_parts(p, len, cap);
/// assert_eq!(&*rebuilt, &[4, 5, 6]);
/// }
/// ```
#[inline]
pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> SmallVec<T, N> {
assert!(!Self::is_zst());

// SAFETY: We require caller to provide same ptr as we alloc
// and we never alloc null pointer.
let ptr = unsafe {
debug_assert!(!ptr.is_null(), "Called `from_raw_parts` with null pointer.");
NonNull::new_unchecked(ptr)
};

SmallVec {
len: TaggedLen::new(length, true, is_zst::<T>()),
raw: RawSmallVec::new_heap(ptr, capacity),
_marker: PhantomData,
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is good, but do we really need an specialized function for this??

we can simply use the From trait

Comment thread src/lib.rs
Comment on lines +2351 to +2372
impl<T: Clone, const N: usize, #[cfg(feature = "allocator_api")] A: Allocator> alloc_param!(SmallVec<T, N, A>) {
/// Creates a [`SmallVec`] value from the slice `slice` with the specified allocator.
#[cfg(feature = "allocator_api")]
pub fn from_slice_in(slice: &[T], alloc: A) -> Self {
if slice.len() > Self::inline_size() {
// Standard Rust vectors are already specialized.
Self::from_vec(slice.to_vec_in(alloc))
} else {
// SAFETY: The precondition is checked in the initial comparison above.
unsafe {
#[cfg(feature = "specialization")]
{
<Self as spec_traits::SpecFromSlice<T, alloc_param!(A)>>::spec_from(slice, alloc)
}

#[cfg(not(feature = "specialization"))]
{
Self::from_slice_fallback(slice, alloc)
}
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many implementations for the same type that we could merge into one

Comment thread src/lib.rs
}

impl<T, const N: usize> PartialOrd for SmallVec<T, N>
impl<T, const N: usize, const M: usize,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is good

Comment thread src/tests.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lacks many tests related to the allocators

@alejandro-vaz

alejandro-vaz commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

@dflemstr Hey, I would love to see this merged, and additionally make it possible to use on stable by for example using this crate as a poly-fill if we're not on nightly: https://crates.io/crates/allocator-api2

I like this idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants