use crate::alloc::allocator::{AllocError, Allocator};
use crate::log;
use bern_units::memory_size::{Byte, ExtByte};
use core::alloc::Layout;
use core::ptr::{slice_from_raw_parts_mut, NonNull};
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
pub struct Bump {
start: NonNull<u8>,
end: NonNull<u8>,
current: AtomicPtr<u8>,
wastage: AtomicUsize,
}
impl Bump {
pub const unsafe fn new(start: NonNull<u8>, end: NonNull<u8>) -> Self {
Bump {
start,
end,
current: AtomicPtr::new(start.as_ptr()),
wastage: AtomicUsize::new(0),
}
}
}
impl Allocator for Bump {
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
loop {
let old = self.current.load(Ordering::Acquire);
let padding = old.align_offset(layout.align());
log::trace!(
"Try to allocate {}B at 0x{:x}",
layout.size(),
old as usize + padding
);
if (self.capacity() - self.usage()) < ((layout.size() + padding) as u32).B() {
return Err(AllocError::OutOfMemory);
}
unsafe {
match self.current.compare_exchange(
old,
old.add(layout.size() + padding),
Ordering::SeqCst,
Ordering::Relaxed,
) {
Ok(_) => {
let memory = slice_from_raw_parts_mut(old.add(padding), layout.size());
self.wastage.fetch_add(padding, Ordering::Relaxed);
return Ok(NonNull::new_unchecked(memory));
}
Err(_) => continue, }
}
}
}
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
log::warn!(
"BumpAllocator cannot deallocate memory (0x{:x}, {}B). Ignoring call from .",
ptr.as_ptr() as usize,
layout.size()
);
self.wastage.fetch_add(layout.size(), Ordering::Relaxed);
}
fn capacity(&self) -> Byte {
Byte((self.end.as_ptr() as usize - self.start.as_ptr() as usize) as u32)
}
fn usage(&self) -> Byte {
Byte((self.current.load(Ordering::Relaxed) as usize - self.start.as_ptr() as usize) as u32)
}
}
unsafe impl Sync for Bump {}
#[cfg(all(test, not(target_os = "none")))]
mod tests {
use super::*;
use core::mem::{align_of, size_of};
struct MyStruct {
a: u32,
b: u8,
}
#[test]
fn alloc_and_dealloc() {
static mut BUFFER: [u32; 128 / 4] = [0; 128 / 4];
let bump = unsafe {
Bump::new(
NonNull::new_unchecked(BUFFER.as_ptr() as *mut _),
NonNull::new_unchecked(BUFFER.as_ptr().add(BUFFER.len()) as *mut _),
)
};
assert_eq!(bump.capacity().0, 128);
let layout =
Layout::from_size_align(size_of::<MyStruct>(), align_of::<MyStruct>()).unwrap();
let raw = bump.alloc(layout).unwrap();
let memory = raw.cast::<MyStruct>();
let s = unsafe { &mut *memory.as_ptr() };
s.a = 42;
s.b = 10;
let usage = size_of::<MyStruct>() as u32;
assert_eq!(
bump.usage().0,
(usage + bump.wastage.load(Ordering::Relaxed) as u32)
);
let wastage = bump.wastage.load(Ordering::Relaxed);
unsafe {
bump.dealloc(raw.cast::<u8>(), layout);
}
assert_eq!(bump.usage().0, usage);
assert_eq!(
bump.wastage.load(Ordering::Relaxed),
wastage + size_of::<MyStruct>()
);
}
#[test]
fn overflow() {
static mut BUFFER: [u8; 128] = [0; 128];
let bump = unsafe {
Bump::new(
NonNull::new_unchecked(BUFFER.as_ptr() as *mut _),
NonNull::new_unchecked(BUFFER.as_ptr().add(BUFFER.len()) as *mut _),
)
};
assert_eq!(bump.capacity().0, 128);
let layout = Layout::from_size_align(16, 1).unwrap();
for _ in 0..8 {
let _dump = bump.alloc(layout).unwrap();
}
assert_eq!(bump.usage(), bump.capacity());
let err = bump.alloc(layout).err().unwrap();
assert_eq!(err, AllocError::OutOfMemory);
}
}