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