sprite-core 0.1.0

Sprite Engine — a fault-tolerant actor runtime for Rust
Documentation
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);
        }
    }
}