Heap

Struct Heap 

Source
pub struct Heap<const N: usize> { /* private fields */ }
Expand description

The interface to a heap. This data structure is stored outside the heap somewhere, typically in a static variable, because every single byte of our heap is potentially available for allocation.

The generic parameter N specifies the number of steps to divide the available heap size by two. This will be the minimum allocable block size.

§Usage

// This can be a block of free system memory on your microcontroller.
const HEAP_MEM: usize  = 0xFFF0_0000;
const HEAP_SIZE: usize = 0x0008_0000;

let mut heap: Heap<16> = unsafe {
    Heap::new(NonNull::new(HEAP_MEM as *mut u8).unwrap(), HEAP_SIZE).unwrap()
};
let mem = heap.allocate(Layout::from_size_align(16, 16).unwrap()).unwrap();

// Yay! We have a 16-byte block of memory from the heap.

§Usage (static initialization)

const HEAP_MEM: usize  = 0xFFF0_0000;
const HEAP_SIZE: usize = 0x0008_0000;
 
// You'll want to wrap this heap in a lock abstraction for real-world use.
static mut ALLOCATOR: Heap<16> = unsafe {
    Heap::new_unchecked(HEAP_MEM as *mut u8, HEAP_SIZE)
};

pub fn some_func() {
  let mem = unsafe {
    ALLOCATOR.allocate(Layout::from_size_align(16, 16).unwrap()).unwrap()
  };

  // Yay! We now have a 16-byte block from the heap without initializing it!
}

Implementations§

Source§

impl<const N: usize> Heap<N>

Source

pub unsafe fn new( heap_base: NonNull<u8>, heap_size: usize, ) -> Result<Self, HeapError>

Create a new heap. If any parameter is invalid, this will return a HeapError.

Examples found in repository?
examples/allocator.rs (line 43)
35fn main() {
36    // Allocate the backing memory for our heap. This memory _MUST_
37    // be aligned by at least 4096.
38    let layout = Layout::from_size_align(16384, 4096).unwrap();
39    let mem = unsafe { std::alloc::alloc(layout) };
40
41    // Construct our locked heap, with a minimum block size of 16 (16384 >> 10).
42    let heap: LockedHeap<10> = LockedHeap(Mutex::new(
43        unsafe { Heap::new(NonNull::new(mem).unwrap(), 16384) }.unwrap(),
44    ));
45    let mut vec = Vec::with_capacity_in(16, &heap);
46
47    vec.push(0usize);
48    vec.push(1usize);
49    vec.push(2usize);
50    vec.push(3usize);
51
52    println!("{:?}", vec);
53
54    // Drop the heap and vector before freeing the backing memory.
55    drop(vec);
56    drop(heap);
57
58    unsafe {
59        std::alloc::dealloc(mem, layout);
60    }
61}
Source

pub const unsafe fn new_unchecked(heap_base: *mut u8, heap_size: usize) -> Self

Create a new heap without checking for parameter validity. Useful for const heap creation.

§Safety

heap_base must be aligned on a MIN_HEAP_ALIGN boundary, heap_size must be a power of 2, and heap_size / 2.pow(free_lists.len()-1) must be greater than or equal to size_of::<FreeBlock>(). Passing in invalid parameters may do horrible things.

Source

pub fn allocate(&mut self, layout: Layout) -> Result<*mut u8, AllocationError>

Allocate a block of memory large enough to contain layout, and aligned to layout. This will return an AllocationError if the alignment is greater than MIN_HEAP_ALIGN, or if we can’t find enough memory.

All allocated memory must be passed to deallocate with the same layout parameter, or else horrible things will happen.

Examples found in repository?
examples/allocator.rs (line 19)
16    fn allocate(&self, layout: std::alloc::Layout) -> Result<NonNull<[u8]>, AllocError> {
17        let mut heap = self.0.lock().map_err(|_| AllocError)?;
18
19        let ptr = heap.allocate(layout).map_err(|_| AllocError)?;
20
21        // SAFETY: The pointer is guaranteed to not be NULL if the heap didn't return an error.
22        Ok(unsafe { NonNull::new_unchecked(std::slice::from_raw_parts_mut(ptr, layout.size())) })
23    }
Source

pub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout)

Deallocate a block allocated using allocate.

§Safety

ptr and layout must match what was passed to / returned from allocate, or our heap will be corrupted.

Examples found in repository?
examples/allocator.rs (line 31)
25    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: std::alloc::Layout) {
26        let mut heap = match self.0.lock() {
27            Ok(h) => h,
28            Err(_) => return,
29        };
30
31        heap.deallocate(ptr.as_ptr(), layout);
32    }

Trait Implementations§

Source§

impl<const N: usize> Debug for Heap<N>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<const N: usize> Send for Heap<N>

Auto Trait Implementations§

§

impl<const N: usize> Freeze for Heap<N>

§

impl<const N: usize> RefUnwindSafe for Heap<N>

§

impl<const N: usize> !Sync for Heap<N>

§

impl<const N: usize> Unpin for Heap<N>

§

impl<const N: usize> UnwindSafe for Heap<N>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.