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}