1#[cfg(unix)]
2mod unix;
3#[cfg(windows)]
4mod windows;
5
6use std::{alloc::Layout, mem::MaybeUninit, ptr::NonNull};
7
8#[cfg(unix)]
9type RawArena = unix::VirtArena;
10#[cfg(windows)]
11type RawArena = windows::VirtArena;
12
13#[derive(Default)]
16pub struct VirtArena(RawArena);
17
18impl VirtArena {
19 pub fn alloc(&self, layout: Layout) -> NonNull<u8> {
21 self.0.alloc(layout)
22 }
23
24 #[allow(clippy::mut_from_ref)]
29 pub unsafe fn alloc_zeroed<T: Sized>(&self) -> &mut T {
30 let layout = Layout::new::<T>();
31 self.0.alloc_zeroed(layout).cast().as_mut()
32 }
33
34 #[allow(clippy::mut_from_ref)]
39 pub unsafe fn alloc_slice_zeroed<T: Sized>(&self, count: usize) -> &mut [T] {
40 let layout = Layout::array::<T>(count).expect("Failed to read the array layout");
41 let ptr = self.0.alloc_zeroed(layout).cast();
42 std::slice::from_raw_parts_mut(ptr.as_ptr(), count)
43 }
44
45 #[allow(clippy::mut_from_ref)]
47 pub fn alloc_uninit<T: Sized>(&self) -> &mut MaybeUninit<T> {
48 let layout = Layout::new::<T>();
49 unsafe { self.0.alloc(layout).cast().as_mut() }
50 }
51
52 #[allow(clippy::mut_from_ref)]
54 pub fn alloc_slice_uninit<T: Sized>(&self, count: usize) -> &mut [MaybeUninit<T>] {
55 let layout = Layout::array::<T>(count).expect("Failed to read the array layout");
56 let ptr = self.0.alloc(layout).cast();
57 unsafe { std::slice::from_raw_parts_mut(ptr.as_ptr(), count) }
58 }
59
60 #[allow(clippy::mut_from_ref)]
63 pub fn alloc_with<T: Sized>(&self, fun: impl FnOnce() -> T) -> &mut T {
64 let uninit = self.alloc_uninit();
65 uninit.write(fun());
66 unsafe { uninit.assume_init_mut() }
67 }
68
69 #[allow(clippy::mut_from_ref)]
71 pub fn alloc_value<T: Sized>(&self, val: T) -> &mut T {
72 self.alloc_with(move || val)
73 }
74
75 pub fn bytes_used(&self) -> usize {
77 self.0.bytes_used()
78 }
79
80 pub fn reset(&mut self) {
83 self.0.reset()
84 }
85}
86
87unsafe impl Send for VirtArena {}
89
90const VIRT_ALLOC_SIZE: usize = 128 * (1 << 30); trait VirtArenaRaw {
93 fn bytes_used(&self) -> usize;
94 fn reset(&mut self);
95
96 fn alloc(&self, layout: Layout) -> NonNull<u8>;
97
98 unsafe fn alloc_zeroed(&self, layout: Layout) -> NonNull<u8> {
99 let ptr = self.alloc(layout);
100 ptr.write_bytes(0, layout.size());
101 ptr
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use std::num::NonZeroU32;
108
109 use super::*;
110
111 #[test]
112 fn arena() {
113 let mut arena = VirtArena::default();
114
115 struct Test {
116 thing: Option<NonZeroU32>,
117 }
118
119 let test1 = unsafe { arena.alloc_zeroed::<Test>() };
120 assert!(test1.thing.is_none());
121
122 let test2 = unsafe { arena.alloc_zeroed::<Test>() };
123 assert!(test2.thing.is_none());
124
125 test1.thing = Some(NonZeroU32::new(345).unwrap());
126
127 arena.reset();
128
129 let test1 = unsafe { arena.alloc_uninit::<Test>().assume_init_mut() };
130 assert_eq!(test1.thing.map(|v| v.get()), Some(345));
131
132 let test2 = unsafe { arena.alloc_uninit::<Test>().assume_init_mut() };
133 assert!(test2.thing.is_none());
134 }
135}