Skip to main content

buddy_system_allocator/
lib.rs

1#![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
34/// A heap that uses buddy system with configurable order.
35///
36/// # Usage
37///
38/// Create a heap and add a memory region to it:
39/// ```
40/// use buddy_system_allocator::*;
41/// # use core::mem::size_of;
42/// // The max order of the buddy system is `ORDER - 1`.
43/// // For example, to create a heap with a maximum block size of 2^32 bytes,
44/// // you should define the heap with `ORDER = 33`.
45/// let mut heap = Heap::<33>::empty();
46/// # let space: [usize; 100] = [0; 100];
47/// # let begin: usize = space.as_ptr() as usize;
48/// # let end: usize = begin + 100 * size_of::<usize>();
49/// # let size: usize = 100 * size_of::<usize>();
50/// unsafe {
51///     heap.init(begin, size);
52///     // or
53///     heap.add_to_heap(begin, end);
54/// }
55/// ```
56pub struct Heap<const ORDER: usize> {
57    // buddy system with max order of `ORDER - 1`
58    free_list: [linked_list::LinkedList; ORDER],
59
60    // statistics
61    user: usize,
62    allocated: usize,
63    total: usize,
64}
65
66impl<const ORDER: usize> Heap<ORDER> {
67    /// Create an empty heap
68    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    /// Create an empty heap
78    pub const fn empty() -> Self {
79        Self::new()
80    }
81
82    /// Add a range of memory `[start, end)` to the heap.
83    ///
84    /// # Safety
85    ///
86    /// The caller must ensure the memory range is valid, writable, and not currently managed by
87    /// any other allocator or by this `Heap`. In particular, the provided `[start, end)` range
88    /// must not overlap with any memory region that has already been added to this `Heap`. The
89    /// range must remain available for the lifetime of this heap.
90    pub unsafe fn add_to_heap(&mut self, mut start: usize, mut end: usize) {
91        // avoid unaligned access on some platforms
92        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            // If the order of size is larger than the max order,
104            // split it into smaller blocks.
105            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    /// Add a range of memory `[start, start + size)` to the heap.
122    ///
123    /// # Safety
124    ///
125    /// The caller must ensure the memory range is valid, writable, and not currently managed by
126    /// any other allocator. Additionally, the range `[start, start + size)` must be disjoint from
127    /// every memory region previously added to this heap instance, whether via
128    /// [`Heap::add_to_heap`] or [`Heap::init`]. The range must remain available for the lifetime
129    /// of this heap.
130    pub unsafe fn init(&mut self, start: usize, size: usize) {
131        unsafe {
132            self.add_to_heap(start, start + size);
133        }
134    }
135
136    /// Alloc a range of memory from the heap satifying `layout` requirements
137    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            // Find the first non-empty size class
145            if !self.free_list[i].is_empty() {
146                // Split buffers
147                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    /// Dealloc a range of memory from the heap.
178    ///
179    /// # Safety
180    ///
181    /// `ptr` and `layout` must exactly match a previous successful allocation from this specific
182    /// `Heap` instance, and that allocation must not already have been deallocated.
183    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        // Put back into free list
191        unsafe {
192            self.free_list[class].push(ptr.as_ptr() as *mut usize);
193        }
194
195        // Merge free buddy lists
196        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            // Free buddy found
211            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    /// Return the number of bytes that user requests
228    pub fn stats_alloc_user(&self) -> usize {
229        self.user
230    }
231
232    /// Return the number of bytes that are actually allocated
233    pub fn stats_alloc_actual(&self) -> usize {
234        self.allocated
235    }
236
237    /// Return the total number of bytes in the heap
238    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/// A locked version of `Heap`
260///
261/// # Usage
262///
263/// Create a locked heap and add a memory region to it:
264/// ```
265/// use buddy_system_allocator::*;
266/// # use core::mem::size_of;
267/// // The max order of the buddy system is `ORDER - 1`.
268/// // For example, to create a heap with a maximum block size of 2^32 bytes,
269/// // you should define the heap with `ORDER = 33`.
270/// let mut heap = LockedHeap::<33>::new();
271/// # let space: [usize; 100] = [0; 100];
272/// # let begin: usize = space.as_ptr() as usize;
273/// # let end: usize = begin + 100 * size_of::<usize>();
274/// # let size: usize = 100 * size_of::<usize>();
275/// unsafe {
276///     heap.lock().init(begin, size);
277///     // or
278///     heap.lock().add_to_heap(begin, end);
279/// }
280/// ```
281#[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    /// Creates an empty heap
288    pub const fn new() -> Self {
289        LockedHeap(Mutex::new(Heap::<ORDER>::new()))
290    }
291
292    /// Creates an empty heap
293    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/// A locked version of `Heap` with rescue before oom
323///
324/// # Usage
325///
326/// Create a locked heap:
327/// ```
328/// use buddy_system_allocator::*;
329/// let heap = LockedHeapWithRescue::new(|heap: &mut Heap<33>, layout: &core::alloc::Layout| {});
330/// ```
331///
332/// Before oom, the allocator will try to call rescue function and try for one more time.
333#[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    /// Creates an empty heap
342    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}