generational_buffer/
generational_buffer.rs

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