use core::mem::MaybeUninit;
pub struct RingBuf<T: Copy, const N: usize> {
buf: [MaybeUninit<T>; N],
head: usize,
len: usize,
}
impl<T: Copy, const N: usize> RingBuf<T, N> {
pub const fn new() -> Self {
const {
assert!(N > 0, "RingBuf capacity N must be > 0");
}
Self {
buf: [const { MaybeUninit::uninit() }; N],
head: 0,
len: 0,
}
}
pub fn push(&mut self, val: T) {
self.buf[self.head] = MaybeUninit::new(val);
self.head = (self.head + 1) % N;
if self.len < N {
self.len += 1;
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn is_full(&self) -> bool {
self.len == N
}
#[inline]
pub const fn capacity(&self) -> usize {
N
}
pub fn clear(&mut self) {
self.head = 0;
self.len = 0;
}
#[inline(always)]
const fn index(&self, i: usize) -> usize {
let back = self.len - i; if self.head >= back {
self.head - back
} else {
N - (back - self.head)
}
}
pub fn get(&self, i: usize) -> Option<T> {
if i >= self.len {
return None;
}
let idx = self.index(i);
Some(unsafe { self.buf[idx].assume_init() })
}
pub fn latest(&self) -> Option<T> {
if self.len == 0 {
return None;
}
let idx = self.index(self.len - 1);
Some(unsafe { self.buf[idx].assume_init() })
}
pub fn iter(&self) -> RingIter<'_, T, N> {
RingIter { ring: self, pos: 0 }
}
}
impl<T: Copy, const N: usize> Default for RingBuf<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T: Copy, const N: usize> core::fmt::Debug for RingBuf<T, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RingBuf")
.field("len", &self.len)
.field("capacity", &N)
.finish()
}
}
pub struct RingIter<'a, T: Copy, const N: usize> {
ring: &'a RingBuf<T, N>,
pos: usize,
}
impl<'a, T: Copy, const N: usize> Iterator for RingIter<'a, T, N> {
type Item = T;
fn next(&mut self) -> Option<T> {
let val = self.ring.get(self.pos)?;
self.pos += 1;
Some(val)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.ring.len().saturating_sub(self.pos);
(remaining, Some(remaining))
}
}
impl<T: Copy, const N: usize> ExactSizeIterator for RingIter<'_, T, N> {}
impl<T: Copy, const N: usize> core::fmt::Debug for RingIter<'_, T, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RingIter")
.field("remaining", &(self.ring.len().saturating_sub(self.pos)))
.finish()
}
}
impl<'a, T: Copy, const N: usize> IntoIterator for &'a RingBuf<T, N> {
type Item = T;
type IntoIter = RingIter<'a, T, N>;
fn into_iter(self) -> RingIter<'a, T, N> {
self.iter()
}
}
impl<T: Copy, const N: usize> crate::traits::Sink<T> for RingBuf<T, N> {
type Error = core::convert::Infallible;
#[inline]
fn try_push(&mut self, val: T) -> Result<(), core::convert::Infallible> {
self.push(val);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct NoDefault(u32);
#[test]
fn new_ring_is_empty() {
let r = RingBuf::<u32, 4>::new();
assert!(r.is_empty());
assert!(!r.is_full());
assert_eq!(r.len(), 0);
assert_eq!(r.latest(), None);
assert_eq!(r.get(0), None);
}
#[test]
fn push_and_get() {
let mut r = RingBuf::<u32, 4>::new();
r.push(10);
r.push(20);
r.push(30);
assert_eq!(r.len(), 3);
assert_eq!(r.get(0), Some(10));
assert_eq!(r.get(1), Some(20));
assert_eq!(r.get(2), Some(30));
assert_eq!(r.get(3), None);
assert_eq!(r.latest(), Some(30));
}
#[test]
fn overwrite_oldest_when_full() {
let mut r = RingBuf::<u32, 3>::new();
r.push(1);
r.push(2);
r.push(3);
assert!(r.is_full());
r.push(4); assert_eq!(r.len(), 3);
assert_eq!(r.get(0), Some(2));
assert_eq!(r.get(1), Some(3));
assert_eq!(r.get(2), Some(4));
assert_eq!(r.latest(), Some(4));
}
#[test]
fn clear_resets_state() {
let mut r = RingBuf::<u32, 4>::new();
r.push(1);
r.push(2);
r.clear();
assert!(r.is_empty());
assert_eq!(r.len(), 0);
assert_eq!(r.latest(), None);
}
#[test]
fn iter_oldest_to_newest() {
let mut r = RingBuf::<u32, 4>::new();
for i in 1..=6 {
r.push(i);
}
let v: std::vec::Vec<u32> = r.iter().collect();
assert_eq!(v, [3, 4, 5, 6]);
}
#[test]
fn iter_exact_size() {
let mut r = RingBuf::<u32, 4>::new();
r.push(1);
r.push(2);
let it = r.iter();
assert_eq!(it.len(), 2);
}
#[test]
fn default_is_new() {
let r: RingBuf<u8, 8> = RingBuf::default();
assert!(r.is_empty());
}
#[test]
fn const_new_works_in_const_context() {
const EMPTY: RingBuf<u32, 4> = RingBuf::new();
static LOG: RingBuf<u32, 8> = RingBuf::new();
assert!(EMPTY.is_empty());
assert_eq!(EMPTY.capacity(), 4);
assert!(LOG.is_empty());
assert_eq!(LOG.capacity(), 8);
let mut r = EMPTY;
r.push(1);
assert_eq!(r.get(0), Some(1));
}
#[test]
fn huge_capacity_does_not_overflow_the_index() {
let mut r = RingBuf::<(), { usize::MAX }>::new();
r.push(());
assert_eq!(r.len(), 1);
assert_eq!(r.get(0), Some(()));
assert_eq!(r.latest(), Some(()));
r.push(());
assert_eq!(r.get(1), Some(()));
assert_eq!(r.latest(), Some(()));
}
#[test]
fn works_without_default_bound() {
let mut r = RingBuf::<NoDefault, 2>::new();
r.push(NoDefault(1));
r.push(NoDefault(2));
assert_eq!(r.get(0), Some(NoDefault(1)));
assert_eq!(r.latest(), Some(NoDefault(2)));
r.push(NoDefault(3)); assert_eq!(r.get(0), Some(NoDefault(2)));
assert_eq!(r.latest(), Some(NoDefault(3)));
}
#[test]
fn capacity_returns_n() {
let r = RingBuf::<u32, 8>::new();
assert_eq!(r.capacity(), 8);
}
#[test]
fn into_iter_for_ref() {
let mut r = RingBuf::<u32, 4>::new();
r.push(1);
r.push(2);
r.push(3);
let v: std::vec::Vec<u32> = (&r).into_iter().collect();
assert_eq!(v, [1, 2, 3]);
}
}