1use core::fmt;
30use core::hash::{Hash, Hasher};
31use core::marker::PhantomData;
32use core::mem;
33use core::num::NonZeroU32;
34
35const FIRST_GENERATION: NonZeroU32 = NonZeroU32::MIN;
37
38#[repr(C)]
42pub struct Handle<T> {
43 index: u32,
44 generation: NonZeroU32,
45 marker: PhantomData<fn() -> T>,
46}
47
48impl<T> Handle<T> {
49 #[inline]
54 const fn new(index: u32, generation: NonZeroU32) -> Self {
55 Self {
56 index,
57 generation,
58 marker: PhantomData,
59 }
60 }
61
62 #[inline]
64 pub const fn index(self) -> u32 {
65 self.index
66 }
67
68 #[inline]
72 pub const fn generation(self) -> u32 {
73 self.generation.get()
74 }
75
76 #[inline]
81 pub const fn to_bits(self) -> u64 {
82 ((self.generation.get() as u64) << 32) | self.index as u64
83 }
84
85 #[inline]
90 pub const fn from_bits(bits: u64) -> Option<Self> {
91 match NonZeroU32::new((bits >> 32) as u32) {
92 Some(generation) => Some(Self::new(bits as u32, generation)),
93 None => None,
94 }
95 }
96
97 #[inline]
102 pub const fn cast<U>(self) -> Handle<U> {
103 Handle::new(self.index, self.generation)
104 }
105}
106
107impl<T> Clone for Handle<T> {
108 #[inline]
109 fn clone(&self) -> Self {
110 *self
111 }
112}
113
114impl<T> Copy for Handle<T> {}
115
116impl<T> PartialEq for Handle<T> {
117 #[inline]
118 fn eq(&self, other: &Self) -> bool {
119 self.index == other.index && self.generation == other.generation
120 }
121}
122
123impl<T> Eq for Handle<T> {}
124
125impl<T> Hash for Handle<T> {
126 #[inline]
127 fn hash<H: Hasher>(&self, state: &mut H) {
128 self.to_bits().hash(state);
129 }
130}
131
132impl<T> fmt::Debug for Handle<T> {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(
135 f,
136 "Handle<{}>({}, gen {})",
137 core::any::type_name::<T>(),
138 self.index,
139 self.generation
140 )
141 }
142}
143
144enum Slot<T> {
146 Occupied { generation: NonZeroU32, value: T },
148 Vacant {
151 generation: NonZeroU32,
152 next_free: Option<u32>,
153 },
154 Retired,
157}
158
159pub struct Pool<T> {
184 slots: Vec<Slot<T>>,
185 free_head: Option<u32>,
187 len: usize,
188 retired: usize,
189}
190
191impl<T> Pool<T> {
192 #[inline]
194 #[must_use]
195 pub const fn new() -> Self {
196 Self {
197 slots: Vec::new(),
198 free_head: None,
199 len: 0,
200 retired: 0,
201 }
202 }
203
204 #[inline]
206 #[must_use]
207 pub fn with_capacity(capacity: usize) -> Self {
208 Self {
209 slots: Vec::with_capacity(capacity),
210 free_head: None,
211 len: 0,
212 retired: 0,
213 }
214 }
215
216 #[inline]
218 #[must_use]
219 pub const fn len(&self) -> usize {
220 self.len
221 }
222
223 #[inline]
225 #[must_use]
226 pub const fn is_empty(&self) -> bool {
227 self.len == 0
228 }
229
230 #[inline]
235 #[must_use]
236 pub const fn retired_slots(&self) -> usize {
237 self.retired
238 }
239
240 pub fn insert(&mut self, value: T) -> Handle<T> {
247 if let Some(index) = self.free_head {
248 let slot = &mut self.slots[index as usize];
249 let Slot::Vacant {
250 generation,
251 next_free,
252 } = *slot
253 else {
254 unreachable!("free list only links Vacant slots");
255 };
256 *slot = Slot::Occupied { generation, value };
257 self.free_head = next_free;
258 self.len += 1;
259 Handle::new(index, generation)
260 } else {
261 let index = u32::try_from(self.slots.len()).expect("Pool exceeded 2^32 slots");
262 self.slots.push(Slot::Occupied {
263 generation: FIRST_GENERATION,
264 value,
265 });
266 self.len += 1;
267 Handle::new(index, FIRST_GENERATION)
268 }
269 }
270
271 #[inline]
273 #[must_use]
274 pub fn get(&self, handle: Handle<T>) -> Option<&T> {
275 match self.slots.get(handle.index as usize) {
276 Some(Slot::Occupied { generation, value }) if *generation == handle.generation => {
277 Some(value)
278 }
279 _ => None,
280 }
281 }
282
283 #[inline]
286 #[must_use]
287 pub fn get_mut(&mut self, handle: Handle<T>) -> Option<&mut T> {
288 match self.slots.get_mut(handle.index as usize) {
289 Some(Slot::Occupied { generation, value }) if *generation == handle.generation => {
290 Some(value)
291 }
292 _ => None,
293 }
294 }
295
296 #[inline]
298 #[must_use]
299 pub fn contains(&self, handle: Handle<T>) -> bool {
300 self.get(handle).is_some()
301 }
302
303 pub fn remove(&mut self, handle: Handle<T>) -> Option<T> {
308 let slot = self.slots.get_mut(handle.index as usize)?;
309 match slot {
310 Slot::Occupied { generation, .. } if *generation == handle.generation => {}
311 _ => return None,
312 }
313 let Slot::Occupied { generation, value } = mem::replace(slot, Slot::Retired) else {
317 unreachable!("checked above");
318 };
319 self.len -= 1;
320 match generation.checked_add(1) {
321 Some(next) => {
322 *slot = Slot::Vacant {
323 generation: next,
324 next_free: self.free_head,
325 };
326 self.free_head = Some(handle.index);
327 }
328 None => self.retired += 1,
331 }
332 Some(value)
333 }
334
335 pub fn clear(&mut self) {
340 self.free_head = None;
341 for index in 0..self.slots.len() {
342 let slot = &mut self.slots[index];
343 let generation = match slot {
344 Slot::Occupied { generation, .. } => match generation.checked_add(1) {
345 Some(next) => next,
346 None => {
347 *slot = Slot::Retired;
348 self.retired += 1;
349 continue;
350 }
351 },
352 Slot::Vacant { generation, .. } => *generation,
353 Slot::Retired => continue,
354 };
355 *slot = Slot::Vacant {
356 generation,
357 next_free: self.free_head,
358 };
359 self.free_head = Some(index as u32);
362 }
363 self.len = 0;
364 }
365
366 pub fn iter(&self) -> impl Iterator<Item = (Handle<T>, &T)> {
368 self.slots
369 .iter()
370 .enumerate()
371 .filter_map(|(index, slot)| match slot {
372 Slot::Occupied { generation, value } => {
373 Some((Handle::new(index as u32, *generation), value))
374 }
375 _ => None,
376 })
377 }
378
379 pub fn iter_mut(&mut self) -> impl Iterator<Item = (Handle<T>, &mut T)> {
381 self.slots
382 .iter_mut()
383 .enumerate()
384 .filter_map(|(index, slot)| match slot {
385 Slot::Occupied { generation, value } => {
386 Some((Handle::new(index as u32, *generation), value))
387 }
388 _ => None,
389 })
390 }
391}
392
393impl<T> Default for Pool<T> {
394 #[inline]
395 fn default() -> Self {
396 Self::new()
397 }
398}
399
400impl<T> Extend<T> for Pool<T> {
401 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
402 for value in iter {
403 self.insert(value);
404 }
405 }
406}
407
408impl<T> FromIterator<T> for Pool<T> {
409 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
410 let mut pool = Self::new();
411 pool.extend(iter);
412 pool
413 }
414}
415
416impl<T> fmt::Debug for Pool<T> {
419 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420 f.debug_struct("Pool")
421 .field("type", &core::any::type_name::<T>())
422 .field("len", &self.len)
423 .field("slots", &self.slots.len())
424 .field("retired", &self.retired)
425 .finish()
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 use proptest::prelude::*;
433 use std::collections::HashMap;
434
435 #[test]
436 fn handle_is_eight_bytes_and_option_is_free() {
437 assert_eq!(mem::size_of::<Handle<String>>(), 8);
438 assert_eq!(mem::size_of::<Option<Handle<String>>>(), 8);
439 }
440
441 #[test]
442 fn handle_traits_do_not_depend_on_t() {
443 fn assert_traits<H: Copy + Eq + Hash + fmt::Debug + Send + Sync>() {}
446 assert_traits::<Handle<*mut ()>>();
447 }
448
449 #[test]
450 fn a_handle_survives_to_bits_and_back_and_a_zero_generation_is_not_a_handle() {
451 let handle = Handle::<u8>::new(0xDEAD_BEEF, NonZeroU32::new(0x0BAD_F00D).unwrap());
452 assert_eq!(Handle::from_bits(handle.to_bits()), Some(handle));
453 assert_ne!(handle.to_bits(), 0);
454 assert_eq!(Handle::<u8>::from_bits(0), None);
455 assert_eq!(Handle::<u8>::from_bits(0xFFFF_FFFF), None);
457 }
458
459 #[test]
460 fn a_pool_hands_back_what_was_inserted_and_a_removed_handle_never_resurrects() {
461 let mut pool = Pool::new();
462 let a = pool.insert("a".to_string());
463 let b = pool.insert("b".to_string());
464 assert_eq!(pool.len(), 2);
465 assert_eq!(pool.get(a).map(String::as_str), Some("a"));
466 assert_eq!(pool.get(b).map(String::as_str), Some("b"));
467
468 assert_eq!(pool.remove(a), Some("a".to_string()));
469 assert_eq!(pool.remove(a), None, "double remove must not resurrect");
470 assert!(!pool.contains(a));
471 assert!(pool.contains(b));
472 assert_eq!(pool.len(), 1);
473
474 pool.get_mut(b).unwrap().push('!');
475 assert_eq!(pool.get(b).map(String::as_str), Some("b!"));
476 }
477
478 #[test]
479 fn removed_slot_is_recycled_with_a_new_generation() {
480 let mut pool = Pool::new();
481 let old = pool.insert(1u32);
482 pool.remove(old);
483 let new = pool.insert(2u32);
484 assert_eq!(new.index(), old.index(), "free list should reuse the slot");
485 assert_ne!(new.generation(), old.generation());
486 assert_eq!(
487 pool.get(old),
488 None,
489 "stale handle must not see the new value"
490 );
491 assert_eq!(pool.get(new), Some(&2));
492 }
493
494 #[test]
495 fn clear_invalidates_everything_but_keeps_slots() {
496 let mut pool = Pool::new();
497 let handles: Vec<_> = (0..8).map(|i| pool.insert(i)).collect();
498 pool.clear();
499 assert!(pool.is_empty());
500 for handle in &handles {
501 assert_eq!(pool.get(*handle), None);
502 }
503 let fresh = pool.insert(99);
505 assert!(handles.iter().any(|h| h.index() == fresh.index()));
506 assert_eq!(pool.get(fresh), Some(&99));
507 }
508
509 #[test]
510 fn iteration_yields_live_pairs_only() {
511 let mut pool = Pool::new();
512 let a = pool.insert(10);
513 let b = pool.insert(20);
514 let c = pool.insert(30);
515 pool.remove(b);
516
517 let mut seen: Vec<_> = pool.iter().map(|(h, v)| (h, *v)).collect();
518 seen.sort_by_key(|(h, _)| h.index());
519 assert_eq!(seen, vec![(a, 10), (c, 30)]);
520
521 for (_, value) in pool.iter_mut() {
522 *value += 1;
523 }
524 assert_eq!(pool.get(a), Some(&11));
525 assert_eq!(pool.get(c), Some(&31));
526 }
527
528 #[test]
529 fn exhausted_generation_retires_the_slot() {
530 let mut pool = Pool::new();
531 let handle = pool.insert(1u8);
532 let last = NonZeroU32::new(u32::MAX).unwrap();
535 pool.slots[0] = Slot::Occupied {
536 generation: last,
537 value: 1,
538 };
539 let live = Handle::<u8>::new(0, last);
540 assert_eq!(pool.get(handle), None);
541 assert_eq!(pool.get(live), Some(&1));
542
543 assert_eq!(pool.remove(live), Some(1));
544 assert_eq!(pool.retired_slots(), 1);
545 assert_eq!(pool.get(live), None);
546
547 let next = pool.insert(2u8);
550 assert_ne!(next.index(), live.index());
551 assert_eq!(pool.get(live), None);
552 }
553
554 #[test]
555 fn collected_and_debug_render() {
556 let pool: Pool<i32> = (0..3).collect();
557 assert_eq!(pool.len(), 3);
558 let rendered = format!("{pool:?}");
559 assert!(rendered.contains("len: 3"), "{rendered}");
560 }
561
562 #[test]
563 fn len_equals_iter_count_after_any_operation() {
564 let mut pool = Pool::new();
569 assert_eq!(pool.len(), pool.iter().count());
570
571 let mut handles = Vec::new();
572 for i in 0..20 {
573 handles.push(pool.insert(i));
574 assert_eq!(pool.len(), pool.iter().count());
575 }
576
577 for h in &handles[..10] {
578 pool.remove(*h);
579 assert_eq!(pool.len(), pool.iter().count());
580 }
581
582 for i in 30..40 {
584 pool.insert(i);
585 assert_eq!(pool.len(), pool.iter().count());
586 }
587
588 pool.clear();
589 assert_eq!(pool.len(), 0);
590 assert_eq!(pool.iter().count(), 0);
591 }
592
593 #[test]
596 fn model_matches_under_scripted_churn() {
597 let mut pool = Pool::new();
598 let mut model: HashMap<Handle<u64>, u64> = HashMap::new();
599 let mut dead: Vec<Handle<u64>> = Vec::new();
600 let mut state = 0x1234_5678_9ABC_DEF0u64;
603 for step in 0..2_000u64 {
604 state = state
605 .wrapping_mul(6364136223846793005)
606 .wrapping_add(1442695040888963407);
607 if state.is_multiple_of(3) && !model.is_empty() {
608 let victim = *model.keys().next().unwrap();
609 assert_eq!(pool.remove(victim), model.remove(&victim));
610 dead.push(victim);
611 } else {
612 let handle = pool.insert(step);
613 assert!(
614 model.insert(handle, step).is_none(),
615 "handle reissued while live"
616 );
617 }
618 assert_eq!(pool.len(), model.len());
619 }
620 for (handle, value) in &model {
621 assert_eq!(pool.get(*handle), Some(value));
622 }
623 for handle in &dead {
624 assert!(!pool.contains(*handle));
625 }
626 }
627
628 #[derive(Clone, Debug)]
632 enum Op {
633 Insert(u64),
634 RemoveLive(usize),
636 RemoveDead(usize),
638 Clear,
639 }
640
641 fn any_op() -> impl Strategy<Value = Op> {
642 prop_oneof![
643 8 => any::<u64>().prop_map(Op::Insert),
644 6 => any::<usize>().prop_map(Op::RemoveLive),
645 2 => any::<usize>().prop_map(Op::RemoveDead),
646 1 => Just(Op::Clear),
647 ]
648 }
649
650 proptest! {
651 #[test]
657 fn prop_pool_matches_hashmap_model(ops in proptest::collection::vec(any_op(), 1..120)) {
658 let mut pool: Pool<u64> = Pool::new();
659 let mut live: HashMap<Handle<u64>, u64> = HashMap::new();
660 let mut dead: Vec<Handle<u64>> = Vec::new();
661
662 for op in &ops {
663 match *op {
664 Op::Insert(value) => {
665 let handle = pool.insert(value);
666 prop_assert!(
667 !dead.contains(&handle),
668 "a dead handle was reissued: {handle:?}"
669 );
670 prop_assert!(live.insert(handle, value).is_none());
671 }
672 Op::RemoveLive(nth) => {
673 if live.is_empty() {
674 continue;
675 }
676 let mut handles: Vec<_> = live.keys().copied().collect();
680 handles.sort_by_key(|handle| handle.to_bits());
681 let victim = handles[nth % handles.len()];
682 prop_assert_eq!(pool.remove(victim), live.remove(&victim));
683 dead.push(victim);
684 }
685 Op::RemoveDead(nth) => {
686 if dead.is_empty() {
687 continue;
688 }
689 let zombie = dead[nth % dead.len()];
690 prop_assert_eq!(pool.remove(zombie), None);
691 }
692 Op::Clear => {
693 pool.clear();
694 dead.extend(live.keys().copied());
695 live.clear();
696 }
697 }
698
699 prop_assert_eq!(pool.len(), live.len());
700 prop_assert_eq!(pool.is_empty(), live.is_empty());
701 prop_assert_eq!(pool.iter().count(), live.len());
702 for handle in &dead {
703 prop_assert!(!pool.contains(*handle), "stale handle resolved: {handle:?}");
704 }
705 }
706
707 for (handle, value) in &live {
708 prop_assert_eq!(pool.get(*handle), Some(value));
709 }
710 let mut iterated: Vec<_> = pool.iter().map(|(h, v)| (h.to_bits(), *v)).collect();
712 let mut expected: Vec<_> = live.iter().map(|(h, v)| (h.to_bits(), *v)).collect();
713 iterated.sort_unstable();
714 expected.sort_unstable();
715 prop_assert_eq!(iterated, expected);
716 }
717 }
718}