use std::marker::PhantomData;
use std::mem;
use std::ptr::{self, NonNull};
use crate::transport::{LKey, UdTransport};
use crate::util::{buffer::*, huge_alloc::*};
pub(crate) struct SlabAllocator<T> {
mem_registry: Vec<HugeAlloc>,
len: usize,
next: *mut u8,
lkey: LKey,
_marker: PhantomData<T>,
}
impl<T> SlabAllocator<T> {
#[inline(always)]
fn is_used_up(&self) -> bool {
self.next.is_null()
|| self.mem_registry.is_empty()
|| self.next == unsafe { self.mem_registry.last().unwrap().ptr.add(Self::ALLOC_SIZE) }
}
#[cold]
fn reserve_memory(&mut self, tp: &mut UdTransport) {
let mem = alloc_raw(Self::ALLOC_SIZE);
let (lkey, _) = unsafe { tp.reg_mem(mem.ptr, Self::ALLOC_SIZE) };
self.next = mem.ptr;
self.lkey = lkey;
self.mem_registry.push(mem);
}
}
impl<T> SlabAllocator<T> {
const ALLOC_SIZE: usize = 1 << 20;
pub fn new() -> Self {
let len = mem::size_of::<T>().next_power_of_two();
assert!(
len <= Self::ALLOC_SIZE,
"SlabAllocator: size class too large"
);
Self {
mem_registry: Vec::new(),
len,
next: ptr::null_mut(),
lkey: 0,
_marker: PhantomData,
}
}
pub fn alloc(&mut self, tp: &mut UdTransport) -> Buffer {
if self.is_used_up() {
self.reserve_memory(tp);
}
let addr = self.next;
self.next = unsafe { self.next.add(self.len) };
Buffer::real(NonNull::new(addr).unwrap(), self.len, self.lkey, 0, None)
}
}