#[repr(align(4))]
pub struct RingBuf<const N: usize> {
buf: core::mem::MaybeUninit<[u8; N]>,
head: usize,
tail: usize,
}
impl<const N: usize> Default for RingBuf<N> {
fn default() -> Self {
Self {
buf: core::mem::MaybeUninit::uninit(),
head: 0,
tail: 0,
}
}
}
impl<const N: usize> RingBuf<N> {
#[inline(always)]
pub fn len(&self) -> usize {
self.head.wrapping_sub(self.tail) % N
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.head == self.tail
}
pub fn push(&mut self, data: &[u8]) -> usize {
let free = N - 1 - self.len();
let n = data.len().min(free);
let ptr = self.buf.as_mut_ptr() as *mut u8;
let mut head = self.head;
for i in 0..n {
unsafe { *ptr.add(head) = *data.get_unchecked(i) };
head += 1;
if head == N {
head = 0;
}
}
self.head = head;
n
}
pub fn peek(&self, n: usize) -> &[u8] {
let available = self.len().min(n);
unsafe {
core::slice::from_raw_parts(self.buf.as_ptr().cast::<u8>().add(self.tail), available)
}
}
pub fn consume(&mut self, n: usize) {
self.tail = (self.tail + n) % N;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let buf = RingBuf::<8>::default();
assert_eq!(buf.len(), 0);
assert_eq!(buf.peek(8), &[]);
}
#[test]
fn push_and_peek() {
let mut buf = RingBuf::<8>::default();
assert_eq!(buf.push(&[1, 2, 3]), 3);
assert_eq!(buf.len(), 3);
assert_eq!(buf.peek(3), &[1, 2, 3]);
}
#[test]
fn consume_advances() {
let mut buf = RingBuf::<8>::default();
buf.push(&[1, 2, 3, 4]);
buf.consume(2);
assert_eq!(buf.len(), 2);
assert_eq!(buf.peek(2), &[3, 4]);
}
#[test]
fn push_wraps() {
let mut buf = RingBuf::<8>::default();
buf.push(&[1, 2, 3, 4, 5]);
buf.consume(4); assert_eq!(buf.push(&[6, 7, 8, 9]), 4); assert_eq!(buf.len(), 5);
assert_eq!(buf.peek(1), &[5]);
}
#[test]
fn full_returns_short() {
let mut buf = RingBuf::<4>::default();
assert_eq!(buf.push(&[1, 2, 3]), 3); assert_eq!(buf.push(&[4]), 0); assert_eq!(buf.len(), 3);
}
#[test]
fn page_aligned_peek_is_contiguous() {
let mut buf = RingBuf::<8>::default();
buf.push(&[1, 2, 3, 4]);
assert_eq!(buf.peek(4), &[1, 2, 3, 4]);
buf.consume(4);
buf.push(&[5, 6, 7, 8]);
assert_eq!(buf.peek(4), &[5, 6, 7, 8]);
buf.consume(4);
buf.push(&[9, 10, 11, 12]);
assert_eq!(buf.peek(4), &[9, 10, 11, 12]);
}
}