use std::task::Waker;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct SlabToken {
index: u32,
generation: u32,
}
impl SlabToken {
const fn new(index: u32, generation: u32) -> Self {
Self { index, generation }
}
#[must_use]
pub const fn index(&self) -> u32 {
self.index
}
#[must_use]
pub const fn generation(&self) -> u32 {
self.generation
}
#[must_use]
pub const fn to_usize(self) -> usize {
#[cfg(target_pointer_width = "64")]
{
((self.generation as usize) << 32) | (self.index as usize)
}
#[cfg(target_pointer_width = "32")]
{
((self.generation as usize & 0xFF) << 24) | (self.index as usize & 0xFF_FFFF)
}
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
{
0 }
}
#[must_use]
pub const fn from_usize(val: usize) -> Self {
#[cfg(target_pointer_width = "64")]
{
Self {
index: val as u32,
generation: (val >> 32) as u32,
}
}
#[cfg(target_pointer_width = "32")]
{
Self {
index: (val & 0xFF_FFFF) as u32,
generation: (val >> 24) as u32,
}
}
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
{
Self {
index: 0,
generation: 0,
}
}
}
#[cfg(target_pointer_width = "64")]
pub const MAX_GENERATION: u32 = u32::MAX;
#[cfg(target_pointer_width = "32")]
pub const MAX_GENERATION: u32 = 0xFF;
#[must_use]
pub const fn invalid() -> Self {
Self {
index: u32::MAX,
generation: u32::MAX,
}
}
}
impl Default for SlabToken {
fn default() -> Self {
Self::invalid()
}
}
#[derive(Debug)]
enum Entry {
Occupied { waker: Waker, generation: u32 },
Vacant { next_free: u32, generation: u32 },
}
impl Entry {
fn generation(&self) -> u32 {
match self {
Self::Occupied { generation, .. } | Self::Vacant { generation, .. } => *generation,
}
}
}
const FREE_LIST_END: u32 = u32::MAX;
#[derive(Debug)]
pub struct TokenSlab {
entries: Vec<Entry>,
free_head: u32,
len: usize,
}
impl TokenSlab {
#[must_use]
pub fn new() -> Self {
Self {
entries: Vec::new(),
free_head: FREE_LIST_END,
len: 0,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
entries: Vec::with_capacity(capacity),
free_head: FREE_LIST_END,
len: 0,
}
}
pub fn insert(&mut self, waker: Waker) -> SlabToken {
if self.free_head == FREE_LIST_END {
let index = self.entries.len() as u32;
#[cfg(target_pointer_width = "32")]
assert!(
index < 0xFF_FFFF,
"TokenSlab capacity exceeded 16.7M entries on 32-bit platform"
);
#[cfg(target_pointer_width = "64")]
assert!(
self.entries.len() < u32::MAX as usize,
"TokenSlab capacity exceeded u32::MAX - 1 (conflicts with FREE_LIST_END)"
);
let generation = 0;
self.entries.push(Entry::Occupied { waker, generation });
self.len += 1;
SlabToken::new(index, generation)
} else {
let index = self.free_head;
let entry = &mut self.entries[index as usize];
let (generation, next_free) = match entry {
Entry::Vacant {
next_free,
generation,
} => (*generation, *next_free),
Entry::Occupied { .. } => {
unreachable!("free list pointed to occupied entry");
}
};
*entry = Entry::Occupied { waker, generation };
self.free_head = next_free;
self.len += 1;
SlabToken::new(index, generation)
}
}
#[must_use]
pub fn get(&self, token: SlabToken) -> Option<&Waker> {
let index = token.index as usize;
if index >= self.entries.len() {
return None;
}
match &self.entries[index] {
Entry::Occupied { waker, generation } if *generation == token.generation => Some(waker),
_ => None,
}
}
#[must_use]
pub fn get_mut(&mut self, token: SlabToken) -> Option<&mut Waker> {
let index = token.index as usize;
if index >= self.entries.len() {
return None;
}
match &mut self.entries[index] {
Entry::Occupied { waker, generation } if *generation == token.generation => Some(waker),
_ => None,
}
}
pub fn remove(&mut self, token: SlabToken) -> Option<Waker> {
let index = token.index as usize;
if index >= self.entries.len() {
return None;
}
let current_generation = self.entries[index].generation();
if current_generation != token.generation {
return None;
}
if matches!(self.entries[index], Entry::Occupied { .. }) {
let new_generation = current_generation.wrapping_add(1) & SlabToken::MAX_GENERATION;
if current_generation == SlabToken::MAX_GENERATION {
let old_entry = std::mem::replace(
&mut self.entries[index],
Entry::Vacant {
next_free: FREE_LIST_END,
generation: new_generation,
},
);
self.len -= 1;
match old_entry {
Entry::Occupied { waker, .. } => Some(waker),
Entry::Vacant { .. } => unreachable!(),
}
} else {
let old_entry = std::mem::replace(
&mut self.entries[index],
Entry::Vacant {
next_free: self.free_head,
generation: new_generation,
},
);
self.free_head = index as u32;
self.len -= 1;
match old_entry {
Entry::Occupied { waker, .. } => Some(waker),
Entry::Vacant { .. } => unreachable!(),
}
}
} else {
None
}
}
#[must_use]
pub fn contains(&self, token: SlabToken) -> bool {
self.get(token).is_some()
}
#[must_use]
pub fn len(&self) -> usize {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub fn capacity(&self) -> usize {
self.entries.capacity()
}
pub fn clear(&mut self) {
self.free_head = FREE_LIST_END;
for index in (0..self.entries.len()).rev() {
let current_generation = self.entries[index].generation();
let generation = current_generation.wrapping_add(1) & SlabToken::MAX_GENERATION;
if current_generation == SlabToken::MAX_GENERATION {
self.entries[index] = Entry::Vacant {
next_free: FREE_LIST_END,
generation,
};
} else {
self.entries[index] = Entry::Vacant {
next_free: self.free_head,
generation,
};
self.free_head = index as u32;
}
}
self.len = 0;
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(SlabToken, &Waker) -> bool,
{
for index in 0..self.entries.len() {
let mut remove = false;
let current_generation = self.entries[index].generation();
if let Entry::Occupied { waker, generation } = &self.entries[index] {
let token = SlabToken::new(index as u32, *generation);
if !f(token, waker) {
remove = true;
}
}
if remove {
let new_generation = current_generation.wrapping_add(1) & SlabToken::MAX_GENERATION;
if current_generation == SlabToken::MAX_GENERATION {
self.entries[index] = Entry::Vacant {
next_free: FREE_LIST_END,
generation: new_generation,
};
} else {
self.entries[index] = Entry::Vacant {
next_free: self.free_head,
generation: new_generation,
};
self.free_head = index as u32;
}
self.len -= 1;
}
}
}
pub fn iter(&self) -> impl Iterator<Item = (SlabToken, &Waker)> {
self.entries
.iter()
.enumerate()
.filter_map(|(index, entry)| {
if let Entry::Occupied { waker, generation } = entry {
Some((SlabToken::new(index as u32, *generation), waker))
} else {
None
}
})
}
}
impl Default for TokenSlab {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::init_test_logging;
fn test_waker() -> Waker {
std::task::Waker::noop().clone()
}
fn init_test(name: &str) {
init_test_logging();
crate::test_phase!(name);
}
#[test]
fn token_pack_unpack() {
init_test("token_pack_unpack");
let token = SlabToken::new(42, 7);
let packed = token.to_usize();
let unpacked = SlabToken::from_usize(packed);
crate::assert_with_log!(token == unpacked, "token round-trip", token, unpacked);
crate::assert_with_log!(
unpacked.index() == 42,
"index round-trip",
42u32,
unpacked.index()
);
crate::assert_with_log!(
unpacked.generation() == 7,
"generation round-trip",
7u32,
unpacked.generation()
);
crate::test_complete!("token_pack_unpack");
}
#[test]
fn token_pack_unpack_max_values() {
init_test("token_pack_unpack_max_values");
#[cfg(target_pointer_width = "64")]
let max_index = u32::MAX - 1;
#[cfg(target_pointer_width = "32")]
let max_index = 0xFF_FFFF - 1;
let token = SlabToken::new(max_index, SlabToken::MAX_GENERATION);
let packed = token.to_usize();
let unpacked = SlabToken::from_usize(packed);
crate::assert_with_log!(token == unpacked, "max token round-trip", token, unpacked);
crate::test_complete!("token_pack_unpack_max_values");
}
#[test]
fn slab_insert_and_get() {
init_test("slab_insert_and_get");
let mut slab = TokenSlab::new();
let waker = test_waker();
let token = slab.insert(waker);
crate::assert_with_log!(slab.len() == 1, "len after insert", 1usize, slab.len());
crate::assert_with_log!(!slab.is_empty(), "slab not empty", false, slab.is_empty());
let contains = slab.contains(token);
crate::assert_with_log!(contains, "slab contains token", true, contains);
let get_some = slab.get(token).is_some();
crate::assert_with_log!(get_some, "slab get returns", true, get_some);
crate::test_complete!("slab_insert_and_get");
}
#[test]
fn slab_remove() {
init_test("slab_remove");
let mut slab = TokenSlab::new();
let waker = test_waker();
let token = slab.insert(waker);
let removed = slab.remove(token);
crate::assert_with_log!(
removed.is_some(),
"remove returns some",
true,
removed.is_some()
);
crate::assert_with_log!(slab.is_empty(), "len after remove", 0usize, slab.len());
crate::assert_with_log!(
slab.is_empty(),
"slab empty after remove",
true,
slab.is_empty()
);
let contains = slab.contains(token);
crate::assert_with_log!(!contains, "token removed", false, contains);
let get_none = slab.get(token).is_none();
crate::assert_with_log!(get_none, "get returns none", true, get_none);
crate::test_complete!("slab_remove");
}
#[test]
fn slab_generation_prevents_aba() {
init_test("slab_generation_prevents_aba");
let mut slab = TokenSlab::new();
let token1 = slab.insert(test_waker());
crate::assert_with_log!(
token1.generation() == 0,
"initial generation",
0u32,
token1.generation()
);
slab.remove(token1);
let token2 = slab.insert(test_waker());
crate::assert_with_log!(
token2.index() == token1.index(),
"slot reused",
token1.index(),
token2.index()
);
crate::assert_with_log!(
token2.generation() == 1,
"generation incremented",
1u32,
token2.generation()
);
let contains_old = slab.contains(token1);
crate::assert_with_log!(!contains_old, "old token invalid", false, contains_old);
let old_get = slab.get(token1).is_none();
crate::assert_with_log!(old_get, "old token get none", true, old_get);
let contains_new = slab.contains(token2);
crate::assert_with_log!(contains_new, "new token valid", true, contains_new);
let new_get = slab.get(token2).is_some();
crate::assert_with_log!(new_get, "new token get some", true, new_get);
crate::test_complete!("slab_generation_prevents_aba");
}
#[test]
fn slab_reuses_free_slots() {
init_test("slab_reuses_free_slots");
let mut slab = TokenSlab::new();
let t1 = slab.insert(test_waker());
let t2 = slab.insert(test_waker());
let t3 = slab.insert(test_waker());
crate::assert_with_log!(slab.len() == 3, "len after inserts", 3usize, slab.len());
slab.remove(t2);
crate::assert_with_log!(slab.len() == 2, "len after remove", 2usize, slab.len());
let t4 = slab.insert(test_waker());
crate::assert_with_log!(
t4.index() == t2.index(),
"reused slot index",
t2.index(),
t4.index()
);
crate::assert_with_log!(
t4.generation() != t2.generation(),
"generation advanced",
true,
t4.generation() != t2.generation()
);
let contains_t1 = slab.contains(t1);
let contains_t3 = slab.contains(t3);
let contains_t4 = slab.contains(t4);
let contains_t2 = slab.contains(t2);
crate::assert_with_log!(contains_t1, "t1 still valid", true, contains_t1);
crate::assert_with_log!(contains_t3, "t3 still valid", true, contains_t3);
crate::assert_with_log!(contains_t4, "t4 valid", true, contains_t4);
crate::assert_with_log!(!contains_t2, "t2 invalidated", false, contains_t2);
crate::test_complete!("slab_reuses_free_slots");
}
#[test]
fn slab_multiple_inserts_removes() {
init_test("slab_multiple_inserts_removes");
let mut slab = TokenSlab::new();
let mut tokens = Vec::new();
for _ in 0..100 {
tokens.push(slab.insert(test_waker()));
}
crate::assert_with_log!(slab.len() == 100, "len after inserts", 100usize, slab.len());
for i in (0..100).step_by(2) {
slab.remove(tokens[i]);
}
crate::assert_with_log!(slab.len() == 50, "len after removes", 50usize, slab.len());
for _ in 0..25 {
tokens.push(slab.insert(test_waker()));
}
crate::assert_with_log!(slab.len() == 75, "len after reinserts", 75usize, slab.len());
crate::test_complete!("slab_multiple_inserts_removes");
}
#[test]
fn slab_get_invalid_index() {
init_test("slab_get_invalid_index");
let slab = TokenSlab::new();
let token = SlabToken::new(999, 0);
let contains = slab.contains(token);
crate::assert_with_log!(!contains, "invalid token not contained", false, contains);
let get_none = slab.get(token).is_none();
crate::assert_with_log!(get_none, "invalid token get none", true, get_none);
crate::test_complete!("slab_get_invalid_index");
}
#[test]
fn slab_remove_invalid_generation() {
init_test("slab_remove_invalid_generation");
let mut slab = TokenSlab::new();
let token = slab.insert(test_waker());
let stale_token = SlabToken::new(token.index(), token.generation() + 1);
let removed = slab.remove(stale_token).is_none();
crate::assert_with_log!(removed, "stale remove fails", true, removed);
let contains = slab.contains(token);
crate::assert_with_log!(contains, "original token still valid", true, contains);
crate::test_complete!("slab_remove_invalid_generation");
}
#[test]
fn slab_double_remove() {
init_test("slab_double_remove");
let mut slab = TokenSlab::new();
let token = slab.insert(test_waker());
let removed1 = slab.remove(token);
let removed2 = slab.remove(token);
crate::assert_with_log!(
removed1.is_some(),
"first remove succeeds",
true,
removed1.is_some()
);
crate::assert_with_log!(
removed2.is_none(),
"second remove fails",
true,
removed2.is_none()
);
crate::test_complete!("slab_double_remove");
}
#[test]
fn slab_clear() {
init_test("slab_clear");
let mut slab = TokenSlab::new();
for _ in 0..10 {
slab.insert(test_waker());
}
crate::assert_with_log!(slab.len() == 10, "len before clear", 10usize, slab.len());
slab.clear();
crate::assert_with_log!(slab.is_empty(), "len after clear", 0usize, slab.len());
crate::assert_with_log!(
slab.is_empty(),
"slab empty after clear",
true,
slab.is_empty()
);
crate::test_complete!("slab_clear");
}
#[test]
fn slab_retain() {
init_test("slab_retain");
let mut slab = TokenSlab::new();
let tokens: Vec<_> = (0..10).map(|_| slab.insert(test_waker())).collect();
crate::assert_with_log!(slab.len() == 10, "len before retain", 10usize, slab.len());
slab.retain(|token, _| token.index() % 2 == 0);
crate::assert_with_log!(slab.len() == 5, "len after retain", 5usize, slab.len());
for (i, token) in tokens.iter().enumerate() {
let contains = slab.contains(*token);
if i % 2 == 0 {
crate::assert_with_log!(contains, "even token retained", true, contains);
} else {
crate::assert_with_log!(!contains, "odd token removed", false, contains);
}
}
crate::test_complete!("slab_retain");
}
#[test]
fn slab_iter() {
init_test("slab_iter");
let mut slab = TokenSlab::new();
let tokens: Vec<_> = (0..5).map(|_| slab.insert(test_waker())).collect();
slab.remove(tokens[2]);
let iter_tokens: Vec<_> = slab.iter().map(|(t, _)| t).collect();
crate::assert_with_log!(
iter_tokens.len() == 4,
"iter length",
4usize,
iter_tokens.len()
);
let contains_0 = iter_tokens.contains(&tokens[0]);
let contains_1 = iter_tokens.contains(&tokens[1]);
let contains_2 = iter_tokens.contains(&tokens[2]);
let contains_3 = iter_tokens.contains(&tokens[3]);
let contains_4 = iter_tokens.contains(&tokens[4]);
crate::assert_with_log!(contains_0, "iter contains token 0", true, contains_0);
crate::assert_with_log!(contains_1, "iter contains token 1", true, contains_1);
crate::assert_with_log!(!contains_2, "iter omits removed", false, contains_2);
crate::assert_with_log!(contains_3, "iter contains token 3", true, contains_3);
crate::assert_with_log!(contains_4, "iter contains token 4", true, contains_4);
crate::test_complete!("slab_iter");
}
#[test]
fn slab_with_capacity() {
init_test("slab_with_capacity");
let slab = TokenSlab::with_capacity(100);
crate::assert_with_log!(
slab.capacity() >= 100,
"capacity at least requested",
true,
slab.capacity() >= 100
);
crate::assert_with_log!(slab.is_empty(), "slab starts empty", true, slab.is_empty());
crate::test_complete!("slab_with_capacity");
}
#[test]
fn token_invalid() {
init_test("token_invalid");
let token = SlabToken::invalid();
crate::assert_with_log!(
token.index() == u32::MAX,
"invalid index",
u32::MAX,
token.index()
);
crate::assert_with_log!(
token.generation() == u32::MAX,
"invalid generation",
u32::MAX,
token.generation()
);
crate::test_complete!("token_invalid");
}
#[test]
fn slab_get_mut() {
init_test("slab_get_mut");
let mut slab = TokenSlab::new();
let token = slab.insert(test_waker());
let has_mut = slab.get_mut(token).is_some();
crate::assert_with_log!(has_mut, "get_mut succeeds", true, has_mut);
slab.remove(token);
let has_mut_after = slab.get_mut(token).is_none();
crate::assert_with_log!(has_mut_after, "get_mut after remove", true, has_mut_after);
crate::test_complete!("slab_get_mut");
}
#[test]
fn slab_clear_invalidates_stale_tokens() {
init_test("slab_clear_invalidates_stale_tokens");
let mut slab = TokenSlab::new();
let stale = slab.insert(test_waker());
slab.clear();
let contains_stale = slab.contains(stale);
crate::assert_with_log!(
!contains_stale,
"stale token invalid after clear",
false,
contains_stale
);
let fresh = slab.insert(test_waker());
crate::assert_with_log!(
fresh.index() == stale.index(),
"slot reused after clear",
stale.index(),
fresh.index()
);
crate::assert_with_log!(
fresh.generation() != stale.generation(),
"generation advanced across clear",
true,
fresh.generation() != stale.generation()
);
crate::assert_with_log!(
slab.get(stale).is_none(),
"old token still rejected after reuse",
true,
slab.get(stale).is_none()
);
crate::test_complete!("slab_clear_invalidates_stale_tokens");
}
#[test]
fn token_default() {
init_test("token_default");
let token = SlabToken::default();
crate::assert_with_log!(
token == SlabToken::invalid(),
"default is invalid",
SlabToken::invalid(),
token
);
crate::test_complete!("token_default");
}
#[test]
fn slab_token_debug_clone_copy_hash() {
use std::collections::HashSet;
let t = SlabToken::from_usize(42);
let dbg = format!("{t:?}");
assert!(dbg.contains("SlabToken"));
let t2 = t;
assert_eq!(t, t2);
let t3 = t;
assert_eq!(t, t3);
let mut set = HashSet::new();
set.insert(t);
set.insert(SlabToken::from_usize(99));
assert_eq!(set.len(), 2);
assert!(set.contains(&t));
}
}