Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added

- Added a `init` macro to make initialization easier.
- Use `OnceCell` to prevent duplicate initialization.

## [v0.6.0] - 2024-09-01

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ allocator_api = []

# Use the Two-Level Segregated Fit allocator
tlsf = ["rlsf", "const-default"]
# Use the LinkedList first-fit allocator
# Use the LinkedList first-fit allocator
llff = ["linked_list_allocator"]

[dependencies]
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ static HEAP: Heap = Heap::empty();
#[entry]
fn main() -> ! {
// Initialize the allocator BEFORE you use it
unsafe {
embedded_alloc::init!(HEAP, 1024);
}
// Alternatively, you can write the code directly to meet specific requirements.
{
use core::mem::MaybeUninit;
const HEAP_SIZE: usize = 1024;
Expand Down
7 changes: 2 additions & 5 deletions examples/global_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ static HEAP: Heap = Heap::empty();
#[entry]
fn main() -> ! {
// Initialize the allocator BEFORE you use it
{
use core::mem::MaybeUninit;
const HEAP_SIZE: usize = 1024;
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
unsafe {
embedded_alloc::init!(HEAP, 1024);
}

let mut xs = Vec::new();
Expand Down
6 changes: 2 additions & 4 deletions examples/llff_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,8 @@ fn test_allocator_api() {

#[entry]
fn main() -> ! {
{
const HEAP_SIZE: usize = 1024;
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
unsafe {
embedded_alloc::init!(HEAP, 1024);
}

#[allow(clippy::type_complexity)]
Expand Down
5 changes: 2 additions & 3 deletions examples/tlsf_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ fn test_allocator_api() {

#[entry]
fn main() -> ! {
{
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
unsafe {
embedded_alloc::init!(HEAP, HEAP_SIZE);
}

#[allow(clippy::type_complexity)]
Expand Down
49 changes: 49 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,52 @@ mod tlsf;
pub use llff::Heap as LlffHeap;
#[cfg(feature = "tlsf")]
pub use tlsf::Heap as TlsfHeap;

/// Initialize the global heap.
///
/// This macro creates a static, uninitialized memory buffer of the specified size and
/// initializes the heap instance with that buffer.
///
/// # Parameters
///
/// - `$heap:ident`: The identifier of the global heap instance to initialize.
/// - `$size:expr`: An expression evaluating to a `usize` that specifies the size of the
/// static memory buffer in bytes. It must be **greater than zero**.
///
/// # Safety
///
/// This macro must be called first, before any operations on the heap, and **only once**.
/// It internally calls `Heap::init(...)` on the heap,
/// so `Heap::init(...)` should not be called directly if this macro is used.
///
/// # Panics
///
/// Calling this macro multiple times or with `size == 0` will cause a panic.
///
/// # Example
///
/// ```rust
/// use cortex_m_rt::entry;
/// use embedded_alloc::LlffHeap as Heap;
///
/// #[global_allocator]
/// static HEAP: Heap = Heap::empty();
///
/// #[entry]
/// fn main() -> ! {
/// // Initialize the allocator BEFORE you use it
/// unsafe {
/// embedded_alloc::init!(HEAP, 1024);
/// }
/// let mut xs = Vec::new();
/// // ...
/// }
/// ```
#[macro_export]
macro_rules! init {
($heap:ident, $size:expr) => {
static mut HEAP_MEM: [::core::mem::MaybeUninit<u8>; $size] =
[::core::mem::MaybeUninit::uninit(); $size];
$heap.init(&raw mut HEAP_MEM as usize, $size)
};
}
36 changes: 20 additions & 16 deletions src/llff.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use core::alloc::{GlobalAlloc, Layout};
use core::cell::RefCell;
use core::cell::{OnceCell, RefCell};
use core::ptr::{self, NonNull};

use critical_section::Mutex;
use linked_list_allocator::Heap as LLHeap;

/// A linked list first fit heap.
pub struct Heap {
heap: Mutex<RefCell<LLHeap>>,
heap: Mutex<RefCell<OnceCell<LLHeap>>>,
}

impl Heap {
Expand All @@ -17,7 +17,7 @@ impl Heap {
/// [`init`](Self::init) method before using the allocator.
pub const fn empty() -> Heap {
Heap {
heap: Mutex::new(RefCell::new(LLHeap::empty())),
heap: Mutex::new(RefCell::new(OnceCell::new())),
}
}

Expand All @@ -41,34 +41,37 @@ impl Heap {
///
/// # Safety
///
/// Obey these or Bad Stuff will happen.
/// This function will panic if either of the following are true:
///
/// - This function must be called exactly ONCE.
/// - `size > 0`
/// - this function is called more than ONCE.
/// - `size == 0`.
pub unsafe fn init(&self, start_addr: usize, size: usize) {
assert!(size > 0);
critical_section::with(|cs| {
self.heap
.borrow(cs)
.borrow_mut()
.init(start_addr as *mut u8, size);
assert!(self
.heap
.borrow_ref_mut(cs)
.set(LLHeap::new(start_addr as *mut u8, size))
.is_ok());
});
}

/// Returns an estimate of the amount of bytes in use.
pub fn used(&self) -> usize {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used())
critical_section::with(|cs| self.heap.borrow_ref_mut(cs).get_mut().unwrap().used())
}

/// Returns an estimate of the amount of bytes available.
pub fn free(&self) -> usize {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free())
critical_section::with(|cs| self.heap.borrow_ref_mut(cs).get_mut().unwrap().free())
}

fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
critical_section::with(|cs| {
self.heap
.borrow(cs)
.borrow_mut()
.borrow_ref_mut(cs)
.get_mut()
.unwrap()
.allocate_first_fit(layout)
.ok()
})
Expand All @@ -77,8 +80,9 @@ impl Heap {
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
critical_section::with(|cs| {
self.heap
.borrow(cs)
.borrow_mut()
.borrow_ref_mut(cs)
.get_mut()
.unwrap()
.deallocate(NonNull::new_unchecked(ptr), layout)
});
}
Expand Down
33 changes: 21 additions & 12 deletions src/tlsf.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use core::alloc::{GlobalAlloc, Layout};
use core::cell::RefCell;
use core::cell::{OnceCell, RefCell};
use core::ptr::{self, NonNull};

use const_default::ConstDefault;
use critical_section::Mutex;
use rlsf::Tlsf;

type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::BITS as usize }>;

/// A two-Level segregated fit heap.
pub struct Heap {
heap: Mutex<RefCell<TlsfHeap>>,
heap: Mutex<RefCell<OnceCell<TlsfHeap>>>,
}

impl Heap {
Expand All @@ -20,7 +19,7 @@ impl Heap {
/// [`init`](Self::init) method before using the allocator.
pub const fn empty() -> Heap {
Heap {
heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)),
heap: Mutex::new(RefCell::new(OnceCell::new())),
}
}

Expand All @@ -44,29 +43,39 @@ impl Heap {
///
/// # Safety
///
/// Obey these or Bad Stuff will happen.
/// This function will panic if either of the following are true:
///
/// - This function must be called exactly ONCE.
/// - `size > 0`
/// - this function is called more than ONCE.
/// - `size == 0`.
pub unsafe fn init(&self, start_addr: usize, size: usize) {
assert!(size > 0);
critical_section::with(|cs| {
let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size);
self.heap.borrow_ref_mut(cs).set(TlsfHeap::new()).unwrap();
self.heap
.borrow(cs)
.borrow_mut()
.borrow_ref_mut(cs)
.get_mut()
.unwrap()
.insert_free_block_ptr(block.into());
});
}

fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate(layout))
critical_section::with(|cs| {
self.heap
.borrow_ref_mut(cs)
.get_mut()
.unwrap()
.allocate(layout)
})
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
critical_section::with(|cs| {
self.heap
.borrow(cs)
.borrow_mut()
.borrow_ref_mut(cs)
.get_mut()
.unwrap()
.deallocate(NonNull::new_unchecked(ptr), layout.align())
})
}
Expand Down