buddy_slab_allocator/lib.rs
1//! buddy-slab-allocator Memory Allocator
2//!
3//! This crate implements a high-performance memory allocator designed for embedded
4//! and kernel environments, featuring:
5//! - Buddy page allocator for page-level allocation
6//! - Slab allocator for small object allocation
7//! - Global allocator coordination
8//! - Zero `std` dependency (fully `#![no_std]`)
9//!
10//! # Features
11//!
12//! - **Buddy Page Allocator**: Efficient page-level memory allocation with automatic merging
13//! - **Slab Byte Allocator**: Fast small object allocation (≤2048 bytes)
14//! - **Global Allocator**: Automatic selection between page and slab allocation based on size
15//! - **No_std Compatible**: Fully `#![no_std]` for embedded/kernel use
16//! - **Optional Logging**: Conditional compilation with `log` feature
17//! - **Memory Tracking**: Detailed statistics with `tracking` feature
18//!
19//! # Quick Start
20//!
21//! ```no_run
22//! use buddy_slab_allocator::{GlobalAllocator, PageAllocator};
23//! use core::alloc::Layout;
24//!
25//! const PAGE_SIZE: usize = 0x1000;
26//! let mut allocator = GlobalAllocator::<PAGE_SIZE>::new();
27//!
28//! // Initialize with memory region
29//! let heap_start = 0x8000_0000;
30//! let heap_size = 16 * 1024 * 1024; // 16MB
31//! allocator.init(heap_start, heap_size).unwrap();
32//!
33//! // Allocate pages
34//! let addr = allocator.alloc_pages(4, PAGE_SIZE).unwrap();
35//! // Use the allocated memory...
36//! allocator.dealloc_pages(addr, 4);
37//! ```
38//!
39//! # Small Object Allocation
40//!
41//! ```no_run
42//! use buddy_slab_allocator::GlobalAllocator;
43//! use core::alloc::Layout;
44//!
45//! const PAGE_SIZE: usize = 0x1000;
46//! let mut allocator = GlobalAllocator::<PAGE_SIZE>::new();
47//! allocator.init(0x8000_0000, 16 * 1024 * 1024).unwrap();
48//!
49//! // Small allocations go through slab allocator
50//! let layout = Layout::from_size_align(64, 8).unwrap();
51//! let ptr = allocator.alloc(layout).unwrap();
52//! // Use the allocated memory...
53//! allocator.dealloc(ptr, layout);
54//! ```
55//!
56//! # Statistics Tracking
57//!
58//! ```no_run
59//! # #[cfg(feature = "tracking")]
60//! # {
61//! use buddy_slab_allocator::GlobalAllocator;
62//!
63//! const PAGE_SIZE: usize = 0x1000;
64//! let mut allocator = GlobalAllocator::<PAGE_SIZE>::new();
65//! allocator.init(0x8000_0000, 16 * 1024 * 1024).unwrap();
66//!
67//! let stats = allocator.get_stats();
68//! println!("Total pages: {}", stats.total_pages);
69//! println!("Used pages: {}", stats.used_pages);
70//! println!("Free pages: {}", stats.free_pages);
71//! # }
72//! ```
73
74#![no_std]
75
76extern crate alloc;
77
78use core::alloc::Layout;
79use core::ptr::NonNull;
80
81// Logging support - conditionally import log crate
82#[cfg(feature = "log")]
83extern crate log;
84
85// Stub macros when log is disabled - these become no-ops
86#[cfg(not(feature = "log"))]
87macro_rules! error {
88 ($($arg:tt)*) => {};
89}
90#[cfg(not(feature = "log"))]
91macro_rules! warn {
92 ($($arg:tt)*) => {};
93}
94#[cfg(not(feature = "log"))]
95macro_rules! info {
96 ($($arg:tt)*) => {};
97}
98#[cfg(not(feature = "log"))]
99macro_rules! debug {
100 ($($arg:tt)*) => {};
101}
102#[cfg(not(feature = "log"))]
103#[allow(unused_macros)]
104macro_rules! trace {
105 ($($arg:tt)*) => {};
106}
107
108/// Default page size for backward compatibility (4KB)
109pub const DEFAULT_PAGE_SIZE: usize = 0x1000;
110
111/// The error type used for allocation operations.
112///
113/// # Examples
114///
115/// ```
116/// use buddy_slab_allocator::AllocError;
117///
118/// fn handle_error(error: AllocError) {
119/// match error {
120/// AllocError::InvalidParam => eprintln!("Invalid parameters"),
121/// AllocError::MemoryOverlap => eprintln!("Memory regions overlap"),
122/// AllocError::NoMemory => eprintln!("Out of memory"),
123/// AllocError::NotAllocated => eprintln!("Double free detected"),
124/// }
125/// }
126/// ```
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum AllocError {
129 /// Invalid `size` or alignment (e.g. unaligned)
130 InvalidParam,
131 /// Memory added by `add_memory` overlapped with existing memory
132 MemoryOverlap,
133 /// No enough memory to allocate
134 NoMemory,
135 /// Attempt to deallocate a memory region that was not allocated
136 NotAllocated,
137}
138
139/// A [`Result`] type with [`AllocError`] as the error type.
140pub type AllocResult<T = ()> = Result<T, AllocError>;
141
142/// Address translator used by allocators to reason about physical addresses.
143///
144/// Implementations should provide a stable virtual-to-physical mapping
145/// for the allocator-managed address range.
146///
147/// # Examples
148///
149/// ```
150/// use buddy_slab_allocator::AddrTranslator;
151///
152/// struct SimpleMapper;
153///
154/// impl AddrTranslator for SimpleMapper {
155/// fn virt_to_phys(&self, va: usize) -> Option<usize> {
156/// // Identity mapping for this example
157/// Some(va)
158/// }
159/// }
160/// ```
161pub trait AddrTranslator: Sync {
162 /// Translate a virtual address to a physical address.
163 ///
164 /// Returns `None` if the address is not valid or not mapped.
165 fn virt_to_phys(&self, va: usize) -> Option<usize>;
166}
167
168/// The base allocator trait inherited by other allocator traits.
169///
170/// Provides common initialization methods for all allocator types.
171pub trait BaseAllocator {
172 /// Initialize the allocator with a free memory region.
173 ///
174 /// # Arguments
175 ///
176 /// * `start` - Starting address of the memory region
177 /// * `size` - Size of the memory region in bytes
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// # use buddy_slab_allocator::BaseAllocator;
183 /// # struct MyAllocator;
184 /// # impl BaseAllocator for MyAllocator {
185 /// # fn init(&mut self, start: usize, size: usize) {}
186 /// # fn add_memory(&mut self, start: usize, size: usize) -> buddy_slab_allocator::AllocResult { Ok(()) }
187 /// # }
188 /// let mut alloc = MyAllocator;
189 /// alloc.init(0x8000_0000, 16 * 1024 * 1024);
190 /// ```
191 fn init(&mut self, start: usize, size: usize);
192
193 /// Add a free memory region to the allocator.
194 ///
195 /// # Arguments
196 ///
197 /// * `start` - Starting address of the memory region
198 /// * `size` - Size of the memory region in bytes
199 ///
200 /// # Returns
201 ///
202 /// Returns `Ok(())` on success, or an error if the region overlaps
203 /// with existing memory.
204 fn add_memory(&mut self, start: usize, size: usize) -> AllocResult;
205}
206
207/// Byte-granularity allocator for arbitrary-size allocations.
208///
209/// Provides methods for allocating and deallocating memory with
210/// byte-level granularity.
211pub trait ByteAllocator {
212 /// Allocate memory with the given size (in bytes) and alignment.
213 ///
214 /// # Arguments
215 ///
216 /// * `layout` - Memory layout specifying size and alignment requirements
217 ///
218 /// # Returns
219 ///
220 /// Returns a pointer to the allocated memory on success, or an error
221 /// if allocation fails.
222 ///
223 /// # Examples
224 ///
225 /// ```
226 /// # use buddy_slab_allocator::ByteAllocator;
227 /// # use core::alloc::Layout;
228 /// # use core::ptr::NonNull;
229 /// # struct MyAllocator;
230 /// # impl ByteAllocator for MyAllocator {
231 /// # fn alloc(&mut self, layout: Layout) -> buddy_slab_allocator::AllocResult<NonNull<u8>> { Ok(NonNull::dangling()) }
232 /// # fn dealloc(&mut self, pos: NonNull<u8>, layout: Layout) {}
233 /// # fn total_bytes(&self) -> usize { 0 }
234 /// # fn used_bytes(&self) -> usize { 0 }
235 /// # fn available_bytes(&self) -> usize { 0 }
236 /// # }
237 /// let mut alloc = MyAllocator;
238 /// let layout = Layout::from_size_align(64, 8).unwrap();
239 /// let ptr = alloc.alloc(layout)?;
240 /// # Ok::<(), buddy_slab_allocator::AllocError>(())
241 /// ```
242 fn alloc(&mut self, layout: Layout) -> AllocResult<NonNull<u8>>;
243
244 /// Deallocate memory at the given position, size, and alignment.
245 ///
246 /// # Arguments
247 ///
248 /// * `pos` - Pointer to the memory to deallocate
249 /// * `layout` - Memory layout specifying size and alignment requirements
250 ///
251 /// # Safety
252 ///
253 /// The pointer must have been previously allocated from this allocator
254 /// with the same layout.
255 fn dealloc(&mut self, pos: NonNull<u8>, layout: Layout);
256
257 /// Returns total memory size in bytes managed by this allocator.
258 fn total_bytes(&self) -> usize;
259
260 /// Returns allocated memory size in bytes.
261 fn used_bytes(&self) -> usize;
262
263 /// Returns available memory size in bytes.
264 fn available_bytes(&self) -> usize;
265}
266
267/// Page-granularity allocator for managing memory in pages.
268///
269/// Provides methods for allocating and deallocating contiguous pages
270/// of memory with specific alignment requirements.
271pub trait PageAllocator: BaseAllocator {
272 /// The size of a memory page in bytes (must be a power of two).
273 const PAGE_SIZE: usize;
274
275 /// Allocate contiguous memory pages with given count and alignment (in bytes).
276 ///
277 /// # Arguments
278 ///
279 /// * `num_pages` - Number of pages to allocate
280 /// * `alignment` - Alignment requirement in bytes (must be power of two)
281 ///
282 /// # Returns
283 ///
284 /// Returns the starting address of the allocated pages on success,
285 /// or an error if allocation fails.
286 ///
287 /// # Examples
288 ///
289 /// ```
290 /// # use buddy_slab_allocator::{PageAllocator, BaseAllocator};
291 /// # struct MyAllocator;
292 /// # impl BaseAllocator for MyAllocator {
293 /// # fn init(&mut self, start: usize, size: usize) {}
294 /// # fn add_memory(&mut self, start: usize, size: usize) -> buddy_slab_allocator::AllocResult { Ok(()) }
295 /// # }
296 /// # impl PageAllocator for MyAllocator {
297 /// # const PAGE_SIZE: usize = 0x1000;
298 /// # fn alloc_pages(&mut self, num_pages: usize, alignment: usize) -> buddy_slab_allocator::AllocResult<usize> { Ok(0) }
299 /// # fn dealloc_pages(&mut self, pos: usize, num_pages: usize) {}
300 /// # fn alloc_pages_at(&mut self, base: usize, num_pages: usize, alignment: usize) -> buddy_slab_allocator::AllocResult<usize> { Ok(0) }
301 /// # fn total_pages(&self) -> usize { 0 }
302 /// # fn used_pages(&self) -> usize { 0 }
303 /// # fn available_pages(&self) -> usize { 0 }
304 /// # }
305 /// let mut alloc = MyAllocator;
306 /// let addr = alloc.alloc_pages(4, 0x1000)?;
307 /// # Ok::<(), buddy_slab_allocator::AllocError>(())
308 /// ```
309 fn alloc_pages(&mut self, num_pages: usize, alignment: usize) -> AllocResult<usize>;
310
311 /// Deallocate contiguous memory pages with given position and count.
312 ///
313 /// # Arguments
314 ///
315 /// * `pos` - Starting address of the pages to deallocate
316 /// * `num_pages` - Number of pages to deallocate
317 ///
318 /// # Safety
319 ///
320 /// The address range must have been previously allocated from this allocator.
321 fn dealloc_pages(&mut self, pos: usize, num_pages: usize);
322
323 /// Allocate contiguous memory pages with given base address, count and alignment (in bytes).
324 ///
325 /// # Arguments
326 ///
327 /// * `base` - Desired starting address for allocation
328 /// * `num_pages` - Number of pages to allocate
329 /// * `alignment` - Alignment requirement in bytes (must be power of two)
330 ///
331 /// # Returns
332 ///
333 /// Returns the starting address of the allocated pages on success,
334 /// or an error if the region cannot be allocated at the specified base.
335 fn alloc_pages_at(
336 &mut self,
337 base: usize,
338 num_pages: usize,
339 alignment: usize,
340 ) -> AllocResult<usize>;
341
342 /// Returns the total number of memory pages managed by this allocator.
343 fn total_pages(&self) -> usize;
344
345 /// Returns the number of allocated memory pages.
346 fn used_pages(&self) -> usize;
347
348 /// Returns the number of available memory pages.
349 fn available_pages(&self) -> usize;
350}
351
352/// ID allocator for managing unique identifiers (e.g., thread IDs).
353///
354/// Provides methods for allocating and deallocating unique IDs with
355/// alignment constraints.
356pub trait IdAllocator: BaseAllocator {
357 /// Allocate contiguous IDs with given count and alignment.
358 ///
359 /// # Arguments
360 ///
361 /// * `count` - Number of IDs to allocate
362 /// * `alignment` - Alignment requirement for the starting ID
363 ///
364 /// # Returns
365 ///
366 /// Returns the starting ID on success, or an error if allocation fails.
367 fn alloc_id(&mut self, count: usize, alignment: usize) -> AllocResult<usize>;
368
369 /// Deallocate contiguous IDs with given position and count.
370 ///
371 /// # Arguments
372 ///
373 /// * `start_id` - Starting ID of the range to deallocate
374 /// * `count` - Number of IDs to deallocate
375 ///
376 /// # Safety
377 ///
378 /// The ID range must have been previously allocated from this allocator.
379 fn dealloc_id(&mut self, start_id: usize, count: usize);
380
381 /// Checks whether the given ID is currently allocated.
382 fn is_allocated(&self, id: usize) -> bool;
383
384 /// Mark the given ID as allocated and prevent it from being reallocated.
385 ///
386 /// # Arguments
387 ///
388 /// * `id` - The ID to mark as permanently allocated
389 ///
390 /// # Returns
391 ///
392 /// Returns `Ok(())` on success, or an error if the ID is already allocated.
393 fn alloc_fixed_id(&mut self, id: usize) -> AllocResult;
394
395 /// Returns the maximum number of IDs supported by this allocator.
396 fn size(&self) -> usize;
397
398 /// Returns the number of currently allocated IDs.
399 fn used(&self) -> usize;
400
401 /// Returns the number of available IDs.
402 fn available(&self) -> usize;
403}
404
405#[inline]
406#[allow(dead_code)]
407const fn align_down(pos: usize, align: usize) -> usize {
408 pos & !(align - 1)
409}
410
411#[inline]
412#[allow(dead_code)]
413const fn align_up(pos: usize, align: usize) -> usize {
414 (pos + align - 1) & !(align - 1)
415}
416
417/// Checks whether the address has the demanded alignment.
418///
419/// Equivalent to `addr % align == 0`, but the alignment must be a power of two.
420#[inline]
421#[allow(dead_code)]
422const fn is_aligned(base_addr: usize, align: usize) -> bool {
423 base_addr & (align - 1) == 0
424}
425
426// Export our allocator implementations
427pub mod buddy;
428#[cfg(feature = "tracking")]
429pub use buddy::BuddyStats;
430pub use buddy::{BuddyPageAllocator, DEFAULT_MAX_ORDER, MAX_ZONES};
431
432pub mod page_allocator;
433pub use page_allocator::CompositePageAllocator;
434
435pub mod slab;
436pub use slab::slab_byte_allocator::{PageAllocatorForSlab, SizeClass, SlabByteAllocator};
437
438pub mod global_allocator;
439pub use global_allocator::GlobalAllocator;
440#[cfg(feature = "tracking")]
441pub use global_allocator::UsageStats;