Skip to main content

refraction_types/
ring_buffer.rs

1use crate::{
2    BOUNDED_RING_BUFFER_DEFAULT_SIZE,
3    ring_iter::{IntoIter, Iter, IterMut},
4};
5
6#[derive(Debug, Clone)]
7pub struct BoundedRingBuffer<T> {
8    pub(crate) data: Vec<T>,
9    pub(crate) get_idx: usize,
10    pub(crate) put_idx: usize,
11    pub(crate) capacity: usize,
12    pub(crate) len: usize,
13}
14
15impl<T> BoundedRingBuffer<T>
16where
17    T: Copy + Default,
18{
19    /// Creates a buffer with the default capacity.
20    #[must_use]
21    pub fn new() -> Self {
22        Self::with_capacity(BOUNDED_RING_BUFFER_DEFAULT_SIZE)
23    }
24
25    /// Creates a buffer with a fixed capacity.
26    ///
27    /// # Panics
28    ///
29    /// Panics if `cap` is `0`.
30    #[must_use]
31    pub fn with_capacity(cap: usize) -> Self {
32        assert_ne!(cap, 0, "Capacity cannot be zero!");
33        Self {
34            data: vec![T::default(); cap],
35            put_idx: 0,
36            get_idx: 0,
37            capacity: cap,
38            len: 0,
39        }
40    }
41
42    /// Returns the number of currently stored elements.
43    #[must_use]
44    pub fn len(&self) -> usize {
45        self.len
46    }
47
48    /// Returns the maximum number of elements the buffer can store.
49    #[must_use]
50    pub fn capacity(&self) -> usize {
51        self.capacity
52    }
53
54    /// Returns the current read index.
55    #[must_use]
56    pub fn get_idx(&self) -> usize {
57        self.get_idx
58    }
59
60    /// Returns the current write index.
61    #[must_use]
62    pub fn put_idx(&self) -> usize {
63        self.put_idx
64    }
65
66    /// Returns `true` when the buffer has no readable elements.
67    #[must_use]
68    pub fn is_empty(&self) -> bool {
69        self.get_idx == self.put_idx && self.len == 0
70    }
71
72    /// Returns an iterator over readable elements in logical queue order.
73    #[must_use]
74    pub fn iter(&self) -> Iter<'_, T> {
75        if self.is_empty() {
76            return Iter([].iter().chain([].iter()));
77        }
78
79        let (first, second) = self.as_slice();
80
81        let second_iter = match second {
82            Some(slice) => slice.iter(),
83            None => [].iter(),
84        };
85
86        Iter(first.iter().chain(second_iter))
87    }
88
89    /// Returns a mutable iterator over readable elements in logical queue order.
90    #[must_use]
91    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
92        if self.is_empty() {
93            return IterMut([].iter_mut().chain([].iter_mut()));
94        }
95
96        let (first, second) = self.as_mut_slice();
97
98        let second_iter = match second {
99            Some(slice) => slice.iter_mut(),
100            None => [].iter_mut(),
101        };
102
103        IterMut(first.iter_mut().chain(second_iter))
104    }
105
106    // into_iter is implemented via IntoIterator trait below
107
108    /// Returns `true` when all capacity is occupied.
109    #[must_use]
110    pub fn full(&self) -> bool {
111        self.get_idx == self.put_idx && self.len == self.capacity
112    }
113
114    /// Returns a shared reference to the element at the current write index.
115    ///
116    /// This does not modify buffer state.
117    #[must_use]
118    pub fn back(&self) -> Option<&T> {
119        if self.is_empty() {
120            None
121        } else {
122            Some(&self.data[(self.put_idx + self.capacity - 1) % self.capacity])
123        }
124    }
125
126    /// Returns a mutable reference to the element at the current write index - 1.
127    ///
128    /// This does not modify indices or length.
129    #[must_use]
130    pub fn back_mut(&mut self) -> Option<&mut T> {
131        if self.is_empty() {
132            None
133        } else {
134            Some(&mut self.data[(self.put_idx + self.capacity - 1) % self.capacity])
135        }
136    }
137
138    /// Returns a shared reference to the front readable element.
139    #[must_use]
140    pub fn front(&self) -> Option<&T> {
141        if self.is_empty() {
142            None
143        } else {
144            Some(&self.data[self.get_idx])
145        }
146    }
147
148    /// Returns a mutable reference to the front readable element.
149    #[must_use]
150    pub fn front_mut(&mut self) -> Option<&mut T> {
151        if self.is_empty() {
152            None
153        } else {
154            let idx = self.get_idx;
155            Some(&mut self.data[idx])
156        }
157    }
158
159    /// Enqueues a single element.
160    ///
161    /// When the buffer is full, the oldest element is overwritten.
162    pub fn enqueue(&mut self, data: T) {
163        if self.full() {
164            self.get_idx = (self.get_idx + 1) % self.capacity;
165        } else {
166            self.len += 1;
167        }
168
169        self.data[self.put_idx] = data;
170        self.put_idx = (self.put_idx + 1) % self.capacity;
171    }
172
173    /// Dequeues and returns the front element, or `None` if empty.
174    #[must_use]
175    pub fn dequeue(&mut self) -> Option<T> {
176        if self.is_empty() {
177            return None;
178        }
179
180        let item = self.data[self.get_idx];
181        self.get_idx = (self.get_idx + 1) % self.capacity;
182        self.len -= 1;
183
184        Some(item)
185    }
186
187    /// Advances the write cursor by `size` slots as if `size` items were written.
188    ///
189    /// If this exceeds available space, the oldest elements are dropped.
190    pub fn advance(&mut self, size: usize) {
191        let size = size.min(self.capacity);
192
193        let new_put_idx = (self.put_idx + size) % self.capacity;
194        let remaining_space = self.capacity - self.len;
195        let offset = size.saturating_sub(remaining_space);
196        let new_get_idx = (self.get_idx + offset) % self.capacity;
197
198        self.get_idx = new_get_idx;
199        self.put_idx = new_put_idx;
200        self.len = (self.len + size).min(self.capacity);
201    }
202
203    /// Enqueues a slice of elements preserving order.
204    ///
205    /// If `data` is larger than capacity, only the last `capacity` elements are kept.
206    pub fn enqueue_slice(&mut self, data: &[T]) {
207        match data.len().cmp(&self.capacity) {
208            std::cmp::Ordering::Less => {
209                let new_put_idx = (self.put_idx + data.len()) % self.capacity;
210                let remaining_space = self.capacity - self.len;
211                let offset = data.len().saturating_sub(remaining_space);
212                let new_get_idx = (self.get_idx + offset) % self.capacity;
213
214                //we are wrapped now
215                if new_put_idx < self.put_idx {
216                    //copy from put to the end
217                    let middle = self.capacity - self.put_idx;
218                    self.data[self.put_idx..].copy_from_slice(&data[0..middle]);
219                    self.data[..new_put_idx].copy_from_slice(&data[middle..]);
220                } else {
221                    self.data[self.put_idx..self.put_idx + data.len()].copy_from_slice(data);
222                }
223
224                self.get_idx = new_get_idx;
225                self.put_idx = new_put_idx;
226                self.len = (self.len + data.len()).min(self.capacity);
227            }
228            std::cmp::Ordering::Equal => {
229                self.data.copy_from_slice(data);
230                self.put_idx = 0;
231                self.get_idx = 0;
232                self.len = data.len();
233            }
234            std::cmp::Ordering::Greater => {
235                let new_data_len = data.len();
236                let start = new_data_len - self.capacity;
237                let new_data = &data[start..new_data_len];
238                self.enqueue_slice(new_data);
239            }
240        }
241    }
242
243    /// Dequeues up to `dequeue_size` elements into `out_buffer`.
244    ///
245    /// Returns the number of elements actually written to `out_buffer`.
246    ///
247    /// # Panics
248    ///
249    /// Panics if `out_buffer.len() < dequeue_size`.
250    #[must_use]
251    pub fn dequeue_slice(&mut self, out_buffer: &mut [T], dequeue_size: usize) -> usize {
252        if self.is_empty() {
253            return 0;
254        }
255
256        assert!(
257            out_buffer.len() >= dequeue_size,
258            "Dequeue size cannot be greater than out buffer"
259        );
260
261        match dequeue_size.cmp(&self.len) {
262            std::cmp::Ordering::Less => {
263                let new_get_idx = (self.get_idx + dequeue_size) % self.capacity;
264
265                if new_get_idx < self.get_idx {
266                    let middle = self.capacity - self.get_idx;
267                    out_buffer[0..middle].copy_from_slice(&self.data[self.get_idx..]);
268                    out_buffer[middle..dequeue_size].copy_from_slice(&self.data[..new_get_idx]);
269                } else {
270                    out_buffer[0..dequeue_size]
271                        .copy_from_slice(&self.data[self.get_idx..new_get_idx]);
272                }
273
274                self.get_idx = new_get_idx;
275                self.len -= dequeue_size;
276
277                dequeue_size
278            }
279            std::cmp::Ordering::Equal => {
280                if self.get_idx >= self.put_idx {
281                    let middle = self.capacity - self.get_idx;
282                    out_buffer[0..middle].copy_from_slice(&self.data[self.get_idx..]);
283                    out_buffer[middle..dequeue_size].copy_from_slice(&self.data[..self.put_idx]);
284                } else {
285                    out_buffer[0..dequeue_size]
286                        .copy_from_slice(&self.data[self.get_idx..self.put_idx]);
287                }
288                self.get_idx = self.put_idx;
289                self.len = 0;
290                dequeue_size
291            }
292            std::cmp::Ordering::Greater => {
293                self.dequeue_slice(&mut out_buffer[0..self.len], self.len)
294            }
295        }
296    }
297
298    /// Returns readable data as one contiguous slice plus an optional wrapped slice.
299    #[must_use]
300    pub fn as_slice(&self) -> (&[T], Option<&[T]>) {
301        if self.get_idx < self.put_idx {
302            (&self.data[self.get_idx..self.put_idx], None)
303        } else {
304            (&self.data[self.get_idx..], Some(&self.data[..self.put_idx]))
305        }
306    }
307
308    /// Returns mutable readable data as one contiguous slice plus an optional wrapped slice.
309    #[must_use]
310    pub fn as_mut_slice(&mut self) -> (&mut [T], Option<&mut [T]>) {
311        if self.get_idx < self.put_idx {
312            (&mut self.data[self.get_idx..self.put_idx], None)
313        } else {
314            let (left, right) = self.data.split_at_mut(self.get_idx);
315            (right, Some(&mut left[..self.put_idx]))
316        }
317    }
318}
319impl<T> IntoIterator for BoundedRingBuffer<T>
320where
321    T: Copy + Default,
322{
323    type Item = T;
324    type IntoIter = IntoIter<T>;
325
326    fn into_iter(self) -> Self::IntoIter {
327        IntoIter(self)
328    }
329}
330
331impl<'a, T> IntoIterator for &'a BoundedRingBuffer<T>
332where
333    T: Copy + Default,
334{
335    type Item = &'a T;
336    type IntoIter = Iter<'a, T>;
337
338    fn into_iter(self) -> Self::IntoIter {
339        self.iter()
340    }
341}
342
343impl<'a, T> IntoIterator for &'a mut BoundedRingBuffer<T>
344where
345    T: Copy + Default,
346{
347    type Item = &'a mut T;
348    type IntoIter = IterMut<'a, T>;
349
350    fn into_iter(self) -> Self::IntoIter {
351        self.iter_mut()
352    }
353}