Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds safe constructor and initialization for Heap #55

Merged
merged 3 commits into from
Oct 17, 2021
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
39 changes: 39 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use core::alloc::GlobalAlloc;
use core::alloc::Layout;
#[cfg(feature = "alloc_ref")]
use core::alloc::{AllocError, Allocator};
use core::mem::MaybeUninit;
#[cfg(feature = "use_spin")]
use core::ops::Deref;
use core::ptr::NonNull;
Expand Down Expand Up @@ -73,6 +74,32 @@ impl Heap {
self.holes = HoleList::new(heap_bottom, heap_size);
}

/// Initialize an empty heap with provided memory.
///
/// The caller is responsible for procuring a region of raw memory that may be utilized by the
/// allocator. This might be done via any method such as (unsafely) taking a region from the
/// program's memory, from a mutable static, or by allocating and leaking such memory from
/// another allocator.
///
/// The latter method may be especially useful if the underlying allocator does not perform
/// deallocation (e.g. a simple bump allocator). Then the overlaid linked-list-allocator can
/// provide memory reclamation.
///
/// # Panics
///
/// This method panics if the heap is already initialized.
pub fn init_from_slice(&mut self, mem: &'static mut [MaybeUninit<u8>]) {
assert!(self.bottom == 0, "The heap has already been initialized.");
let size = mem.len();
let address = mem.as_ptr() as usize;
// SAFETY: All initialization requires the bottom address to be valid, which implies it
// must not be 0. Initially the address is 0. The assertion above ensures that no
// initialization had been called before.
// The given address and size is valid according to the safety invariants of the mutable
// reference handed to us by the caller.
unsafe { self.init(address, size) }
}

/// Creates a new heap with the given `bottom` and `size`. The bottom address must be valid
/// and the memory in the `[heap_bottom, heap_bottom + heap_size)` range must not be used for
/// anything else. This function is unsafe because it can cause undefined behavior if the
Expand All @@ -90,6 +117,18 @@ impl Heap {
}
}

/// Creates a new heap from a slice of raw memory.
///
/// This has the same effect as [`init_from_slice`] on an empty heap, but it is combined into a
/// single operation that can not panic.
pub fn from_slice(mem: &'static mut [MaybeUninit<u8>]) -> Heap {
let size = mem.len();
let address = mem.as_ptr() as usize;
// SAFETY: The given address and size is valid according to the safety invariants of the
// mutable reference handed to us by the caller.
unsafe { Self::new(address, size) }
}

/// Allocates a chunk of the given size with the given alignment. Returns a pointer to the
/// beginning of that chunk if it was successful. Else it returns `None`.
/// This function scans the list of free memory blocks and uses the first block that is big
Expand Down
17 changes: 10 additions & 7 deletions src/test.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
use super::*;
use core::alloc::Layout;
use std::mem::{align_of, size_of};
use std::mem::{align_of, size_of, MaybeUninit};
use std::prelude::v1::*;

fn new_heap() -> Heap {
const HEAP_SIZE: usize = 1000;
let heap_space = Box::into_raw(Box::new([0u8; HEAP_SIZE]));
let heap_space = Box::leak(Box::new([MaybeUninit::uninit(); HEAP_SIZE]));
let assumed_location = heap_space.as_ptr() as usize;

let heap = unsafe { Heap::new(heap_space as usize, HEAP_SIZE) };
assert!(heap.bottom == heap_space as usize);
let heap = Heap::from_slice(heap_space);
assert!(heap.bottom == assumed_location);
assert!(heap.size == HEAP_SIZE);
heap
}

fn new_max_heap() -> Heap {
const HEAP_SIZE: usize = 1024;
const HEAP_SIZE_MAX: usize = 2048;
let heap_space = Box::into_raw(Box::new([0u8; HEAP_SIZE_MAX]));
let heap_space = Box::leak(Box::new([MaybeUninit::<u8>::uninit(); HEAP_SIZE_MAX]));
let start_ptr = heap_space.as_ptr() as usize;

let heap = unsafe { Heap::new(heap_space as usize, HEAP_SIZE) };
assert!(heap.bottom == heap_space as usize);
// Unsafe so that we have provenance over the whole allocation.
let heap = unsafe { Heap::new(start_ptr, HEAP_SIZE) };
assert!(heap.bottom == start_ptr);
assert!(heap.size == HEAP_SIZE);
heap
}
Expand Down