Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions crates/oxc_allocator/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
53 changes: 51 additions & 2 deletions crates/oxc_allocator/src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ use std::{
fmt::Debug,
hash::{Hash, Hasher},
ops,
ptr::NonNull,
};

use allocator_api2::vec;
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)]
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
}
}