use core::mem::MaybeUninit;
pub struct RingBuffer<T, const N: usize> {
buf: [MaybeUninit<T>; N],
head: usize,
len: usize,
}
impl<T: Copy, const N: usize> Default for RingBuffer<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T: Copy, const N: usize> RingBuffer<T, N> {
#[must_use]
pub const fn new() -> Self {
Self {
buf: unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() },
head: 0,
len: 0,
}
}
#[inline(always)]
pub const fn push(&mut self, value: T) {
let idx = (self.head + self.len) % N;
self.buf[idx] = MaybeUninit::new(value);
if self.len < N {
self.len += 1;
} else {
self.head = (self.head + 1) % N;
}
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
pub const fn is_full(&self) -> bool {
self.len == N
}
#[inline(always)]
pub const fn capacity(&self) -> usize {
N
}
#[inline(always)]
pub const fn get(&self, index: usize) -> Option<&T> {
if index >= self.len {
return None;
}
let actual = (self.head + index) % N;
Some(unsafe { self.buf[actual].assume_init_ref() })
}
#[inline(always)]
pub const fn last(&self) -> Option<&T> {
if self.len == 0 {
return None;
}
self.get(self.len - 1)
}
#[inline(always)]
pub const fn first(&self) -> Option<&T> {
self.get(0)
}
pub const fn clear(&mut self) {
self.head = 0;
self.len = 0;
}
pub const fn iter(&self) -> RingBufferIter<'_, T, N> {
RingBufferIter {
buf: self,
index: 0,
}
}
}
pub struct RingBufferIter<'a, T, const N: usize> {
buf: &'a RingBuffer<T, N>,
index: usize,
}
impl<'a, T: Copy, const N: usize> IntoIterator for &'a RingBuffer<T, N> {
type Item = &'a T;
type IntoIter = RingBufferIter<'a, T, N>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: Copy, const N: usize> Iterator for RingBufferIter<'a, T, N> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let item = self.buf.get(self.index)?;
self.index += 1;
Some(item)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.buf.len() - self.index;
(remaining, Some(remaining))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_empty() {
let rb: RingBuffer<i32, 4> = RingBuffer::new();
assert!(rb.is_empty());
assert_eq!(rb.len(), 0);
assert_eq!(rb.capacity(), 4);
}
#[test]
fn test_push_and_get() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
rb.push(10);
rb.push(20);
rb.push(30);
assert_eq!(rb.len(), 3);
assert_eq!(*rb.get(0).unwrap(), 10);
assert_eq!(*rb.get(2).unwrap(), 30);
}
#[test]
fn test_wrap_around() {
let mut rb: RingBuffer<i32, 3> = RingBuffer::new();
rb.push(1);
rb.push(2);
rb.push(3);
assert!(rb.is_full());
rb.push(4); assert_eq!(rb.len(), 3);
assert_eq!(*rb.first().unwrap(), 2);
assert_eq!(*rb.last().unwrap(), 4);
}
#[test]
fn test_get_out_of_bounds() {
let mut rb: RingBuffer<u8, 2> = RingBuffer::new();
rb.push(1);
assert!(rb.get(0).is_some());
assert!(rb.get(1).is_none());
}
#[test]
fn test_clear() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
rb.push(1);
rb.push(2);
rb.clear();
assert!(rb.is_empty());
assert_eq!(rb.len(), 0);
}
#[test]
fn test_iter() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
for i in 0..6 {
rb.push(i);
}
let mut iter = rb.iter();
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), Some(&4));
assert_eq!(iter.next(), Some(&5));
assert_eq!(iter.next(), None);
}
#[test]
fn test_first_last() {
let mut rb: RingBuffer<i32, 3> = RingBuffer::new();
assert!(rb.first().is_none());
assert!(rb.last().is_none());
rb.push(10);
assert_eq!(*rb.first().unwrap(), 10);
assert_eq!(*rb.last().unwrap(), 10);
rb.push(20);
assert_eq!(*rb.first().unwrap(), 10);
assert_eq!(*rb.last().unwrap(), 20);
}
#[test]
fn test_single_element_buffer() {
let mut rb: RingBuffer<i32, 1> = RingBuffer::new();
rb.push(1);
assert_eq!(rb.len(), 1);
rb.push(2);
assert_eq!(rb.len(), 1);
assert_eq!(*rb.get(0).unwrap(), 2);
}
#[test]
fn test_default_is_empty() {
let rb: RingBuffer<i32, 8> = RingBuffer::default();
assert!(rb.is_empty());
assert_eq!(rb.len(), 0);
assert_eq!(rb.capacity(), 8);
}
#[test]
fn test_capacity_preserved_after_wrap() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
for i in 0..10 {
rb.push(i);
}
assert_eq!(rb.capacity(), 4);
assert_eq!(rb.len(), 4);
}
#[test]
fn test_iter_into_iter() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
rb.push(10);
rb.push(20);
rb.push(30);
let mut iter = (&rb).into_iter();
assert_eq!(iter.next(), Some(&10));
assert_eq!(iter.next(), Some(&20));
assert_eq!(iter.next(), Some(&30));
assert_eq!(iter.next(), None);
}
#[test]
fn test_iter_size_hint() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
rb.push(1);
rb.push(2);
rb.push(3);
let mut iter = rb.iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
iter.next();
assert_eq!(iter.size_hint(), (2, Some(2)));
}
#[test]
fn test_push_fill_then_overwrite_ordering() {
let mut rb: RingBuffer<i32, 5> = RingBuffer::new();
for i in 0..5 {
rb.push(i * 10);
}
rb.push(100);
rb.push(200);
rb.push(300);
assert_eq!(*rb.first().unwrap(), 30);
assert_eq!(*rb.last().unwrap(), 300);
}
#[test]
fn test_clear_then_reuse() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
rb.push(1);
rb.push(2);
rb.clear();
assert!(rb.is_empty());
rb.push(99);
assert_eq!(rb.len(), 1);
assert_eq!(*rb.get(0).unwrap(), 99);
}
#[test]
fn test_get_all_indices() {
let mut rb: RingBuffer<i32, 4> = RingBuffer::new();
rb.push(10);
rb.push(20);
rb.push(30);
rb.push(40);
for i in 0..4 {
assert!(rb.get(i).is_some(), "index {i} should be valid");
}
assert!(rb.get(4).is_none());
}
#[test]
fn test_iter_empty_buffer() {
let rb: RingBuffer<i32, 4> = RingBuffer::new();
let count = rb.iter().count();
assert_eq!(count, 0);
}
#[test]
fn test_sensor_window_simulation() {
let mut rb: RingBuffer<i32, 8> = RingBuffer::new();
for i in 0..10i32 {
rb.push(i * 100);
}
assert_eq!(rb.len(), 8);
assert_eq!(*rb.first().unwrap(), 200);
assert_eq!(*rb.last().unwrap(), 900);
}
#[test]
fn test_ring_buffer_u8() {
let mut rb: RingBuffer<u8, 4> = RingBuffer::new();
rb.push(1u8);
rb.push(255u8);
assert_eq!(*rb.get(0).unwrap(), 1u8);
assert_eq!(*rb.get(1).unwrap(), 255u8);
}
}