mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
feat(allocator): add methods for boxed slices ArenaBox<[T]> (#19968)
Add methods to allocator `Box` to make it easier to work with boxed slices. * `Box::<[T]>::new_empty_boxed_slice` - create a new `Box<[T]>` with an empty slice (equivalent to `Vec::new_in`). * `Box::<[T]>::into_bump_slice` + `Box::<[T]>::into_bump_slice_mut` - convert a boxed slice to a `&[T]` / `&mut [T]` with lifetime of the arena (equivalent to same methods on `Vec`). * Implement `Dummy` and `TakeIn` for boxed slices.
This commit is contained in:
1 parent
7502afec08
commit
8345318d0a
2 files changed
+99
-3
No files matched your search
@@ -7,6 +7,7 @@ use std::{
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
@@ -149,12 +150,68 @@ impl<T: ?Sized> Box<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Box<'static, [T]> {
|
||||
/// Create a new empty `Box<[T]>`.
|
||||
///
|
||||
/// This method does not allocate. The returned boxed slice is represented by a dangling,
|
||||
/// correctly-aligned pointer with length 0, similar to how `Vec::new_in` produces an empty vector.
|
||||
#[inline]
|
||||
pub fn new_empty_boxed_slice() -> Self {
|
||||
const { Self::ASSERT_T_IS_NOT_DROP };
|
||||
|
||||
// `NonNull::<T>::dangling()` yields a non-null, properly aligned pointer.
|
||||
// We pair it with length 0 to construct a `NonNull<[T]>` representing an empty slice.
|
||||
// Correct alignment is the only requirement for it to be sound to dereference this pointer
|
||||
// to a slice, because the slice is empty.
|
||||
// See: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html
|
||||
let ptr = NonNull::dangling();
|
||||
let slice_ptr = NonNull::slice_from_raw_parts(ptr, 0);
|
||||
|
||||
Self(slice_ptr, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Box<'a, [T]> {
|
||||
/// Convert a boxed slice [`Box<[T]>`] into slice [`&'a [T]`].
|
||||
///
|
||||
/// The returned slice has the same lifetime as the allocator.
|
||||
//
|
||||
// `#[inline(always)]` because this is a no-op. `Box<[T]>` and `&[T]` have the same layout.
|
||||
#[expect(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
pub fn into_bump_slice(self) -> &'a [T] {
|
||||
let r = self.as_ref();
|
||||
// Extend lifetime of reference to lifetime of the allocator.
|
||||
// SAFETY: `self` is consumed by this method, so there cannot be any mutable references to it.
|
||||
// The reference lives until the allocator is dropped or reset (`'a` lifetime).
|
||||
// Don't need `mem::forget(self)` here, because `Box` does not implement `Drop`.
|
||||
unsafe { mem::transmute::<&[T], &'a [T]>(r) }
|
||||
}
|
||||
|
||||
/// Convert a boxed slice [`Box<[T]>`] into mutable slice [`&'a mut [T]`].
|
||||
///
|
||||
/// The returned slice has the same lifetime as the allocator.
|
||||
//
|
||||
// `#[inline(always)]` because this is a no-op. `Box<[T]>` and `&mut [T]` have the same layout.
|
||||
#[expect(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
pub fn into_bump_slice_mut(mut self) -> &'a mut [T] {
|
||||
let r = self.as_mut();
|
||||
// Extend lifetime of reference to lifetime of the allocator.
|
||||
// SAFETY: `self` is consumed by this method, so there cannot be any other references to it.
|
||||
// The reference lives until the allocator is dropped or reset (`'a` lifetime).
|
||||
// Don't need `mem::forget(self)` here, because `Box` does not implement `Drop`.
|
||||
unsafe { mem::transmute::<&mut [T], &'a mut [T]>(r) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for Box<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: self.0 is always a unique reference allocated from a Bump in Box::new_in
|
||||
// SAFETY: `self.0` is always a unique reference allocated from a `Bump` in `Box::new_in`,
|
||||
// or an empty slice allocated from `Box::new_empty_boxed_slice`
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
}
|
||||
@@ -162,7 +219,8 @@ impl<T: ?Sized> Deref for Box<'_, T> {
|
||||
impl<T: ?Sized> DerefMut for Box<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
// SAFETY: self.0 is always a unique reference allocated from a Bump in Box::new_in
|
||||
// SAFETY: `self.0` is always a unique reference allocated from a `Bump` in `Box::new_in`,
|
||||
// or an empty slice allocated from `Box::new_empty_boxed_slice`
|
||||
unsafe { self.0.as_mut() }
|
||||
}
|
||||
}
|
||||
@@ -230,8 +288,9 @@ impl<T: Hash> Hash for Box<'_, T> {
|
||||
mod test {
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
use crate::{Allocator, Vec};
|
||||
|
||||
use super::Box;
|
||||
use crate::Allocator;
|
||||
|
||||
#[test]
|
||||
fn box_deref_mut() {
|
||||
@@ -242,6 +301,33 @@ mod test {
|
||||
assert_eq!(*b, "v");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_empty_boxed_slice() {
|
||||
let b = Box::<[u32]>::new_empty_boxed_slice();
|
||||
assert!(b.is_empty());
|
||||
assert_eq!(b.len(), 0);
|
||||
assert_eq!(&*b, &[] as &[u32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boxed_slice_into_bump_slice() {
|
||||
let allocator = Allocator::default();
|
||||
let v = Vec::from_iter_in([1, 2, 3], &allocator);
|
||||
let b = v.into_boxed_slice();
|
||||
let slice = b.into_bump_slice();
|
||||
assert_eq!(slice, &[1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boxed_slice_into_bump_slice_mut() {
|
||||
let allocator = Allocator::default();
|
||||
let v = Vec::from_iter_in([10, 20, 30], &allocator);
|
||||
let b = v.into_boxed_slice();
|
||||
let slice = b.into_bump_slice_mut();
|
||||
slice[1] = 99;
|
||||
assert_eq!(slice, &[10, 99, 30]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_debug() {
|
||||
let allocator = Allocator::default();
|
||||
|
||||
@@ -23,6 +23,8 @@ pub trait TakeIn<'a>: Dummy<'a> {
|
||||
|
||||
impl<'a, T> TakeIn<'a> for Vec<'a, T> {}
|
||||
|
||||
impl<'a, T> TakeIn<'a> for Box<'a, [T]> {}
|
||||
|
||||
/// A trait to create a dummy AST node.
|
||||
pub trait Dummy<'a>: Sized {
|
||||
/// Create a dummy node.
|
||||
@@ -46,6 +48,14 @@ impl<'a, T: Dummy<'a>> Dummy<'a> for Box<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Dummy<'a> for Box<'a, [T]> {
|
||||
/// Create a dummy empty [`Box<[T]>`] boxed slice.
|
||||
#[inline]
|
||||
fn dummy(_allocator: &'a Allocator) -> Self {
|
||||
Box::new_empty_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Dummy<'a> for Vec<'a, T> {
|
||||
/// Create a dummy [`Vec`].
|
||||
#[inline]
|
||||
|
||||
Reference in new issue
Block a user