use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
pub struct RingBuffer<T, const N: usize> {
data: [MaybeUninit<T>; N],
head: usize,
len: usize,
_marker: UnsafeCell<()>,
}
impl<T, const N: usize> RingBuffer<T, N> {
#[inline]
pub fn new() -> Self {
Self {
data: unsafe { MaybeUninit::uninit().assume_init() },
head: 0,
len: 0,
_marker: UnsafeCell::new(()),
}
}
#[inline]
pub fn clear(&mut self) {
for i in 0..self.len {
let idx = (self.head + i) % N;
unsafe {
self.data[idx].assume_init_drop();
}
}
self.head = 0;
self.len = 0;
}
#[inline]
pub fn front(&self) -> Option<&T> {
if self.len == 0 {
None
} else {
Some(unsafe { self.data[self.head].assume_init_ref() })
}
}
#[inline]
pub fn back(&self) -> Option<&T> {
if self.len == 0 {
None
} else {
let idx = (self.head + self.len - 1) % N;
Some(unsafe { self.data[idx].assume_init_ref() })
}
}
#[inline]
pub fn push_back(&mut self, value: T) {
assert!(
self.len < N,
"RingBuffer overflow: len={}, N={}",
self.len,
N
);
let idx = (self.head + self.len) % N;
self.data[idx] = MaybeUninit::new(value);
self.len += 1;
}
#[inline]
pub fn pop_front(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
let value = unsafe { self.data[self.head].assume_init_read() };
self.head = (self.head + 1) % N;
self.len -= 1;
Some(value)
}
}
#[inline]
pub fn pop_back(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
let idx = (self.head + self.len) % N;
Some(unsafe { self.data[idx].assume_init_read() })
}
}
}
impl<T, const N: usize> Default for RingBuffer<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> Drop for RingBuffer<T, N> {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_buffer_is_empty() {
let buf: RingBuffer<i32, 8> = RingBuffer::new();
assert_eq!(buf.front(), None);
assert_eq!(buf.back(), None);
}
#[test]
fn test_push_back_and_front() {
let mut buf: RingBuffer<i32, 8> = RingBuffer::new();
buf.push_back(1);
buf.push_back(2);
buf.push_back(3);
assert_eq!(buf.front(), Some(&1));
assert_eq!(buf.back(), Some(&3));
}
#[test]
fn test_pop_front() {
let mut buf: RingBuffer<i32, 8> = RingBuffer::new();
buf.push_back(1);
buf.push_back(2);
buf.push_back(3);
assert_eq!(buf.pop_front(), Some(1));
assert_eq!(buf.pop_front(), Some(2));
assert_eq!(buf.front(), Some(&3));
}
#[test]
fn test_pop_back() {
let mut buf: RingBuffer<i32, 8> = RingBuffer::new();
buf.push_back(1);
buf.push_back(2);
buf.push_back(3);
assert_eq!(buf.pop_back(), Some(3));
assert_eq!(buf.pop_back(), Some(2));
assert_eq!(buf.back(), Some(&1));
}
#[test]
fn test_clear() {
let mut buf: RingBuffer<i32, 8> = RingBuffer::new();
buf.push_back(1);
buf.push_back(2);
buf.clear();
assert_eq!(buf.front(), None);
assert_eq!(buf.back(), None);
}
#[test]
fn test_wraparound() {
let mut buf: RingBuffer<i32, 4> = RingBuffer::new();
buf.push_back(1);
buf.push_back(2);
buf.push_back(3);
buf.pop_front(); buf.pop_front(); buf.push_back(4);
buf.push_back(5);
assert_eq!(buf.pop_front(), Some(3));
assert_eq!(buf.pop_front(), Some(4));
assert_eq!(buf.pop_front(), Some(5));
assert_eq!(buf.front(), None);
}
#[test]
fn test_tuple_type() {
let mut buf: RingBuffer<(usize, u64), 256> = RingBuffer::new();
buf.push_back((0, 12345));
buf.push_back((1, 67890));
assert_eq!(buf.front(), Some(&(0, 12345)));
assert_eq!(buf.back(), Some(&(1, 67890)));
assert_eq!(buf.pop_front(), Some((0, 12345)));
assert_eq!(buf.front(), Some(&(1, 67890)));
}
#[test]
fn test_monotonic_deque_pattern() {
let mut buf: RingBuffer<(usize, u64), 8> = RingBuffer::new();
let window_size = 4;
let hashes = [50u64, 30, 40, 20, 60, 10, 70, 80, 15, 25];
for (pos, &hash) in hashes.iter().enumerate() {
while let Some(&(p, _)) = buf.front() {
if p + window_size <= pos {
buf.pop_front();
} else {
break;
}
}
while let Some(&(_, v)) = buf.back() {
if v >= hash {
buf.pop_back();
} else {
break;
}
}
buf.push_back((pos, hash));
if pos >= window_size - 1 {
let min = buf.front().unwrap();
let window_start = pos.saturating_sub(window_size - 1);
let actual_min = hashes[window_start..=pos].iter().min().unwrap();
assert_eq!(min.1, *actual_min);
}
}
}
}