feat(allocator): add Vec::into_boxed_slice (#6195)

Note that this PR does not implement the inverse operation (`Box::to_vec` for `[T]`).
This commit is contained in:
DonIsaac 2024-10-12 04:29:43 +00:00
parent 7240ee23fc
commit 5ee1ef3d92
2 changed files with 70 additions and 2 deletions

View file

@ -76,6 +76,25 @@ impl<'alloc, T> Box<'alloc, T> {
}
}
impl<'alloc, T: ?Sized> Box<'alloc, T> {
/// Create a [`Box`] from a raw pointer to a value.
///
/// The [`Box`] takes ownership of the data pointed to by `ptr`.
///
/// # SAFETY
/// Data pointed to by `ptr` must live as long as `'alloc`.
/// This requirement is met if the pointer was obtained from other data in the arena.
///
/// Data pointed to by `ptr` must *only* be used for this `Box`. i.e. it must be unique,
/// with no other aliases. You must not, for example, create 2 `Box`es from the same pointer.
///
/// `ptr` must have been created from a `*mut T` or `&mut T` (not a `*const T` / `&T`).
#[inline]
pub(crate) const unsafe fn from_non_null(ptr: NonNull<T>) -> Self {
Self(ptr, PhantomData)
}
}
impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> {
type Target = T;

View file

@ -7,6 +7,7 @@ use std::{
fmt::Debug,
hash::{Hash, Hasher},
ops,
ptr::NonNull,
};
use allocator_api2::vec;
@ -14,7 +15,7 @@ use bumpalo::Bump;
#[cfg(any(feature = "serialize", test))]
use serde::{ser::SerializeSeq, Serialize, Serializer};
use crate::Allocator;
use crate::{Allocator, Box};
/// Bumpalo Vec
#[derive(Debug, PartialEq, Eq)]
@ -101,6 +102,39 @@ impl<'alloc, T> Vec<'alloc, T> {
vec.extend(iter);
Self(vec)
}
/// Converts the vector into [`Box<[T]>`][owned slice].
///
/// Any excess capacity the vector has will not be included in the slice.
/// The excess memory will be leaked in the arena (i.e. not reused by another allocation).
///
/// # Examples
///
/// ```
/// use oxc_allocator::{Allocator, Vec};
///
/// let allocator = Allocator::default();
/// let mut v = Vec::with_capacity_in(10, &allocator);
/// v.extend([1, 2, 3]);
/// let b = v.into_boxed_slice();
///
/// assert_eq!(&*b, &[1, 2, 3]);
/// assert_eq!(b.len(), 3);
/// ```
///
/// [owned slice]: Box
pub fn into_boxed_slice(self) -> Box<'alloc, [T]> {
let slice = self.0.leak();
let ptr = NonNull::from(slice);
// SAFETY: `ptr` points to a valid slice `[T]`.
// `allocator_api2::vec::Vec::leak` consumes the inner `Vec` without dropping it.
// Lifetime of returned `Box<'alloc, [T]>` is same as lifetime of consumed `Vec<'alloc, T>`,
// so data in the `Box` must be valid for its lifetime.
// `Vec` uniquely owned the data, and we have consumed the `Vec`, so the new `Box` has
// unique ownership of the data (no aliasing).
// `ptr` was created from a `&mut [T]`.
unsafe { Box::from_non_null(ptr) }
}
}
impl<'alloc, T> ops::Deref for Vec<'alloc, T> {
@ -178,7 +212,7 @@ impl<'alloc, T: Hash> Hash for Vec<'alloc, T> {
#[cfg(test)]
mod test {
use super::Vec;
use crate::Allocator;
use crate::{Allocator, Box};
#[test]
fn vec_with_capacity() {
@ -211,4 +245,19 @@ mod test {
program
}
}
#[test]
fn vec_to_boxed_slice() {
let allocator = Allocator::default();
let mut v = Vec::with_capacity_in(10, &allocator);
v.extend([1, 2, 3]);
let b = v.into_boxed_slice();
// Check return value is an `oxc_allocator::Box`, not an `allocator_api2::boxed::Box`
let b: Box<[u8]> = b;
assert_eq!(&*b, &[1, 2, 3]);
// Check length of slice is equal to what `v.len()` was, not `v.capacity()`
assert_eq!(b.len(), 3);
}
}