use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use crate::lock::Mutex;
use crate::{Align, Alloc, Allocator, Brand, Error, Layout};
pub struct Array<const N: usize> {
buf: UnsafeCell<[MaybeUninit<u8>; N]>,
inner: Mutex<ArrayInner>,
brand: Brand,
}
unsafe impl<const N: usize> Sync for Array<N> {}
struct ArrayInner {
next: usize,
count: usize,
}
impl<const N: usize> Array<N> {
pub fn new() -> Self {
Self {
buf: UnsafeCell::new([MaybeUninit::uninit(); N]),
inner: Mutex::new(ArrayInner { next: 0, count: 0 }),
brand: Brand::new(),
}
}
}
unsafe impl<const N: usize> Allocator for Array<N> {
fn brand(&self) -> &Brand {
&self.brand
}
fn alloc_bytes(&self, layout: Layout) -> Result<Alloc<'_, u8>, Error> {
let mut this_lock = self.inner.lock();
let this = &mut *this_lock;
if layout.size > N - this.next {
return Err(Error::OutOfMemory);
}
let buf = self.buf.get().cast::<u8>();
let align = layout.align.align();
let start = unsafe { buf.add(this.next) };
let start = start.map_addr(|addr| addr.div_ceil(align) * align);
let end = start as usize + layout.size;
debug_assert!(start as usize % align == 0);
if end > buf as usize + N {
return Err(Error::OutOfMemory);
}
this.next = end - buf as usize;
this.count += 1;
drop(this_lock);
let ptr = NonNull::new(start).unwrap();
Ok(unsafe { Alloc::new(ptr, layout, self) })
}
fn dealloc_bytes<'this>(&'this self, alloc: Alloc<'this, u8>) {
if alloc.allocator().brand() != &self.brand {
return;
}
let mut this = self.inner.lock();
this.count -= 1;
if this.count == 0 {
this.next = 0;
}
}
fn dangling(&self, align: Align) -> Alloc<'_, u8> {
let layout = Layout { size: 0, align };
unsafe {
Alloc::new(
NonNull::new(align.align() as *mut u8).unwrap(),
layout,
self,
)
}
}
fn total_size(&self) -> Option<usize> {
Some(N)
}
fn remaining_size(&self) -> Option<usize> {
Some(N - self.inner.lock().next)
}
}
impl<const N: usize> Default for Array<N> {
fn default() -> Self {
Self::new()
}
}