const INDEX_BITS: u32 = 32;
const INDEX_MASK: u64 = (1 << INDEX_BITS) - 1;
pub(crate) const NOT_AN_OP: u64 = u64::MAX;
struct Slot<T> {
generation: u32,
op: Option<T>,
}
pub(crate) struct Ops<T> {
slots: Vec<Slot<T>>,
free: Vec<u32>,
capacity: usize,
}
fn user_data(index: u32, generation: u32) -> u64 {
(u64::from(generation) << INDEX_BITS) | u64::from(index)
}
impl<T> Ops<T> {
pub(crate) fn new(capacity: u32) -> Self {
Self {
slots: Vec::new(),
free: Vec::new(),
capacity: capacity.min(u32::MAX - 1) as usize,
}
}
pub(crate) fn insert(&mut self, op: T) -> Result<u64, T> {
if let Some(index) = self.free.pop() {
let slot = &mut self.slots[index as usize];
slot.op = Some(op);
return Ok(user_data(index, slot.generation));
}
if self.slots.len() >= self.capacity {
return Err(op);
}
let index = self.slots.len() as u32;
self.slots.push(Slot {
generation: 0,
op: Some(op),
});
Ok(user_data(index, 0))
}
pub(crate) fn get_mut(&mut self, user_data: u64) -> Option<&mut T> {
let slot = self.slots.get_mut((user_data & INDEX_MASK) as usize)?;
if u64::from(slot.generation) != user_data >> INDEX_BITS {
return None;
}
slot.op.as_mut()
}
pub(crate) fn remove(&mut self, user_data: u64) -> Option<T> {
let index = (user_data & INDEX_MASK) as usize;
let slot = self.slots.get_mut(index)?;
if u64::from(slot.generation) != user_data >> INDEX_BITS {
return None;
}
let op = slot.op.take()?;
slot.generation = slot.generation.wrapping_add(1);
self.free.push(index as u32);
Some(op)
}
pub(crate) fn len(&self) -> usize {
self.slots.len() - self.free.len()
}
pub(crate) fn keys(&self) -> impl Iterator<Item = u64> + '_ {
self.slots
.iter()
.enumerate()
.filter(|(_, slot)| slot.op.is_some())
.map(|(index, slot)| user_data(index as u32, slot.generation))
}
#[cfg(test)]
pub(crate) fn iter(&self) -> impl Iterator<Item = &T> {
self.slots.iter().filter_map(|slot| slot.op.as_ref())
}
pub(crate) fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
self.free.clear();
self.slots.drain(..).filter_map(|slot| slot.op)
}
}
#[cfg(test)]
mod tests {
use super::{INDEX_MASK, NOT_AN_OP, Ops};
#[test]
fn a_completion_for_a_reused_slot_finds_nothing() {
let mut ops = Ops::new(8);
let a = ops.insert("first").unwrap();
assert_eq!(ops.remove(a), Some("first"));
let b = ops.insert("second").unwrap();
assert_eq!(a & INDEX_MASK, b & INDEX_MASK, "the slot is reused");
assert_ne!(a, b);
assert!(ops.get_mut(a).is_none(), "the old user data names nothing");
assert!(ops.remove(a).is_none(), "nor removes the new operation");
assert_eq!(ops.remove(b), Some("second"));
assert_eq!(ops.len(), 0);
}
#[test]
fn no_operation_has_the_wakes_user_data() {
let mut ops = Ops::new(10_000);
for i in 0..10_000u32 {
let u = ops.insert(i).unwrap();
assert_ne!(u, NOT_AN_OP);
if i % 3 == 0 {
ops.remove(u);
}
}
}
#[test]
fn a_full_slab_gives_the_operation_back_until_one_completes() {
let mut ops = Ops::new(2);
let a = ops.insert(1).unwrap();
ops.insert(2).unwrap();
assert_eq!(ops.insert(3), Err(3));
ops.remove(a);
assert!(ops.insert(3).is_ok());
}
}