buddy_system_allocator/
lib.rs1#![no_std]
2
3#[cfg(test)]
4#[macro_use]
5extern crate std;
6
7#[cfg(feature = "use_spin")]
8extern crate spin;
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13#[cfg(feature = "use_spin")]
14use core::alloc::GlobalAlloc;
15use core::alloc::Layout;
16use core::cmp::{max, min};
17use core::fmt;
18use core::mem::size_of;
19#[cfg(feature = "use_spin")]
20use core::ops::Deref;
21use core::ptr::NonNull;
22#[cfg(feature = "use_spin")]
23use spin::Mutex;
24
25#[cfg(feature = "alloc")]
26mod frame;
27pub mod linked_list;
28#[cfg(test)]
29mod test;
30
31#[cfg(feature = "alloc")]
32pub use frame::*;
33
34pub struct Heap<const ORDER: usize> {
57 free_list: [linked_list::LinkedList; ORDER],
59
60 user: usize,
62 allocated: usize,
63 total: usize,
64}
65
66impl<const ORDER: usize> Heap<ORDER> {
67 pub const fn new() -> Self {
69 Heap {
70 free_list: [linked_list::LinkedList::new(); ORDER],
71 user: 0,
72 allocated: 0,
73 total: 0,
74 }
75 }
76
77 pub const fn empty() -> Self {
79 Self::new()
80 }
81
82 pub unsafe fn add_to_heap(&mut self, mut start: usize, mut end: usize) {
91 start = (start + size_of::<usize>() - 1) & (!size_of::<usize>() + 1);
93 end &= !size_of::<usize>() + 1;
94 assert!(start <= end);
95
96 let mut total = 0;
97 let mut current_start = start;
98
99 while current_start + size_of::<usize>() <= end {
100 let lowbit = current_start & (!current_start + 1);
101 let mut size = min(lowbit, prev_power_of_two(end - current_start));
102
103 let mut order = size.trailing_zeros() as usize;
106 if order > ORDER - 1 {
107 order = ORDER - 1;
108 size = 1 << order;
109 }
110 total += size;
111
112 unsafe {
113 self.free_list[order].push(current_start as *mut usize);
114 }
115 current_start += size;
116 }
117
118 self.total += total;
119 }
120
121 pub unsafe fn init(&mut self, start: usize, size: usize) {
131 unsafe {
132 self.add_to_heap(start, start + size);
133 }
134 }
135
136 pub fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, ()> {
138 let size = max(
139 layout.size().next_power_of_two(),
140 max(layout.align(), size_of::<usize>()),
141 );
142 let class = size.trailing_zeros() as usize;
143 for i in class..self.free_list.len() {
144 if !self.free_list[i].is_empty() {
146 for j in (class + 1..i + 1).rev() {
148 if let Some(block) = self.free_list[j].pop() {
149 unsafe {
150 self.free_list[j - 1]
151 .push((block as usize + (1 << (j - 1))) as *mut usize);
152 self.free_list[j - 1].push(block);
153 }
154 } else {
155 return Err(());
156 }
157 }
158
159 let result = NonNull::new(
160 self.free_list[class]
161 .pop()
162 .expect("current block should have free space now")
163 as *mut u8,
164 );
165 if let Some(result) = result {
166 self.user += layout.size();
167 self.allocated += size;
168 return Ok(result);
169 } else {
170 return Err(());
171 }
172 }
173 }
174 Err(())
175 }
176
177 pub unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
184 let size = max(
185 layout.size().next_power_of_two(),
186 max(layout.align(), size_of::<usize>()),
187 );
188 let class = size.trailing_zeros() as usize;
189
190 unsafe {
192 self.free_list[class].push(ptr.as_ptr() as *mut usize);
193 }
194
195 let mut current_ptr = ptr.as_ptr() as usize;
197 let mut current_class = class;
198
199 while current_class < self.free_list.len() - 1 {
200 let buddy = current_ptr ^ (1 << current_class);
201 let mut flag = false;
202 for block in self.free_list[current_class].iter_mut() {
203 if block.value() as usize == buddy {
204 block.pop();
205 flag = true;
206 break;
207 }
208 }
209
210 if flag {
212 self.free_list[current_class].pop();
213 current_ptr = min(current_ptr, buddy);
214 current_class += 1;
215 unsafe {
216 self.free_list[current_class].push(current_ptr as *mut usize);
217 }
218 } else {
219 break;
220 }
221 }
222
223 self.user -= layout.size();
224 self.allocated -= size;
225 }
226
227 pub fn stats_alloc_user(&self) -> usize {
229 self.user
230 }
231
232 pub fn stats_alloc_actual(&self) -> usize {
234 self.allocated
235 }
236
237 pub fn stats_total_bytes(&self) -> usize {
239 self.total
240 }
241}
242
243impl<const ORDER: usize> Default for Heap<ORDER> {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249impl<const ORDER: usize> fmt::Debug for Heap<ORDER> {
250 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
251 fmt.debug_struct("Heap")
252 .field("user", &self.user)
253 .field("allocated", &self.allocated)
254 .field("total", &self.total)
255 .finish()
256 }
257}
258
259#[cfg(feature = "use_spin")]
282#[derive(Default)]
283pub struct LockedHeap<const ORDER: usize>(Mutex<Heap<ORDER>>);
284
285#[cfg(feature = "use_spin")]
286impl<const ORDER: usize> LockedHeap<ORDER> {
287 pub const fn new() -> Self {
289 LockedHeap(Mutex::new(Heap::<ORDER>::new()))
290 }
291
292 pub const fn empty() -> Self {
294 LockedHeap(Mutex::new(Heap::<ORDER>::new()))
295 }
296}
297
298#[cfg(feature = "use_spin")]
299impl<const ORDER: usize> Deref for LockedHeap<ORDER> {
300 type Target = Mutex<Heap<ORDER>>;
301
302 fn deref(&self) -> &Self::Target {
303 &self.0
304 }
305}
306
307#[cfg(feature = "use_spin")]
308unsafe impl<const ORDER: usize> GlobalAlloc for LockedHeap<ORDER> {
309 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
310 self.0
311 .lock()
312 .alloc(layout)
313 .ok()
314 .map_or(core::ptr::null_mut(), |allocation| allocation.as_ptr())
315 }
316
317 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
318 unsafe { self.0.lock().dealloc(NonNull::new_unchecked(ptr), layout) }
319 }
320}
321
322#[cfg(feature = "use_spin")]
334pub struct LockedHeapWithRescue<const ORDER: usize> {
335 inner: Mutex<Heap<ORDER>>,
336 rescue: fn(&mut Heap<ORDER>, &Layout),
337}
338
339#[cfg(feature = "use_spin")]
340impl<const ORDER: usize> LockedHeapWithRescue<ORDER> {
341 pub const fn new(rescue: fn(&mut Heap<ORDER>, &Layout)) -> Self {
343 LockedHeapWithRescue {
344 inner: Mutex::new(Heap::<ORDER>::new()),
345 rescue,
346 }
347 }
348}
349
350#[cfg(feature = "use_spin")]
351impl<const ORDER: usize> Deref for LockedHeapWithRescue<ORDER> {
352 type Target = Mutex<Heap<ORDER>>;
353
354 fn deref(&self) -> &Self::Target {
355 &self.inner
356 }
357}
358
359#[cfg(feature = "use_spin")]
360unsafe impl<const ORDER: usize> GlobalAlloc for LockedHeapWithRescue<ORDER> {
361 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
362 let mut inner = self.inner.lock();
363 match inner.alloc(layout) {
364 Ok(allocation) => allocation.as_ptr(),
365 Err(_) => {
366 (self.rescue)(&mut inner, &layout);
367 inner
368 .alloc(layout)
369 .ok()
370 .map_or(core::ptr::null_mut(), |allocation| allocation.as_ptr())
371 }
372 }
373 }
374
375 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
376 unsafe {
377 self.inner
378 .lock()
379 .dealloc(NonNull::new_unchecked(ptr), layout)
380 }
381 }
382}
383
384pub(crate) fn prev_power_of_two(num: usize) -> usize {
385 1 << (usize::BITS as usize - num.leading_zeros() as usize - 1)
386}