use std::collections::VecDeque;
pub const DEFAULT_REPLAY_BYTES: usize = 256 * 1024;
pub struct ByteRing {
capacity: usize,
chunks: VecDeque<Vec<u8>>,
total: usize,
}
impl ByteRing {
pub fn new(capacity: usize) -> Self {
Self { capacity: capacity.max(1), chunks: VecDeque::new(), total: 0 }
}
pub fn push(&mut self, data: &[u8]) {
if data.is_empty() {
return;
}
let chunk: Vec<u8> = if data.len() > self.capacity {
data[data.len() - self.capacity..].to_vec()
} else {
data.to_vec()
};
self.total += chunk.len();
self.chunks.push_back(chunk);
while self.total > self.capacity
&& let Some(old) = self.chunks.pop_front()
{
self.total -= old.len();
}
}
pub fn snapshot(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.total);
for c in &self.chunks {
out.extend_from_slice(c);
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn evicts_oldest_beyond_capacity() {
let mut ring = ByteRing::new(10);
ring.push(b"aaaaa");
ring.push(b"bbbbb");
ring.push(b"ccccc");
assert_eq!(ring.snapshot(), b"bbbbbccccc");
}
#[test]
fn oversized_single_chunk_keeps_tail() {
let mut ring = ByteRing::new(4);
ring.push(b"0123456789");
assert_eq!(ring.snapshot(), b"6789");
}
#[test]
fn order_preserved_across_many_chunks() {
let mut ring = ByteRing::new(6);
for i in 0..10u8 {
ring.push(&[i]);
}
assert_eq!(ring.snapshot(), vec![4, 5, 6, 7, 8, 9]);
}
#[test]
fn empty_push_is_noop() {
let mut ring = ByteRing::new(4);
ring.push(b"");
assert!(ring.snapshot().is_empty());
}
}