Add an "allocator_api" feature - #387
Conversation
|
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. |
|
Sorry for the delay in reviewing this. Just wanted to let you know this is still on my to-do list. |
|
Hey, I would love to see this merged, and additionally make it possible to use on |
6de72bd to
737b6cf
Compare
| } | ||
|
|
||
| #[cfg(feature = "allocator_api")] | ||
| macro_rules! alloc_param { |
There was a problem hiding this comment.
this must be removed
| /// 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 { |
There was a problem hiding this comment.
there has to be a better way for doing this that doesn't require a macro
|
|
||
| impl<T, const N: usize> RawSmallVec<T, N> { | ||
| #[inline(always)] | ||
| const fn is_zst<T>() -> bool { |
There was a problem hiding this comment.
this is unnecessary and bloats the codebase
this can be done inline whenever it is needed
| /// The methods correspond to the `allocator_api` Rust nightly feature. | ||
| #[derive(Clone, Copy)] | ||
| #[cfg(not(feature = "allocator_api"))] | ||
| struct A; |
There was a problem hiding this comment.
this should be done instead with allocator-api2
| } | ||
| } | ||
|
|
||
| pub struct RawSmallVec<T, const N: usize, #[cfg(feature = "allocator_api")] A: Allocator = Global> { |
There was a problem hiding this comment.
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
| } | ||
| } | ||
|
|
||
| impl<T, const N: usize, #[cfg(feature = "allocator_api")] A: Allocator> alloc_param!(RawSmallVec<T, N, A>) { |
|
|
||
| /// 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, | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
this is good, but do we really need an specialized function for this??
we can simply use the From trait
| 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) | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
too many implementations for the same type that we could merge into one
| } | ||
|
|
||
| impl<T, const N: usize> PartialOrd for SmallVec<T, N> | ||
| impl<T, const N: usize, const M: usize, |
There was a problem hiding this comment.
lacks many tests related to the allocators
I like this idea |
This should cross out the last item in the wishlist (#183).
If the feature is enabled, the
SmallVectype 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-standardfrom_slice_inthat mimics the
to_vec_infunction in Rust.