Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tracking = []
bench = []

[dependencies]
axallocator = "0.2"
cfg-if = "1.0"
log = { version = "0.4", optional = true }

Expand Down
275 changes: 4 additions & 271 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@

extern crate alloc;

use core::alloc::Layout;
use core::ptr::NonNull;

// Logging support - conditionally import log crate
#[cfg(feature = "log")]
extern crate log;
Expand Down Expand Up @@ -105,40 +102,13 @@ macro_rules! trace {
($($arg:tt)*) => {};
}

pub use axallocator::{
AllocError, AllocResult, BaseAllocator, ByteAllocator, IdAllocator, PageAllocator,
};

/// Default page size for backward compatibility (4KB)
pub const DEFAULT_PAGE_SIZE: usize = 0x1000;

/// The error type used for allocation operations.
///
/// # Examples
///
/// ```
/// use buddy_slab_allocator::AllocError;
///
/// fn handle_error(error: AllocError) {
/// match error {
/// AllocError::InvalidParam => eprintln!("Invalid parameters"),
/// AllocError::MemoryOverlap => eprintln!("Memory regions overlap"),
/// AllocError::NoMemory => eprintln!("Out of memory"),
/// AllocError::NotAllocated => eprintln!("Double free detected"),
/// }
/// }
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AllocError {
/// Invalid `size` or alignment (e.g. unaligned)
InvalidParam,
/// Memory added by `add_memory` overlapped with existing memory
MemoryOverlap,
/// No enough memory to allocate
NoMemory,
/// Attempt to deallocate a memory region that was not allocated
NotAllocated,
}

/// A [`Result`] type with [`AllocError`] as the error type.
pub type AllocResult<T = ()> = Result<T, AllocError>;

/// Address translator used by allocators to reason about physical addresses.
///
/// Implementations should provide a stable virtual-to-physical mapping
Expand All @@ -165,243 +135,6 @@ pub trait AddrTranslator: Sync {
fn virt_to_phys(&self, va: usize) -> Option<usize>;
}

/// The base allocator trait inherited by other allocator traits.
///
/// Provides common initialization methods for all allocator types.
pub trait BaseAllocator {
/// Initialize the allocator with a free memory region.
///
/// # Arguments
///
/// * `start` - Starting address of the memory region
/// * `size` - Size of the memory region in bytes
///
/// # Examples
///
/// ```
/// # use buddy_slab_allocator::BaseAllocator;
/// # struct MyAllocator;
/// # impl BaseAllocator for MyAllocator {
/// # fn init(&mut self, start: usize, size: usize) {}
/// # fn add_memory(&mut self, start: usize, size: usize) -> buddy_slab_allocator::AllocResult { Ok(()) }
/// # }
/// let mut alloc = MyAllocator;
/// alloc.init(0x8000_0000, 16 * 1024 * 1024);
/// ```
fn init(&mut self, start: usize, size: usize);

/// Add a free memory region to the allocator.
///
/// # Arguments
///
/// * `start` - Starting address of the memory region
/// * `size` - Size of the memory region in bytes
///
/// # Returns
///
/// Returns `Ok(())` on success, or an error if the region overlaps
/// with existing memory.
fn add_memory(&mut self, start: usize, size: usize) -> AllocResult;
}

/// Byte-granularity allocator for arbitrary-size allocations.
///
/// Provides methods for allocating and deallocating memory with
/// byte-level granularity.
pub trait ByteAllocator {
/// Allocate memory with the given size (in bytes) and alignment.
///
/// # Arguments
///
/// * `layout` - Memory layout specifying size and alignment requirements
///
/// # Returns
///
/// Returns a pointer to the allocated memory on success, or an error
/// if allocation fails.
///
/// # Examples
///
/// ```
/// # use buddy_slab_allocator::ByteAllocator;
/// # use core::alloc::Layout;
/// # use core::ptr::NonNull;
/// # struct MyAllocator;
/// # impl ByteAllocator for MyAllocator {
/// # fn alloc(&mut self, layout: Layout) -> buddy_slab_allocator::AllocResult<NonNull<u8>> { Ok(NonNull::dangling()) }
/// # fn dealloc(&mut self, pos: NonNull<u8>, layout: Layout) {}
/// # fn total_bytes(&self) -> usize { 0 }
/// # fn used_bytes(&self) -> usize { 0 }
/// # fn available_bytes(&self) -> usize { 0 }
/// # }
/// let mut alloc = MyAllocator;
/// let layout = Layout::from_size_align(64, 8).unwrap();
/// let ptr = alloc.alloc(layout)?;
/// # Ok::<(), buddy_slab_allocator::AllocError>(())
/// ```
fn alloc(&mut self, layout: Layout) -> AllocResult<NonNull<u8>>;

/// Deallocate memory at the given position, size, and alignment.
///
/// # Arguments
///
/// * `pos` - Pointer to the memory to deallocate
/// * `layout` - Memory layout specifying size and alignment requirements
///
/// # Safety
///
/// The pointer must have been previously allocated from this allocator
/// with the same layout.
fn dealloc(&mut self, pos: NonNull<u8>, layout: Layout);

/// Returns total memory size in bytes managed by this allocator.
fn total_bytes(&self) -> usize;

/// Returns allocated memory size in bytes.
fn used_bytes(&self) -> usize;

/// Returns available memory size in bytes.
fn available_bytes(&self) -> usize;
}

/// Page-granularity allocator for managing memory in pages.
///
/// Provides methods for allocating and deallocating contiguous pages
/// of memory with specific alignment requirements.
pub trait PageAllocator: BaseAllocator {
/// The size of a memory page in bytes (must be a power of two).
const PAGE_SIZE: usize;

/// Allocate contiguous memory pages with given count and alignment (in bytes).
///
/// # Arguments
///
/// * `num_pages` - Number of pages to allocate
/// * `alignment` - Alignment requirement in bytes (must be power of two)
///
/// # Returns
///
/// Returns the starting address of the allocated pages on success,
/// or an error if allocation fails.
///
/// # Examples
///
/// ```
/// # use buddy_slab_allocator::{PageAllocator, BaseAllocator};
/// # struct MyAllocator;
/// # impl BaseAllocator for MyAllocator {
/// # fn init(&mut self, start: usize, size: usize) {}
/// # fn add_memory(&mut self, start: usize, size: usize) -> buddy_slab_allocator::AllocResult { Ok(()) }
/// # }
/// # impl PageAllocator for MyAllocator {
/// # const PAGE_SIZE: usize = 0x1000;
/// # fn alloc_pages(&mut self, num_pages: usize, alignment: usize) -> buddy_slab_allocator::AllocResult<usize> { Ok(0) }
/// # fn dealloc_pages(&mut self, pos: usize, num_pages: usize) {}
/// # fn alloc_pages_at(&mut self, base: usize, num_pages: usize, alignment: usize) -> buddy_slab_allocator::AllocResult<usize> { Ok(0) }
/// # fn total_pages(&self) -> usize { 0 }
/// # fn used_pages(&self) -> usize { 0 }
/// # fn available_pages(&self) -> usize { 0 }
/// # }
/// let mut alloc = MyAllocator;
/// let addr = alloc.alloc_pages(4, 0x1000)?;
/// # Ok::<(), buddy_slab_allocator::AllocError>(())
/// ```
fn alloc_pages(&mut self, num_pages: usize, alignment: usize) -> AllocResult<usize>;

/// Deallocate contiguous memory pages with given position and count.
///
/// # Arguments
///
/// * `pos` - Starting address of the pages to deallocate
/// * `num_pages` - Number of pages to deallocate
///
/// # Safety
///
/// The address range must have been previously allocated from this allocator.
fn dealloc_pages(&mut self, pos: usize, num_pages: usize);

/// Allocate contiguous memory pages with given base address, count and alignment (in bytes).
///
/// # Arguments
///
/// * `base` - Desired starting address for allocation
/// * `num_pages` - Number of pages to allocate
/// * `alignment` - Alignment requirement in bytes (must be power of two)
///
/// # Returns
///
/// Returns the starting address of the allocated pages on success,
/// or an error if the region cannot be allocated at the specified base.
fn alloc_pages_at(
&mut self,
base: usize,
num_pages: usize,
alignment: usize,
) -> AllocResult<usize>;

/// Returns the total number of memory pages managed by this allocator.
fn total_pages(&self) -> usize;

/// Returns the number of allocated memory pages.
fn used_pages(&self) -> usize;

/// Returns the number of available memory pages.
fn available_pages(&self) -> usize;
}

/// ID allocator for managing unique identifiers (e.g., thread IDs).
///
/// Provides methods for allocating and deallocating unique IDs with
/// alignment constraints.
pub trait IdAllocator: BaseAllocator {
/// Allocate contiguous IDs with given count and alignment.
///
/// # Arguments
///
/// * `count` - Number of IDs to allocate
/// * `alignment` - Alignment requirement for the starting ID
///
/// # Returns
///
/// Returns the starting ID on success, or an error if allocation fails.
fn alloc_id(&mut self, count: usize, alignment: usize) -> AllocResult<usize>;

/// Deallocate contiguous IDs with given position and count.
///
/// # Arguments
///
/// * `start_id` - Starting ID of the range to deallocate
/// * `count` - Number of IDs to deallocate
///
/// # Safety
///
/// The ID range must have been previously allocated from this allocator.
fn dealloc_id(&mut self, start_id: usize, count: usize);

/// Checks whether the given ID is currently allocated.
fn is_allocated(&self, id: usize) -> bool;

/// Mark the given ID as allocated and prevent it from being reallocated.
///
/// # Arguments
///
/// * `id` - The ID to mark as permanently allocated
///
/// # Returns
///
/// Returns `Ok(())` on success, or an error if the ID is already allocated.
fn alloc_fixed_id(&mut self, id: usize) -> AllocResult;

/// Returns the maximum number of IDs supported by this allocator.
fn size(&self) -> usize;

/// Returns the number of currently allocated IDs.
fn used(&self) -> usize;

/// Returns the number of available IDs.
fn available(&self) -> usize;
}

#[inline]
#[allow(dead_code)]
const fn align_down(pos: usize, align: usize) -> usize {
Expand Down
11 changes: 10 additions & 1 deletion src/slab/slab_byte_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use core::ptr::NonNull;
#[cfg(feature = "log")]
use log::warn;

use crate::{AllocError, AllocResult, ByteAllocator};
use crate::{AllocError, AllocResult, BaseAllocator, ByteAllocator};

// Re-export public types from sibling modules
pub use super::slab_cache::SlabCache;
Expand Down Expand Up @@ -151,6 +151,15 @@ impl<const PAGE_SIZE: usize> Default for SlabByteAllocator<PAGE_SIZE> {
}
}

// This implementation is never called, so no-op implementations are fine
impl<const PAGE_SIZE: usize> BaseAllocator for SlabByteAllocator<PAGE_SIZE> {
fn init(&mut self, _start: usize, _size: usize) {}

fn add_memory(&mut self, _start: usize, _size: usize) -> AllocResult {
Ok(())
}
}

impl<const PAGE_SIZE: usize> ByteAllocator for SlabByteAllocator<PAGE_SIZE> {
fn alloc(&mut self, layout: Layout) -> AllocResult<NonNull<u8>> {
let size_class = SizeClass::from_layout(layout).ok_or(AllocError::InvalidParam)?;
Expand Down
9 changes: 2 additions & 7 deletions tests/dma32_pages_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,8 @@ fn test_alloc_dma32_pages_uninitialized() {
// Try to allocate pages - should fail because allocator is not initialized
let result = allocator.alloc_dma32_pages(1, PAGE_SIZE);
assert!(
result.is_err(),
"Expected error when allocating from uninitialized allocator"
);
assert_eq!(
result.unwrap_err(),
AllocError::NoMemory,
"Expected NoMemory error"
matches!(result, Err(AllocError::NoMemory)),
"Expected NoMemory error when allocating from uninitialized allocator"
);
}

Expand Down
4 changes: 2 additions & 2 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,12 @@ fn test_error_conditions() {

// Test invalid parameter
let result = allocator.alloc_pages(0, PAGE_SIZE);
assert_eq!(result, Err(AllocError::InvalidParam));
assert!(matches!(result, Err(AllocError::InvalidParam)));

// Test allocation too large
let huge_pages = TEST_HEAP_SIZE / PAGE_SIZE + 1000;
let result = allocator.alloc_pages(huge_pages, PAGE_SIZE);
assert_eq!(result, Err(AllocError::NoMemory));
assert!(matches!(result, Err(AllocError::NoMemory)));

dealloc_test_heap(heap_ptr, heap_layout);
}
Expand Down