use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use crate::critical::CriticalSlots;
use crate::free_slots::FreeSlots;
pub struct BoxPool<T, const N: usize, B = CriticalSlots<N>> {
pub(crate) slots: UnsafeCell<MaybeUninit<[T; N]>>,
pub(crate) free: B,
}
unsafe impl<T, const N: usize, B: FreeSlots<N>> Sync for BoxPool<T, N, B> {}
pub struct StaticBoxPool<T, const N: usize, B: FreeSlots<N> = CriticalSlots<N>> {
data: UnsafeCell<MaybeUninit<BoxPool<T, N, B>>>,
}
unsafe impl<T, const N: usize, B: FreeSlots<N>> Sync for StaticBoxPool<T, N, B> {}
impl<T, const N: usize, B: FreeSlots<N>> StaticBoxPool<T, N, B> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
data: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub fn init_pool(&'static self) -> &'static BoxPool<T, N, B> {
assert!(N > 0, "Pool capacity must be greater than 0");
unsafe {
let ptr: *mut BoxPool<T, N, B> = self.data.get().cast::<BoxPool<T, N, B>>();
ptr.write(BoxPool::new());
(*ptr).free.fill(N);
&*ptr
}
}
}
impl<T, const N: usize, B: FreeSlots<N>> BoxPool<T, N, B> {
fn new() -> Self {
Self {
slots: UnsafeCell::new(MaybeUninit::uninit()),
free: B::new(),
}
}
pub fn alloc(&'static self, value: T) -> Result<BoxGuard<T, N, B>, T> {
match self.free.take() {
Some(idx) => {
unsafe {
self.slot_mut_ptr(idx).write(value);
}
Ok(BoxGuard {
pool: self,
index: idx,
})
}
None => Err(value),
}
}
pub(crate) fn release(&self, idx: usize) {
unsafe {
self.slot_mut_ptr(idx).drop_in_place();
}
self.free.put(idx);
}
#[inline]
pub(crate) unsafe fn slot_mut_ptr(&self, idx: usize) -> *mut T {
unsafe {
let base: *mut T = (*self.slots.get()).as_mut_ptr().cast::<T>();
base.add(idx)
}
}
#[inline]
pub(crate) unsafe fn slot_ptr(&self, idx: usize) -> *const T {
unsafe {
let base: *const T = (*self.slots.get()).as_ptr().cast::<T>();
base.add(idx)
}
}
}
#[must_use = "discarding a BoxGuard immediately frees the slot — likely a bug"]
pub struct BoxGuard<T: 'static, const N: usize, B: FreeSlots<N> + 'static = CriticalSlots<N>> {
pub(crate) pool: &'static BoxPool<T, N, B>,
pub(crate) index: usize,
}
impl<T, const N: usize, B: FreeSlots<N>> Deref for BoxGuard<T, N, B> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.pool.slot_ptr(self.index) }
}
}
impl<T, const N: usize, B: FreeSlots<N>> DerefMut for BoxGuard<T, N, B> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.pool.slot_mut_ptr(self.index) }
}
}
impl<T, const N: usize, B: FreeSlots<N>> Drop for BoxGuard<T, N, B> {
fn drop(&mut self) {
self.pool.release(self.index);
}
}
unsafe impl<T: Send, const N: usize, B: FreeSlots<N>> Send for BoxGuard<T, N, B> {}
impl<T, const N: usize, B: FreeSlots<N>> BoxGuard<T, N, B> {
pub fn into_raw(self) -> *mut T {
assert!(
core::mem::size_of::<T>() > 0,
"BoxGuard::into_raw is not supported for zero-sized types (ZST)"
);
let ptr = unsafe { self.pool.slot_mut_ptr(self.index) };
core::mem::forget(self);
ptr
}
pub unsafe fn from_raw(pool: &'static BoxPool<T, N, B>, ptr: *mut T) -> Self {
unsafe {
let base: *const T = (*pool.slots.get()).as_ptr().cast::<T>();
let index = (ptr as *const T).offset_from(base) as usize;
debug_assert!(index < N, "ptr is beyond pool.slots range");
BoxGuard { pool, index }
}
}
pub fn try_clone(&self) -> Result<Self, T>
where
T: Clone,
{
self.pool.alloc(T::clone(self))
}
}