use crate::slab_box::SlabBox;
use core::{
cell::UnsafeCell,
mem::MaybeUninit,
slice::from_raw_parts,
sync::atomic::{AtomicU8, AtomicUsize, Ordering},
};
use heapless::mpmc::MpMcQueue;
pub struct BSlab<const N: usize, const SZ: usize> {
bufs: MaybeUninit<[UnsafeCell<[u8; SZ]>; N]>,
arcs: [AtomicUsize; N],
alloc_q: UnsafeCell<MaybeUninit<MpMcQueue<usize, N>>>,
state: AtomicU8,
}
unsafe impl<const N: usize, const SZ: usize> Sync for BSlab<N, SZ> {}
pub(crate) struct SlabIdxData<const SZ: usize> {
pub(crate) buf: &'static UnsafeCell<[u8; SZ]>,
pub(crate) arc: &'static AtomicUsize,
}
impl<const N: usize, const SZ: usize> BSlab<N, SZ> {
pub const fn new() -> Self {
const ZERO_ARC: AtomicUsize = AtomicUsize::new(0);
Self {
bufs: MaybeUninit::uninit(),
arcs: [ZERO_ARC; N],
alloc_q: UnsafeCell::new(MaybeUninit::uninit()),
state: AtomicU8::new(Self::UNINIT),
}
}
const UNINIT: u8 = 0;
const INITIALIZING: u8 = 1;
const INITIALIZED: u8 = 2;
pub fn is_init(&self) -> Result<(), ()> {
if Self::INITIALIZED == self.state.load(Ordering::SeqCst) {
Ok(())
} else {
Err(())
}
}
pub(crate) fn get_q(&self) -> Result<&MpMcQueue<usize, N>, ()> {
self.is_init()?;
unsafe {
Ok(&*(*self.alloc_q.get()).as_ptr())
}
}
pub fn alloc_box(&'static self) -> Option<SlabBox<N, SZ>> {
let idx = self.get_q().ok()?.dequeue()?;
let arc = unsafe { self.get_idx_unchecked(idx).arc };
arc.store(1, Ordering::SeqCst);
Some(SlabBox { slab: self, idx })
}
pub(crate) unsafe fn get_idx_unchecked(&'static self, idx: usize) -> SlabIdxData<SZ> {
SlabIdxData {
buf: &*self.bufs.as_ptr().cast::<UnsafeCell<[u8; SZ]>>().add(idx),
arc: &self.arcs[idx],
}
}
pub fn init(&self) -> Result<(), ()> {
self.state
.compare_exchange(
Self::UNINIT,
Self::INITIALIZING,
Ordering::SeqCst,
Ordering::SeqCst,
)
.map_err(drop)?;
let good_q = unsafe {
(*self.alloc_q.get()).as_mut_ptr().write(MpMcQueue::new());
&*(*self.alloc_q.get()).as_ptr()
};
unsafe {
let buf_ptr = self.bufs.as_ptr().cast::<UnsafeCell<[u8; SZ]>>();
let bufs_slice: &[UnsafeCell<[u8; SZ]>] = from_raw_parts(buf_ptr, N);
for slab in bufs_slice {
slab.get().write_bytes(0x00, 1);
}
}
for i in 0..N {
good_q.enqueue(i).map_err(drop)?;
}
self.state
.compare_exchange(
Self::INITIALIZING,
Self::INITIALIZED,
Ordering::SeqCst,
Ordering::SeqCst,
)
.map_err(drop)?;
Ok(())
}
}