generational_buffer/
generational_buffer.rs

1use std::{
2    fmt,
3    hash::Hash,
4    marker::PhantomData,
5};
6
7/// A handle that combines an index with a generation counter
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct Handle<T> {
10    index: usize,
11    generation: u32,
12    phantom: PhantomData<T>,
13}
14
15impl<T> Handle<T> {
16    fn new(index: usize, generation: u32) -> Self {
17        Self {
18            index,
19            generation,
20            phantom: PhantomData,
21        }
22    }
23}
24
25/// A generic append-only circular buffer with generational IDs
26///
27/// Inserting returns a `Handle` that can be used to access the value later,
28/// checking the item hasn't been replaced in the meantime.
29pub struct GenerationalBuffer<T> {
30    entries: Vec<T>,
31    max_capacity: usize,
32    next_index: usize,
33    current_generation: u32,
34}
35
36impl<T> GenerationalBuffer<T> {
37    /// Creates a new generational buffer with the specified maximum capacity
38    pub fn new(max_capacity: usize) -> Self {
39        Self {
40            entries: Vec::new(),
41            max_capacity,
42            next_index: 0,
43            current_generation: 0,
44        }
45    }
46
47    /// Returns the maximum capacity of the buffer
48    pub fn capacity(&self) -> usize {
49        self.max_capacity
50    }
51
52    /// Returns the current number of entries in the buffer
53    pub fn len(&self) -> usize {
54        self.entries.len()
55    }
56
57    /// Returns true if the buffer is empty
58    pub fn is_empty(&self) -> bool {
59        self.entries.is_empty()
60    }
61
62    /// Returns true if the buffer has reached its maximum capacity
63    pub fn is_full(&self) -> bool {
64        self.entries.len() == self.max_capacity
65    }
66
67    /// Inserts a value into the buffer and returns a handle to it
68    pub fn push(&mut self, value: T) -> Handle<T> {
69        let index = self.next_index;
70        let generation = self.current_generation;
71
72        if self.entries.len() < self.max_capacity {
73            // Buffer is not full yet, just append
74            self.entries.push(value);
75        } else {
76            // Buffer is full, overwrite the oldest entry
77            self.entries[index] = value;
78        }
79
80        // Create handle with current generation
81        let handle = Handle::new(index, generation);
82
83        // Advance to the next position
84        self.next_index = (self.next_index + 1) % self.max_capacity;
85
86        // If we've wrapped around, increment the generation
87        if self.next_index == 0 {
88            self.current_generation = self.current_generation.wrapping_add(1);
89        }
90
91        handle
92    }
93
94    /// Gets a reference to the value associated with the handle
95    pub fn get(&self, handle: Handle<T>) -> Option<&T> {
96        if handle.index >= self.entries.len() {
97            return None;
98        }
99
100        // Calculate the generation that should be at this index
101        let expected_generation = self.calculate_generation_at_index(handle.index);
102
103        // Check if the generation matches
104        if handle.generation == expected_generation {
105            Some(&self.entries[handle.index])
106        } else {
107            None
108        }
109    }
110
111    /// Gets a mutable reference to the value associated with the handle
112    pub fn get_mut(&mut self, handle: Handle<T>) -> Option<&mut T> {
113        if handle.index >= self.entries.len() {
114            return None;
115        }
116
117        // Calculate the generation that should be at this index
118        let expected_generation = self.calculate_generation_at_index(handle.index);
119
120        // Check if the generation matches
121        if handle.generation == expected_generation {
122            Some(&mut self.entries[handle.index])
123        } else {
124            None
125        }
126    }
127
128    /// Checks if a handle is still valid (points to existing data)
129    pub fn is_valid(&self, handle: Handle<T>) -> bool {
130        if handle.index >= self.entries.len() {
131            return false;
132        }
133
134        let expected_generation = self.calculate_generation_at_index(handle.index);
135        handle.generation == expected_generation
136    }
137
138    /// Returns an iterator over all entries with their handles,
139    ///  in no particular order
140    pub fn iter(&self) -> impl Iterator<Item = (Handle<T>, &T)> {
141        self.entries.iter().enumerate().map(|(i, value)| {
142            let generation = self.calculate_generation_at_index(i);
143            (Handle::new(i, generation), value)
144        })
145    }
146
147    /// Returns an iterator over all entries
148    pub fn values(&self) -> impl Iterator<Item = &T> {
149        self.entries.iter()
150    }
151
152    /// Returns an iterator over all valid handles, in no particular order
153    pub fn handles(&self) -> impl Iterator<Item = Handle<T>> + '_ {
154        (0..self.entries.len()).map(move |i| {
155            let generation = self.calculate_generation_at_index(i);
156            Handle::new(i, generation)
157        })
158    }
159
160    /// Clears all entries in the buffer
161    pub fn clear(&mut self) {
162        self.entries.clear();
163        self.next_index = 0;
164        self.current_generation = 0;
165    }
166
167    /// Calculate what generation should be at a given index
168    fn calculate_generation_at_index(&self, index: usize) -> u32 {
169        if index < self.next_index {
170            self.current_generation
171        } else {
172            self.current_generation.saturating_sub(1)
173        }
174    }
175}
176
177impl<T: fmt::Debug> fmt::Debug for GenerationalBuffer<T> {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        f.debug_struct("GenerationalBuffer")
180            .field("capacity", &self.capacity())
181            .field("len", &self.len())
182            .field("next_index", &self.next_index)
183            .field("current_generation", &self.current_generation)
184            .field("entries", &self.entries)
185            .finish()
186    }
187}
188
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_basic_operations() {
196        let mut buffer = GenerationalBuffer::new(3);
197
198        // Insert some values
199        let h1 = buffer.push(10);
200        let h2 = buffer.push(20);
201        let h3 = buffer.push(30);
202
203        // Check values
204        assert_eq!(buffer.get(h1), Some(&10));
205        assert_eq!(buffer.get(h2), Some(&20));
206        assert_eq!(buffer.get(h3), Some(&30));
207        assert_eq!(buffer.len(), 3);
208        assert!(buffer.is_full());
209    }
210
211    #[test]
212    fn test_circular_wrapping() {
213        let mut buffer = GenerationalBuffer::new(2);
214
215        // Fill the buffer
216        let h1 = buffer.push(10);
217        let h2 = buffer.push(20);
218
219        // Wrap around - this should invalidate h1
220        let h3 = buffer.push(30);
221
222        // h1 should now be invalid due to generation mismatch
223        assert_eq!(buffer.get(h1), None);
224        assert_eq!(buffer.get(h2), Some(&20));
225        assert_eq!(buffer.get(h3), Some(&30));
226        assert!(!buffer.is_valid(h1));
227        assert!(buffer.is_valid(h2));
228        assert!(buffer.is_valid(h3));
229        assert_eq!(buffer.len(), 2);
230
231        // let's do one more turn
232        let h4 = buffer.push(40); // This should overwrite h2
233        let h5 = buffer.push(50); // This should overwrite h3
234        assert_eq!(buffer.get(h4), Some(&40));
235        assert_eq!(buffer.get(h5), Some(&50));
236        assert!(!buffer.is_valid(h2)); // h2 should be invalid now
237        assert!(!buffer.is_valid(h3)); // h3 should be invalid now
238    }
239
240    #[test]
241    fn test_generation_calculation() {
242        let mut buffer = GenerationalBuffer::new(3);
243
244        // Fill buffer multiple times
245        let handles: Vec<_> = (0..10).map(|i| buffer.push(i)).collect();
246
247        // Only the last 3 handles should be valid
248        for (i, &handle) in handles.iter().enumerate() {
249            if i < 7 {
250                assert!(!buffer.is_valid(handle), "Handle {i} should be invalid");
251            } else {
252                assert!(buffer.is_valid(handle), "Handle {i} should be valid");
253            }
254        }
255    }
256
257    #[test]
258    fn test_iterator() {
259        let mut buffer = GenerationalBuffer::new(3);
260
261        buffer.push(10);
262        buffer.push(20);
263        buffer.push(30);
264        buffer.push(40);
265        buffer.push(50);
266        buffer.push(60);
267        buffer.push(70);
268        buffer.push(80);
269
270        let mut values: Vec<i32> = buffer.values().cloned().collect();
271        values.sort(); // no order is currently guaranteed
272        assert_eq!(values, vec![60, 70, 80]);
273
274        let handles: Vec<_> = buffer.handles().collect();
275        assert_eq!(handles.len(), 3);
276
277        // Verify all handles are valid
278        for handle in handles {
279            assert!(buffer.is_valid(handle));
280        }
281    }
282
283    #[test]
284    fn test_growing_buffer() {
285        let mut buffer = GenerationalBuffer::new(5);
286
287        // Add elements one by one
288        let h1 = buffer.push(1);
289        let h2 = buffer.push(2);
290        let h3 = buffer.push(3);
291
292        assert_eq!(buffer.len(), 3);
293        assert!(!buffer.is_full());
294
295        // All handles should be valid
296        assert!(buffer.is_valid(h1));
297        assert!(buffer.is_valid(h2));
298        assert!(buffer.is_valid(h3));
299
300        // Values should be accessible
301        assert_eq!(buffer.get(h1), Some(&1));
302        assert_eq!(buffer.get(h2), Some(&2));
303        assert_eq!(buffer.get(h3), Some(&3));
304    }
305}