use core::{
alloc::{AllocError, Layout},
cell::UnsafeCell,
mem,
ptr::{self, NonNull},
sync::atomic::{AtomicPtr, Ordering::*},
};
use super::{BaseAlloc, Chunk, StaticHandle};
#[derive(Debug)]
pub struct Static<const HEADER_CAP: usize> {
memory: UnsafeCell<[usize; HEADER_CAP]>,
top: AtomicPtr<()>,
}
impl<const HEADER_CAP: usize> Default for Static<HEADER_CAP> {
fn default() -> Self {
Self::INIT
}
}
unsafe impl<const HEADER_CAP: usize> Sync for Static<HEADER_CAP> {}
impl<const HEADER_CAP: usize> Static<HEADER_CAP> {
pub const INIT: Self = Self::new();
pub const fn new() -> Self {
Static {
memory: UnsafeCell::new([0; HEADER_CAP]),
top: AtomicPtr::<()>::new(ptr::null_mut()),
}
}
fn alloc_inner(&'static self, layout: Layout) -> Option<Chunk<&Self>> {
let layout = layout.align_to(mem::align_of::<usize>()).ok()?;
let (Ok(mut top) | Err(mut top)) =
self.top
.compare_exchange(ptr::null_mut(), self.memory.get().cast(), AcqRel, Acquire);
loop {
let aligned = (top.addr().checked_add(layout.align() - 1))? & !(layout.align() - 1);
let end = aligned
.checked_add(layout.size())
.filter(|&end| end < self.memory.get().addr() + HEADER_CAP)?;
let new = NonNull::new(top.with_addr(aligned))?;
match self
.top
.compare_exchange_weak(top, top.with_addr(end), AcqRel, Acquire)
{
Ok(_) => break Some(unsafe { Chunk::from_static(new.cast(), layout) }),
Err(t) => top = t,
}
}
}
}
unsafe impl<const HEADER_CAP: usize> BaseAlloc for &'static Static<HEADER_CAP> {
const IS_ZEROED: bool = true;
type Handle = StaticHandle;
type Error = AllocError;
fn allocate(&self, layout: Layout, _commit: bool) -> Result<Chunk<Self>, AllocError> {
self.alloc_inner(layout).ok_or(AllocError)
}
unsafe fn deallocate(_: &mut Chunk<Self>) {}
}