pub struct ReadBuf {
buf: Box<[u8]>,
head: usize,
tail: usize,
capacity: usize,
pre_padding: usize,
}
impl ReadBuf {
#[must_use]
pub fn new(capacity: usize, pre_padding: usize, post_padding: usize) -> Self {
let total = pre_padding + capacity + post_padding;
Self {
buf: vec![0u8; total].into_boxed_slice(),
head: pre_padding,
tail: pre_padding,
capacity,
pre_padding,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self::new(capacity, 0, 0)
}
#[inline]
pub fn data(&self) -> &[u8] {
&self.buf[self.head..self.tail]
}
#[inline]
pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.buf[self.head..self.tail]
}
#[inline]
pub fn advance(&mut self, n: usize) {
if n > self.len() {
Self::panic_advance(n, self.len());
}
self.head += n;
if self.head == self.tail {
self.head = self.pre_padding;
self.tail = self.pre_padding;
}
}
#[inline]
pub fn spare(&mut self) -> &mut [u8] {
let end = self.pre_padding + self.capacity;
&mut self.buf[self.tail..end]
}
#[inline]
pub fn filled(&mut self, n: usize) {
let new_tail = self.tail + n;
let end = self.pre_padding + self.capacity;
if new_tail > end {
Self::panic_filled(n, self.tail, end);
}
self.tail = new_tail;
}
#[inline]
pub fn pre_padding_mut(&mut self) -> &mut [u8] {
&mut self.buf[..self.head]
}
#[inline]
pub fn len(&self) -> usize {
self.tail - self.head
}
#[inline]
pub fn is_empty(&self) -> bool {
self.head == self.tail
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn remaining(&self) -> usize {
self.pre_padding + self.capacity - self.tail
}
pub fn clear(&mut self) {
self.head = self.pre_padding;
self.tail = self.pre_padding;
}
#[inline]
pub fn consumed(&self) -> usize {
self.head - self.pre_padding
}
pub fn compact(&mut self) {
if self.head <= self.pre_padding || self.head == self.tail {
return; }
let len = self.tail - self.head;
self.buf.copy_within(self.head..self.tail, self.pre_padding);
self.head = self.pre_padding;
self.tail = self.pre_padding + len;
}
#[cold]
#[inline(never)]
fn panic_advance(n: usize, len: usize) -> ! {
panic!("advance({n}) exceeds buffered data ({len})")
}
#[cold]
#[inline(never)]
fn panic_filled(n: usize, tail: usize, end: usize) -> ! {
panic!("filled({n}) would exceed capacity (tail={tail}, end={end})")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_allocation_size() {
let buf = ReadBuf::new(100, 16, 4);
assert_eq!(buf.buf.len(), 120);
assert_eq!(buf.capacity(), 100);
assert_eq!(buf.len(), 0);
assert_eq!(buf.remaining(), 100);
}
#[test]
fn with_capacity_zero_padding() {
let buf = ReadBuf::with_capacity(4096);
assert_eq!(buf.capacity(), 4096);
assert_eq!(buf.buf.len(), 4096); assert_eq!(buf.remaining(), 4096);
assert_eq!(buf.pre_padding, 0);
}
#[test]
fn spare_filled_data() {
let mut buf = ReadBuf::with_capacity(64);
buf.spare()[..5].copy_from_slice(b"Hello");
buf.filled(5);
assert_eq!(buf.data(), b"Hello");
assert_eq!(buf.len(), 5);
assert_eq!(buf.remaining(), 59);
}
#[test]
fn advance_consumes() {
let mut buf = ReadBuf::with_capacity(64);
buf.spare()[..10].copy_from_slice(b"HelloWorld");
buf.filled(10);
buf.advance(5);
assert_eq!(buf.data(), b"World");
assert_eq!(buf.len(), 5);
}
#[test]
fn advance_auto_reset() {
let mut buf = ReadBuf::with_capacity(64);
buf.spare()[..5].copy_from_slice(b"Hello");
buf.filled(5);
buf.advance(5);
assert!(buf.is_empty());
assert_eq!(buf.remaining(), 64);
}
#[test]
fn data_mut_in_place() {
let mut buf = ReadBuf::with_capacity(64);
buf.spare()[..4].copy_from_slice(&[0x00, 0x01, 0x02, 0x03]);
buf.filled(4);
for b in buf.data_mut().iter_mut() {
*b ^= 0xFF;
}
assert_eq!(buf.data(), &[0xFF, 0xFE, 0xFD, 0xFC]);
}
#[test]
fn pre_padding_mut_accessible() {
let mut buf = ReadBuf::new(64, 16, 4);
buf.spare()[..5].copy_from_slice(b"World");
buf.filled(5);
let padding = buf.pre_padding_mut();
assert_eq!(padding.len(), 16);
padding[12..16].copy_from_slice(b"Hdr:");
buf.advance(3);
assert_eq!(buf.pre_padding_mut().len(), 19); }
#[test]
fn pre_padding_mut_after_advance() {
let mut buf = ReadBuf::new(64, 8, 0);
buf.spare()[..10].copy_from_slice(b"0123456789");
buf.filled(10);
buf.advance(5);
let padding = buf.pre_padding_mut();
assert_eq!(padding.len(), 13);
}
#[test]
fn remaining_tracks() {
let mut buf = ReadBuf::with_capacity(32);
assert_eq!(buf.remaining(), 32);
buf.spare()[..10].copy_from_slice(&[0; 10]);
buf.filled(10);
assert_eq!(buf.remaining(), 22);
buf.advance(10);
assert_eq!(buf.remaining(), 32); }
#[test]
fn clear_resets() {
let mut buf = ReadBuf::with_capacity(64);
buf.spare()[..10].copy_from_slice(&[0; 10]);
buf.filled(10);
buf.clear();
assert!(buf.is_empty());
assert_eq!(buf.remaining(), 64);
}
#[test]
fn spare_when_full() {
let mut buf = ReadBuf::with_capacity(8);
buf.spare()[..8].copy_from_slice(&[0; 8]);
buf.filled(8);
assert!(buf.spare().is_empty());
assert_eq!(buf.remaining(), 0);
}
#[test]
fn zero_length_operations() {
let mut buf = ReadBuf::with_capacity(32);
buf.filled(0);
assert!(buf.is_empty());
buf.advance(0);
assert!(buf.is_empty());
}
#[test]
fn large_capacity_smoke() {
let mut buf = ReadBuf::with_capacity(256 * 1024);
let data: Vec<u8> = (0..1024).map(|i| (i & 0xFF) as u8).collect();
buf.spare()[..1024].copy_from_slice(&data);
buf.filled(1024);
assert_eq!(buf.data(), data.as_slice());
buf.advance(512);
assert_eq!(buf.data(), &data[512..]);
buf.advance(512);
assert!(buf.is_empty());
assert_eq!(buf.remaining(), 256 * 1024);
}
#[test]
fn multiple_write_advance_cycles() {
let mut buf = ReadBuf::with_capacity(32);
for i in 0u8..10 {
buf.spare()[..4].copy_from_slice(&[i; 4]);
buf.filled(4);
assert_eq!(buf.data(), &[i; 4]);
buf.advance(4);
assert!(buf.is_empty());
}
}
#[test]
#[should_panic(expected = "advance")]
fn advance_exceeds_data() {
let mut buf = ReadBuf::with_capacity(32);
buf.spare()[..5].copy_from_slice(b"Hello");
buf.filled(5);
buf.advance(10);
}
#[test]
#[should_panic(expected = "filled")]
fn filled_exceeds_capacity() {
let mut buf = ReadBuf::with_capacity(8);
buf.filled(16);
}
#[test]
fn partial_advance_then_fill() {
let mut buf = ReadBuf::with_capacity(32);
buf.spare()[..10].copy_from_slice(b"0123456789");
buf.filled(10);
buf.advance(5);
assert_eq!(buf.data(), b"56789");
buf.spare()[..3].copy_from_slice(b"ABC");
buf.filled(3);
assert_eq!(buf.data(), b"56789ABC");
}
#[test]
fn head_drift_and_compact() {
let mut buf = ReadBuf::with_capacity(32);
buf.spare()[..20].copy_from_slice(&[0xAA; 20]);
buf.filled(20);
buf.advance(15);
assert_eq!(buf.len(), 5);
assert_eq!(buf.remaining(), 12);
buf.spare()[..12].copy_from_slice(&[0xBB; 12]);
buf.filled(12);
assert_eq!(buf.remaining(), 0); assert_eq!(buf.len(), 17);
buf.advance(10);
assert_eq!(buf.len(), 7);
assert_eq!(buf.remaining(), 0);
assert!(buf.spare().is_empty());
buf.compact();
assert_eq!(buf.len(), 7); assert_eq!(buf.remaining(), 25);
assert_eq!(&buf.data()[..5], &[0xBB; 5]); }
#[test]
fn compact_noop_when_at_front() {
let mut buf = ReadBuf::with_capacity(32);
buf.spare()[..10].copy_from_slice(&[0x42; 10]);
buf.filled(10);
buf.compact();
assert_eq!(buf.len(), 10);
assert_eq!(buf.remaining(), 22);
}
#[test]
fn compact_noop_when_empty() {
let mut buf = ReadBuf::with_capacity(32);
buf.compact(); assert_eq!(buf.remaining(), 32);
}
#[test]
fn with_padding_smoke() {
let mut buf = ReadBuf::new(64, 16, 32);
assert_eq!(buf.buf.len(), 112); assert_eq!(buf.capacity(), 64);
assert_eq!(buf.remaining(), 64);
buf.spare()[..10].copy_from_slice(b"0123456789");
buf.filled(10);
assert_eq!(buf.data(), b"0123456789");
}
}