use std::mem::{MaybeUninit, size_of};
use std::pin::Pin;
use num_integer::Integer;
use crate::{DropPolicy, PinnedPoolBuilder, PinnedSlab, PinnedSlabInserter, PinnedSlabIterator};
#[derive(Debug)]
pub struct PinnedPool<T> {
slabs: Vec<PinnedSlab<T, SLAB_CAPACITY>>,
slab_with_vacant_slot_index: Option<usize>,
drop_policy: DropPolicy,
length: usize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Key {
index_in_pool: usize,
}
#[cfg(not(miri))]
const SLAB_CAPACITY: usize = 128;
#[cfg(miri)]
const SLAB_CAPACITY: usize = 4;
impl<T> PinnedPool<T> {
#[must_use]
pub(crate) fn new_inner(drop_policy: DropPolicy) -> Self {
assert!(
size_of::<T>() > 0,
"PinnedPool must have non-zero item size"
);
Self {
slabs: Vec::new(),
drop_policy,
slab_with_vacant_slot_index: None,
length: 0,
}
}
#[must_use]
#[inline]
pub fn new() -> Self {
Self::builder().build()
}
#[inline]
pub fn builder() -> PinnedPoolBuilder<T> {
PinnedPoolBuilder::new()
}
#[must_use]
#[cfg_attr(test, mutants::skip)] #[inline]
pub fn len(&self) -> usize {
debug_assert_eq!(self.length, self.slabs.iter().map(PinnedSlab::len).sum());
self.length
}
#[must_use]
#[inline]
pub fn capacity(&self) -> usize {
self.slabs.len()
.checked_mul(SLAB_CAPACITY)
.expect("overflow here would mean the pool can hold more items than virtual memory can fit, which makes no sense - it would never grow that big")
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.length == 0
}
#[cfg_attr(test, mutants::skip)] pub fn reserve(&mut self, additional: usize) {
let required_capacity = self
.len()
.checked_add(additional)
.expect("capacity overflow: requested capacity exceeds maximum possible value");
if self.capacity() >= required_capacity {
return;
}
let current_slabs = self.slabs.len();
let required_slabs = required_capacity.div_ceil(SLAB_CAPACITY);
let additional_slabs = required_slabs.saturating_sub(current_slabs);
for _ in 0..additional_slabs {
self.slabs.push(PinnedSlab::new(self.drop_policy));
}
}
#[cfg_attr(test, mutants::skip)] pub fn shrink_to_fit(&mut self) {
let new_len = self
.slabs
.iter()
.enumerate()
.rev()
.find_map(|(idx, slab)| {
if !slab.is_empty() {
Some(idx.checked_add(1).expect("slab index cannot overflow"))
} else {
None
}
})
.unwrap_or(0);
if new_len < self.slabs.len() {
self.slab_with_vacant_slot_index = None;
}
self.slabs.truncate(new_len);
}
#[must_use]
#[inline]
pub fn get(&self, key: Key) -> Pin<&T> {
let coordinates = ItemCoordinates::<SLAB_CAPACITY>::from_key(key);
self.slabs
.get(coordinates.slab_index)
.map(|s| s.get(coordinates.index_in_slab))
.expect("key was not associated with an item in the pool")
}
#[must_use]
#[inline]
pub fn get_mut(&mut self, key: Key) -> Pin<&mut T> {
let index = ItemCoordinates::<SLAB_CAPACITY>::from_key(key);
self.slabs
.get_mut(index.slab_index)
.map(|s| s.get_mut(index.index_in_slab))
.expect("key was not associated with an item in the pool")
}
#[must_use]
pub fn begin_insert<'a, 'b>(&'a mut self) -> PinnedPoolInserter<'b, T>
where
'a: 'b,
{
let slab_index = self.index_of_slab_with_vacant_slot();
#[expect(
clippy::indexing_slicing,
reason = "we just identified that there is a slab with a vacant slot at this index"
)]
let slab = &mut self.slabs[slab_index];
let predicted_slab_filled_slots = slab.len().wrapping_add(1);
if predicted_slab_filled_slots == SLAB_CAPACITY {
self.slab_with_vacant_slot_index = None;
}
let slab_inserter = slab.begin_insert();
PinnedPoolInserter {
slab_inserter,
slab_index,
pool_length: &mut self.length,
}
}
#[must_use]
#[inline]
pub fn insert(&mut self, value: T) -> Key {
let inserter = self.begin_insert();
let key = inserter.key();
inserter.insert(value);
key
}
pub fn remove(&mut self, key: Key) {
let index = ItemCoordinates::<SLAB_CAPACITY>::from_key(key);
let slab = self
.slabs
.get_mut(index.slab_index)
.expect("key was not associated with an item in the pool");
slab.remove(index.index_in_slab);
self.length = self.length.wrapping_sub(1);
self.update_vacant_slot_cache(index.slab_index);
}
#[expect(
clippy::iter_without_into_iter,
reason = "items from this collection cannot be consumed"
)]
pub fn iter(&self) -> PinnedPoolIterator<'_, T> {
PinnedPoolIterator::new(self)
}
#[must_use]
fn add_new_slab(&mut self) -> usize {
self.slabs.push(PinnedSlab::new(self.drop_policy));
self.slabs
.len()
.checked_sub(1)
.expect("we just pushed a slab, so this cannot overflow because len >= 1")
}
#[must_use]
fn index_of_slab_with_vacant_slot(&mut self) -> usize {
if let Some(index) = self.slab_with_vacant_slot_index {
return index;
}
if self.len() == self.capacity() {
let index = self.add_new_slab();
self.set_vacant_slot_cache(index);
return index;
}
let index = self
.slabs
.iter()
.enumerate()
.find_map(|(index, slab)| if !slab.is_full() { Some(index) } else { None })
.expect("since len() != capacity(), at least one slab must have vacant slots");
self.set_vacant_slot_cache(index);
index
}
#[cfg_attr(test, mutants::skip)] fn update_vacant_slot_cache(&mut self, slab_with_vacant_slot_index: usize) {
if self
.slab_with_vacant_slot_index
.is_none_or(|current| current > slab_with_vacant_slot_index)
{
self.slab_with_vacant_slot_index = Some(slab_with_vacant_slot_index);
}
}
#[cfg_attr(test, mutants::skip)] fn set_vacant_slot_cache(&mut self, slab_index: usize) {
self.slab_with_vacant_slot_index = Some(slab_index);
}
#[cfg_attr(test, mutants::skip)] #[cfg(debug_assertions)]
#[expect(dead_code, reason = "we will probably use it later")]
pub(crate) fn integrity_check(&self) {
for slab in &self.slabs {
slab.integrity_check();
}
}
}
impl<T> Default for PinnedPool<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct PinnedPoolInserter<'s, T> {
slab_inserter: PinnedSlabInserter<'s, T, SLAB_CAPACITY>,
slab_index: usize,
pool_length: &'s mut usize,
}
impl<'s, T> PinnedPoolInserter<'s, T> {
#[inline]
pub fn insert<'v>(self, value: T) -> Pin<&'v T>
where
's: 'v,
{
let result = self.slab_inserter.insert(value);
*self.pool_length = self.pool_length.wrapping_add(1);
result
}
#[inline]
pub fn insert_mut<'v>(self, value: T) -> Pin<&'v mut T>
where
's: 'v,
{
let result = self.slab_inserter.insert_mut(value);
*self.pool_length = self.pool_length.wrapping_add(1);
result
}
#[inline]
pub unsafe fn insert_with<'v>(self, f: impl FnOnce(&mut MaybeUninit<T>)) -> Pin<&'v T>
where
's: 'v,
{
let result = unsafe { self.slab_inserter.insert_with(f) };
*self.pool_length = self.pool_length.wrapping_add(1);
result
}
#[inline]
pub unsafe fn insert_with_mut<'v>(self, f: impl FnOnce(&mut MaybeUninit<T>)) -> Pin<&'v mut T>
where
's: 'v,
{
let result = unsafe { self.slab_inserter.insert_with_mut(f) };
*self.pool_length = self.pool_length.wrapping_add(1);
result
}
#[must_use]
#[inline]
pub fn key(&self) -> Key {
ItemCoordinates::<SLAB_CAPACITY>::from_parts(self.slab_index, self.slab_inserter.index())
.to_key()
}
}
#[derive(Debug)]
struct ItemCoordinates<const SLAB_CAPACITY: usize> {
slab_index: usize,
index_in_slab: usize,
}
impl<const SLAB_CAPACITY: usize> ItemCoordinates<SLAB_CAPACITY> {
#[must_use]
fn from_parts(slab: usize, index_in_slab: usize) -> Self {
Self {
slab_index: slab,
index_in_slab,
}
}
#[must_use]
fn from_key(key: Key) -> Self {
let (slab_index, index_in_slab) = key.index_in_pool.div_rem(&SLAB_CAPACITY);
Self {
slab_index,
index_in_slab,
}
}
#[must_use]
fn to_key(&self) -> Key {
Key {
index_in_pool: self
.slab_index
.wrapping_mul(SLAB_CAPACITY)
.wrapping_add(self.index_in_slab),
}
}
}
#[derive(Debug)]
#[must_use]
pub struct PinnedPoolIterator<'a, T> {
pool: &'a PinnedPool<T>,
slab_iterator: Option<PinnedSlabIterator<'a, T, SLAB_CAPACITY>>,
current_slab_index: usize,
}
impl<'a, T> PinnedPoolIterator<'a, T> {
fn new(pool: &'a PinnedPool<T>) -> Self {
let first_slab_iterator = pool.slabs.first().map(|s| s.iter());
Self {
pool,
slab_iterator: first_slab_iterator,
current_slab_index: 0,
}
}
}
impl<'a, T> Iterator for PinnedPoolIterator<'a, T> {
type Item = Pin<&'a T>;
fn next(&mut self) -> Option<Self::Item> {
let Some(slab_iterator) = &mut self.slab_iterator else {
return None;
};
if let Some(item) = slab_iterator.next() {
return Some(item);
}
self.current_slab_index = self
.current_slab_index
.checked_add(1)
.expect("overflow here would mean the collection exceeds the size of virtual memory");
self.slab_iterator = self
.pool
.slabs
.get(self.current_slab_index)
.map(|slab| slab.iter());
self.next()
}
}
#[cfg(test)]
mod tests {
#![allow(
clippy::indexing_slicing,
clippy::cast_possible_truncation,
reason = "we do not need to worry about these things when writing test code"
)]
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use std::{ptr, thread};
use super::*;
#[test]
fn smoke_test() {
let mut pool = PinnedPool::<u32>::new();
assert_eq!(pool.len(), 0);
assert!(pool.is_empty());
let key_a = pool.insert(42);
let key_b = pool.insert(43);
let key_c = pool.insert(44);
assert_eq!(pool.len(), 3);
assert!(!pool.is_empty());
assert!(pool.capacity() >= 3);
assert_eq!(*pool.get(key_a), 42);
assert_eq!(*pool.get(key_b), 43);
assert_eq!(*pool.get(key_c), 44);
pool.remove(key_b);
let key_d = pool.insert(45);
assert_eq!(*pool.get(key_a), 42);
assert_eq!(*pool.get(key_c), 44);
assert_eq!(*pool.get(key_d), 45);
}
#[test]
#[should_panic]
fn panic_when_empty_oob_get() {
let pool = PinnedPool::<u32>::new();
_ = pool.get(Key { index_in_pool: 0 });
}
#[test]
#[should_panic]
fn panic_when_oob_get() {
let mut pool = PinnedPool::<u32>::new();
_ = pool.insert(42);
_ = pool.get(Key {
index_in_pool: 1234,
});
}
#[test]
fn begin_insert_returns_correct_key() {
let mut pool = PinnedPool::<u32>::new();
let inserter = pool.begin_insert();
let key = inserter.key();
assert_eq!(key.index_in_pool, 0);
inserter.insert(10);
assert_eq!(*pool.get(key), 10);
let inserter = pool.begin_insert();
let key = inserter.key();
assert_eq!(key.index_in_pool, 1);
inserter.insert(11);
assert_eq!(*pool.get(key), 11);
let inserter = pool.begin_insert();
let key = inserter.key();
assert_eq!(key.index_in_pool, 2);
inserter.insert(12);
assert_eq!(*pool.get(key), 12);
}
#[test]
fn abandoned_inserter_is_noop() {
let mut pool = PinnedPool::<u32>::new();
_ = pool.begin_insert();
let inserter = pool.begin_insert();
let key = inserter.key();
inserter.insert(20);
assert_eq!(*pool.get(key), 20);
_ = pool.insert(123);
_ = pool.insert(456);
}
#[test]
#[should_panic]
fn remove_empty_panics() {
let mut pool = PinnedPool::<u32>::new();
pool.remove(Key { index_in_pool: 0 });
}
#[test]
#[should_panic]
fn remove_vacant_panics() {
let mut pool = PinnedPool::<u32>::new();
_ = pool.insert(1234);
pool.remove(Key { index_in_pool: 1 });
}
#[test]
#[should_panic]
fn remove_oob_panics() {
let mut pool = PinnedPool::<u32>::new();
_ = pool.insert(1234);
pool.remove(Key {
index_in_pool: 9999999,
});
}
#[test]
#[should_panic]
fn get_vacant_panics() {
let mut pool = PinnedPool::<u32>::new();
_ = pool.insert(1234);
_ = pool.get(Key { index_in_pool: 1 });
}
#[test]
#[should_panic]
fn get_mut_vacant_panics() {
let mut pool = PinnedPool::<u32>::new();
_ = pool.insert(1234);
_ = pool.get_mut(Key { index_in_pool: 1 });
}
#[test]
fn in_refcell_works_fine() {
let pool = RefCell::new(PinnedPool::<u32>::new());
let key_a = {
let mut pool = pool.borrow_mut();
let key_a = pool.insert(42);
let key_b = pool.insert(43);
let key_c = pool.insert(44);
assert_eq!(*pool.get(key_a), 42);
assert_eq!(*pool.get(key_b), 43);
assert_eq!(*pool.get(key_c), 44);
pool.remove(key_b);
let key_d = pool.insert(45);
assert_eq!(*pool.get(key_a), 42);
assert_eq!(*pool.get(key_c), 44);
assert_eq!(*pool.get(key_d), 45);
key_a
};
{
let pool = pool.borrow();
assert_eq!(*pool.get(key_a), 42);
}
}
#[test]
fn multithreaded_via_mutex() {
let shared_pool = Arc::new(Mutex::new(PinnedPool::<u32>::new()));
let key_a;
let key_b;
let key_c;
{
let mut pool = shared_pool.lock().unwrap();
key_a = pool.insert(42);
key_b = pool.insert(43);
key_c = pool.insert(44);
assert_eq!(*pool.get(key_a), 42);
assert_eq!(*pool.get(key_b), 43);
assert_eq!(*pool.get(key_c), 44);
}
thread::spawn({
let shared_pool = Arc::clone(&shared_pool);
move || {
let mut pool = shared_pool.lock().unwrap();
pool.remove(key_b);
let d = pool.insert(45);
assert_eq!(*pool.get(key_a), 42);
assert_eq!(*pool.get(key_c), 44);
assert_eq!(*pool.get(d), 45);
}
});
let chain = shared_pool.lock().unwrap();
assert!(!chain.is_empty());
}
#[test]
#[should_panic]
fn drop_item_with_forbidden_to_drop_policy_panics() {
let mut pool = PinnedPool::<u32>::builder()
.drop_policy(DropPolicy::MustNotDropItems)
.build();
_ = pool.insert(123);
}
#[test]
fn drop_itemless_with_forbidden_to_drop_policy_ok() {
drop(
PinnedPool::<u32>::builder()
.drop_policy(DropPolicy::MustNotDropItems)
.build(),
);
}
#[test]
fn out_of_band_access() {
let mut pool = PinnedPool::<u32>::new();
let key_a = pool.insert(42);
let a_ptr = ptr::from_mut(pool.get_mut(key_a).get_mut());
unsafe {
*a_ptr += 1;
}
let inserter = pool.begin_insert();
unsafe {
*a_ptr += 1;
}
_ = inserter.insert(123);
pool.remove(key_a);
}
#[test]
fn fill_first_slab_before_allocating_second() {
let mut pool = PinnedPool::<u32>::new();
for _ in 0..SLAB_CAPACITY {
_ = pool.insert(1234);
}
assert_eq!(pool.slabs.len(), 1);
assert!(pool.slabs[0].is_full());
_ = pool.insert(1234);
assert_eq!(pool.slabs.len(), 2);
}
#[test]
fn fill_first_slab_even_after_abandoned_insert() {
let mut pool = PinnedPool::<u32>::new();
for _ in 0..(SLAB_CAPACITY - 1) {
_ = pool.insert(1234);
}
assert_eq!(pool.slabs.len(), 1);
assert!(!pool.slabs[0].is_full());
_ = pool.begin_insert();
_ = pool.insert(1234);
assert_eq!(pool.slabs.len(), 1);
assert!(pool.slabs[0].is_full());
}
#[test]
fn fill_hole_before_allocating_new_slab() {
let mut pool = PinnedPool::<u32>::new();
for _ in 0..SLAB_CAPACITY {
_ = pool.insert(1234);
}
let key_to_remove = Key { index_in_pool: 0 };
pool.remove(key_to_remove);
let key_filled = pool.insert(5678);
assert_eq!(key_filled.index_in_pool, 0);
assert_eq!(*pool.get(key_filled), 5678);
}
#[test]
fn fill_first_hole_ascending() {
let mut pool = PinnedPool::<u32>::new();
for _ in 0..SLAB_CAPACITY {
_ = pool.insert(1234);
}
for _ in 0..SLAB_CAPACITY {
_ = pool.insert(5678);
}
let key_to_remove = Key { index_in_pool: 0 };
pool.remove(key_to_remove);
let key_to_remove = Key {
index_in_pool: SLAB_CAPACITY,
};
pool.remove(key_to_remove);
let key_filled = pool.insert(91011);
assert_eq!(key_filled.index_in_pool, 0);
assert_eq!(*pool.get(key_filled), 91011);
}
#[test]
fn fill_first_hole_descending() {
let mut pool = PinnedPool::<u32>::new();
for _ in 0..SLAB_CAPACITY {
_ = pool.insert(1234);
}
for _ in 0..SLAB_CAPACITY {
_ = pool.insert(5678);
}
let key_to_remove = Key {
index_in_pool: SLAB_CAPACITY,
};
pool.remove(key_to_remove);
let key_to_remove = Key { index_in_pool: 0 };
pool.remove(key_to_remove);
let key_filled = pool.insert(91011);
assert_eq!(key_filled.index_in_pool, 0);
assert_eq!(*pool.get(key_filled), 91011);
}
#[test]
#[should_panic]
fn zst_is_panic() {
drop(PinnedPool::<()>::new());
}
#[test]
fn insert_mut_then_get_is_correct_value() {
let mut pool = PinnedPool::<u32>::new();
let inserter = pool.begin_insert();
let key = inserter.key();
let mut item = inserter.insert_mut(42);
*item = 99;
assert_eq!(*pool.get(key), 99);
}
#[test]
fn default_works_fine() {
let mut pool: PinnedPool<u32> = PinnedPool::default();
assert!(pool.is_empty());
assert_eq!(pool.len(), 0);
assert_eq!(pool.capacity(), 0);
let key = pool.insert(1234);
assert!(!pool.is_empty());
assert_eq!(pool.len(), 1);
assert_eq!(pool.get(key).get_ref(), &1234);
pool.remove(key);
}
#[test]
fn shrink_to_fit_removes_empty_slabs() {
let mut pool = PinnedPool::<u32>::new();
let mut keys = Vec::new();
for i in 0..(SLAB_CAPACITY * 3) {
keys.push(pool.insert(i as u32));
}
assert_eq!(pool.capacity(), SLAB_CAPACITY * 3);
for key in keys.iter().skip(SLAB_CAPACITY) {
pool.remove(*key);
}
assert_eq!(pool.capacity(), SLAB_CAPACITY * 3);
pool.shrink_to_fit();
assert_eq!(pool.capacity(), SLAB_CAPACITY);
for (i, key) in keys.iter().take(SLAB_CAPACITY).enumerate() {
assert_eq!(*pool.get(*key), i as u32);
}
}
#[test]
fn shrink_to_fit_all_empty_slabs() {
let mut pool = PinnedPool::<u32>::new();
let mut keys = Vec::new();
for i in 0..(SLAB_CAPACITY * 2) {
keys.push(pool.insert(i as u32));
}
assert_eq!(pool.capacity(), SLAB_CAPACITY * 2);
for key in keys {
pool.remove(key);
}
assert_eq!(pool.capacity(), SLAB_CAPACITY * 2);
pool.shrink_to_fit();
assert_eq!(pool.capacity(), 0);
assert!(pool.is_empty());
}
#[test]
fn shrink_to_fit_no_empty_slabs() {
let mut pool = PinnedPool::<u32>::new();
let mut keys = Vec::new();
for i in 0..(SLAB_CAPACITY * 2) {
keys.push(pool.insert(i as u32));
}
let original_capacity = pool.capacity();
pool.shrink_to_fit();
assert_eq!(pool.capacity(), original_capacity);
for (i, key) in keys.iter().enumerate() {
assert_eq!(*pool.get(*key), i as u32);
}
}
#[test]
fn shrink_to_fit_empty_pool() {
let mut pool = PinnedPool::<u32>::new();
assert_eq!(pool.capacity(), 0);
pool.shrink_to_fit();
assert_eq!(pool.capacity(), 0);
assert!(pool.is_empty());
}
#[test]
fn shrink_then_grow_allocates_new_slab() {
let mut pool = PinnedPool::<u32>::new();
let mut keys = Vec::new();
for i in 0..SLAB_CAPACITY {
keys.push(pool.insert(i as u32));
}
let overflow_key = pool.insert(9999_u32);
assert_eq!(pool.slabs.len(), 2);
assert_eq!(pool.capacity(), SLAB_CAPACITY * 2);
pool.remove(overflow_key);
pool.shrink_to_fit();
assert_eq!(pool.slabs.len(), 1);
assert_eq!(pool.capacity(), SLAB_CAPACITY);
assert!(pool.slabs[0].is_full());
let new_key = pool.insert(8888_u32);
assert_eq!(pool.slabs.len(), 2);
assert_eq!(pool.capacity(), SLAB_CAPACITY * 2);
assert_eq!(new_key.index_in_pool, SLAB_CAPACITY);
assert_eq!(*pool.get(new_key), 8888);
for key in keys {
pool.remove(key);
}
pool.remove(new_key);
}
#[test]
fn reserve_increases_capacity() {
let mut pool = PinnedPool::<u32>::new();
assert_eq!(pool.capacity(), 0);
pool.reserve(10);
assert!(pool.capacity() >= 10);
let initial_capacity = pool.capacity();
let key = pool.insert(42);
assert_eq!(pool.capacity(), initial_capacity);
pool.remove(key);
}
#[test]
fn reserve_with_existing_items() {
let mut pool = PinnedPool::<u32>::new();
let key1 = pool.insert(1);
let key2 = pool.insert(2);
let current_len = pool.len();
pool.reserve(5);
assert!(pool.capacity() >= current_len + 5);
assert_eq!(*pool.get(key1), 1);
assert_eq!(*pool.get(key2), 2);
pool.remove(key1);
pool.remove(key2);
}
#[test]
fn reserve_zero_does_nothing() {
let mut pool = PinnedPool::<u32>::new();
let initial_capacity = pool.capacity();
pool.reserve(0);
assert_eq!(pool.capacity(), initial_capacity);
}
#[test]
fn reserve_with_sufficient_capacity_does_nothing() {
let mut pool = PinnedPool::<u32>::new();
pool.reserve(10);
let capacity_after_reserve = pool.capacity();
pool.reserve(5);
assert_eq!(pool.capacity(), capacity_after_reserve);
}
#[test]
fn reserve_large_capacity() {
let mut pool = PinnedPool::<u32>::new();
let large_count = SLAB_CAPACITY * 3 + 50;
pool.reserve(large_count);
assert!(pool.capacity() >= large_count);
let mut keys = Vec::new();
for i in 0..large_count {
keys.push(pool.insert(i as u32));
}
for (i, &key) in keys.iter().enumerate() {
assert_eq!(*pool.get(key), i as u32);
}
for key in keys {
pool.remove(key);
}
}
#[test]
#[should_panic(expected = "capacity overflow")]
fn reserve_overflow_panics() {
let mut pool = PinnedPool::<u32>::new();
let _key = pool.insert(42);
pool.reserve(usize::MAX);
}
#[test]
fn trait_object_usage() {
trait Greet {
fn greet(&self) -> String;
}
#[derive(Debug)]
struct Person {
name: String,
}
impl Greet for Person {
fn greet(&self) -> String {
format!("Hello, I'm {}", self.name)
}
}
let mut pool = PinnedPool::<Person>::new();
let person_key = pool.insert(Person {
name: "Alice".to_string(),
});
let person_ref = pool.get(person_key);
let greet_obj: &dyn Greet = person_ref.get_ref();
assert_eq!(greet_obj.greet(), "Hello, I'm Alice");
pool.remove(person_key);
}
#[test]
fn trait_object_with_pinned_references() {
trait Identifiable {
fn get_id(&self) -> u64;
fn set_id(&mut self, id: u64);
}
#[derive(Debug)]
struct Item {
id: u64,
#[expect(dead_code, reason = "Used for demo purposes")]
data: String,
}
impl Identifiable for Item {
fn get_id(&self) -> u64 {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = id;
}
}
let mut pool = PinnedPool::<Item>::new();
let item_key = pool.insert(Item {
id: 123,
data: "test data".to_string(),
});
{
let item_ref = pool.get(item_key);
let trait_obj: &dyn Identifiable = item_ref.get_ref();
assert_eq!(trait_obj.get_id(), 123);
}
{
let item_ref = pool.get_mut(item_key);
let trait_obj: &mut dyn Identifiable = item_ref.get_mut();
trait_obj.set_id(456);
assert_eq!(trait_obj.get_id(), 456);
}
{
let item_ref = pool.get(item_key);
assert_eq!(item_ref.id, 456);
}
pool.remove(item_key);
}
#[test]
fn insert_with_partial_initialization() {
use std::mem::MaybeUninit;
struct HalfFull {
value: usize,
memory: [MaybeUninit<u8>; 16],
}
fn initialize_half_full(uninit: &mut MaybeUninit<HalfFull>) {
let ptr = uninit.as_mut_ptr();
let value_ptr = unsafe { &raw mut (*ptr).value };
unsafe {
value_ptr.write(42);
}
let memory_ptr = unsafe { &raw mut (*ptr).memory };
unsafe {
memory_ptr.write([MaybeUninit::uninit(); 16]);
}
}
let mut pool = PinnedPool::<HalfFull>::new();
let inserter = pool.begin_insert();
let key = inserter.key();
let value_ref = unsafe { inserter.insert_with(initialize_half_full) };
assert_eq!(value_ref.value, 42);
let retrieved = pool.get(key);
assert_eq!(retrieved.value, 42);
assert_eq!(pool.len(), 1);
pool.remove(key);
}
#[test]
fn insert_with_mut_works() {
let mut pool = PinnedPool::<String>::new();
let inserter = pool.begin_insert();
let key = inserter.key();
let mut value_ref = unsafe {
inserter.insert_with_mut(|uninit| {
uninit.write(String::from("Hello"));
})
};
value_ref.as_mut().get_mut().push_str(", World!");
assert_eq!(&*value_ref, "Hello, World!");
assert_eq!(&*pool.get(key), "Hello, World!");
assert_eq!(pool.len(), 1);
pool.remove(key);
}
#[test]
fn iter_empty_pool() {
let pool = PinnedPool::<u32>::new();
let mut iter = pool.iter();
assert!(iter.next().is_none());
}
#[test]
fn iter_single_item() {
let mut pool = PinnedPool::<String>::new();
_ = pool.insert("hello".to_string());
let items: Vec<_> = pool.iter().collect();
assert_eq!(items.len(), 1);
assert_eq!(&*items[0], "hello");
}
#[test]
fn iter_multiple_items() {
let mut pool = PinnedPool::<i32>::new();
let _key1 = pool.insert(10);
let _key2 = pool.insert(20);
let _key3 = pool.insert(30);
let items: Vec<_> = pool.iter().map(|item| *item).collect();
assert_eq!(items.len(), 3);
assert!(items.contains(&10));
assert!(items.contains(&20));
assert!(items.contains(&30));
}
#[test]
fn iter_with_gaps() {
let mut pool = PinnedPool::<u64>::new();
let _key1 = pool.insert(100);
let key2 = pool.insert(200);
let _key3 = pool.insert(300);
pool.remove(key2);
let items: Vec<_> = pool.iter().map(|item| *item).collect();
assert_eq!(items.len(), 2);
assert!(items.contains(&100));
assert!(items.contains(&300));
assert!(!items.contains(&200));
}
#[test]
fn iter_across_multiple_slabs() {
const COUNT: usize = SLAB_CAPACITY * 2;
let mut pool = PinnedPool::<usize>::new();
for i in 0..COUNT {
_ = pool.insert(i);
}
let items: Vec<_> = pool.iter().map(|item| *item).collect();
assert_eq!(items.len(), COUNT);
for i in 0..COUNT {
assert!(items.contains(&i));
}
}
#[test]
fn iter_multiple_iterators() {
let mut pool = PinnedPool::<u8>::new();
_ = pool.insert(1);
_ = pool.insert(2);
_ = pool.insert(3);
let iter1 = pool.iter();
let iter2 = pool.iter();
let items1: Vec<_> = iter1.map(|item| *item).collect();
let items2: Vec<_> = iter2.map(|item| *item).collect();
assert_eq!(items1, items2);
assert_eq!(items1, vec![1, 2, 3]);
}
}