darkfi/util/
ringbuffer.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::collections::{vec_deque::Iter, VecDeque};
20
21/// A ring buffer of fixed capacity
22#[derive(Default, Eq, PartialEq, Clone, Debug)]
23pub struct RingBuffer<T, const N: usize>(VecDeque<T>);
24
25impl<T: Eq + PartialEq + Clone, const N: usize> RingBuffer<T, N> {
26    /// Create a new [`RingBuffer`] with given fixed capacity
27    pub fn new() -> RingBuffer<T, N> {
28        Self(VecDeque::with_capacity(N))
29    }
30
31    /// Push an element to the back of the `RingBuffer`, removing
32    /// the front element in case the buffer is full.
33    pub fn push(&mut self, value: T) {
34        if self.0.len() == N {
35            self.0.pop_front();
36        }
37        self.0.push_back(value);
38    }
39
40    /// Returns the current number of items in the buffer
41    pub fn len(&self) -> usize {
42        self.0.len()
43    }
44
45    /// Returns true if buffer is empty, false otherwise
46    pub fn is_empty(&self) -> bool {
47        self.0.is_empty()
48    }
49
50    /// Removes and returns the oldest item in the buffer
51    pub fn pop(&mut self) -> Option<T> {
52        self.0.pop_front()
53    }
54
55    /// Returns a front-to-back iterator
56    pub fn iter(&self) -> Iter<'_, T> {
57        self.0.iter()
58    }
59
60    /// Returns true if the buffer contains an element equal to the given value
61    pub fn contains(&self, x: &T) -> bool {
62        self.0.contains(x)
63    }
64
65    /// Provides a reference to the back element, or `None` if empty.
66    pub fn back(&self) -> Option<&T> {
67        self.0.back()
68    }
69
70    /// Cast the ringbuffer into a vec
71    pub fn to_vec(&self) -> Vec<T> {
72        self.0.iter().cloned().collect()
73    }
74
75    /// Rearranges the internal storage of this deque so it is one contiguous slice.
76    pub fn make_contiguous(&mut self) -> &mut [T] {
77        self.0.make_contiguous()
78    }
79}
80
81impl<T, const N: usize> std::ops::Index<usize> for RingBuffer<T, N> {
82    type Output = T;
83
84    #[inline]
85    fn index(&self, index: usize) -> &T {
86        self.0.get(index).expect("Out of bounds access")
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn behaviour() {
96        const BUF_SIZE: usize = 10;
97        let mut buf = RingBuffer::<usize, BUF_SIZE>::new();
98
99        for i in 0..BUF_SIZE {
100            buf.push(i);
101        }
102
103        assert!(!buf.is_empty());
104        assert!(buf.len() == BUF_SIZE);
105
106        for i in 0..BUF_SIZE {
107            buf.push(i + 10);
108        }
109
110        assert!(buf.len() == BUF_SIZE);
111
112        for (i, v) in buf.iter().enumerate() {
113            assert_eq!(*v, i + 10);
114        }
115    }
116}