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    /// This is the recommended high-throughput API for streaming workloads.
206    ///
207    /// If `data` is larger than capacity, only the last `capacity` elements are kept.
208    pub fn enqueue_slice(&mut self, data: &[T]) {
209        match data.len().cmp(&self.capacity) {
210            std::cmp::Ordering::Less => {
211                let new_put_idx = (self.put_idx + data.len()) % self.capacity;
212                let remaining_space = self.capacity - self.len;
213                let offset = data.len().saturating_sub(remaining_space);
214                let new_get_idx = (self.get_idx + offset) % self.capacity;
215
216                //we are wrapped now
217                if new_put_idx < self.put_idx {
218                    //copy from put to the end
219                    let middle = self.capacity - self.put_idx;
220                    self.data[self.put_idx..].copy_from_slice(&data[0..middle]);
221                    self.data[..new_put_idx].copy_from_slice(&data[middle..]);
222                } else {
223                    self.data[self.put_idx..self.put_idx + data.len()].copy_from_slice(data);
224                }
225
226                self.get_idx = new_get_idx;
227                self.put_idx = new_put_idx;
228                self.len = (self.len + data.len()).min(self.capacity);
229            }
230            std::cmp::Ordering::Equal => {
231                self.data.copy_from_slice(data);
232                self.put_idx = 0;
233                self.get_idx = 0;
234                self.len = data.len();
235            }
236            std::cmp::Ordering::Greater => {
237                let new_data_len = data.len();
238                let start = new_data_len - self.capacity;
239                let new_data = &data[start..new_data_len];
240                self.enqueue_slice(new_data);
241            }
242        }
243    }
244
245    /// Dequeues up to `dequeue_size` elements into `out_buffer`.
246    ///
247    /// This is the recommended high-throughput API for draining buffered data.
248    ///
249    /// Returns the number of elements actually written to `out_buffer`.
250    ///
251    /// # Panics
252    ///
253    /// Panics if `out_buffer.len() < dequeue_size`.
254    #[must_use]
255    pub fn dequeue_slice(&mut self, out_buffer: &mut [T], dequeue_size: usize) -> usize {
256        if self.is_empty() {
257            return 0;
258        }
259
260        assert!(
261            out_buffer.len() >= dequeue_size,
262            "Dequeue size cannot be greater than out buffer"
263        );
264
265        match dequeue_size.cmp(&self.len) {
266            std::cmp::Ordering::Less => {
267                let new_get_idx = (self.get_idx + dequeue_size) % self.capacity;
268
269                if new_get_idx < self.get_idx {
270                    let middle = self.capacity - self.get_idx;
271                    out_buffer[0..middle].copy_from_slice(&self.data[self.get_idx..]);
272                    out_buffer[middle..dequeue_size].copy_from_slice(&self.data[..new_get_idx]);
273                } else {
274                    out_buffer[0..dequeue_size]
275                        .copy_from_slice(&self.data[self.get_idx..new_get_idx]);
276                }
277
278                self.get_idx = new_get_idx;
279                self.len -= dequeue_size;
280
281                dequeue_size
282            }
283            std::cmp::Ordering::Equal => {
284                if self.get_idx >= self.put_idx {
285                    let middle = self.capacity - self.get_idx;
286                    out_buffer[0..middle].copy_from_slice(&self.data[self.get_idx..]);
287                    out_buffer[middle..dequeue_size].copy_from_slice(&self.data[..self.put_idx]);
288                } else {
289                    out_buffer[0..dequeue_size]
290                        .copy_from_slice(&self.data[self.get_idx..self.put_idx]);
291                }
292                self.get_idx = self.put_idx;
293                self.len = 0;
294                dequeue_size
295            }
296            std::cmp::Ordering::Greater => {
297                self.dequeue_slice(&mut out_buffer[0..self.len], self.len)
298            }
299        }
300    }
301
302    /// Returns readable data as one contiguous slice plus an optional wrapped slice.
303    #[must_use]
304    pub fn as_slice(&self) -> (&[T], Option<&[T]>) {
305        if self.get_idx < self.put_idx {
306            (&self.data[self.get_idx..self.put_idx], None)
307        } else {
308            (&self.data[self.get_idx..], Some(&self.data[..self.put_idx]))
309        }
310    }
311
312    /// Returns mutable readable data as one contiguous slice plus an optional wrapped slice.
313    #[must_use]
314    pub fn as_mut_slice(&mut self) -> (&mut [T], Option<&mut [T]>) {
315        if self.get_idx < self.put_idx {
316            (&mut self.data[self.get_idx..self.put_idx], None)
317        } else {
318            let (left, right) = self.data.split_at_mut(self.get_idx);
319            (right, Some(&mut left[..self.put_idx]))
320        }
321    }
322}
323impl<T> IntoIterator for BoundedRingBuffer<T>
324where
325    T: Copy + Default,
326{
327    type Item = T;
328    type IntoIter = IntoIter<T>;
329
330    fn into_iter(self) -> Self::IntoIter {
331        IntoIter(self)
332    }
333}
334
335impl<'a, T> IntoIterator for &'a BoundedRingBuffer<T>
336where
337    T: Copy + Default,
338{
339    type Item = &'a T;
340    type IntoIter = Iter<'a, T>;
341
342    fn into_iter(self) -> Self::IntoIter {
343        self.iter()
344    }
345}
346
347impl<'a, T> IntoIterator for &'a mut BoundedRingBuffer<T>
348where
349    T: Copy + Default,
350{
351    type Item = &'a mut T;
352    type IntoIter = IterMut<'a, T>;
353
354    fn into_iter(self) -> Self::IntoIter {
355        self.iter_mut()
356    }
357}