Add bump_allocator for obtaining memory without freeing it

This commit is contained in:
Berkus Decker 2020-10-17 22:29:57 +03:00
parent 4672ddace6
commit 97e2c09871
3 changed files with 97 additions and 0 deletions

View File

@ -11,8 +11,10 @@
#![no_main]
#![feature(asm)]
#![feature(global_asm)]
#![feature(allocator_api)]
#![feature(ptr_internals)]
#![feature(format_args_nl)]
#![feature(nonnull_slice_from_raw_parts)]
#![feature(custom_test_frameworks)]
#![test_runner(crate::tests::test_runner)]
#![reexport_test_harness_main = "test_main"]

View File

@ -0,0 +1,92 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/
// @todo Use alloc-fmt crate for logging in allocators
use {
crate::println,
core::{
alloc::{AllocError, AllocRef, Layout},
cell::Cell,
ptr::NonNull,
},
};
pub struct BumpAllocator {
next: Cell<usize>,
pool_end: usize,
name: &'static str,
}
unsafe impl AllocRef for BumpAllocator {
/// Allocate a memory block from the pool.
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let start = crate::mm::aligned_addr_unchecked(self.next.get(), layout.align());
let end = start + layout.size();
println!(
"[i] {}:\n Allocating Start {:#010x} End {:#010x}",
self.name, start, end
);
if end > self.pool_end {
return Err(AllocError);
}
self.next.set(end);
println!(
"[i] {}:\n Allocated Addr {:#010x} Size {:#x}",
self.name,
start,
layout.size()
);
Ok(NonNull::slice_from_raw_parts(
unsafe { NonNull::new_unchecked(start as *mut u8) },
layout.size(),
))
}
/// A bump allocator doesn't care about releasing memory.
unsafe fn dealloc(&self, _ptr: NonNull<u8>, _layout: Layout) {}
}
impl BumpAllocator {
/// Create a named bump allocator between start and end addresses.
pub const fn new(pool_start: usize, pool_end: usize, name: &'static str) -> Self {
Self {
next: Cell::new(pool_start),
pool_end,
name,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
// Validate allocator allocates from the provided address range
// Validate allocation fails when range is exhausted
#[test_case]
fn test_allocates_within_init_range() {
let allocator = BumpAllocator::new(256, 512, "Test allocator 1");
let result1 = allocator.alloc(unsafe { Layout::from_size_align_unchecked(128, 1) });
assert!(result1.is_ok());
let result2 = allocator.alloc(unsafe { Layout::from_size_align_unchecked(128, 32) });
println!("{:?}", result2);
assert!(result2.is_ok());
let result3 = allocator.alloc(unsafe { Layout::from_size_align_unchecked(1, 1) });
assert!(result3.is_err());
}
// Creating with end <= start sshould fail
// @todo return Result<> from new?
#[test_case]
fn test_bad_allocator() {
let bad_allocator = BumpAllocator::new(512, 256, "Test allocator 2");
let result1 = bad_allocator.alloc(unsafe { Layout::from_size_align_unchecked(1, 1) });
assert!(result1.is_err());
}
}

View File

@ -3,6 +3,9 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/
pub mod bump_allocator;
pub use bump_allocator::BumpAllocator;
/// Align address downwards.
///
/// Returns the greatest x with alignment `align` so that x <= addr.