use std::pin::Pin;
use num::Integer;
use crate::{DropPolicy, PinnedPoolBuilder, PinnedSlab, PinnedSlabInserter};
#[derive(Debug)]
pub struct PinnedPool<T> {
slabs: Vec<PinnedSlab<T, SLAB_CAPACITY>>,
slab_with_vacant_slot_index: Option<usize>,
drop_policy: DropPolicy,
}
#[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,
}
}
#[must_use]
pub fn new() -> Self {
Self::builder().build()
}
pub fn builder() -> PinnedPoolBuilder<T> {
PinnedPoolBuilder::new()
}
#[must_use]
pub fn len(&self) -> usize {
self.slabs.iter().map(PinnedSlab::len).sum()
}
#[must_use]
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]
pub fn is_empty(&self) -> bool {
self.slabs.iter().all(PinnedSlab::is_empty)
}
#[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]
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]
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();
let slab = self
.slabs
.get_mut(slab_index)
.expect("we just verified that there is a slab with a vacant slot at this index");
let predicted_slab_filled_slots = slab.len()
.checked_add(1)
.expect("we cannot overflow because there is at least one free slot, so it means there must be room to increment");
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,
}
}
#[must_use]
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 Some(slab) = self.slabs.get_mut(index.slab_index) else {
panic!("key was not associated with an item in the pool")
};
slab.remove(index.index_in_slab);
self.update_vacant_slot_cache(index.slab_index);
}
#[must_use]
fn index_of_slab_with_vacant_slot(&mut self) -> usize {
if let Some(index) = self.slab_with_vacant_slot_index {
return index;
}
let index = if let Some((index, _)) = self
.slabs
.iter()
.enumerate()
.find(|(_, slab)| !slab.is_full())
{
index
} else {
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")
};
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> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct PinnedPoolInserter<'s, T> {
slab_inserter: PinnedSlabInserter<'s, T, SLAB_CAPACITY>,
slab_index: usize,
}
impl<'s, T> PinnedPoolInserter<'s, T> {
pub fn insert<'v>(self, value: T) -> Pin<&'v T>
where
's: 'v,
{
self.slab_inserter.insert(value)
}
pub fn insert_mut<'v>(self, value: T) -> Pin<&'v mut T>
where
's: 'v,
{
self.slab_inserter.insert_mut(value)
}
#[must_use]
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.checked_mul(SLAB_CAPACITY)
.and_then(|x| x.checked_add(self.index_in_slab))
.expect("key indicates an item beyond the range of virtual memory - impossible to reach this point from a valid history")
}
}
}
#[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 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);
}
}