use crate::alloc::Vec;
use crate::error::OutOfMemory;
use core::fmt;
use core::num::NonZeroU32;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Id(EntryIndex);
impl fmt::Debug for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Id").field(&self.0.index()).finish()
}
}
impl Id {
#[inline]
pub fn into_raw(self) -> u32 {
u32::try_from(self.0.index()).unwrap()
}
#[inline]
pub fn from_raw(raw: u32) -> Self {
let raw = usize::try_from(raw).unwrap();
Self(EntryIndex::new(raw))
}
}
pub struct Slab<T> {
entries: Vec<Entry<T>>,
free: Option<EntryIndex>,
len: u32,
}
impl<T> fmt::Debug for Slab<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
enum Entry<T> {
Occupied(T),
Free {
next_free: Option<EntryIndex>,
},
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
struct EntryIndex(NonZeroU32);
impl EntryIndex {
#[inline]
fn new(index: usize) -> Self {
assert!(index <= Slab::<()>::MAX_CAPACITY);
let x = u32::try_from(index + 1).unwrap();
Self(NonZeroU32::new(x).unwrap())
}
#[inline]
fn index(&self) -> usize {
let index = self.0.get() - 1;
usize::try_from(index).unwrap()
}
}
impl<T> Default for Slab<T> {
#[inline]
fn default() -> Self {
Self {
entries: Vec::default(),
free: None,
len: 0,
}
}
}
impl<T> core::ops::Index<Id> for Slab<T> {
type Output = T;
#[inline]
fn index(&self, id: Id) -> &Self::Output {
self.get(id)
.expect("id from different slab or value was deallocated")
}
}
impl<T> core::ops::IndexMut<Id> for Slab<T> {
#[inline]
fn index_mut(&mut self, id: Id) -> &mut Self::Output {
self.get_mut(id)
.expect("id from different slab or value was deallocated")
}
}
impl<T> Slab<T> {
pub const MAX_CAPACITY: usize = (u32::MAX - 1) as usize;
#[inline]
pub fn new() -> Self {
Slab::default()
}
#[inline]
pub fn with_capacity(capacity: usize) -> Result<Self, OutOfMemory> {
let mut slab = Self::new();
slab.reserve(capacity)?;
Ok(slab)
}
pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> {
let cap = self.capacity();
let len = self.len();
assert!(cap >= len);
if cap - len >= additional {
return Ok(());
}
self.entries.reserve(additional)?;
assert!(self.entries.capacity() <= Self::MAX_CAPACITY);
Ok(())
}
fn double_capacity(&mut self) -> Result<(), OutOfMemory> {
const MIN_CAPACITY: usize = 16;
let additional = core::cmp::max(self.entries.capacity(), MIN_CAPACITY);
self.reserve(additional)
}
#[inline]
pub fn capacity(&self) -> usize {
self.entries.capacity()
}
#[inline]
pub fn len(&self) -> usize {
usize::try_from(self.len).unwrap()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn try_alloc(&mut self, value: T) -> Result<Id, T> {
if let Some(index) = self.try_alloc_index() {
let next_free = match self.entries[index.index()] {
Entry::Free { next_free } => next_free,
Entry::Occupied { .. } => unreachable!(),
};
self.free = next_free;
self.entries[index.index()] = Entry::Occupied(value);
self.len += 1;
Ok(Id(index))
} else {
Err(value)
}
}
#[inline]
fn try_alloc_index(&mut self) -> Option<EntryIndex> {
self.free.take().or_else(|| {
if self.entries.len() < self.entries.capacity() {
let index = EntryIndex::new(self.entries.len());
self.entries
.push(Entry::Free { next_free: None })
.expect("have capacity");
Some(index)
} else {
None
}
})
}
#[inline]
pub fn alloc(&mut self, value: T) -> Result<Id, OutOfMemory> {
self.try_alloc(value)
.or_else(|value| self.alloc_slow(value))
}
#[inline]
pub fn next_id(&self) -> Id {
let index = self.free.unwrap_or_else(|| EntryIndex::new(self.len()));
Id(index)
}
#[inline(never)]
#[cold]
fn alloc_slow(&mut self, value: T) -> Result<Id, OutOfMemory> {
self.double_capacity()?;
let id = self.try_alloc(value).ok().unwrap();
Ok(id)
}
#[inline]
pub fn get(&self, id: Id) -> Option<&T> {
match self
.entries
.get(id.0.index())
.expect("id from different slab")
{
Entry::Occupied(x) => Some(x),
Entry::Free { .. } => None,
}
}
#[inline]
pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
match self
.entries
.get_mut(id.0.index())
.expect("id from different slab")
{
Entry::Occupied(x) => Some(x),
Entry::Free { .. } => None,
}
}
#[inline]
pub fn contains(&self, id: Id) -> bool {
match self.entries.get(id.0.index()) {
Some(Entry::Occupied(_)) => true,
None | Some(Entry::Free { .. }) => false,
}
}
#[inline]
pub fn dealloc(&mut self, id: Id) -> T {
let entry = core::mem::replace(
self.entries
.get_mut(id.0.index())
.expect("id from a different slab"),
Entry::Free { next_free: None },
);
match entry {
Entry::Free { .. } => panic!("attempt to deallocate an entry that is already vacant"),
Entry::Occupied(value) => {
let next_free = core::mem::replace(&mut self.free, Some(id.0));
self.entries[id.0.index()] = Entry::Free { next_free };
self.len -= 1;
value
}
}
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (Id, &T)> + '_ {
assert!(self.entries.len() <= Self::MAX_CAPACITY);
self.entries
.iter()
.enumerate()
.filter_map(|(i, e)| match e {
Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
Entry::Free { .. } => None,
})
}
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Id, &mut T)> + '_ {
assert!(self.entries.len() <= Self::MAX_CAPACITY);
self.entries
.iter_mut()
.enumerate()
.filter_map(|(i, e)| match e {
Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
Entry::Free { .. } => None,
})
}
#[inline]
pub fn drain(&mut self) -> impl Iterator<Item = (Id, T)> + '_ {
assert!(self.entries.len() <= Self::MAX_CAPACITY);
self.len = 0;
self.free = None;
self.entries
.drain(..)
.enumerate()
.filter_map(|(i, e)| match e {
Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
Entry::Free { .. } => None,
})
}
}