Skip to main content

csskit_arena/
arena_box.rs

1use crate::Arena;
2use allocator_api2::alloc::{Allocator, Layout};
3use std::{
4	fmt,
5	hash::{Hash, Hasher},
6	marker::PhantomData,
7	ops::{Deref, DerefMut},
8	ptr::NonNull,
9};
10
11/// An arena-allocated box that retains a reference to its allocator, enabling [`Clone`] support.
12///
13/// This type is intended for recursive AST nodes (e.g. `color-mix()` containing nested `<color>` values) where
14/// indirection is required to break the cycle, but the allocation should still live in the parsing arena.
15#[repr(C)]
16pub struct Box<'a, T, A: Allocator = &'a Arena> {
17	ptr: NonNull<T>,
18	alloc: A,
19	marker: PhantomData<&'a mut T>,
20}
21
22impl<'a, T, A: Allocator> Box<'a, T, A> {
23	/// Allocate `value` in the given `alloc`.
24	#[inline]
25	pub fn new_in(alloc: A, value: T) -> Self {
26		let ptr = alloc.allocate(Layout::new::<T>()).expect("arena exhausted").cast::<T>();
27		unsafe { ptr.as_ptr().write(value) };
28		Self { ptr, alloc, marker: PhantomData }
29	}
30}
31
32impl<'a, T> Box<'a, T> {
33	/// Gives up ownership of the value. The value stays in the arena, thus its `Drop` does not run.
34	#[inline]
35	pub fn leak(self) -> &'a mut T {
36		let ptr = self.ptr;
37		std::mem::forget(self);
38		// SAFETY: `ptr` addresses a live value in an arena that outlives `'a`, and this `Box` was the
39		// only owner of it. The arena frees the whole region at once.
40		unsafe { &mut *ptr.as_ptr() }
41	}
42}
43
44impl<'a, T, A: Allocator> Deref for Box<'a, T, A> {
45	type Target = T;
46
47	#[inline]
48	fn deref(&self) -> &T {
49		unsafe { self.ptr.as_ref() }
50	}
51}
52
53impl<'a, T, A: Allocator> DerefMut for Box<'a, T, A> {
54	#[inline]
55	fn deref_mut(&mut self) -> &mut T {
56		unsafe { self.ptr.as_mut() }
57	}
58}
59
60impl<'a, T, A: Allocator> Drop for Box<'a, T, A> {
61	fn drop(&mut self) {
62		unsafe { self.ptr.as_ptr().drop_in_place() };
63	}
64}
65
66impl<'a, T: Clone, A: Allocator + Clone> Clone for Box<'a, T, A> {
67	fn clone(&self) -> Self {
68		Box::new_in(self.alloc.clone(), (**self).clone())
69	}
70}
71
72impl<'a, T: fmt::Debug, A: Allocator> fmt::Debug for Box<'a, T, A> {
73	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74		fmt::Debug::fmt(&**self, f)
75	}
76}
77
78impl<'a, T: fmt::Display, A: Allocator> fmt::Display for Box<'a, T, A> {
79	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80		fmt::Display::fmt(&**self, f)
81	}
82}
83
84impl<'a, T: PartialEq, A: Allocator> PartialEq for Box<'a, T, A> {
85	fn eq(&self, other: &Self) -> bool {
86		(**self).eq(&**other)
87	}
88}
89
90impl<'a, T: Eq, A: Allocator> Eq for Box<'a, T, A> {}
91
92impl<'a, T: PartialOrd, A: Allocator> PartialOrd for Box<'a, T, A> {
93	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
94		(**self).partial_cmp(&**other)
95	}
96}
97
98impl<'a, T: Ord, A: Allocator> Ord for Box<'a, T, A> {
99	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
100		(**self).cmp(&**other)
101	}
102}
103
104impl<'a, T: Hash, A: Allocator> Hash for Box<'a, T, A> {
105	fn hash<H: Hasher>(&self, state: &mut H) {
106		(**self).hash(state);
107	}
108}
109
110#[cfg(feature = "serde")]
111impl<'a, T: serde::Serialize, A: Allocator> serde::Serialize for Box<'a, T, A> {
112	fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
113		(**self).serialize(serializer)
114	}
115}