/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2025 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::collections::{vec_deque::Iter, VecDeque}; /// A ring buffer of fixed capacity #[derive(Default, Eq, PartialEq, Clone, Debug)] pub struct RingBuffer(VecDeque); impl RingBuffer { /// Create a new [`RingBuffer`] with given fixed capacity pub fn new() -> RingBuffer { Self(VecDeque::with_capacity(N)) } /// Push an element to the back of the `RingBuffer`, removing /// the front element in case the buffer is full. pub fn push(&mut self, value: T) { if self.0.len() == N { self.0.pop_front(); } self.0.push_back(value); } /// Returns the current number of items in the buffer pub fn len(&self) -> usize { self.0.len() } /// Returns true if buffer is empty, false otherwise pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Removes and returns the oldest item in the buffer pub fn pop(&mut self) -> Option { self.0.pop_front() } /// Returns a front-to-back iterator pub fn iter(&self) -> Iter<'_, T> { self.0.iter() } /// Returns true if the buffer contains an element equal to the given value pub fn contains(&self, x: &T) -> bool { self.0.contains(x) } /// Provides a reference to the back element, or `None` if empty. pub fn back(&self) -> Option<&T> { self.0.back() } /// Cast the ringbuffer into a vec pub fn to_vec(&self) -> Vec { self.0.iter().cloned().collect() } /// Rearranges the internal storage of this deque so it is one contiguous slice. pub fn make_contiguous(&mut self) -> &mut [T] { self.0.make_contiguous() } } impl std::ops::Index for RingBuffer { type Output = T; #[inline] fn index(&self, index: usize) -> &T { self.0.get(index).expect("Out of bounds access") } } #[cfg(test)] mod tests { use super::*; #[test] fn behaviour() { const BUF_SIZE: usize = 10; let mut buf = RingBuffer::::new(); for i in 0..BUF_SIZE { buf.push(i); } assert!(!buf.is_empty()); assert!(buf.len() == BUF_SIZE); for i in 0..BUF_SIZE { buf.push(i + 10); } assert!(buf.len() == BUF_SIZE); for (i, v) in buf.iter().enumerate() { assert_eq!(*v, i + 10); } } }