generational_buffer/
generational_buffer.rs

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