From 5ee1ef3d92816f4f14011c729d2a71bb518fc1eb Mon Sep 17 00:00:00 2001 From: DonIsaac <22823424+DonIsaac@users.noreply.github.com> Date: Sat, 12 Oct 2024 04:29:43 +0000 Subject: [PATCH] feat(allocator): add `Vec::into_boxed_slice` (#6195) Note that this PR does not implement the inverse operation (`Box::to_vec` for `[T]`). --- crates/oxc_allocator/src/boxed.rs | 19 +++++++++++ crates/oxc_allocator/src/vec.rs | 53 +++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/crates/oxc_allocator/src/boxed.rs b/crates/oxc_allocator/src/boxed.rs index 5ef4eef87a7e8..02e0957e5669e 100644 --- a/crates/oxc_allocator/src/boxed.rs +++ b/crates/oxc_allocator/src/boxed.rs @@ -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) -> 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 90149bafff53b..38de7d4cf300b 100644 --- a/crates/oxc_allocator/src/vec.rs +++ b/crates/oxc_allocator/src/vec.rs @@ -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); + } }