generational_buffer/
generational_buffer.rs1use std::{
2 fmt,
3 hash::Hash,
4 marker::PhantomData,
5};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct Handle<T> {
13 index: usize,
14 generation: u32,
15 phantom: PhantomData<T>,
16}
17
18impl<T> Handle<T> {
19 fn new(index: usize, generation: u32) -> Self {
20 Self {
21 index,
22 generation,
23 phantom: PhantomData,
24 }
25 }
26}
27
28pub struct GenerationalBuffer<T> {
33 entries: Vec<T>,
34 max_capacity: usize,
35 next_index: usize,
36 current_generation: u32,
37}
38
39impl<T> GenerationalBuffer<T> {
40 pub fn new(max_capacity: usize) -> Self {
42 Self {
43 entries: Vec::new(),
44 max_capacity,
45 next_index: 0,
46 current_generation: 0,
47 }
48 }
49
50 pub fn capacity(&self) -> usize {
52 self.max_capacity
53 }
54
55 pub fn len(&self) -> usize {
57 self.entries.len()
58 }
59
60 pub fn is_empty(&self) -> bool {
62 self.entries.is_empty()
63 }
64
65 pub fn is_full(&self) -> bool {
67 self.entries.len() == self.max_capacity
68 }
69
70 pub fn push(&mut self, value: T) -> Handle<T> {
74 let index = self.next_index;
75 let generation = self.current_generation;
76
77 if self.entries.len() < self.max_capacity {
78 self.entries.push(value);
80 } else {
81 self.entries[index] = value;
83 }
84
85 let handle = Handle::new(index, generation);
87
88 self.next_index = (self.next_index + 1) % self.max_capacity;
90
91 if self.next_index == 0 {
93 self.current_generation = self.current_generation.wrapping_add(1);
94 }
95
96 handle
97 }
98
99 pub fn get(&self, handle: Handle<T>) -> Option<&T> {
101 if handle.index >= self.entries.len() {
102 return None;
103 }
104
105 let expected_generation = self.calculate_generation_at_index(handle.index);
107
108 if handle.generation == expected_generation {
110 Some(&self.entries[handle.index])
111 } else {
112 None
113 }
114 }
115
116 pub fn get_mut(&mut self, handle: Handle<T>) -> Option<&mut T> {
118 if handle.index >= self.entries.len() {
119 return None;
120 }
121
122 let expected_generation = self.calculate_generation_at_index(handle.index);
124
125 if handle.generation == expected_generation {
127 Some(&mut self.entries[handle.index])
128 } else {
129 None
130 }
131 }
132
133 pub fn is_valid(&self, handle: Handle<T>) -> bool {
135 if handle.index >= self.entries.len() {
136 return false;
137 }
138
139 let expected_generation = self.calculate_generation_at_index(handle.index);
140 handle.generation == expected_generation
141 }
142
143 pub fn iter(&self) -> impl Iterator<Item = (Handle<T>, &T)> {
146 self.entries.iter().enumerate().map(|(i, value)| {
147 let generation = self.calculate_generation_at_index(i);
148 (Handle::new(i, generation), value)
149 })
150 }
151
152 pub fn values(&self) -> impl Iterator<Item = &T> {
154 self.entries.iter()
155 }
156
157 pub fn handles(&self) -> impl Iterator<Item = Handle<T>> + '_ {
159 (0..self.entries.len()).map(move |i| {
160 let generation = self.calculate_generation_at_index(i);
161 Handle::new(i, generation)
162 })
163 }
164
165 fn calculate_generation_at_index(&self, index: usize) -> u32 {
167 if index < self.next_index {
168 self.current_generation
169 } else {
170 self.current_generation.saturating_sub(1)
171 }
172 }
173}
174
175impl<T: fmt::Debug> fmt::Debug for GenerationalBuffer<T> {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 f.debug_struct("GenerationalBuffer")
178 .field("capacity", &self.capacity())
179 .field("len", &self.len())
180 .field("next_index", &self.next_index)
181 .field("current_generation", &self.current_generation)
182 .field("entries", &self.entries)
183 .finish()
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_basic_operations() {
193 let mut buffer = GenerationalBuffer::new(3);
194
195 let h1 = buffer.push(10);
197 let h2 = buffer.push(20);
198 let h3 = buffer.push(30);
199
200 assert_eq!(buffer.get(h1), Some(&10));
202 assert_eq!(buffer.get(h2), Some(&20));
203 assert_eq!(buffer.get(h3), Some(&30));
204 assert_eq!(buffer.len(), 3);
205 assert!(buffer.is_full());
206 }
207
208 #[test]
209 fn test_circular_wrapping() {
210 let mut buffer = GenerationalBuffer::new(2);
211
212 let h1 = buffer.push(10);
214 let h2 = buffer.push(20);
215
216 let h3 = buffer.push(30);
218
219 assert_eq!(buffer.get(h1), None);
221 assert_eq!(buffer.get(h2), Some(&20));
222 assert_eq!(buffer.get(h3), Some(&30));
223 assert!(!buffer.is_valid(h1));
224 assert!(buffer.is_valid(h2));
225 assert!(buffer.is_valid(h3));
226 assert_eq!(buffer.len(), 2);
227
228 let h4 = buffer.push(40); let h5 = buffer.push(50); assert_eq!(buffer.get(h4), Some(&40));
232 assert_eq!(buffer.get(h5), Some(&50));
233 assert!(!buffer.is_valid(h2)); assert!(!buffer.is_valid(h3)); }
236
237 #[test]
238 fn test_generation_calculation() {
239 let mut buffer = GenerationalBuffer::new(3);
240
241 let handles: Vec<_> = (0..10).map(|i| buffer.push(i)).collect();
243
244 for (i, &handle) in handles.iter().enumerate() {
246 if i < 7 {
247 assert!(!buffer.is_valid(handle), "Handle {i} should be invalid");
248 } else {
249 assert!(buffer.is_valid(handle), "Handle {i} should be valid");
250 }
251 }
252 }
253
254 #[test]
255 fn test_iterator() {
256 let mut buffer = GenerationalBuffer::new(3);
257
258 buffer.push(10);
259 buffer.push(20);
260 buffer.push(30);
261 buffer.push(40);
262 buffer.push(50);
263 buffer.push(60);
264 buffer.push(70);
265 buffer.push(80);
266
267 let mut values: Vec<i32> = buffer.values().cloned().collect();
268 values.sort(); assert_eq!(values, vec![60, 70, 80]);
270
271 let handles: Vec<_> = buffer.handles().collect();
272 assert_eq!(handles.len(), 3);
273
274 for handle in handles {
276 assert!(buffer.is_valid(handle));
277 }
278 }
279
280 #[test]
281 fn test_growing_buffer() {
282 let mut buffer = GenerationalBuffer::new(5);
283
284 let h1 = buffer.push(1);
286 let h2 = buffer.push(2);
287 let h3 = buffer.push(3);
288
289 assert_eq!(buffer.len(), 3);
290 assert!(!buffer.is_full());
291
292 assert!(buffer.is_valid(h1));
294 assert!(buffer.is_valid(h2));
295 assert!(buffer.is_valid(h3));
296
297 assert_eq!(buffer.get(h1), Some(&1));
299 assert_eq!(buffer.get(h2), Some(&2));
300 assert_eq!(buffer.get(h3), Some(&3));
301 }
302}