1use core::alloc::Layout;
19use core::cell::Cell;
20use core::mem::MaybeUninit;
21use core::ptr::NonNull;
22
23use crate::memory::tag::{MemTag, Realm};
24
25const ARENA_ALIGN: usize = 64;
29
30pub struct Arena {
32 ptr: NonNull<u8>,
33 cap: usize,
34 used: Cell<usize>,
35 peak: Cell<usize>,
36 overflows: Cell<u32>,
40 tag: Option<MemTag>,
42}
43
44#[expect(
49 clippy::mut_from_ref,
50 reason = "allocations never overlap and reset takes &mut self, so the general-case lint does not apply"
51)]
52impl Arena {
53 pub fn with_capacity(bytes: usize) -> Self {
56 Self::new(bytes, None)
57 }
58
59 pub fn tagged(bytes: usize, tag: MemTag) -> Self {
63 crate::memory::ledger().add(tag, Realm::Host, bytes as u64);
64 Self::new(bytes, Some(tag))
65 }
66
67 fn new(bytes: usize, tag: Option<MemTag>) -> Self {
68 if bytes == 0 {
69 return Self {
70 ptr: NonNull::dangling(),
71 cap: 0,
72 used: Cell::new(0),
73 peak: Cell::new(0),
74 overflows: Cell::new(0),
75 tag,
76 };
77 }
78 let layout = Layout::from_size_align(bytes, ARENA_ALIGN).expect("arena layout");
79 let ptr = unsafe { alloc::alloc::alloc(layout) };
81 let Some(ptr) = NonNull::new(ptr) else {
82 alloc::alloc::handle_alloc_error(layout)
83 };
84 Self {
85 ptr,
86 cap: bytes,
87 used: Cell::new(0),
88 peak: Cell::new(0),
89 overflows: Cell::new(0),
90 tag,
91 }
92 }
93
94 pub fn capacity(&self) -> usize {
96 self.cap
97 }
98
99 pub fn used(&self) -> usize {
101 self.used.get()
102 }
103
104 pub fn remaining(&self) -> usize {
106 self.cap - self.used.get()
107 }
108
109 pub fn peak(&self) -> usize {
111 self.peak.get()
112 }
113
114 pub fn overflows(&self) -> u32 {
117 self.overflows.get()
118 }
119
120 pub fn clear_overflows(&self) {
122 self.overflows.set(0);
123 }
124
125 pub fn reset(&mut self) {
132 self.used.set(0);
133 }
134
135 pub fn alloc<T: Copy>(&self, value: T) -> Option<&mut T> {
138 let ptr = self.bump(size_of::<T>(), align_of::<T>())?.cast::<T>();
139 unsafe {
144 ptr.write(value);
145 Some(&mut *ptr.as_ptr())
146 }
147 }
148
149 #[cfg(test)]
150 pub(crate) fn alloc_slice<T: Copy>(&self, len: usize, value: T) -> Option<&mut [T]> {
152 let slice = self.uninit_slice::<T>(len)?;
153 for slot in slice.iter_mut() {
154 slot.write(value);
155 }
156 Some(unsafe { assume_init_mut(slice) })
158 }
159
160 #[cfg(test)]
162 pub(crate) fn alloc_slice_copy<T: Copy>(&self, src: &[T]) -> Option<&mut [T]> {
163 let slice = self.uninit_slice::<T>(src.len())?;
164 for (slot, value) in slice.iter_mut().zip(src) {
165 slot.write(*value);
166 }
167 Some(unsafe { assume_init_mut(slice) })
170 }
171
172 pub fn vec<T: Copy>(&self, capacity: usize) -> Option<ArenaVec<'_, T>> {
175 Some(ArenaVec {
176 buf: self.uninit_slice::<T>(capacity)?,
177 len: 0,
178 })
179 }
180
181 fn uninit_slice<T>(&self, len: usize) -> Option<&mut [MaybeUninit<T>]> {
182 let bytes = size_of::<T>().saturating_mul(len);
185 let ptr = self.bump(bytes, align_of::<T>())?.cast::<MaybeUninit<T>>();
186 Some(unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), len) })
191 }
192
193 fn bump(&self, size: usize, align: usize) -> Option<NonNull<u8>> {
196 let carved = self.try_bump(size, align);
197 if carved.is_none() {
198 self.overflows.set(self.overflows.get().saturating_add(1));
199 }
200 carved
201 }
202
203 fn try_bump(&self, size: usize, align: usize) -> Option<NonNull<u8>> {
204 if align > ARENA_ALIGN || self.cap == 0 {
205 return None;
206 }
207 let start = self.used.get().checked_next_multiple_of(align)?;
208 let end = start.checked_add(size)?;
209 if end > self.cap {
210 return None;
211 }
212 self.used.set(end);
213 if end > self.peak.get() {
214 self.peak.set(end);
215 }
216 Some(unsafe { NonNull::new_unchecked(self.ptr.as_ptr().add(start)) })
219 }
220}
221
222impl Drop for Arena {
223 fn drop(&mut self) {
224 if let Some(tag) = self.tag {
225 crate::memory::ledger().release(tag, Realm::Host, self.cap as u64);
226 }
227 if self.cap == 0 {
228 return;
229 }
230 let layout = Layout::from_size_align(self.cap, ARENA_ALIGN).expect("arena layout");
231 unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), layout) };
235 }
236}
237
238unsafe impl Send for Arena {}
243
244pub struct ArenaVec<'a, T: Copy> {
247 buf: &'a mut [MaybeUninit<T>],
248 len: usize,
249}
250
251impl<T: Copy> ArenaVec<'_, T> {
252 pub fn len(&self) -> usize {
254 self.len
255 }
256
257 pub fn is_empty(&self) -> bool {
259 self.len == 0
260 }
261
262 pub fn capacity(&self) -> usize {
264 self.buf.len()
265 }
266
267 pub(crate) fn is_full(&self) -> bool {
268 self.len == self.buf.len()
269 }
270
271 #[must_use]
274 pub fn push(&mut self, value: T) -> bool {
275 if self.is_full() {
276 return false;
277 }
278 self.buf[self.len].write(value);
279 self.len += 1;
280 true
281 }
282
283 pub fn extend(&mut self, values: impl IntoIterator<Item = T>) -> usize {
286 let before = self.len;
287 for value in values {
288 if !self.push(value) {
289 break;
290 }
291 }
292 self.len - before
293 }
294
295 pub fn clear(&mut self) {
297 self.len = 0;
298 }
299
300 pub fn as_slice(&self) -> &[T] {
302 unsafe { assume_init_ref(&self.buf[..self.len]) }
304 }
305
306 pub fn as_mut_slice(&mut self) -> &mut [T] {
308 unsafe { assume_init_mut(&mut self.buf[..self.len]) }
310 }
311}
312
313impl<T: Copy> core::ops::Deref for ArenaVec<'_, T> {
314 type Target = [T];
315
316 fn deref(&self) -> &[T] {
317 self.as_slice()
318 }
319}
320
321impl<T: Copy> core::ops::DerefMut for ArenaVec<'_, T> {
322 fn deref_mut(&mut self) -> &mut [T] {
323 self.as_mut_slice()
324 }
325}
326
327impl<T: Copy + core::fmt::Debug> core::fmt::Debug for ArenaVec<'_, T> {
328 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
329 self.as_slice().fmt(f)
330 }
331}
332
333unsafe fn assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
335 unsafe { &*(slice as *const [MaybeUninit<T>] as *const [T]) }
338}
339
340unsafe fn assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
342 unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn allocations_come_back_with_their_values() {
352 let arena = Arena::with_capacity(4096);
353 let a = arena.alloc(7u32).expect("fits");
354 let b = arena.alloc_slice(4, 1u16).expect("fits");
355 let c = arena.alloc_slice_copy(&[9u64, 8, 7]).expect("fits");
356
357 assert_eq!(*a, 7);
358 assert_eq!(b, &[1, 1, 1, 1]);
359 assert_eq!(c, &[9, 8, 7]);
360 }
361
362 #[test]
365 fn separate_allocations_do_not_overlap() {
366 let arena = Arena::with_capacity(4096);
367 let first = arena.alloc_slice(8, 0u32).expect("fits");
368 let second = arena.alloc_slice(8, 0u32).expect("fits");
369 first.fill(0xAAAA_AAAA);
370 second.fill(0x5555_5555);
371 assert!(first.iter().all(|&v| v == 0xAAAA_AAAA));
372 assert!(second.iter().all(|&v| v == 0x5555_5555));
373 }
374
375 #[test]
376 fn allocations_are_aligned_for_their_type() {
377 let arena = Arena::with_capacity(4096);
378 let _ = arena.alloc(1u8).expect("fits");
380 let wide = arena.alloc(1u128).expect("fits");
381 assert!((wide as *const u128).is_aligned());
382
383 let _ = arena.alloc(1u8).expect("fits");
384 let slice = arena.alloc_slice(3, 0u64).expect("fits");
385 assert!(slice.as_ptr().is_aligned());
386 }
387
388 #[test]
390 fn a_full_arena_declines_rather_than_panicking() {
391 let arena = Arena::with_capacity(64);
392 assert!(arena.alloc_slice(8, 0u64).is_some());
393 assert!(arena.alloc(0u8).is_none());
394 assert_eq!(arena.remaining(), 0);
395 }
396
397 #[test]
398 fn an_empty_arena_declines_everything() {
399 let arena = Arena::with_capacity(0);
400 assert_eq!(arena.capacity(), 0);
401 assert!(arena.alloc(1u8).is_none());
402 }
403
404 #[test]
405 fn reset_hands_the_whole_arena_back() {
406 let mut arena = Arena::with_capacity(128);
407 {
408 let slice = arena.alloc_slice(16, 0u64).expect("fits");
409 assert_eq!(slice.len(), 16);
410 }
411 assert_eq!(arena.used(), 128);
412 assert!(arena.alloc(0u8).is_none());
413
414 arena.reset();
415 assert_eq!(arena.used(), 0);
416 assert!(arena.alloc_slice(16, 0u64).is_some());
417 }
418
419 #[test]
422 fn peak_survives_a_reset() {
423 let mut arena = Arena::with_capacity(1024);
424 let _ = arena.alloc_slice(64, 0u8).expect("fits");
425 arena.reset();
426 let _ = arena.alloc_slice(8, 0u8).expect("fits");
427
428 assert_eq!(arena.used(), 8);
429 assert_eq!(arena.peak(), 64);
430 }
431
432 #[test]
435 fn an_over_aligned_type_is_declined() {
436 #[repr(align(128))]
437 #[derive(Clone, Copy)]
438 struct Overaligned(u8);
439
440 let arena = Arena::with_capacity(4096);
441 let value = Overaligned(7);
442 assert_eq!(value.0, 7);
443 assert!(arena.alloc(value).is_none());
444 }
445
446 #[test]
447 fn a_vector_pushes_into_its_reservation() {
448 let arena = Arena::with_capacity(4096);
449 let mut v = arena.vec::<u32>(4).expect("fits");
450 assert!(v.is_empty());
451 for i in 0..4 {
452 assert!(v.push(i));
453 }
454 assert!(v.is_full());
455 assert_eq!(v.as_slice(), &[0, 1, 2, 3]);
456 assert_eq!(v.len(), 4);
457 }
458
459 #[test]
462 fn a_vector_declines_pushes_past_its_reservation() {
463 let arena = Arena::with_capacity(4096);
464 let mut v = arena.vec::<u8>(2).expect("fits");
465 assert!(v.push(1));
466 assert!(v.push(2));
467 assert!(!v.push(3));
468 assert_eq!(v.as_slice(), &[1, 2]);
469 }
470
471 #[test]
472 fn extend_reports_what_it_took() {
473 let arena = Arena::with_capacity(4096);
474 let mut v = arena.vec::<u16>(3).expect("fits");
475 assert_eq!(v.extend([1, 2, 3, 4, 5]), 3);
476 assert_eq!(v.as_slice(), &[1, 2, 3]);
477 }
478
479 #[test]
480 fn a_vector_sorts_and_reads_back_through_the_slice() {
481 let arena = Arena::with_capacity(4096);
482 let mut v = arena.vec::<u32>(5).expect("fits");
483 assert_eq!(v.extend([5, 3, 1, 4, 2]), 5);
484 v.sort_unstable();
485 assert_eq!(&*v, &[1, 2, 3, 4, 5]);
486 }
487
488 #[test]
489 fn clearing_a_vector_keeps_its_reservation() {
490 let arena = Arena::with_capacity(4096);
491 let mut v = arena.vec::<u8>(2).expect("fits");
492 assert!(v.push(1));
493 v.clear();
494 assert!(v.is_empty());
495 assert!(v.push(2));
496 assert_eq!(v.as_slice(), &[2]);
497 }
498
499 #[test]
501 fn two_vectors_hold_separate_reservations() {
502 let arena = Arena::with_capacity(4096);
503 let mut a = arena.vec::<u32>(2).expect("fits");
504 let mut b = arena.vec::<u32>(2).expect("fits");
505 assert_eq!(a.extend([1, 2]), 2);
506 assert_eq!(b.extend([3, 4]), 2);
507 assert_eq!(a.as_slice(), &[1, 2]);
508 assert_eq!(b.as_slice(), &[3, 4]);
509 }
510
511 #[test]
515 fn a_tagged_arena_reports_its_reservation_for_as_long_as_it_lives() {
516 const BYTES: usize = 8192;
517 let held = || {
518 crate::memory::ledger()
519 .usage(MemTag::Scratch, Realm::Host)
520 .bytes
521 };
522
523 let before = held();
524 {
525 let arena = Arena::tagged(BYTES, MemTag::Scratch);
526 assert_eq!(held(), before + BYTES as u64);
527 let _ = arena.alloc_slice(16, 0u8).expect("fits");
529 assert_eq!(held(), before + BYTES as u64);
530 }
531 assert_eq!(held(), before);
532 }
533
534 #[test]
535 fn an_untagged_arena_reports_nothing() {
536 let held = || crate::memory::ledger().usage(MemTag::Ui, Realm::Host).bytes;
537 let before = held();
538 let _arena = Arena::with_capacity(8192);
539 assert_eq!(held(), before);
540 }
541
542 #[test]
543 fn a_reservation_larger_than_the_arena_is_declined() {
544 let arena = Arena::with_capacity(64);
545 assert!(arena.vec::<u64>(1024).is_none());
546 }
547
548 #[test]
551 fn declined_requests_are_counted() {
552 let arena = Arena::with_capacity(64);
553 assert_eq!(arena.overflows(), 0);
554
555 assert!(arena.alloc_slice(8, 0u64).is_some());
556 assert_eq!(arena.overflows(), 0, "a request that fits counts nothing");
557
558 assert!(arena.alloc(0u8).is_none());
559 assert!(arena.vec::<u32>(4).is_none());
560 assert_eq!(arena.overflows(), 2);
561
562 arena.clear_overflows();
563 assert_eq!(arena.overflows(), 0);
564 }
565
566 #[test]
569 fn reset_keeps_the_sizing_evidence() {
570 let mut arena = Arena::with_capacity(64);
571 let _ = arena.alloc_slice(8, 0u64).expect("fits");
572 assert!(arena.alloc(0u8).is_none());
573
574 arena.reset();
575 assert_eq!(arena.used(), 0, "the cursor rewinds");
576 assert_eq!(arena.peak(), 64, "the peak does not");
577 assert_eq!(arena.overflows(), 1, "nor does the overflow count");
578 }
579
580 #[test]
583 fn every_decline_path_reaches_the_counter() {
584 #[repr(align(128))]
585 #[derive(Clone, Copy)]
586 struct Overaligned(u8);
587
588 let arena = Arena::with_capacity(4096);
589 let value = Overaligned(7);
590 assert_eq!(value.0, 7);
591 assert!(arena.alloc(value).is_none());
592 assert!(arena.vec::<u64>(usize::MAX).is_none());
593 assert_eq!(arena.overflows(), 2);
594
595 let empty = Arena::with_capacity(0);
596 assert!(empty.alloc(1u8).is_none());
597 assert_eq!(empty.overflows(), 1);
598 }
599}