use std::{mem::{ManuallyDrop, MaybeUninit}, u32};
pub struct IdSlab<T> {
entries: Vec<Entry<T>>,
free_slots: Vec<usize>,
}
struct Entry<T> {
id: u64,
value: MaybeUninit<T>,
}
pub struct IdSlabVacantEntry<'a, T> {
slab: &'a mut IdSlab<T>,
id: u64,
slot: usize,
}
const EMPTY_BIT: u64 = 1u64 << 63;
const EMPTY_BIT_NEG: u64 = !EMPTY_BIT;
const USE_NUM: u64 = (u32::MAX as u64) + 1;
const SLOT_MASK: u64 = 0x00000000FFFFFFFF;
impl<T> IdSlab<T> {
pub fn with_capacity(capacity: usize) -> Self {
let mut slab = IdSlab {
entries: Vec::with_capacity(capacity),
free_slots: Vec::with_capacity(capacity),
};
for pos in 0..capacity {
slab.entries.push(Entry {
id: EMPTY_BIT | (pos as u64),
value: MaybeUninit::uninit(),
});
slab.free_slots.push(capacity - (pos + 1));
}
slab
}
pub fn capacity(&self) -> usize {
self.entries.len()
}
pub fn len(&self) -> usize {
self.entries.len() - self.free_slots.len()
}
pub fn available(&self) -> usize {
self.free_slots.len()
}
pub fn get(&self, id: u64) -> Option<&T> {
let slot = self.slot(id)?;
let entry = &self.entries[slot];
if (entry.id & EMPTY_BIT) == EMPTY_BIT {
return None;
}
unsafe { Some(entry.value.assume_init_ref()) }
}
pub fn get_mut(&mut self, id: u64) -> Option<&mut T> {
let slot = self.slot(id)?;
let entry = &mut self.entries[slot];
if (entry.id & EMPTY_BIT) == EMPTY_BIT {
return None;
}
unsafe { Some(entry.value.assume_init_mut()) }
}
pub fn remove(&mut self, id: u64) -> Option<T> {
let slot = self.slot(id)?;
let entry = &mut self.entries[slot];
if (entry.id & EMPTY_BIT) == EMPTY_BIT {
return None;
}
entry.id = EMPTY_BIT | (entry.id + USE_NUM);
assert_eq!(entry.id & EMPTY_BIT, EMPTY_BIT);
self.free_slots.push(slot);
Some(unsafe {
std::mem::replace(&mut entry.value, MaybeUninit::uninit()).assume_init()
})
}
fn slot(&self, id: u64) -> Option<usize> {
let slot = (id & SLOT_MASK) as usize;
if self.entries.len() <= slot {
return None;
}
Some(slot)
}
pub fn insert(&mut self, value: T) -> Result<u64, T> {
let slot = match self.free_slots.pop() {
Some(v) => v,
None => return Err(value),
};
let entry = &mut self.entries[slot];
assert!((entry.id & EMPTY_BIT) == EMPTY_BIT);
entry.id = EMPTY_BIT_NEG & entry.id;
assert!((entry.id & EMPTY_BIT) == 0);
entry.value.write(value);
Ok(entry.id)
}
pub fn vacant_entry(&mut self) -> Option<IdSlabVacantEntry<T>> {
let slot = self.free_slots.pop()?;
let id = self.entries[slot].id & EMPTY_BIT_NEG;
Some(IdSlabVacantEntry {
slab: self,
id,
slot,
})
}
pub fn iter(&self) -> IdSlabIter<T> {
IdSlabIter {
slab: self,
slot: 0,
remaining: self.len(),
}
}
pub fn iter_mut(&mut self) -> IdSlabIterMut<T> {
let remaining = self.len();
IdSlabIterMut { slab: self, slot: 0, remaining }
}
}
impl<'a, T> IdSlabVacantEntry<'a, T> {
pub fn id(&self) -> u64 {
self.id
}
pub fn insert(self, value: T) -> u64 {
let entry = &mut self.slab.entries[self.slot];
assert!(entry.id & EMPTY_BIT == EMPTY_BIT);
let id = EMPTY_BIT_NEG & entry.id;
assert!((id & EMPTY_BIT) == 0);
entry.id = id;
entry.value.write(value);
let _ = ManuallyDrop::new(self);
id
}
}
impl<'a, T> Drop for IdSlabVacantEntry<'a, T> {
fn drop(&mut self) {
self.slab.free_slots.push(self.slot);
}
}
pub struct IdSlabIter<'a, T> {
slab: &'a IdSlab<T>,
slot: usize,
remaining: usize,
}
impl<'a, T> Iterator for IdSlabIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
while self.slot < self.slab.entries.len() && self.remaining > 0 {
let entry = &self.slab.entries[self.slot];
self.slot += 1;
if (entry.id & EMPTY_BIT) == EMPTY_BIT {
continue;
}
self.remaining -= 1;
return Some(unsafe { entry.value.assume_init_ref() });
}
None
}
}
pub struct IdSlabIterMut<'a, T> {
slab: &'a mut IdSlab<T>,
slot: usize,
remaining: usize,
}
impl<'a, T> Iterator for IdSlabIterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
let len = self.slab.entries.len();
while self.slot < len && self.remaining > 0 {
let entry = &mut self.slab.entries[self.slot];
self.slot += 1;
if (entry.id & EMPTY_BIT) == EMPTY_BIT {
continue;
}
self.remaining -= 1;
return Some(unsafe { std::mem::transmute(entry.value.assume_init_mut()) });
}
None
}
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use rand::{seq::SliceRandom, thread_rng};
use super::IdSlab;
#[test]
fn test() {
let mut slab = IdSlab::<String>::with_capacity(16);
let world_id = slab.insert("hello world".to_string()).unwrap();
let mut old_ids = HashSet::new();
let mut ids = Vec::new();
for i in 0..100 {
for j in 0..8 {
let entry = slab.vacant_entry().unwrap();
assert!(old_ids.insert(entry.id()));
ids.push(entry.insert(format!("{} - {}", i, j)));
assert_eq!(slab.len(), ids.len() + 1);
}
ids.shuffle(&mut thread_rng());
while 7 < ids.len() {
let id = ids.pop().unwrap();
slab.remove(id).unwrap();
assert_eq!(slab.len(), ids.len() + 1);
}
}
assert_eq!(slab.get(world_id).unwrap(), "hello world");
for id in ids {
slab.remove(id).unwrap();
}
}
}