Skip to content

Commit 64eee51

Browse files
committed
feat(allocator): add methods for boxed slices ArenaBox<[T]>
1 parent 7502afe commit 64eee51

File tree

2 files changed

+99
-3
lines changed

2 files changed

+99
-3
lines changed

crates/oxc_allocator/src/boxed.rs

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{
77
fmt::{self, Debug, Display, Formatter},
88
hash::{Hash, Hasher},
99
marker::PhantomData,
10+
mem,
1011
ops::{Deref, DerefMut},
1112
ptr::{self, NonNull},
1213
};
@@ -149,20 +150,77 @@ impl<T: ?Sized> Box<'_, T> {
149150
}
150151
}
151152

153+
impl<T> Box<'static, [T]> {
154+
/// Create a new empty `Box<[T]>`.
155+
///
156+
/// This method does not allocate. The returned boxed slice is represented by a dangling,
157+
/// correctly-aligned pointer with length 0, similar to how `Vec::new_in` produces an empty vector.
158+
#[inline]
159+
pub fn new_empty_boxed_slice() -> Self {
160+
const { Self::ASSERT_T_IS_NOT_DROP };
161+
162+
// `NonNull::<T>::dangling()` yields a non-null, properly aligned pointer.
163+
// We pair it with length 0 to construct a `NonNull<[T]>` representing an empty slice.
164+
// Correct alignment is the only requirement for it to be sound to dereference this pointer
165+
// to a slice, because the slice is empty.
166+
// See: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html
167+
let ptr = NonNull::dangling();
168+
let slice_ptr = NonNull::slice_from_raw_parts(ptr, 0);
169+
170+
Self(slice_ptr, PhantomData)
171+
}
172+
}
173+
174+
impl<'a, T> Box<'a, [T]> {
175+
/// Convert a boxed slice [`Box<[T]>`] into slice [`&'a [T]`].
176+
///
177+
/// The returned slice has the same lifetime as the allocator.
178+
//
179+
// `#[inline(always)]` because this is a no-op. `Box<[T]>` and `&[T]` have the same layout.
180+
#[expect(clippy::inline_always)]
181+
#[inline(always)]
182+
pub fn into_bump_slice(self) -> &'a [T] {
183+
let r = self.as_ref();
184+
// Extend lifetime of reference to lifetime of the allocator.
185+
// SAFETY: `self` is consumed by this method, so there cannot be any mutable references to it.
186+
// The reference lives until the allocator is dropped or reset (`'a` lifetime).
187+
// Don't need `mem::forget(self)` here, because `Box` does not implement `Drop`.
188+
unsafe { mem::transmute::<&[T], &'a [T]>(r) }
189+
}
190+
191+
/// Convert a boxed slice [`Box<[T]>`] into mutable slice [`&'a mut [T]`].
192+
///
193+
/// The returned slice has the same lifetime as the allocator.
194+
//
195+
// `#[inline(always)]` because this is a no-op. `Box<[T]>` and `&mut [T]` have the same layout.
196+
#[expect(clippy::inline_always)]
197+
#[inline(always)]
198+
pub fn into_bump_slice_mut(mut self) -> &'a mut [T] {
199+
let r = self.as_mut();
200+
// Extend lifetime of reference to lifetime of the allocator.
201+
// SAFETY: `self` is consumed by this method, so there cannot be any other references to it.
202+
// The reference lives until the allocator is dropped or reset (`'a` lifetime).
203+
// Don't need `mem::forget(self)` here, because `Box` does not implement `Drop`.
204+
unsafe { mem::transmute::<&mut [T], &'a mut [T]>(r) }
205+
}
206+
}
207+
152208
impl<T: ?Sized> Deref for Box<'_, T> {
153209
type Target = T;
154210

155211
#[inline]
156212
fn deref(&self) -> &T {
157-
// SAFETY: self.0 is always a unique reference allocated from a Bump in Box::new_in
213+
// SAFETY: `self.0` is always a unique reference allocated from a `Bump` in `Box::new_in`,
214+
// or an empty slice allocated from `Box::new_empty_boxed_slice`
158215
unsafe { self.0.as_ref() }
159216
}
160217
}
161218

162219
impl<T: ?Sized> DerefMut for Box<'_, T> {
163220
#[inline]
164221
fn deref_mut(&mut self) -> &mut T {
165-
// SAFETY: self.0 is always a unique reference allocated from a Bump in Box::new_in
222+
// SAFETY: `self.0` is always a unique reference allocated from a `Bump` in `Box::new_in`,
223+
// or an empty slice allocated from `Box::new_empty_boxed_slice`
166224
unsafe { self.0.as_mut() }
167225
}
168226
}
@@ -230,8 +288,9 @@ impl<T: Hash> Hash for Box<'_, T> {
230288
mod test {
231289
use std::hash::{DefaultHasher, Hash, Hasher};
232290

291+
use crate::{Allocator, Vec};
292+
233293
use super::Box;
234-
use crate::Allocator;
235294

236295
#[test]
237296
fn box_deref_mut() {
@@ -242,6 +301,33 @@ mod test {
242301
assert_eq!(*b, "v");
243302
}
244303

304+
#[test]
305+
fn new_empty_boxed_slice() {
306+
let b = Box::<[u32]>::new_empty_boxed_slice();
307+
assert!(b.is_empty());
308+
assert_eq!(b.len(), 0);
309+
assert_eq!(&*b, &[] as &[u32]);
310+
}
311+
312+
#[test]
313+
fn boxed_slice_into_bump_slice() {
314+
let allocator = Allocator::default();
315+
let v = Vec::from_iter_in([1, 2, 3], &allocator);
316+
let b = v.into_boxed_slice();
317+
let slice = b.into_bump_slice();
318+
assert_eq!(slice, &[1, 2, 3]);
319+
}
320+
321+
#[test]
322+
fn boxed_slice_into_bump_slice_mut() {
323+
let allocator = Allocator::default();
324+
let v = Vec::from_iter_in([10, 20, 30], &allocator);
325+
let b = v.into_boxed_slice();
326+
let slice = b.into_bump_slice_mut();
327+
slice[1] = 99;
328+
assert_eq!(slice, &[10, 99, 30]);
329+
}
330+
245331
#[test]
246332
fn box_debug() {
247333
let allocator = Allocator::default();

crates/oxc_allocator/src/take_in.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub trait TakeIn<'a>: Dummy<'a> {
2323

2424
impl<'a, T> TakeIn<'a> for Vec<'a, T> {}
2525

26+
impl<'a, T> TakeIn<'a> for Box<'a, [T]> {}
27+
2628
/// A trait to create a dummy AST node.
2729
pub trait Dummy<'a>: Sized {
2830
/// Create a dummy node.
@@ -46,6 +48,14 @@ impl<'a, T: Dummy<'a>> Dummy<'a> for Box<'a, T> {
4648
}
4749
}
4850

51+
impl<'a, T> Dummy<'a> for Box<'a, [T]> {
52+
/// Create a dummy empty [`Box<[T]>`] boxed slice.
53+
#[inline]
54+
fn dummy(_allocator: &'a Allocator) -> Self {
55+
Box::new_empty_boxed_slice()
56+
}
57+
}
58+
4959
impl<'a, T> Dummy<'a> for Vec<'a, T> {
5060
/// Create a dummy [`Vec`].
5161
#[inline]

0 commit comments

Comments
 (0)