#![no_std]
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
use core::mem::{self, MaybeUninit};
use core::ptr::null_mut;
use core::sync::atomic::{AtomicUsize, Ordering};
pub struct Slab<T> {
consumed: AtomicUsize,
storage: UnsafeCell<MaybeUninit<T>>,
}
impl<T> Slab<T> {
pub const unsafe fn new(storage: T) -> Self {
Slab {
consumed: AtomicUsize::new(0),
storage: UnsafeCell::new(MaybeUninit::new(storage)),
}
}
}
impl<T> Slab<T> {
pub fn take(&self, layout: Layout) -> Option<*mut u8> {
let length = mem::size_of::<T>();
let base_ptr = self.storage.get()
as *mut T
as *mut u8;
let alignment = layout.align();
let requested = layout.size();
let mut consumed = 0;
loop {
let available = length.checked_sub(consumed).unwrap();
let ptr_to = base_ptr.wrapping_add(consumed);
let offset = ptr_to.align_offset(alignment);
if requested > available.saturating_sub(offset) {
return None; }
assert!(offset < available);
let at_aligned = consumed.checked_add(offset).unwrap();
let allocated = at_aligned.checked_add(requested).unwrap();
assert!(allocated <= length);
assert!(at_aligned < length);
let observed = self.consumed.compare_and_swap(
consumed,
allocated,
Ordering::SeqCst);
if observed != consumed {
consumed = observed;
continue;
}
let aligned = unsafe {
(base_ptr as *mut u8).add(at_aligned)
};
return Some(aligned);
}
}
}
unsafe impl<T> Sync for Slab<T> { }
unsafe impl<T> GlobalAlloc for Slab<T> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.take(layout).unwrap_or_else(null_mut)
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}