1use crate::header::{GcColor, GcHeader};
8use crate::marker::Marker;
9use crate::region::Region;
10use std::alloc::Layout;
11use std::cell::UnsafeCell;
12
13const TLAB_SIZE: usize = 32 * 1024;
15
16struct Tlab {
18 base: *mut u8,
20 cursor: usize,
22 limit: usize,
24 region_index: usize,
26}
27
28impl Tlab {
29 fn new(base: *mut u8, size: usize, region_index: usize) -> Self {
31 Self {
32 base,
33 cursor: 0,
34 limit: size,
35 region_index,
36 }
37 }
38
39 fn try_alloc(&mut self, total_size: usize) -> Option<*mut u8> {
41 if self.cursor + total_size > self.limit {
42 return None;
43 }
44 let ptr = unsafe { self.base.add(self.cursor) };
45 self.cursor += total_size;
46 Some(ptr)
47 }
48}
49
50pub struct BumpAllocator {
52 regions: UnsafeCell<Vec<Region>>,
54 tlab: UnsafeCell<Option<Tlab>>,
56}
57
58unsafe impl Send for BumpAllocator {}
61unsafe impl Sync for BumpAllocator {}
62
63impl BumpAllocator {
64 pub fn new() -> Self {
66 Self {
67 regions: UnsafeCell::new(Vec::new()),
68 tlab: UnsafeCell::new(None),
69 }
70 }
71
72 pub fn alloc(&self, layout: Layout) -> *mut u8 {
77 let header_size = std::mem::size_of::<GcHeader>();
78 let obj_size = layout.size();
79 let total = (header_size + obj_size + 7) & !7; let tlab = unsafe { &mut *self.tlab.get() };
83 if let Some(t) = tlab {
84 if let Some(raw_ptr) = t.try_alloc(total) {
85 let header_ptr = raw_ptr as *mut GcHeader;
87 unsafe {
88 header_ptr.write(GcHeader::new(0, obj_size as u32));
89 }
90 return unsafe { raw_ptr.add(header_size) };
91 }
92 }
93
94 self.alloc_slow(layout, total)
96 }
97
98 fn alloc_slow(&self, layout: Layout, total: usize) -> *mut u8 {
100 let header_size = std::mem::size_of::<GcHeader>();
101 let obj_size = layout.size();
102
103 if total > TLAB_SIZE {
105 return self.alloc_large(layout);
106 }
107
108 let regions = unsafe { &mut *self.regions.get() };
110 let tlab = unsafe { &mut *self.tlab.get() };
111
112 let new_region = Region::new();
113 let base = new_region.base();
114 let region_index = regions.len();
115 regions.push(new_region);
116
117 let new_tlab = Tlab::new(base, TLAB_SIZE, region_index);
119 *tlab = Some(new_tlab);
120
121 let t = tlab.as_mut().unwrap();
123 let raw_ptr = t
124 .try_alloc(total)
125 .expect("fresh TLAB should have space for allocation");
126 let header_ptr = raw_ptr as *mut GcHeader;
127 unsafe {
128 header_ptr.write(GcHeader::new(0, obj_size as u32));
129 }
130 unsafe { raw_ptr.add(header_size) }
131 }
132
133 fn alloc_large(&self, layout: Layout) -> *mut u8 {
135 let regions = unsafe { &mut *self.regions.get() };
136 let mut region = Region::new();
137 let ptr = region
138 .try_alloc(layout)
139 .expect("fresh region should fit large allocation");
140 regions.push(region);
141 ptr
142 }
143
144 fn flush_tlab(&self) {
147 let tlab = unsafe { &mut *self.tlab.get() };
148 if let Some(t) = tlab {
149 let regions = unsafe { &mut *self.regions.get() };
150 if t.region_index < regions.len() {
151 regions[t.region_index].set_cursor(t.cursor);
152 }
153 }
154 }
155
156 pub fn flush_tlab_for_sweep(&self) {
158 self.flush_tlab();
159 }
160
161 pub fn sweep(&self, _marker: &Marker) -> usize {
164 self.flush_tlab();
166
167 let regions = unsafe { &mut *self.regions.get() };
168 let mut total_collected = 0;
169
170 for region in regions.iter_mut() {
171 let mut live_bytes = 0;
172 region.for_each_object_mut(|header, _obj_ptr| {
173 if header.color() == GcColor::White {
174 total_collected += header.size as usize;
176 } else {
177 live_bytes += header.size as usize;
179 header.set_color(GcColor::White);
180 }
181 });
182 region.set_live_bytes(live_bytes);
183 }
184
185 let tlab = unsafe { &mut *self.tlab.get() };
187 *tlab = None;
188
189 total_collected
190 }
191
192 pub fn total_region_bytes(&self) -> usize {
194 let regions = unsafe { &*self.regions.get() };
195 regions.len() * crate::region::REGION_SIZE
196 }
197
198 pub fn region_count(&self) -> usize {
200 let regions = unsafe { &*self.regions.get() };
201 regions.len()
202 }
203
204 pub fn regions(&self) -> &Vec<Region> {
206 unsafe { &*self.regions.get() }
207 }
208
209 pub fn regions_mut(&self) -> &mut Vec<Region> {
211 unsafe { &mut *self.regions.get() }
212 }
213}
214
215impl Default for BumpAllocator {
216 fn default() -> Self {
217 Self::new()
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_basic_allocation() {
227 let alloc = BumpAllocator::new();
228 let layout = Layout::from_size_align(64, 8).unwrap();
229 let ptr = alloc.alloc(layout);
230 assert!(!ptr.is_null());
231
232 let header_ptr =
234 unsafe { (ptr as *const u8).sub(std::mem::size_of::<GcHeader>()) } as *const GcHeader;
235 let header = unsafe { &*header_ptr };
236 assert_eq!(header.size, 64);
237 assert_eq!(header.color(), GcColor::White);
238 }
239
240 #[test]
241 fn test_multiple_allocations() {
242 let alloc = BumpAllocator::new();
243 let layout = Layout::from_size_align(32, 8).unwrap();
244
245 let mut ptrs = Vec::new();
246 for _ in 0..100 {
247 ptrs.push(alloc.alloc(layout));
248 }
249
250 for i in 0..ptrs.len() {
252 for j in (i + 1)..ptrs.len() {
253 assert_ne!(ptrs[i], ptrs[j]);
254 }
255 }
256 }
257
258 #[test]
259 fn test_tlab_refill() {
260 let alloc = BumpAllocator::new();
261 let layout = Layout::from_size_align(1024, 8).unwrap();
262
263 for _ in 0..50 {
265 let ptr = alloc.alloc(layout);
266 assert!(!ptr.is_null());
267 }
268 }
269}