diff --git a/crates/oxc_allocator/src/boxed.rs b/crates/oxc_allocator/src/boxed.rs index 5ef4eef87a7e8..f003acaa39035 100644 --- a/crates/oxc_allocator/src/boxed.rs +++ b/crates/oxc_allocator/src/boxed.rs @@ -76,6 +76,18 @@ impl<'alloc, T> Box<'alloc, T> { } } +impl<'alloc, T: ?Sized> Box<'alloc, T> { + /// Create a [`Box`] from a raw pointer to a value in the arena. + /// + /// # SAFETY + /// It is only safe to create a `Box` this way with pointers taken out of + /// another `Box` in the same arena. + #[inline] + pub(crate) const unsafe fn from_non_null(ptr: NonNull) -> Self { + Self(ptr, PhantomData) + } +} + impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> { type Target = T; diff --git a/crates/oxc_allocator/src/vec.rs b/crates/oxc_allocator/src/vec.rs index 551faaf3f1613..5ea602df2a356 100644 --- a/crates/oxc_allocator/src/vec.rs +++ b/crates/oxc_allocator/src/vec.rs @@ -14,7 +14,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 +101,33 @@ impl<'alloc, T> Vec<'alloc, T> { vec.extend(iter); Self(vec) } + + /// Converts the vector into [`Box<[T]>`][owned slice]. + /// + /// If the vector has excess capacity, its items will be moved into a + /// newly-allocated buffer with exactly the right capacity. That is, any + /// excess capacity will be removed. + /// + /// [owned slice]: Box + /// + /// # Examples + /// + /// ``` + /// use oxc_allocator::{Allocator, Vec}; + /// + /// let allocator = Allocator::default(); + /// let v = Vec::from_iter_in([1, 2, 3], &allocator); + /// let b = v.into_boxed_slice(); + /// + /// assert_eq!(&*b, &[1, 2, 3]); + /// ``` + pub fn into_boxed_slice(self) -> Box<'alloc, [T]> { + let b = self.0.into_boxed_slice(); + let (ptr, _) = allocator_api2::boxed::Box::into_non_null(b); + // SAFETY: this is effectively a transmute from allocator_api2's box to + // our own representation. Ptr is non-null and points to initialized, well-aligned memory. + unsafe { Box::from_non_null(ptr) } + } } impl<'alloc, T> ops::Deref for Vec<'alloc, T> { @@ -219,4 +246,16 @@ mod test { program } } + + #[test] + fn test_vec_to_boxed_slice() { + // file: oxc_allocator/src/vec.rs + use crate::boxed::Box; + + let allocator = Allocator::default(); + let v = Vec::from_iter_in([1, 2, 3], &allocator); + let b: Box<[i32]> = v.into_boxed_slice(); // <- compiler error + + assert_eq!(&*b, &[1, 2, 3]); + } }