use super::{RcCell, RcSlot};
use crate::shared::Full;
pub struct Slab<T> {
inner: crate::bounded::Slab<RcCell<T>>,
}
impl<T> Slab<T> {
#[inline]
pub unsafe fn with_capacity(capacity: usize) -> Self {
Self {
inner: unsafe { crate::bounded::Slab::with_capacity(capacity) },
}
}
#[inline]
pub fn alloc(&self, value: T) -> RcSlot<T> {
let slot = self.inner.alloc(RcCell::new(value));
unsafe { RcSlot::from_ptr(slot.into_raw().cast()) }
}
#[inline]
pub fn try_alloc(&self, value: T) -> Result<RcSlot<T>, Full<T>> {
match self.inner.try_alloc(RcCell::new(value)) {
Ok(slot) => Ok(unsafe { RcSlot::from_ptr(slot.into_raw().cast()) }),
Err(Full(rc_cell)) => Err(Full(rc_cell.into_inner())),
}
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn free(&self, handle: RcSlot<T>) {
let count = handle.dec_ref();
if count == 0 {
unsafe { handle.drop_value() };
let cell_ptr = handle.slot_cell_ptr();
core::mem::forget(handle);
unsafe { self.inner.free_ptr(cell_ptr) };
} else {
core::mem::forget(handle);
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
}
impl<T> core::fmt::Debug for Slab<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("rc::bounded::Slab")
.field("capacity", &self.capacity())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alloc_borrow_free() {
let slab = unsafe { Slab::with_capacity(10) };
let handle = slab.alloc(42u64);
assert_eq!(handle.refcount(), 1);
{
let guard = handle.borrow();
assert_eq!(*guard, 42);
}
slab.free(handle);
}
#[test]
fn clone_and_free_both() {
let slab = unsafe { Slab::with_capacity(10) };
let h1 = slab.alloc(42u64);
let h2 = h1.clone();
assert_eq!(h1.refcount(), 2);
slab.free(h2);
assert_eq!(h1.refcount(), 1);
slab.free(h1);
}
#[test]
fn borrow_and_borrow_mut() {
let slab = unsafe { Slab::with_capacity(10) };
let handle = slab.alloc(String::from("hello"));
{
let guard = handle.borrow();
assert_eq!(&*guard, "hello");
}
{
let mut guard = handle.borrow_mut();
guard.push_str(" world");
}
{
let guard = handle.borrow();
assert_eq!(&*guard, "hello world");
}
slab.free(handle);
}
#[test]
fn mutation_visible_across_clones() {
let slab = unsafe { Slab::with_capacity(10) };
let h1 = slab.alloc(1u64);
let h2 = h1.clone();
{
let mut g = h1.borrow_mut();
*g = 99;
}
{
let g = h2.borrow();
assert_eq!(*g, 99);
}
slab.free(h2);
slab.free(h1);
}
#[test]
#[should_panic(expected = "already borrowed")]
fn double_borrow_panics() {
let slab = unsafe { Slab::with_capacity(10) };
let h1 = slab.alloc(42u64);
let h2 = h1.clone();
let _g1 = h1.borrow();
let _g2 = h2.borrow();
}
#[test]
#[should_panic(expected = "already borrowed")]
fn borrow_while_mut_panics() {
let slab = unsafe { Slab::with_capacity(10) };
let h1 = slab.alloc(42u64);
let h2 = h1.clone();
let _g1 = h1.borrow_mut();
let _g2 = h2.borrow();
}
#[test]
fn try_alloc_full() {
let slab = unsafe { Slab::with_capacity(1) };
let h1 = slab.alloc(1u64);
let result = slab.try_alloc(2u64);
assert!(result.is_err());
assert_eq!(result.unwrap_err().0, 2);
slab.free(h1);
}
#[test]
fn drop_tracking() {
use core::cell::Cell;
struct DropCounter<'a> {
count: &'a Cell<u32>,
}
impl Drop for DropCounter<'_> {
fn drop(&mut self) {
self.count.set(self.count.get() + 1);
}
}
let drops = Cell::new(0);
let slab = unsafe { Slab::with_capacity(10) };
let h1 = slab.alloc(DropCounter { count: &drops });
let h2 = h1.clone();
slab.free(h2);
assert_eq!(drops.get(), 0);
slab.free(h1);
assert_eq!(drops.get(), 1);
}
#[test]
#[should_panic(expected = "already borrowed")]
fn mut_while_borrow_panics() {
let slab = unsafe { Slab::with_capacity(10) };
let h1 = slab.alloc(42u64);
let h2 = h1.clone();
let _g1 = h1.borrow();
let _g2 = h2.borrow_mut(); }
#[test]
fn pin_and_pin_mut() {
let slab = unsafe { Slab::with_capacity(10) };
let handle = slab.alloc(42u64);
{
let pinned = handle.pin();
assert_eq!(*pinned, 42);
}
{
let mut pinned = handle.pin_mut();
*pinned = 99;
}
{
let g = handle.borrow();
assert_eq!(*g, 99);
}
slab.free(handle);
}
#[test]
fn into_raw_from_raw_roundtrip() {
let slab = unsafe { Slab::with_capacity(10) };
let handle = slab.alloc(42u64);
let raw = handle.into_raw();
let handle = unsafe { crate::RcSlot::from_raw(raw) };
{
let g = handle.borrow();
assert_eq!(*g, 42);
}
slab.free(handle);
}
#[test]
fn freelist_integrity_after_rc_cycle() {
let slab = unsafe { Slab::with_capacity(4) };
let h1 = slab.alloc(1u64);
let h2 = slab.alloc(2u64);
let h3 = slab.alloc(3u64);
let h4 = slab.alloc(4u64);
assert!(slab.try_alloc(5u64).is_err());
slab.free(h4);
slab.free(h3);
slab.free(h2);
slab.free(h1);
let h1 = slab.alloc(10u64);
let h2 = slab.alloc(20u64);
let h3 = slab.alloc(30u64);
let h4 = slab.alloc(40u64);
{
assert_eq!(*h1.borrow(), 10);
}
{
assert_eq!(*h2.borrow(), 20);
}
{
assert_eq!(*h3.borrow(), 30);
}
{
assert_eq!(*h4.borrow(), 40);
}
slab.free(h4);
slab.free(h3);
slab.free(h2);
slab.free(h1);
}
#[cfg(debug_assertions)]
#[test]
fn debug_drop_panics() {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let slab = unsafe { Slab::with_capacity(10) };
let _h = slab.alloc(42u64);
}));
assert!(result.is_err());
}
}