pub struct RingBuffer<T, const N: usize> {
inner: [T; N],
prod_idx: usize,
cons_idx: usize,
}
impl<T, const N: usize> RingBuffer<T, N> {
pub fn new(init: [T; N]) -> Self {
assert!(N > 1);
Self {
inner: init,
prod_idx: 0,
cons_idx: 0,
}
}
pub fn reset(&mut self) {
self.prod_idx = 0;
self.cons_idx = 0;
}
fn prod_avail(&self) -> bool {
unsafe {
match self.prod_idx < self.cons_idx {
true => self.prod_idx.unchecked_add(1) != self.cons_idx,
false => N.unchecked_sub(self.prod_idx).unchecked_add(self.cons_idx) != 1,
}
}
}
pub fn cons_avail(&self) -> bool {
self.cons_idx != self.prod_idx
}
fn advance(target: &mut usize) {
*target += 1;
if *target == N {
*target = 0;
}
}
pub fn push(&mut self, item: T) -> bool {
if self.prod_avail() {
self.inner[self.prod_idx] = item;
Self::advance(&mut self.prod_idx);
true
} else {
false
}
}
pub fn peek(&self) -> Option<&T> {
self.cons_avail().then(|| &self.inner[self.cons_idx])
}
pub fn advance_cons(&mut self) {
Self::advance(&mut self.cons_idx);
}
}
#[cfg(test)]
mod tests {
use crate::buffer::RingBuffer;
const LEN: usize = 64;
#[test]
fn test2() {
let mut buffer = RingBuffer::new([0; 2]);
assert!(buffer.push(42));
assert!(!buffer.prod_avail());
assert!(buffer.cons_avail());
assert_eq!(buffer.peek(), Some(&42));
buffer.advance_cons();
assert!(buffer.prod_avail());
assert!(!buffer.cons_avail());
}
#[test]
fn test_buffer() {
let mut buffer = RingBuffer::new([0; LEN]);
assert!(buffer.prod_avail());
assert!(!buffer.cons_avail());
assert!(buffer.push(1));
assert!(buffer.prod_avail());
assert!(buffer.cons_avail());
assert!(buffer.peek().is_some());
assert!(buffer.prod_avail());
assert!(buffer.cons_avail());
buffer.advance_cons();
assert!(buffer.prod_avail());
assert!(!buffer.cons_avail());
for i in 0..LEN - 1 {
assert!(buffer.push(i));
}
assert!(!buffer.prod_avail());
assert!(buffer.cons_avail());
for i in 0..LEN - 1 {
assert_eq!(buffer.peek(), Some(&i));
buffer.advance_cons();
}
assert!(buffer.prod_avail());
assert!(!buffer.cons_avail());
}
#[test]
fn test_wrap() {
let mut buffer = RingBuffer::new([0; LEN]);
for i in 0..LEN / 2 {
assert!(buffer.push(i))
}
assert_eq!(buffer.prod_idx, LEN / 2);
for _ in 0..LEN / 2 {
buffer.advance_cons();
}
assert_eq!(buffer.prod_idx, buffer.cons_idx);
for i in 0..LEN - 1 {
assert!(buffer.push(i))
}
assert_eq!(buffer.prod_idx, LEN / 2 - 1);
for _ in 0..LEN - 1 {
buffer.advance_cons();
}
assert_eq!(buffer.prod_idx, buffer.cons_idx);
}
}