Skip to main content

rustidy_util/
arena.rs

1//! Arenas
2
3// TODO: Ensure that being able to drop arenas is sound.
4
5// Imports
6use {
7	core::{
8		cell::UnsafeCell,
9		fmt,
10		hash::Hash,
11		marker::PhantomData,
12		mem::{self, MaybeUninit},
13		ops,
14		ptr,
15	},
16	std::hash::Hasher,
17};
18
19pub const CAPACITY: usize = 4096;
20
21type Slot<T> = UnsafeCell<MaybeUninit<T>>;
22type Row<T> = [Slot<T>; CAPACITY];
23
24/// Arena for `T`'s Data
25#[derive(Debug)]
26pub struct Arena<T> {
27	/// Tracks all the initialized fields.
28	///
29	/// It's length also corresponds to the current
30	/// length of the arena.
31	// TODO: Make this a bitvec?
32	init: Vec<bool>,
33
34	/// Rows for each value.
35	rows: Vec<Box<Row<T>>>,
36}
37
38impl<T> Arena<T> {
39	/// Creates a new, empty, arena
40	#[must_use]
41	pub const fn new() -> Self {
42		Self { init: vec![], rows: vec![], }
43	}
44
45	/// Removes any trailing dead values
46	fn clean_trailing(&mut self) {
47		let dead_elements = self
48			.init
49			.iter()
50			.rev()
51			.position(|is_init| *is_init)
52			.unwrap_or(self.init.len());
53		self
54			.init
55			.truncate(self.init.len() - dead_elements);
56	}
57
58	/// Gets a slot by index
59	fn slot(&self, idx: usize) -> *mut MaybeUninit<T> {
60		debug_assert!(self.init[idx]);
61
62		let row = &self.rows[idx / CAPACITY];
63		row[idx % CAPACITY].get()
64	}
65
66	/// Takes a slot by index, uninitialized it.
67	///
68	/// # SAFETY
69	/// You must ensure that no references to the slot exist.
70	unsafe fn take_slot(&mut self, idx: usize) -> T {
71		let slot = self.slot(idx);
72		self.init[idx] = false;
73
74		// SAFETY: No other references to `slot` exist
75		unsafe {
76			ptr::read(slot).assume_init()
77		}
78	}
79}
80
81impl<T> !Send for Arena<T> {}
82impl<T> !Sync for Arena<T> {}
83
84impl<T> Default for Arena<T> {
85	fn default() -> Self {
86		Self::new()
87	}
88}
89
90/// Arena index for `T`
91pub struct ArenaIdx<T: ArenaData> {
92	inner:   u32,
93	phantom: PhantomData<T>,
94}
95
96impl<T: ArenaData> ArenaIdx<T> {
97	/// Creates a new value in the arena
98	pub fn new(value: T) -> Self {
99		T::with_arena(|arena| {
100			// SAFETY: We create no other references to `arena` in this block
101			let arena = unsafe {
102				arena.as_mut_unchecked()
103			};
104
105			// Before inserting, remove any trailing dead values
106			arena.clean_trailing();
107
108			// Then find our slot.
109			let idx = arena.init.len();
110			let row = match arena.rows.get(idx / CAPACITY) {
111				Some(row) => row,
112				// Note: If the row we're looking for doesn't exist, then
113				//       we must be at the end of the last row, so create
114				//       a new one.
115				None => {
116					// SAFETY: We're initializing an array of uninitialized values
117					let row = unsafe {
118						Box::<[Slot<T>; CAPACITY]>::new_uninit()
119							.assume_init()
120					};
121					arena.rows.push_mut(row)
122				},
123			};
124			let slot = &row[idx % CAPACITY];
125
126			// Finally push the new value and set it as initialized.
127			// SAFETY: No other mutable references to `slot` exist, since
128			//         the slot was empty.
129			unsafe {
130				slot.as_mut_unchecked().write(value)
131			};
132			arena.init.push(true);
133
134			Self {
135				inner: idx.try_into().expect("Too many indices"),
136				phantom: PhantomData,
137			}
138		})
139	}
140
141	/// Moves out of this value
142	#[must_use = "If you don't need the value, just drop `self`"]
143	pub fn take(self) -> T {
144		let idx = self.inner as usize;
145		mem::forget(self);
146
147		T::with_arena(|arena| {
148			// SAFETY: We create no other references to `arena` in this block
149			let arena = unsafe {
150				arena.as_mut_unchecked()
151			};
152
153			// SAFETY: No other mutable references to the slot exist, since we take `self`
154			unsafe {
155				arena.take_slot(idx)
156			}
157		})
158	}
159
160	/// Moves out of this value if `f` returns `Ok`.
161	pub fn try_take_map<F, R>(self, f: F) -> Result<R, Self>
162	where
163		F: FnOnce(T) -> Result<R, T>,
164	{
165		// TODO: Optimize this?
166		match f(self.take()) {
167			Ok(value) => Ok(value),
168			Err(value) => Err(Self::new(value)),
169		}
170	}
171
172	/// Returns a unique id for this arena index
173	#[must_use]
174	pub const fn id(&self) -> u32 {
175		self.inner
176	}
177}
178
179impl<T: ArenaData> !Send for ArenaIdx<T> {}
180impl<T: ArenaData> !Sync for ArenaIdx<T> {}
181
182// TODO: Implement clone via reference counting with copy-on-mutable access?
183impl<T: ArenaData + Clone> Clone for ArenaIdx<T> {
184	fn clone(&self) -> Self {
185		Self::new((**self).clone())
186	}
187}
188impl<T: ArenaData> ops::Deref for ArenaIdx<T> {
189	type Target = T;
190
191	fn deref(&self) -> &Self::Target {
192		let idx = self.inner as usize;
193
194		T::with_arena(|arena| {
195			// SAFETY: We create no other references to `arena` in this block
196			let arena = unsafe {
197				arena.as_ref_unchecked()
198			};
199
200			let slot = arena.slot(idx);
201
202			// SAFETY: No mutable reference to the value exists since we take `&self`
203			unsafe {
204				MaybeUninit::assume_init_ref(&*slot)
205			}
206		})
207	}
208}
209
210impl<T: ArenaData> ops::DerefMut for ArenaIdx<T> {
211	fn deref_mut(&mut self) -> &mut Self::Target {
212		let idx = self.inner as usize;
213
214		T::with_arena(|arena| {
215			// SAFETY: We create no other references to `arena` in this block
216			let arena = unsafe {
217				arena.as_ref_unchecked()
218			};
219
220			let slot = arena.slot(idx);
221
222			// SAFETY: No other references to the value exist since we take `&mut self`
223			unsafe {
224				MaybeUninit::assume_init_mut(&mut *slot)
225			}
226		})
227	}
228}
229
230impl<T: ArenaData> Drop for ArenaIdx<T> {
231	fn drop(&mut self) {
232		let idx = self.inner as usize;
233
234		T::with_arena(|arena| {
235			// SAFETY: We create no other references to `arena` in this block
236			let arena = unsafe {
237				arena.as_mut_unchecked()
238			};
239
240			// SAFETY: No other mutable references to the slot exist, since we take `&mut self`
241			// TODO: Should we manually implement this to avoid moving the value onto the stack?
242			let _ = unsafe {
243				arena.take_slot(idx)
244			};
245		});
246	}
247}
248
249impl<T: ArenaData + PartialEq> PartialEq for ArenaIdx<T> {
250	fn eq(&self, other: &Self) -> bool {
251		if self.inner == other.inner {
252			return true;
253		}
254
255		(**self) == (**other)
256	}
257}
258
259impl<T: ArenaData + Eq> Eq for ArenaIdx<T> {}
260
261impl<T: ArenaData> Hash for ArenaIdx<T> {
262	fn hash<H: Hasher>(&self, state: &mut H) {
263		self.inner.hash(state);
264	}
265}
266
267impl<T: ArenaData + fmt::Debug> fmt::Debug for ArenaIdx<T> {
268	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269		f
270			.debug_struct("ArenaIdx")
271			.field("idx", &self.inner)
272			.field("inner", &**self).finish()
273	}
274}
275
276
277impl<T: ArenaData + serde::Serialize> serde::Serialize for ArenaIdx<T> {
278	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
279	where
280		S: serde::Serializer,
281	{
282		(**self).serialize(serializer)
283	}
284}
285
286impl<'de, T: ArenaData + serde::Deserialize<'de>> serde::Deserialize<'de> for ArenaIdx<T> {
287	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
288	where
289		D: serde::Deserializer<'de>,
290	{
291		let value = T::deserialize(deserializer)?;
292		Ok(Self::new(value))
293	}
294}
295
296/// Arena data
297pub trait ArenaData: Sized + 'static {
298	fn with_arena<O>(f: impl FnOnce(&UnsafeCell<Arena<Self>>) -> O) -> O;
299}
300
301/// Implements `ArenaData` for `$Ty`
302pub macro decl_arena(
303	$Ty:ty
304) {
305	impl ArenaData for $Ty {
306		fn with_arena<O>(f: impl FnOnce(&UnsafeCell<Arena<Self>>) -> O) -> O {
307			f(&ARENA)
308		}
309	}
310
311	#[thread_local]
312	static ARENA: UnsafeCell<Arena<$Ty>> = UnsafeCell::new(Arena::new());
313}
314
315/// Derive macro for `ArenaData`
316// TODO: Can we accept just a `$I:item` and get the type from there?
317pub macro ArenaData {
318	derive() ($( #[$meta:meta] )* $vis:vis struct $Ty:ident $($rest:tt)*) => {
319		decl_arena! { $Ty }
320	},
321
322	derive() ($( #[$meta:meta] )* $vis:vis enum $Ty:ident $($rest:tt)*) => {
323		decl_arena! { $Ty }
324	}
325}