Skip to main content

axallocator/
lib.rs

1//! Various allocator algorithms in a unified interface.
2//!
3//! There are three types of allocators:
4//!
5//! - [`ByteAllocator`]: Byte-granularity memory allocator. (e.g.,
6//!   [`BuddyByteAllocator`], [`SlabByteAllocator`])
7//! - [`PageAllocator`]: Page-granularity memory allocator. (e.g.,
8//!   [`BitmapPageAllocator`])
9//! - [`IdAllocator`]: Used to allocate unique IDs.
10
11#![no_std]
12#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
13
14#[cfg(feature = "bitmap")]
15mod bitmap;
16#[cfg(feature = "bitmap")]
17pub use bitmap::BitmapPageAllocator;
18
19#[cfg(feature = "buddy")]
20mod buddy;
21#[cfg(feature = "buddy")]
22pub use buddy::BuddyByteAllocator;
23
24#[cfg(feature = "slab")]
25mod slab;
26#[cfg(feature = "slab")]
27pub use slab::SlabByteAllocator;
28
29#[cfg(feature = "tlsf")]
30mod tlsf;
31#[cfg(feature = "tlsf")]
32pub use tlsf::TlsfByteAllocator;
33
34use core::alloc::Layout;
35use core::ptr::NonNull;
36
37#[cfg(feature = "axerrno")]
38use axerrno::AxError;
39
40/// The error type used for allocation.
41#[derive(Debug)]
42pub enum AllocError {
43    /// Invalid `size` or `align_pow2`. (e.g. unaligned)
44    InvalidParam,
45    /// Memory added by `add_memory` overlapped with existed memory.
46    MemoryOverlap,
47    /// No enough memory to allocate.
48    NoMemory,
49    /// Deallocate an unallocated memory region.
50    NotAllocated,
51}
52
53#[cfg(feature = "axerrno")]
54impl From<AllocError> for AxError {
55    fn from(value: AllocError) -> Self {
56        match value {
57            AllocError::NoMemory => AxError::NoMemory,
58            _ => AxError::InvalidInput,
59        }
60    }
61}
62
63/// A [`Result`] type with [`AllocError`] as the error type.
64pub type AllocResult<T = ()> = Result<T, AllocError>;
65
66/// The base allocator inherited by other allocators.
67pub trait BaseAllocator {
68    /// Initialize the allocator with a free memory region.
69    fn init(&mut self, start: usize, size: usize);
70
71    /// Add a free memory region to the allocator.
72    fn add_memory(&mut self, start: usize, size: usize) -> AllocResult;
73}
74
75/// Byte-granularity allocator.
76pub trait ByteAllocator: BaseAllocator {
77    /// Allocate memory with the given size (in bytes) and alignment.
78    fn alloc(&mut self, layout: Layout) -> AllocResult<NonNull<u8>>;
79
80    /// Deallocate memory at the given position, size, and alignment.
81    fn dealloc(&mut self, pos: NonNull<u8>, layout: Layout);
82
83    /// Returns total memory size in bytes.
84    fn total_bytes(&self) -> usize;
85
86    /// Returns allocated memory size in bytes.
87    fn used_bytes(&self) -> usize;
88
89    /// Returns available memory size in bytes.
90    fn available_bytes(&self) -> usize;
91}
92
93/// Page-granularity allocator.
94pub trait PageAllocator: BaseAllocator {
95    /// The size of a memory page.
96    const PAGE_SIZE: usize;
97
98    /// Allocate contiguous memory pages with given count and alignment.
99    fn alloc_pages(&mut self, num_pages: usize, align_pow2: usize) -> AllocResult<usize>;
100
101    /// Deallocate contiguous memory pages with given position and count.
102    fn dealloc_pages(&mut self, pos: usize, num_pages: usize);
103
104    /// Allocate contiguous memory pages with given base address, count and alignment.
105    fn alloc_pages_at(
106        &mut self,
107        base: usize,
108        num_pages: usize,
109        align_pow2: usize,
110    ) -> AllocResult<usize>;
111
112    /// Returns the total number of memory pages.
113    fn total_pages(&self) -> usize;
114
115    /// Returns the number of allocated memory pages.
116    fn used_pages(&self) -> usize;
117
118    /// Returns the number of available memory pages.
119    fn available_pages(&self) -> usize;
120}
121
122/// Used to allocate unique IDs (e.g., thread ID).
123pub trait IdAllocator: BaseAllocator {
124    /// Allocate contiguous IDs with given count and alignment.
125    fn alloc_id(&mut self, count: usize, align_pow2: usize) -> AllocResult<usize>;
126
127    /// Deallocate contiguous IDs with given position and count.
128    fn dealloc_id(&mut self, start_id: usize, count: usize);
129
130    /// Whether the given `id` was allocated.
131    fn is_allocated(&self, id: usize) -> bool;
132
133    /// Mark the given `id` has been allocated and cannot be reallocated.
134    fn alloc_fixed_id(&mut self, id: usize) -> AllocResult;
135
136    /// Returns the maximum number of supported IDs.
137    fn size(&self) -> usize;
138
139    /// Returns the number of allocated IDs.
140    fn used(&self) -> usize;
141
142    /// Returns the number of available IDs.
143    fn available(&self) -> usize;
144}
145
146#[inline]
147#[allow(dead_code)]
148const fn align_down(pos: usize, align: usize) -> usize {
149    pos & !(align - 1)
150}
151
152#[inline]
153#[allow(dead_code)]
154const fn align_up(pos: usize, align: usize) -> usize {
155    (pos + align - 1) & !(align - 1)
156}
157
158/// Checks whether the address has the demanded alignment.
159///
160/// Equivalent to `addr % align == 0`, but the alignment must be a power of two.
161#[inline]
162#[allow(dead_code)]
163const fn is_aligned(base_addr: usize, align: usize) -> bool {
164    base_addr & (align - 1) == 0
165}
166
167#[cfg(feature = "allocator_api")]
168mod allocator_api {
169    extern crate alloc;
170
171    use super::ByteAllocator;
172    use alloc::rc::Rc;
173    use core::alloc::{AllocError, Allocator, Layout};
174    use core::cell::RefCell;
175    use core::ptr::NonNull;
176
177    /// A byte-allocator wrapped in [`Rc<RefCell>`] that implements [`core::alloc::Allocator`].
178    pub struct AllocatorRc<A: ByteAllocator>(Rc<RefCell<A>>);
179
180    impl<A: ByteAllocator> AllocatorRc<A> {
181        /// Creates a new allocator with the given memory pool.
182        pub fn new(mut inner: A, pool: &mut [u8]) -> Self {
183            inner.init(pool.as_mut_ptr() as usize, pool.len());
184            Self(Rc::new(RefCell::new(inner)))
185        }
186    }
187
188    unsafe impl<A: ByteAllocator> Allocator for AllocatorRc<A> {
189        fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
190            match layout.size() {
191                0 => Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0)),
192                size => {
193                    let raw_addr = self.0.borrow_mut().alloc(layout).map_err(|_| AllocError)?;
194                    Ok(NonNull::slice_from_raw_parts(raw_addr, size))
195                }
196            }
197        }
198
199        unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
200            self.0.borrow_mut().dealloc(ptr, layout)
201        }
202    }
203
204    impl<A: ByteAllocator> Clone for AllocatorRc<A> {
205        fn clone(&self) -> Self {
206            Self(self.0.clone())
207        }
208    }
209}
210
211#[cfg(feature = "allocator_api")]
212pub use allocator_api::AllocatorRc;