use super::SlabIndexT;
use std::marker::PhantomData;
#[derive(Copy, Clone)]
pub struct RawSlabKey<T> {
index: SlabIndexT,
phantom_data: PhantomData<T>,
}
pub struct RawSlab<T> {
storage: Vec<Option<T>>,
free_list: Vec<SlabIndexT>,
}
impl<T> RawSlab<T> {
pub fn new() -> Self {
let initial_count: SlabIndexT = 32;
let mut storage = Vec::with_capacity(initial_count as usize);
let mut free_list = Vec::with_capacity(initial_count as usize);
for index in (0..initial_count).rev() {
storage.push(None);
free_list.push(index);
}
RawSlab { storage, free_list }
}
pub fn allocate(&mut self, value: T) -> RawSlabKey<T> {
let index = self.free_list.pop();
if index.is_none() {
let index = self.storage.len() as SlabIndexT;
self.storage.push(Some(value));
return RawSlabKey::<T> {
index,
phantom_data: PhantomData,
};
} else {
let index = index.unwrap();
assert!(self.storage[index as usize].is_none());
self.storage[index as usize] = Some(value);
return RawSlabKey::<T> {
index,
phantom_data: PhantomData,
};
}
}
pub fn free(&mut self, slab_key: &RawSlabKey<T>) {
assert!(
self.storage[slab_key.index as usize].is_some(),
"tried to free a none value"
);
self.storage[slab_key.index as usize] = None;
self.free_list.push(slab_key.index);
}
pub fn get(&self, slab_key: &RawSlabKey<T>) -> Option<&T> {
self.storage[slab_key.index as usize].as_ref()
}
pub fn get_mut(&mut self, slab_key: &RawSlabKey<T>) -> Option<&mut T> {
self.storage[slab_key.index as usize].as_mut()
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.storage.iter().filter_map(|x| x.as_ref())
}
pub fn active_count(&self) -> usize {
self.storage.len() - self.free_list.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestStruct {
value: u32,
}
impl TestStruct {
fn new(value: u32) -> Self {
TestStruct { value }
}
}
#[test]
fn test_allocate_deallocate_one() {
let mut pool = RawSlab::<TestStruct>::new();
let value = TestStruct::new(123);
let key = pool.allocate(value);
assert_eq!(1, pool.active_count());
pool.free(&key);
assert_eq!(0, pool.active_count());
}
#[test]
#[should_panic(expected = "tried to free a none value")]
fn test_double_free() {
let mut pool = RawSlab::<TestStruct>::new();
let value = TestStruct::new(123);
let key = pool.allocate(value);
assert_eq!(1, pool.active_count());
pool.free(&key);
assert_eq!(0, pool.active_count());
pool.free(&key);
}
#[test]
fn test_allocate_deallocate_fifo() {
let mut pool = RawSlab::<TestStruct>::new();
let mut keys = vec![];
for i in 0..1000 {
let value = TestStruct::new(i);
let key = pool.allocate(value);
keys.push(key);
}
assert_eq!(1000, pool.active_count());
for k in &keys {
pool.free(k);
}
assert_eq!(0, pool.active_count());
}
#[test]
fn test_allocate_deallocate_lifo() {
let mut pool = RawSlab::<TestStruct>::new();
let mut keys = vec![];
for i in 0..1000 {
let value = TestStruct::new(i);
let key = pool.allocate(value);
keys.push(key);
}
assert_eq!(1000, pool.active_count());
for i in (0..keys.len()).rev() {
pool.free(&keys[i]);
}
assert_eq!(0, pool.active_count());
}
#[test]
fn test_get_success() {
let mut pool = RawSlab::<TestStruct>::new();
let mut keys = vec![];
for i in 0..10 {
let value = TestStruct::new(i);
let key = pool.allocate(value);
keys.push(key);
}
assert_eq!(10, pool.active_count());
assert_eq!(5, pool.get(&keys[5]).unwrap().value);
}
#[test]
fn test_get_fail_out_of_range() {
let mut pool = RawSlab::<TestStruct>::new();
let value = TestStruct::new(123);
let key = pool.allocate(value);
assert_eq!(1, pool.active_count());
assert!(pool.get(&key).is_some());
pool.free(&key);
assert_eq!(0, pool.active_count());
assert!(pool.get(&key).is_none());
}
#[test]
fn test_get_mut_success() {
let mut pool = RawSlab::<TestStruct>::new();
let mut keys = vec![];
for i in 0..10 {
let value = TestStruct::new(i);
let key = pool.allocate(value);
keys.push(key);
}
assert_eq!(10, pool.active_count());
assert_eq!(5, pool.get_mut(&keys[5]).unwrap().value);
}
#[test]
fn test_get_mut_fail_out_of_range() {
let mut pool = RawSlab::<TestStruct>::new();
let value = TestStruct::new(123);
let key = pool.allocate(value);
assert_eq!(1, pool.active_count());
assert!(pool.get_mut(&key).is_some());
pool.free(&key);
assert_eq!(0, pool.active_count());
assert!(pool.get_mut(&key).is_none());
}
}