avian2d/data_structures/id_pool.rs
1//! An [`IdPool`] for managing and reusing identifiers.
2
3use alloc::collections::BinaryHeap;
4use core::cmp::Reverse;
5
6/// A pool for efficient allocation and reuse of `u32` identifiers.
7///
8/// Freed IDs are stored in a min-heap, and reused such that the lowest available IDs are allocated first.
9#[derive(Clone, Debug, Default)]
10#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
11pub struct IdPool {
12 /// A min-heap of free IDs. The lowest free IDs are allocated first.
13 free_ids: BinaryHeap<Reverse<u32>>,
14 /// The next ID to be allocated. Only incremented when no free IDs are available.
15 next_index: u32,
16}
17
18impl IdPool {
19 /// Creates a new empty [`IdPool`].
20 #[inline(always)]
21 pub const fn new() -> Self {
22 Self {
23 free_ids: BinaryHeap::new(),
24 next_index: 0,
25 }
26 }
27
28 /// Creates a new [`IdPool`] with the given initial capacity.
29 ///
30 /// This is useful for preallocating space for IDs to avoid reallocations.
31 #[inline(always)]
32 pub fn with_capacity(capacity: usize) -> Self {
33 Self {
34 free_ids: BinaryHeap::with_capacity(capacity),
35 next_index: 0,
36 }
37 }
38
39 /// Allocates a new ID.
40 ///
41 /// If there are free IDs available, the lowest free ID is reused.
42 #[inline(always)]
43 pub fn alloc(&mut self) -> u32 {
44 if let Some(id) = self.free_ids.pop() {
45 id.0
46 } else {
47 let id = self.next_index;
48 self.next_index += 1;
49 id
50 }
51 }
52
53 /// Frees an ID, making it available for reuse.
54 ///
55 /// The ID is assumed to not already be freed.
56 #[inline(always)]
57 pub fn free(&mut self, id: u32) {
58 debug_assert!(id < self.next_index);
59 self.free_ids.push(Reverse(id));
60 }
61
62 /// Clears the pool, removing all free IDs and resetting the next index.
63 #[inline(always)]
64 pub fn clear(&mut self) {
65 self.free_ids.clear();
66 self.next_index = 0;
67 }
68
69 /// Returns the number of allocated IDs.
70 #[inline(always)]
71 pub fn len(&self) -> usize {
72 self.next_index as usize - self.free_ids.len()
73 }
74
75 /// Returns the number of free IDs.
76 #[inline(always)]
77 pub fn free_len(&self) -> usize {
78 self.free_ids.len()
79 }
80
81 /// Returns the total number of IDs (allocated + free).
82 #[inline(always)]
83 pub fn total_len(&self) -> usize {
84 self.next_index as usize
85 }
86
87 /// Returns `true` if the pool is empty (no allocated IDs).
88 #[inline(always)]
89 pub fn is_empty(&self) -> bool {
90 self.len() == 0
91 }
92}