subms_arena_allocator/
lib.rs1use std::alloc::{Layout, alloc, dealloc};
19use std::cell::Cell;
20use std::mem;
21use std::ptr;
22
23pub struct Bump {
25 chunks: Vec<Chunk>,
26 cursor: Cell<usize>,
28}
29
30struct Chunk {
31 ptr: *mut u8,
32 layout: Layout,
33}
34
35impl Drop for Chunk {
36 fn drop(&mut self) {
37 unsafe { dealloc(self.ptr, self.layout) };
38 }
39}
40
41impl Bump {
42 pub fn new() -> Self {
44 Self::with_capacity(4096)
45 }
46
47 pub fn with_capacity(initial_bytes: usize) -> Self {
49 let initial_bytes = initial_bytes.max(64);
50 let layout = Layout::from_size_align(initial_bytes, 16).expect("layout");
51 let ptr = unsafe { alloc(layout) };
52 assert!(!ptr.is_null(), "OOM allocating first arena chunk");
53 Self {
54 chunks: vec![Chunk { ptr, layout }],
55 cursor: Cell::new(0),
56 }
57 }
58
59 pub fn alloc_copy<T: Copy>(&mut self, value: T) -> &mut T {
62 let layout = Layout::new::<T>();
63 let p = self.alloc_raw(layout);
64 unsafe {
65 ptr::write(p as *mut T, value);
66 &mut *(p as *mut T)
67 }
68 }
69
70 pub fn alloc_raw(&mut self, layout: Layout) -> *mut u8 {
73 let size = layout.size();
74 let align = layout.align();
75 let cur = self.cursor.get();
77 let aligned = align_up(cur, align);
78 let end = aligned + size;
79 let chunk = self.chunks.last().expect("at least one chunk");
80 if end <= chunk.layout.size() {
81 self.cursor.set(end);
82 return unsafe { chunk.ptr.add(aligned) };
83 }
84 self.grow(size + align);
86 let chunk = self.chunks.last().unwrap();
87 let aligned = align_up(0, align);
88 self.cursor.set(aligned + size);
89 unsafe { chunk.ptr.add(aligned) }
90 }
91
92 fn grow(&mut self, min_bytes: usize) {
93 let last = self.chunks.last().expect("at least one chunk");
94 let new_size = (last.layout.size() * 2).max(min_bytes);
95 let layout = Layout::from_size_align(new_size, 16).expect("layout");
96 let ptr = unsafe { alloc(layout) };
97 assert!(!ptr.is_null(), "OOM growing arena");
98 self.chunks.push(Chunk { ptr, layout });
99 self.cursor.set(0);
100 }
101
102 pub fn reset(&mut self) {
105 if self.chunks.len() > 1 {
107 let largest = self
108 .chunks
109 .iter()
110 .enumerate()
111 .max_by_key(|(_, c)| c.layout.size())
112 .map(|(i, _)| i)
113 .unwrap();
114 let keeper = self.chunks.swap_remove(largest);
115 self.chunks.clear();
116 self.chunks.push(keeper);
117 }
118 self.cursor.set(0);
119 }
120
121 pub fn total_capacity(&self) -> usize {
123 self.chunks.iter().map(|c| c.layout.size()).sum()
124 }
125}
126
127impl Default for Bump {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133fn align_up(p: usize, align: usize) -> usize {
135 debug_assert!(align.is_power_of_two(), "alignment must be a power of two");
136 (p + align - 1) & !(align - 1)
137}
138
139#[cfg(feature = "harness")]
140pub mod recipe;
141
142#[allow(dead_code)]
144fn _silence_unused() {
145 let _ = mem::size_of::<()>();
146}