use crate::elem::{Elem, STATE_MASK, STATE_USED};
use crate::generation::{Generation, ElemState};
use crate::slot::Slot;
use crate::Index;
use crate::IndexMap;
use alloc::{vec, vec::Vec};
use core::{error, fmt};
#[cfg(feature = "serde")]
use serde::{
Serialize, Deserialize, Serializer, Deserializer,
ser::SerializeStruct,
de::{self, Visitor, MapAccess, SeqAccess},
};
#[derive(Debug, PartialEq, Eq)]
pub enum IndexError {
BrokenNextLink,
BrokenPrevLink,
ElementIsFree,
ElementIsFreeSentinel,
IndexIsNone,
IndexIsStale,
IndexOutOfBounds,
}
impl error::Error for IndexError {}
impl fmt::Display for IndexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BrokenNextLink => write!(f, "Element's next link is inconsistent"),
Self::BrokenPrevLink => write!(f, "Element's previous link is inconsistent"),
Self::ElementIsFree => write!(f, "Element is on the free list"),
Self::ElementIsFreeSentinel => write!(f, "Element is the free list sentinel"),
Self::IndexIsNone => write!(f, "Index is NONE"),
Self::IndexIsStale => write!(f, "Index is stale (generation mismatch)"),
Self::IndexOutOfBounds => write!(f, "Index is out of bounds"),
}
}
}
#[must_use]
pub struct ElemPool<T> {
elems: Vec<Elem<T>>,
freed: usize,
used: usize,
}
impl<T: Clone> Clone for ElemPool<T> {
fn clone(&self) -> Self {
Self {
elems: self.elems.clone(),
freed: self.freed,
used: self.used,
}
}
}
#[cfg(feature = "serde")]
impl<T: Serialize> Serialize for ElemPool<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("ElemPool", 3)?;
state.serialize_field("elems", &self.elems)?;
state.serialize_field("freed", &self.freed)?;
state.serialize_field("used", &self.used)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl<'de, T: Deserialize<'de>> Deserialize<'de> for ElemPool<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field { Elems, Freed, Used }
struct ElemPoolVisitor<T>(core::marker::PhantomData<T>);
impl<'de, T: Deserialize<'de>> Visitor<'de> for ElemPoolVisitor<T> {
type Value = ElemPool<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct ElemPool")
}
fn visit_seq<V>(self, mut seq: V) -> Result<ElemPool<T>, V::Error>
where
V: SeqAccess<'de>,
{
let elems: Vec<Elem<T>> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let freed: usize = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let used: usize = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let pool = ElemPool { elems, freed, used };
pool.validate_integrity().map_err(de::Error::custom)?;
Ok(pool)
}
fn visit_map<V>(self, mut map: V) -> Result<ElemPool<T>, V::Error>
where
V: MapAccess<'de>,
{
let mut elems: Option<Vec<Elem<T>>> = None;
let mut freed: Option<usize> = None;
let mut used: Option<usize> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Elems => {
if elems.is_some() {
return Err(de::Error::duplicate_field("elems"));
}
elems = Some(map.next_value()?);
}
Field::Freed => {
if freed.is_some() {
return Err(de::Error::duplicate_field("freed"));
}
freed = Some(map.next_value()?);
}
Field::Used => {
if used.is_some() {
return Err(de::Error::duplicate_field("used"));
}
used = Some(map.next_value()?);
}
}
}
let elems = elems.ok_or_else(|| de::Error::missing_field("elems"))?;
let freed = freed.ok_or_else(|| de::Error::missing_field("freed"))?;
let used = used.ok_or_else(|| de::Error::missing_field("used"))?;
let pool = ElemPool { elems, freed, used };
pool.validate_integrity().map_err(de::Error::custom)?;
Ok(pool)
}
}
const FIELDS: &[&str] = &["elems", "freed", "used"];
deserializer.deserialize_struct("ElemPool", FIELDS, ElemPoolVisitor(core::marker::PhantomData))
}
}
impl<T> Default for ElemPool<T> {
fn default() -> Self {
let sentinel_elem = Elem::new_self_ref(Slot::new(0), ElemState::Sentinel);
Self {
elems: vec![sentinel_elem],
freed: 0,
used: 0,
}
}
}
impl<T> ElemPool<T> {
pub fn new() -> Self {
Default::default()
}
#[cfg(test)]
#[inline(always)]
fn free_sentinel_index() -> Index<T> {
Index::from(0u32)
}
#[inline]
pub fn len(&self) -> usize {
self.used
}
#[inline]
pub fn is_empty(&self) -> bool {
self.used == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.elems.len() - 1
}
#[inline]
pub fn free_len(&self) -> usize {
self.freed
}
#[inline]
pub fn list_count(&self) -> usize {
self.capacity() - self.used - self.freed
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.elems.reserve(additional);
}
pub(crate) fn reset(&mut self) {
for elem in self.elems.iter_mut() {
if elem.is_used() {
#[allow(unsafe_code)]
unsafe { let _ = elem.take_data_unchecked(); }
}
}
self.elems.clear();
self.elems.push(Elem::new_self_ref(Slot::new(0), ElemState::Sentinel));
self.freed = 0;
self.used = 0;
}
pub fn validate_integrity(&self) -> Result<(), alloc::string::String> {
use alloc::format;
let len = self.elems.len();
if len == 0 {
return Err("pool is empty (missing free-list sentinel at slot 0)".into());
}
if !self.elems[0].is_sentinel() {
return Err(format!(
"slot 0 must be a sentinel, found state {:?}",
self.elems[0].state()
));
}
let mut actual_used = 0usize;
let mut actual_free = 0usize;
for (i, elem) in self.elems.iter().enumerate() {
if elem.is_zombie() {
return Err(format!("slot {i} is in ZOMBIE state (transient state should not be serialized)"));
}
let (prev, next) = elem.links();
let Some(next_idx) = next.get() else {
return Err(format!("slot {i} has NONE next link"));
};
let Some(prev_idx) = prev.get() else {
return Err(format!("slot {i} has NONE prev link"));
};
if next_idx >= len {
return Err(format!("slot {i} next link ({next_idx}) is out of bounds (len={len})"));
}
if prev_idx >= len {
return Err(format!("slot {i} prev link ({prev_idx}) is out of bounds (len={len})"));
}
let next_elem = &self.elems[next_idx];
if next_elem.prev.get() != Some(i) {
return Err(format!(
"slot {i} -> next={next_idx}, but slot {next_idx}.prev={:?} (expected {i})",
next_elem.prev.get()
));
}
let prev_elem = &self.elems[prev_idx];
if prev_elem.next.get() != Some(i) {
return Err(format!(
"slot {i} -> prev={prev_idx}, but slot {prev_idx}.next={:?} (expected {i})",
prev_elem.next.get()
));
}
if elem.is_used() {
actual_used += 1;
} else if elem.is_free() {
actual_free += 1;
}
}
if actual_free != self.freed {
return Err(format!(
"freed count mismatch: header says {}, actual free elements = {actual_free}",
self.freed
));
}
if actual_used != self.used {
return Err(format!(
"used count mismatch: header says {}, actual used elements = {actual_used}",
self.used
));
}
Ok(())
}
#[inline]
pub fn contains(&self, index: Index<T>) -> bool {
self.validate_vers(index)
.map(|slot| self.elems[slot].is_used())
.unwrap_or(false)
}
#[inline]
pub fn validate_index(&self, index: Index<T>) -> Result<(), IndexError> {
let slot = index.get().ok_or(IndexError::IndexIsNone)?;
let elem = self.elems.get(slot).ok_or(IndexError::IndexOutOfBounds)?;
if elem.vers.as_raw() != index.vers {
return Err(IndexError::IndexIsStale);
}
if elem.is_free() {
return Err(IndexError::ElementIsFree);
}
let prev_slot = elem.prev.get().ok_or(IndexError::BrokenPrevLink)?;
let prev_elem = self.elems.get(prev_slot).ok_or(IndexError::BrokenPrevLink)?;
if prev_elem.next.get() != Some(slot) {
return Err(IndexError::BrokenPrevLink);
}
let next_slot = elem.next.get().ok_or(IndexError::BrokenNextLink)?;
let next_elem = self.elems.get(next_slot).ok_or(IndexError::BrokenNextLink)?;
if next_elem.prev.get() != Some(slot) {
return Err(IndexError::BrokenNextLink);
}
Ok(())
}
pub fn iter_elems(&self) -> core::slice::Iter<'_, Elem<T>> {
self.elems.iter()
}
pub fn iter_elems_mut(&mut self) -> core::slice::IterMut<'_, Elem<T>> {
self.elems.iter_mut()
}
#[inline]
fn validate_vers(&self, index: Index<T>) -> Result<usize, IndexError> {
let slot = index.get().ok_or(IndexError::IndexIsNone)?;
let elem = self.elems.get(slot).ok_or(IndexError::IndexOutOfBounds)?;
if elem.vers.as_raw() != index.vers {
return Err(IndexError::IndexIsStale);
}
Ok(slot)
}
pub(crate) fn index_new(&mut self) -> Result<Index<T>, IndexError> {
let next_free_slot = self.elems[0].next;
if let Some(slot_idx) = next_free_slot.get()
&& slot_idx != 0 {
self.slot_linkout(next_free_slot);
let elem = &mut self.elems[slot_idx];
let new_vers = elem.bump_gen(ElemState::Zombie);
self.freed -= 1;
return Ok(Index::new(next_free_slot.as_raw(), new_vers));
}
let slot = self.elems.len() as u32;
let slot_ref = Slot::new(slot);
let mut new_elem = Elem::default();
let new_vers = new_elem.bump_gen(ElemState::Zombie);
new_elem.set_links(slot_ref, slot_ref);
self.elems.push(new_elem);
Ok(Index::new(slot, new_vers))
}
pub(crate) fn index_make_sentinel(&mut self, index: Index<T>) -> Result<Index<T>, IndexError> {
let slot = self.validate_vers(index)?;
let elem = &mut self.elems[slot];
if !elem.is_zombie() {
return Err(IndexError::ElementIsFree);
}
let new_vers = elem.make_sentinel();
let slot_ref = Slot::new(slot as u32);
elem.set_links(slot_ref, slot_ref);
Ok(Index::new(index.slot, new_vers))
}
pub(crate) fn index_new_with_data(&mut self, data: T) -> Result<Index<T>, IndexError> {
let new_idx = self.index_new()?;
let slot = new_idx.slot as usize;
let elem = &mut self.elems[slot];
if !elem.is_zombie() {
return Err(IndexError::ElementIsFree);
}
elem.write_data(data);
let new_vers = elem.make_used();
self.used += 1;
Ok(Index::new(new_idx.slot, new_vers))
}
pub(crate) fn index_del(&mut self, index: Index<T>) -> Result<(), IndexError> {
if index.slot == 0 {
return Err(IndexError::ElementIsFreeSentinel);
}
let slot = index.slot as usize;
let elem = self.elems.get(slot).ok_or(IndexError::IndexOutOfBounds)?;
let is_zombie_match = elem.is_zombie() &&
elem.vers.same_counter(Generation::from_raw(index.vers)) &&
((index.vers & STATE_MASK) == STATE_USED);
if elem.vers.as_raw() != index.vers && !is_zombie_match {
return Err(IndexError::IndexIsStale);
}
let elem = &mut self.elems[slot];
let new_vers = elem.make_free();
self.slot_link_after(Slot::new(slot as u32), Slot::new(0));
self.freed += 1;
let _ = new_vers;
Ok(())
}
#[inline]
pub(crate) fn get_elem_mut(&mut self, index: Index<T>) -> Result<&mut Elem<T>, IndexError> {
let slot = self.validate_vers(index)?;
Ok(&mut self.elems[slot])
}
#[inline]
pub(crate) fn next(&self, index: Index<T>) -> Index<T> {
if let Ok(slot) = self.validate_vers(index) {
if let Some(next_slot) = self.elems[slot].next.get() {
let next_vers = self.elems[next_slot].vers.as_raw();
Index::new(self.elems[slot].next.as_raw(), next_vers)
} else {
Index::NONE
}
} else {
Index::NONE
}
}
#[inline]
pub(crate) fn prev(&self, index: Index<T>) -> Index<T> {
if let Ok(slot) = self.validate_vers(index) {
if let Some(prev_slot) = self.elems[slot].prev.get() {
let prev_vers = self.elems[prev_slot].vers.as_raw();
Index::new(self.elems[slot].prev.as_raw(), prev_vers)
} else {
Index::NONE
}
} else {
Index::NONE
}
}
#[inline]
pub(crate) fn data(&self, index: Index<T>) -> Option<&T> {
let slot = self.validate_vers(index).ok()?;
let elem = &self.elems[slot];
if elem.is_used() {
#[allow(unsafe_code)]
Some(unsafe { elem.data_ref_unchecked() })
} else {
None
}
}
#[inline]
pub(crate) fn data_mut(&mut self, index: Index<T>) -> Option<&mut T> {
let slot = self.validate_vers(index).ok()?;
let elem = &mut self.elems[slot];
if elem.is_used() {
#[allow(unsafe_code)]
Some(unsafe { elem.data_mut_unchecked() })
} else {
None
}
}
#[inline]
pub(crate) fn data_swap(&mut self, index: Index<T>, new_data: Option<T>) -> Option<T> {
let slot = self.validate_vers(index).ok()?;
let elem = &mut self.elems[slot];
if let Some(data) = new_data {
let old_data = if elem.is_used() {
#[allow(unsafe_code)]
Some(unsafe { elem.take_data_unchecked() })
} else {
None
};
elem.write_data(data);
if elem.is_zombie() {
elem.vers = elem.vers.with_state(ElemState::Used);
}
if old_data.is_none() && elem.is_used() {
self.used += 1;
}
old_data
} else {
if elem.is_used() {
#[allow(unsafe_code)]
let old_data = unsafe { elem.take_data_unchecked() };
elem.make_zombie();
self.used -= 1;
Some(old_data)
} else {
None
}
}
}
#[inline]
fn slot_linkout(&mut self, slot: Slot) {
let slot_idx = slot.unwrap();
let elem = &self.elems[slot_idx];
let (prev_slot, next_slot) = (elem.prev, elem.next);
self.elems[prev_slot.unwrap()].next = next_slot;
self.elems[next_slot.unwrap()].prev = prev_slot;
let elem = &mut self.elems[slot_idx];
elem.set_links(slot, slot);
}
#[inline]
fn slot_link_after(&mut self, slot: Slot, after_slot: Slot) {
let next_slot = self.elems[after_slot.unwrap()].next;
self.elems[after_slot.unwrap()].next = slot;
self.elems[next_slot.unwrap()].prev = slot;
self.elems[slot.unwrap()].set_links(after_slot, next_slot);
}
#[inline]
fn slot_link_before(&mut self, slot: Slot, before_slot: Slot) {
let prev_slot = self.elems[before_slot.unwrap()].prev;
self.elems[before_slot.unwrap()].prev = slot;
self.elems[prev_slot.unwrap()].next = slot;
self.elems[slot.unwrap()].set_links(prev_slot, before_slot);
}
#[inline]
pub(crate) fn next_slot(&self, slot: usize) -> Slot {
self.elems[slot].next
}
#[inline]
pub(crate) fn prev_slot(&self, slot: usize) -> Slot {
self.elems[slot].prev
}
#[inline]
pub(crate) fn elem_mut(&mut self, slot: usize) -> &mut Elem<T> {
&mut self.elems[slot]
}
#[inline]
pub(crate) fn data_at(&self, slot: usize) -> Option<&T> {
let elem = &self.elems[slot];
if elem.is_used() {
#[allow(unsafe_code)]
Some(unsafe { elem.data_ref_unchecked() })
} else {
None
}
}
#[inline]
pub(crate) fn data_at_mut(&mut self, slot: usize) -> Option<&mut T> {
let elem = &mut self.elems[slot];
if elem.is_used() {
#[allow(unsafe_code)]
Some(unsafe { elem.data_mut_unchecked() })
} else {
None
}
}
#[inline]
pub(crate) fn index_from_slot(&self, slot: usize) -> Index<T> {
Index::new(slot as u32, self.elems[slot].vers.as_raw())
}
#[inline]
pub(crate) fn index_linkout(&mut self, index: Index<T>) -> Result<(), IndexError> {
let slot = self.validate_vers(index)?;
self.slot_linkout(Slot::new(slot as u32));
Ok(())
}
#[inline]
pub(crate) fn index_link_after(
&mut self,
this: Index<T>,
after: Index<T>,
) -> Result<(), IndexError> {
let this_slot = self.validate_vers(this)?;
let after_slot = self.validate_vers(after)?;
self.slot_link_after(Slot::new(this_slot as u32), Slot::new(after_slot as u32));
Ok(())
}
#[inline]
pub(crate) fn index_link_before(
&mut self,
this: Index<T>,
before: Index<T>,
) -> Result<(), IndexError> {
let this_slot = self.validate_vers(this)?;
let before_slot = self.validate_vers(before)?;
self.slot_link_before(Slot::new(this_slot as u32), Slot::new(before_slot as u32));
Ok(())
}
#[must_use = "the remapping table must be used to update external handles"]
pub fn shrink_to_fit(&mut self) -> IndexMap<Index<T>, Index<T>> {
let old_len = self.elems.len();
let target_len = old_len - self.freed;
if target_len == old_len {
return IndexMap::new();
}
let tail_len = old_len - target_len;
let mut is_free_tail = vec![false; tail_len];
let mut vacancies = Vec::with_capacity(tail_len);
let mut current_free_slot = self.elems[0].next;
while let Some(idx) = current_free_slot.get() {
if idx == 0 {
break;
}
if idx < target_len {
vacancies.push(idx);
} else {
is_free_tail[idx - target_len] = true;
}
current_free_slot = self.elems[idx].next;
}
let mut slot_remap = vec![u32::MAX; tail_len];
let num_moved = tail_len - vacancies.len().min(tail_len);
let mut remapping = IndexMap::with_capacity(num_moved);
for source in target_len..old_len {
let tail_idx = source - target_len;
if is_free_tail[tail_idx] {
continue; }
let dest = vacancies.pop().expect("Logic Error: Mismatch in free/used counts");
slot_remap[tail_idx] = dest as u32;
let vers = self.elems[source].vers.as_raw();
self.elems.swap(dest, source);
let old_idx = Index::new(source as u32, vers);
let new_idx = Index::new(dest as u32, vers);
remapping.insert(old_idx, new_idx);
let (prev_slot, next_slot) = self.elems[dest].links();
let resolve = |slot: Slot, remap: &[u32], tgt_len: usize, old_len: usize| -> usize {
if let Some(slot_usize) = slot.get() {
if slot_usize >= tgt_len && slot_usize < old_len {
let new_slot = remap[slot_usize - tgt_len];
if new_slot != u32::MAX {
return new_slot as usize;
}
}
slot_usize
} else {
0 }
};
let effective_prev = resolve(prev_slot, &slot_remap, target_len, old_len);
self.elems[effective_prev].next = Slot::new(dest as u32);
let effective_next = resolve(next_slot, &slot_remap, target_len, old_len);
self.elems[effective_next].prev = Slot::new(dest as u32);
}
self.elems.truncate(target_len);
self.freed = 0;
self.elems[0].set_links(Slot::new(0), Slot::new(0));
remapping
}
}
impl<T> fmt::Display for ElemPool<T>
where T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
"ElemPool used {}/{}, {} free:",
self.len(),
self.capacity(),
self.freed
)?;
for (i, elem) in self.elems.iter().enumerate() {
if elem.is_used() {
#[allow(unsafe_code)]
let data = unsafe { elem.data_ref_unchecked() };
writeln!(f, " [{}]: {} = {}", i, elem, data)?;
} else {
writeln!(f, " [{}]: {}", i, elem)?;
}
}
Ok(())
}
}
impl<T: fmt::Debug> fmt::Debug for ElemPool<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ElemPool")
.field("elems", &self.elems)
.field("freed", &self.freed)
.field("used", &self.used)
.finish()
}
}
impl<T> Drop for ElemPool<T> {
fn drop(&mut self) {
for elem in self.elems.iter_mut() {
if elem.is_used() {
#[allow(unsafe_code)]
unsafe {
let _ = elem.take_data_unchecked();
}
}
}
}
}
#[cfg(test)]
mod tests {
use rand::RngExt;
use super::*;
use crate::list::PieList;
fn create_pool_with_elems<T>(count: usize, default_data: T) -> (ElemPool<T>, Vec<Index<T>>)
where T: Clone,
{
let mut pool = ElemPool::new();
let mut indices = Vec::new();
for _i in 0..count {
let index = pool.index_new_with_data(default_data.clone()).unwrap();
indices.push(index);
}
(pool, indices)
}
#[test]
fn test_pool_creation_and_len() {
let pool: ElemPool<i32> = ElemPool::new();
assert_eq!(pool.len(), 0); assert_eq!(pool.capacity(), 0);
assert!(pool.is_empty());
assert_eq!(pool.elems.len(), 1); }
#[test]
fn test_index_new_and_len() {
let (pool, indices) = create_pool_with_elems(3, 100);
assert_eq!(pool.len(), 3); assert_eq!(pool.capacity(), 3);
assert!(!pool.is_empty());
assert_eq!(indices.len(), 3);
assert_eq!(indices[0].get(), Some(1));
assert_eq!(indices[1].get(), Some(2));
assert_eq!(indices[2].get(), Some(3));
}
#[test]
fn test_del_and_reuse() {
let (mut pool, indices) = create_pool_with_elems(5, 0);
assert_eq!(pool.len(), 5);
assert_eq!(pool.freed, 0);
let deleted_index = indices[2];
let _data = pool.data_swap(deleted_index, None);
assert_eq!(pool.len(), 4);
pool.index_del(deleted_index).unwrap();
assert_eq!(pool.freed, 1);
assert!(!pool.contains(deleted_index));
let first_free = pool.elems[0].next;
assert_eq!(first_free, Slot::new(deleted_index.slot));
let reused_index = pool.index_new().unwrap();
assert_eq!(reused_index.slot, deleted_index.slot);
assert_ne!(reused_index.vers, deleted_index.vers);
assert_eq!(pool.len(), 4);
assert_eq!(pool.freed, 0);
pool.data_swap(reused_index, Some(999));
assert_eq!(pool.len(), 5);
}
#[test]
fn test_del_errors() {
let mut pool: ElemPool<i32> = ElemPool::new();
let index = pool.index_new().unwrap();
pool.data_swap(index, Some(100));
assert_eq!(
pool.index_del(ElemPool::free_sentinel_index()),
Err(IndexError::ElementIsFreeSentinel)
);
}
#[test]
fn test_contains() {
let (pool, indices) = create_pool_with_elems(2, 0);
assert!(pool.contains(indices[0]));
assert!(pool.contains(indices[1]));
let nonexistent_index = Index::from(99 as u32);
assert!(!pool.contains(nonexistent_index));
assert!(!pool.contains(Index::NONE));
}
#[test]
fn test_linking_logic() {
let (mut pool, indices) = create_pool_with_elems(3, 0);
let i1 = indices[0];
let i2 = indices[1];
let i3 = indices[2];
assert_eq!(pool.next(i1), i1);
assert_eq!(pool.prev(i1), i1);
pool.index_link_after(i2, i1).unwrap();
assert_eq!(pool.next(i1), i2);
assert_eq!(pool.prev(i2), i1);
assert_eq!(pool.next(i2), i1); assert_eq!(pool.prev(i1), i2);
pool.index_link_after(i3, i2).unwrap();
assert_eq!(pool.next(i2), i3);
assert_eq!(pool.prev(i3), i2);
assert_eq!(pool.next(i3), i1); assert_eq!(pool.prev(i1), i3);
assert_eq!(pool.next(i1), i2);
assert_eq!(pool.next(i2), i3);
assert_eq!(pool.next(i3), i1);
pool.index_linkout(i2).unwrap();
assert_eq!(pool.next(i1), i3);
assert_eq!(pool.prev(i3), i1);
assert_eq!(pool.next(i3), i1);
assert_eq!(pool.prev(i1), i3);
assert_eq!(pool.next(i2), i2);
assert_eq!(pool.prev(i2), i2);
pool.index_link_before(i2, i3).unwrap();
assert_eq!(pool.next(i1), i2);
assert_eq!(pool.next(i2), i3);
assert_eq!(pool.next(i3), i1);
}
#[test]
fn test_validate_index() {
let (mut pool, _) = create_pool_with_elems(0, 0);
let mut list = PieList::new(&mut pool);
list.push_back(10, &mut pool).unwrap();
list.push_back(20, &mut pool).unwrap();
list.push_back(30, &mut pool).unwrap();
let i1 = pool.next(list.sentinel);
let i2 = pool.next(i1);
let i3 = pool.next(i2);
assert_eq!(pool.validate_index(i1), Ok(()));
assert_eq!(pool.validate_index(i2), Ok(()));
assert_eq!(pool.validate_index(i3), Ok(()));
assert_eq!(
pool.validate_index(Index::NONE),
Err(IndexError::IndexIsNone)
);
assert_eq!(
pool.validate_index(Index::from(99_u32)),
Err(IndexError::IndexOutOfBounds)
);
pool.data_swap(i2, None);
assert_eq!(pool.validate_index(i2), Err(IndexError::IndexIsStale));
{
let zombie_ver = pool.elems[i2.slot as usize].vers;
let _zombie_idx = Index::<i32>::new(i2.slot, zombie_ver.as_raw());
pool.elems[i2.slot as usize].vers = zombie_ver.with_state(crate::generation::ElemState::Free);
let free_ver = pool.elems[i2.slot as usize].vers;
let free_idx = Index::<i32>::new(i2.slot, free_ver.as_raw());
assert_eq!(pool.validate_index(free_idx), Err(IndexError::ElementIsFree));
pool.elems[i2.slot as usize].vers = crate::generation::Generation::from_raw(i2.vers);
pool.data_swap(i2, Some(20));
}
pool.elems[i1.slot as usize].next = Slot::new(i3.slot);
assert_eq!(pool.validate_index(i2), Err(IndexError::BrokenPrevLink));
assert_eq!(pool.validate_index(i1), Err(IndexError::BrokenNextLink));
list.clear(&mut pool);
}
#[test]
fn test_shrink_simple() {
let mut pool = ElemPool::new();
let mut list = PieList::new(&mut pool);
list.push_back("A", &mut pool).unwrap();
let idx_to_remove = list.push_back("RemoveMe", &mut pool).unwrap();
list.push_back("B", &mut pool).unwrap();
pool.index_linkout(idx_to_remove).unwrap();
list.len -= 1;
let _ = pool.data_swap(idx_to_remove, None);
pool.index_del(idx_to_remove).unwrap();
assert_eq!(pool.freed, 1);
let old_cap = pool.capacity();
let map = pool.shrink_to_fit();
list.remap(&map);
assert_eq!(pool.freed, 0);
assert!(pool.capacity() < old_cap);
let vec: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(vec, vec!["A", "B"]);
let mut curr = pool.next(list.sentinel);
while curr != list.sentinel {
assert!(pool.validate_index(curr).is_ok());
curr = pool.next(curr);
}
list.clear(&mut pool);
}
#[test]
fn test_shrink_with_sentinel_move() {
let mut pool = ElemPool::new();
let mut noise_list = PieList::new(&mut pool);
noise_list.push_back("Noise", &mut pool).unwrap();
let mut list = PieList::new(&mut pool); list.push_back("Data", &mut pool).unwrap();
noise_list.clear(&mut pool);
let map = pool.shrink_to_fit();
list.remap(&map);
assert_eq!(list.len(), 1);
assert_eq!(*list.front(&pool).unwrap(), "Data");
let sent_elem = &pool.elems[list.sentinel.slot as usize];
assert!(sent_elem.next != Slot::new(list.sentinel.slot), "Sentinel should point to Data");
assert!(sent_elem.prev != Slot::new(list.sentinel.slot), "Sentinel should point to Data");
list.clear(&mut pool);
}
#[test]
fn test_shrink_randomized_stress() {
let mut pool = ElemPool::<usize>::new();
let mut lists = Vec::new();
let mut rng = rand::rng();
for _ in 0..10 {
lists.push(PieList::new(&mut pool));
}
for _ in 0..1000 {
let list_idx = rng.random_range(0..10);
let val = rng.random_range(0..10000);
lists[list_idx].push_back(val, &mut pool).unwrap();
}
for _ in 0..400 {
let list_idx = rng.random_range(0..10);
if !lists[list_idx].is_empty() {
lists[list_idx].pop_front(&mut pool);
}
}
let total_items_before: usize = lists.iter().map(|l| l.len()).sum();
assert_eq!(pool.len(), total_items_before);
assert!(pool.freed > 0);
let map = pool.shrink_to_fit();
for list in lists.iter_mut() {
list.remap(&map);
}
assert_eq!(pool.freed, 0);
assert_eq!(pool.len(), total_items_before);
let total_items_after: usize = lists.iter().map(|l| l.len()).sum();
assert_eq!(total_items_after, total_items_before);
for list in lists.iter_mut() {
let mut count = 0;
let mut curr = pool.next(list.sentinel);
while curr != list.sentinel {
assert!(pool.validate_index(curr).is_ok());
count += 1;
curr = pool.next(curr);
}
assert_eq!(count, list.len());
list.clear(&mut pool);
}
}
#[test]
fn test_reserve() {
let mut pool = ElemPool::<i32>::new();
pool.reserve(100);
let mut list = PieList::new(&mut pool);
for v in 0..100 {
list.push_back(v, &mut pool).unwrap();
}
let items: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(items, (0..100).collect::<Vec<_>>());
assert_eq!(pool.len(), 100);
list.clear(&mut pool);
}
#[test]
fn test_validate_integrity_good_pool() {
let mut pool = ElemPool::<i32>::new();
let mut list = PieList::new(&mut pool);
for v in 0..20 {
list.push_back(v, &mut pool).unwrap();
}
for _ in 0..5 {
list.pop_front(&mut pool);
}
assert!(pool.validate_integrity().is_ok());
list.clear(&mut pool);
assert!(pool.validate_integrity().is_ok());
}
#[test]
fn test_reset_preserves_capacity() {
let mut pool = ElemPool::<i32>::new();
let mut list = PieList::new(&mut pool);
for v in 0..50 {
list.push_back(v, &mut pool).unwrap();
}
let _ = list.without_leak_check();
let old_cap = pool.elems.capacity();
pool.reset();
assert_eq!(pool.len(), 0);
assert_eq!(pool.free_len(), 0);
assert!(pool.elems.capacity() >= old_cap);
let mut list2 = PieList::new(&mut pool);
list2.push_back(99, &mut pool).unwrap();
assert_eq!(*list2.front(&pool).unwrap(), 99);
list2.clear(&mut pool);
}
#[test]
fn test_reset_empty_pool() {
let mut pool = ElemPool::<i32>::new();
pool.reset();
assert_eq!(pool.len(), 0);
assert_eq!(pool.free_len(), 0);
let mut list = PieList::new(&mut pool);
list.push_back(1, &mut pool).unwrap();
list.clear(&mut pool);
}
}