use std::fmt::Debug;
use std::mem::MaybeUninit;
pub struct LookaheadBuf<T: Copy, const CAP: usize> {
slots: [MaybeUninit<T>; CAP],
head: usize,
len: usize,
}
impl<T: Copy, const CAP: usize> Debug for LookaheadBuf<T, CAP> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LookaheadBuf").field("cap", &CAP).field("head", &self.head).field("len", &self.len).finish()
}
}
impl<T: Copy, const CAP: usize> Default for LookaheadBuf<T, CAP> {
fn default() -> Self {
Self::new()
}
}
impl<T: Copy, const CAP: usize> LookaheadBuf<T, CAP> {
#[inline(always)]
pub fn new() -> Self {
debug_assert!(CAP.is_power_of_two(), "LookaheadBuf CAP must be a power of two");
Self { slots: [const { MaybeUninit::uninit() }; CAP], head: 0, len: 0 }
}
#[inline(always)]
pub fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
pub fn is_full(&self) -> bool {
self.len == CAP
}
#[inline(always)]
pub const fn capacity(&self) -> usize {
CAP
}
#[inline(always)]
pub fn push_back(&mut self, value: T) {
assert!(self.len < CAP, "LookaheadBuf overflow: pushed {CAP} tokens without consuming");
let idx = (self.head + self.len) & (CAP - 1);
self.slots[idx] = MaybeUninit::new(value);
self.len += 1;
}
#[inline(always)]
pub fn pop_front(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
let value = unsafe { self.slots[self.head].assume_init() };
self.head = (self.head + 1) & (CAP - 1);
self.len -= 1;
Some(value)
}
#[inline(always)]
pub fn get(&self, n: usize) -> Option<T> {
if n >= self.len {
return None;
}
let idx = (self.head + n) & (CAP - 1);
Some(unsafe { self.slots[idx].assume_init() })
}
#[inline(always)]
pub fn clear(&mut self) {
self.head = 0;
self.len = 0;
}
}
#[cfg(test)]
mod tests {
use super::LookaheadBuf;
#[test]
fn roundtrip_smoke() {
let mut buf: LookaheadBuf<u32, 8> = LookaheadBuf::new();
assert!(buf.is_empty());
for i in 0..6 {
buf.push_back(i);
}
assert_eq!(buf.len(), 6);
assert_eq!(buf.get(0), Some(0));
assert_eq!(buf.get(5), Some(5));
assert_eq!(buf.get(6), None);
for i in 0..6 {
assert_eq!(buf.pop_front(), Some(i));
}
assert!(buf.is_empty());
}
#[test]
fn wraps_around() {
let mut buf: LookaheadBuf<u32, 4> = LookaheadBuf::new();
for i in 0..4 {
buf.push_back(i);
}
assert!(buf.is_full());
for _ in 0..3 {
buf.pop_front();
}
assert_eq!(buf.len(), 1);
buf.push_back(99);
buf.push_back(100);
assert_eq!(buf.get(0), Some(3));
assert_eq!(buf.get(1), Some(99));
assert_eq!(buf.get(2), Some(100));
}
}