use std::cell::UnsafeCell;
use std::marker::PhantomData;
pub struct Slab<const N: usize = 4096> {
active: UnsafeCell<[u8; N]>,
backup: UnsafeCell<[u8; N]>,
head: UnsafeCell<usize>,
}
impl<const N: usize> Slab<N> {
pub fn new() -> Self {
Self {
active: UnsafeCell::new([0u8; N]),
backup: UnsafeCell::new([0u8; N]),
head: UnsafeCell::new(0),
}
}
pub unsafe fn alloc<T>(&self, val: T) -> SlabRef<T, N> {
let size = std::mem::size_of::<T>();
let align = std::mem::align_of::<T>();
let head = &mut *self.head.get();
let aligned = (*head + align - 1) & !(align - 1);
assert!(aligned + size <= N, "slab overflow");
let ptr = (*self.active.get()).as_mut_ptr().add(aligned);
std::ptr::write(ptr as *mut T, val);
*head = aligned + size;
SlabRef { offset: aligned, _phantom: PhantomData }
}
pub unsafe fn reset(&self) {
*self.head.get() = 0;
}
pub unsafe fn checkpoint(&self) {
let head = *self.head.get();
std::ptr::copy_nonoverlapping(
(*self.active.get()).as_ptr(),
(*self.backup.get()).as_mut_ptr(),
head,
);
}
pub unsafe fn recover(&self) {
let head = *self.head.get();
std::ptr::copy_nonoverlapping(
(*self.backup.get()).as_ptr(),
(*self.active.get()).as_mut_ptr(),
head,
);
}
}
pub struct SlabRef<T, const N: usize = 4096> {
offset: usize,
_phantom: PhantomData<T>,
}
impl<T: Copy, const N: usize> SlabRef<T, N> {
pub unsafe fn get(&self, slab: &Slab<N>) -> T {
let ptr = (*slab.active.get()).as_ptr().add(self.offset) as *const T;
std::ptr::read(ptr)
}
pub unsafe fn set(&self, slab: &Slab<N>, val: T) {
let ptr = (*slab.active.get()).as_ptr().add(self.offset) as *mut T;
std::ptr::write(ptr, val);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slab_alloc_and_reset() {
let slab = Slab::<256>::new();
unsafe {
let r = slab.alloc(42i64);
assert_eq!(r.get(&slab), 42);
r.set(&slab, 100);
assert_eq!(r.get(&slab), 100);
slab.reset();
}
}
#[test]
fn slab_checkpoint_recover() {
let slab = Slab::<256>::new();
unsafe {
let r = slab.alloc(42i64);
slab.checkpoint();
r.set(&slab, 999);
assert_eq!(r.get(&slab), 999);
slab.recover();
assert_eq!(r.get(&slab), 42);
}
}
}