use std::{
self,
fmt::{self, Debug, Display, Formatter},
hash::{Hash, Hasher},
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
ptr::{self, NonNull},
};
#[cfg(feature = "serialize")]
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer as SerdeSerializer};
use crate::Allocator;
#[repr(transparent)]
pub struct Box<'alloc, T: ?Sized>(NonNull<T>, PhantomData<(&'alloc (), T)>);
impl<T: ?Sized> Box<'_, T> {
const ASSERT_T_IS_NOT_DROP: () =
assert!(!std::mem::needs_drop::<T>(), "Cannot create a Box<T> where T is a Drop type");
}
impl<T> Box<'_, T> {
#[expect(clippy::inline_always)]
#[inline(always)]
pub fn new_in(value: T, allocator: &Allocator) -> Self {
const { Self::ASSERT_T_IS_NOT_DROP };
Self(NonNull::from(allocator.alloc(value)), PhantomData)
}
pub const unsafe fn dangling() -> Self {
unsafe { Self::from_non_null(ptr::NonNull::dangling()) }
}
#[inline]
pub fn unbox(self) -> T {
unsafe { ptr::read(self.0.as_ptr()) }
}
}
impl<T: ?Sized> Box<'_, T> {
#[expect(clippy::inline_always)]
#[inline(always)]
pub fn as_non_null(boxed: &Self) -> NonNull<T> {
boxed.0
}
#[expect(clippy::inline_always, clippy::needless_pass_by_value)]
#[inline(always)]
pub fn into_non_null(boxed: Self) -> NonNull<T> {
boxed.0
}
pub const unsafe fn from_non_null(ptr: NonNull<T>) -> Self {
const { Self::ASSERT_T_IS_NOT_DROP };
Self(ptr, PhantomData)
}
}
impl<T> Box<'static, [T]> {
#[inline]
pub fn new_empty_boxed_slice() -> Self {
const { Self::ASSERT_T_IS_NOT_DROP };
let ptr = NonNull::dangling();
let slice_ptr = NonNull::slice_from_raw_parts(ptr, 0);
Self(slice_ptr, PhantomData)
}
}
impl<'a, T> Box<'a, [T]> {
#[expect(clippy::inline_always)]
#[inline(always)]
pub fn into_bump_slice(self) -> &'a [T] {
let r = self.as_ref();
unsafe { mem::transmute::<&[T], &'a [T]>(r) }
}
#[expect(clippy::inline_always)]
#[inline(always)]
pub fn into_bump_slice_mut(mut self) -> &'a mut [T] {
let r = self.as_mut();
unsafe { mem::transmute::<&mut [T], &'a mut [T]>(r) }
}
}
impl<T: ?Sized> Deref for Box<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
impl<T: ?Sized> DerefMut for Box<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.0.as_mut() }
}
}
impl<T: ?Sized> AsRef<T> for Box<'_, T> {
#[inline]
fn as_ref(&self) -> &T {
self
}
}
impl<T: ?Sized> AsMut<T> for Box<'_, T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
self
}
}
impl<T: ?Sized + Display> Display for Box<'_, T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.deref().fmt(f)
}
}
impl<T: ?Sized + Debug> Debug for Box<'_, T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.deref().fmt(f)
}
}
#[cfg(feature = "serialize")]
impl<T: Serialize> Serialize for Box<'_, T> {
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.deref().serialize(serializer)
}
}
#[cfg(feature = "serialize")]
impl<T: ESTree> ESTree for Box<'_, T> {
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
self.deref().serialize(serializer);
}
}
impl<T: Hash> Hash for Box<'_, T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
#[cfg(test)]
mod test {
use std::hash::{DefaultHasher, Hash, Hasher};
use crate::{Allocator, Vec};
use super::Box;
#[test]
fn box_deref_mut() {
let allocator = Allocator::default();
let mut b = Box::new_in("x", &allocator);
let b = &mut *b;
*b = allocator.alloc("v");
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();
let b = Box::new_in("x", &allocator);
let b = format!("{b:?}");
assert_eq!(b, "\"x\"");
}
#[test]
fn box_hash() {
fn hash(val: &impl Hash) -> u64 {
let mut hasher = DefaultHasher::default();
val.hash(&mut hasher);
hasher.finish()
}
let allocator = Allocator::default();
let a = Box::new_in("x", &allocator);
let b = Box::new_in("x", &allocator);
assert_eq!(hash(&a), hash(&b));
}
#[cfg(feature = "serialize")]
#[test]
fn box_serialize() {
let allocator = Allocator::default();
let b = Box::new_in("x", &allocator);
let s = serde_json::to_string(&b).unwrap();
assert_eq!(s, r#""x""#);
}
#[cfg(feature = "serialize")]
#[test]
fn box_serialize_estree() {
use oxc_estree::{CompactTSSerializer, ESTree};
let allocator = Allocator::default();
let b = Box::new_in("x", &allocator);
let mut serializer = CompactTSSerializer::default();
b.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(s, r#""x""#);
}
#[test]
fn lifetime_variance() {
fn _assert_box_variant_lifetime<'a: 'b, 'b, T>(program: Box<'a, T>) -> Box<'b, T> {
program
}
}
}