#[derive(Debug, Clone)]
pub struct RingBuffer<T>
where
T: Copy + Default,
{
data: Vec<T>,
head: usize,
tail: usize,
len: usize,
}
impl<T> RingBuffer<T>
where
T: Copy + Default,
{
pub fn with_capacity(cap: usize) -> Self {
Self {
data: vec![T::default(); cap],
head: 0,
tail: 0,
len: 0,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn capacity(&self) -> usize {
self.data.len()
}
#[inline]
pub fn available(&self) -> usize {
self.capacity().saturating_sub(self.len)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn clear(&mut self) {
self.head = 0;
self.tail = 0;
self.len = 0;
}
#[inline]
pub fn push(&mut self, value: T) -> bool {
if self.len == self.capacity() || self.capacity() == 0 {
return false;
}
self.data[self.tail] = value;
self.tail = (self.tail + 1) % self.capacity();
self.len += 1;
true
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 || self.capacity() == 0 {
return None;
}
let value = self.data[self.head];
self.head = (self.head + 1) % self.capacity();
self.len -= 1;
if self.len == 0 {
self.head = 0;
self.tail = 0;
}
Some(value)
}
pub fn discard(&mut self, n: usize) -> usize {
let to_drop = n.min(self.len);
if to_drop == 0 || self.capacity() == 0 {
return 0;
}
self.head = (self.head + to_drop) % self.capacity();
self.len -= to_drop;
if self.len == 0 {
self.head = 0;
self.tail = 0;
}
to_drop
}
pub fn peek_slice(&self, out: &mut [T]) -> usize {
let to_copy = out.len().min(self.len);
if to_copy == 0 || self.capacity() == 0 {
return 0;
}
let first = to_copy.min(self.capacity() - self.head);
out[..first].copy_from_slice(&self.data[self.head..self.head + first]);
let second = to_copy - first;
if second > 0 {
out[first..first + second].copy_from_slice(&self.data[..second]);
}
to_copy
}
pub fn push_slice(&mut self, input: &[T]) -> usize {
if input.is_empty() || self.capacity() == 0 || self.available() == 0 {
return 0;
}
let to_push = input.len().min(self.available());
let first = to_push.min(self.capacity() - self.tail);
self.data[self.tail..self.tail + first].copy_from_slice(&input[..first]);
self.tail = (self.tail + first) % self.capacity();
let second = to_push - first;
if second > 0 {
self.data[..second].copy_from_slice(&input[first..first + second]);
self.tail = second;
}
self.len += to_push;
to_push
}
pub fn pop_slice(&mut self, output: &mut [T]) -> usize {
if output.is_empty() || self.capacity() == 0 || self.len == 0 {
return 0;
}
let to_pop = output.len().min(self.len);
let first = to_pop.min(self.capacity() - self.head);
output[..first].copy_from_slice(&self.data[self.head..self.head + first]);
self.head = (self.head + first) % self.capacity();
let second = to_pop - first;
if second > 0 {
output[first..first + second].copy_from_slice(&self.data[..second]);
self.head = second;
}
self.len -= to_pop;
if self.len == 0 {
self.head = 0;
self.tail = 0;
}
to_pop
}
}
#[cfg(test)]
mod tests {
use super::RingBuffer;
#[test]
fn push_pop_wrap() {
let mut rb = RingBuffer::<i32>::with_capacity(4);
assert_eq!(rb.push_slice(&[1, 2, 3]), 3);
let mut out = [0; 2];
assert_eq!(rb.pop_slice(&mut out), 2);
assert_eq!(out, [1, 2]);
assert_eq!(rb.push_slice(&[4, 5, 6]), 3);
let mut out2 = [0; 4];
assert_eq!(rb.pop_slice(&mut out2), 4);
assert_eq!(out2, [3, 4, 5, 6]);
}
#[test]
fn bounded_capacity() {
let mut rb = RingBuffer::<f32>::with_capacity(2);
assert_eq!(rb.push_slice(&[1.0, 2.0, 3.0]), 2);
assert_eq!(rb.len(), 2);
assert_eq!(rb.available(), 0);
}
}