use nexus_slab::Full;
use nexus_slab::Slot;
use nexus_slab::{bounded, unbounded};
pub use bounded::Slab as BoundedSlab;
pub use unbounded::Slab as UnboundedSlab;
pub unsafe trait SlabStore {
type Item;
fn alloc(&self, value: Self::Item) -> Slot<Self::Item>;
fn free(&self, slot: Slot<Self::Item>);
fn take(&self, slot: Slot<Self::Item>) -> Self::Item;
}
pub trait BoundedStore: SlabStore {
fn try_alloc(&self, value: Self::Item) -> Result<Slot<Self::Item>, Full<Self::Item>>;
}
unsafe impl<T> SlabStore for bounded::Slab<T> {
type Item = T;
#[inline]
fn alloc(&self, value: T) -> Slot<T> {
self.try_alloc(value).unwrap_or_else(|full| {
drop(full);
panic!(
"bounded slab: capacity exceeded (type: {})",
std::any::type_name::<T>(),
);
})
}
#[inline]
fn free(&self, slot: Slot<T>) {
bounded::Slab::free(self, slot);
}
#[inline]
fn take(&self, slot: Slot<T>) -> T {
bounded::Slab::take(self, slot)
}
}
impl<T> BoundedStore for bounded::Slab<T> {
#[inline]
fn try_alloc(&self, value: T) -> Result<Slot<T>, Full<T>> {
bounded::Slab::try_alloc(self, value)
}
}
unsafe impl<T> SlabStore for unbounded::Slab<T> {
type Item = T;
#[inline]
fn alloc(&self, value: T) -> Slot<T> {
unbounded::Slab::alloc(self, value)
}
#[inline]
fn free(&self, slot: Slot<T>) {
unbounded::Slab::free(self, slot);
}
#[inline]
fn take(&self, slot: Slot<T>) -> T {
unbounded::Slab::take(self, slot)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bounded_store_roundtrip() {
let slab = unsafe { bounded::Slab::<u64>::with_capacity(16) };
let slot = SlabStore::alloc(&slab, 42);
assert_eq!(*slot, 42);
let val = SlabStore::take(&slab, slot);
assert_eq!(val, 42);
}
#[test]
fn unbounded_store_roundtrip() {
let slab = unsafe { unbounded::Slab::<u64>::with_chunk_capacity(16) };
let slot = SlabStore::alloc(&slab, 99);
assert_eq!(*slot, 99);
SlabStore::free(&slab, slot);
}
#[test]
fn bounded_try_alloc_graceful() {
let slab = unsafe { bounded::Slab::<u64>::with_capacity(1) };
let s1 = BoundedStore::try_alloc(&slab, 1).unwrap();
let err = BoundedStore::try_alloc(&slab, 2).unwrap_err();
assert_eq!(err.into_inner(), 2);
SlabStore::free(&slab, s1);
}
#[test]
#[should_panic(expected = "capacity exceeded")]
fn bounded_alloc_panics_on_full() {
let slab = unsafe { bounded::Slab::<u64>::with_capacity(1) };
let _s1 = SlabStore::alloc(&slab, 1);
let _s2 = SlabStore::alloc(&slab, 2); }
}