pub type Token = std::num::NonZeroU32;
#[derive(Debug, Clone)]
struct Entry<T> {
item: Option<T>,
next: Token,
prev: Token,
}
#[derive(Debug, Clone)]
pub struct LinkedSlab<T> {
entries: Vec<Entry<T>>,
next_free: Token,
}
impl<T> LinkedSlab<T> {
pub fn with_capacity(capacity: usize) -> Self {
Self {
entries: Vec::with_capacity(capacity),
next_free: Token::new(1).unwrap(),
}
}
#[cfg(fuzzing)]
pub fn len(&self) -> usize {
self.entries.iter().filter(|e| e.item.is_some()).count()
}
#[cfg(any(fuzzing, test))]
pub fn iter_entries(&self) -> impl Iterator<Item = &T> + '_ {
self.entries.iter().filter_map(|e| e.item.as_ref())
}
#[cfg(any(fuzzing, test))]
pub fn validate(&self) {
let mut freelist = std::collections::HashSet::new();
let mut next_free = self.next_free;
while next_free.get() as usize - 1 != self.entries.len() {
freelist.insert(next_free.get() as usize);
let e = &self.entries[next_free.get() as usize - 1];
assert!(e.item.is_none(), "{next_free} is in freelist but has item");
next_free = e.next;
}
for (i, e) in self.entries.iter().enumerate() {
if e.item.is_some() {
assert!(!freelist.contains(&(i + 1)));
assert!(!freelist.contains(&(e.prev.get() as usize)));
assert!(!freelist.contains(&(e.next.get() as usize)));
}
}
}
pub fn insert(&mut self, item: T) -> Token {
let token = self.next_free;
let idx = (token.get() - 1) as usize;
if idx < self.entries.len() {
let entry = &mut self.entries[idx];
self.next_free = entry.next;
(entry.prev, entry.next) = (token, token);
debug_assert!(entry.item.is_none());
entry.item = Some(item);
} else {
debug_assert_eq!(idx, self.entries.len());
self.next_free = Token::new(token.get().wrapping_add(1)).expect("Capacity overflow");
self.entries.push(Entry {
next: token,
prev: token,
item: Some(item),
});
}
token
}
#[inline]
pub fn get(&self, index: Token) -> Option<(&T, Token)> {
if let Some(entry) = self.entries.get((index.get() - 1) as usize) {
if let Some(v) = &entry.item {
return Some((v, entry.next));
}
}
None
}
#[inline]
pub fn get_mut(&mut self, index: Token) -> Option<(&mut T, Token)> {
if let Some(entry) = self.entries.get_mut((index.get() - 1) as usize) {
if let Some(v) = &mut entry.item {
return Some((v, entry.next));
}
}
None
}
#[inline]
pub unsafe fn get_unchecked(&self, index: Token) -> (&T, Token) {
unsafe {
let entry = self.entries.get_unchecked((index.get() - 1) as usize);
let v = entry.item.as_ref().unwrap_unchecked();
(v, entry.next)
}
}
#[inline]
pub unsafe fn get_mut_unchecked(&mut self, index: Token) -> (&mut T, Token) {
unsafe {
let entry = self.entries.get_unchecked_mut((index.get() - 1) as usize);
let v = entry.item.as_mut().unwrap_unchecked();
(v, entry.next)
}
}
pub fn link(&mut self, idx: Token, target_head: Option<Token>) -> Token {
let (prev, next) = if let Some(target_head) = target_head {
let head = &mut self.entries[(target_head.get() - 1) as usize];
debug_assert!(head.item.is_some());
if head.prev == target_head {
head.prev = idx;
head.next = idx;
(target_head, target_head)
} else {
let before_head_idx = head.prev;
head.prev = idx;
let before_head = &mut self.entries[(before_head_idx.get() - 1) as usize];
debug_assert!(before_head.item.is_some());
debug_assert_eq!(before_head.next, target_head);
before_head.next = idx;
(before_head_idx, target_head)
}
} else {
(idx, idx)
};
let entry = &mut self.entries[(idx.get() - 1) as usize];
debug_assert!(entry.item.is_some());
debug_assert_eq!(entry.next, idx);
debug_assert_eq!(entry.prev, idx);
(entry.prev, entry.next) = (prev, next);
next
}
pub fn unlink(&mut self, idx: Token) -> Option<Token> {
let entry = &mut self.entries[(idx.get() - 1) as usize];
debug_assert!(entry.item.is_some());
let (prev_idx, next_idx) = (entry.prev, entry.next);
if next_idx == idx {
debug_assert_eq!(prev_idx, idx);
None
} else {
(entry.prev, entry.next) = (idx, idx);
let next = &mut self.entries[(next_idx.get() - 1) as usize];
debug_assert!(next.item.is_some());
debug_assert_eq!(next.prev, idx);
next.prev = prev_idx;
let prev = &mut self.entries[(prev_idx.get() - 1) as usize];
debug_assert!(prev.item.is_some());
debug_assert_eq!(prev.next, idx);
prev.next = next_idx;
Some(next_idx)
}
}
pub fn remove(&mut self, idx: Token) -> Option<(T, Option<Token>)> {
let next = self.unlink(idx);
let entry = &mut self.entries[(idx.get() - 1) as usize];
let old_item = entry.item.take()?;
entry.next = self.next_free;
self.next_free = idx;
Some((old_item, next))
}
pub fn next_free(&self) -> Token {
self.next_free
}
pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
self.next_free = Token::new(1).unwrap();
self.entries.drain(..).flat_map(|i| i.item)
}
pub fn iter(&self) -> impl Iterator<Item = &'_ T> + '_ {
self.entries.iter().flat_map(|i| i.item.as_ref())
}
pub fn iter_from(
&self,
continuation: Option<Token>,
) -> impl Iterator<Item = (Token, &'_ T)> + '_ {
let skip = continuation.map_or(0, |c| c.get() as usize);
self.entries
.iter()
.skip(skip)
.enumerate()
.filter_map(move |(i, e)| {
if let Some(item) = &e.item {
Some((Token::new((skip + i + 1) as u32)?, item))
} else {
None
}
})
}
pub fn memory_used(&self) -> usize {
self.entries.len() * size_of::<Entry<T>>()
}
}