use alloc::vec::Vec;
const MIN_ALLOC: usize = 4096;
pub(crate) struct Buffer {
data: Vec<u8>,
filled: usize,
pos: usize,
pin: Option<(usize, usize)>,
cap: usize,
eof: bool,
}
impl Buffer {
pub(crate) const fn new(cap: usize) -> Self {
Self {
data: Vec::new(),
filled: 0,
pos: 0,
pin: None,
cap,
eof: false,
}
}
#[inline]
pub(crate) fn unread(&self) -> &[u8] {
self.data.get(self.pos..self.filled).unwrap_or(&[])
}
#[inline]
pub(crate) const fn pos(&self) -> usize {
self.pos
}
#[inline]
pub(crate) fn from(&self, start: usize) -> &[u8] {
self.data.get(start..self.filled).unwrap_or(&[])
}
#[inline]
pub(crate) fn range(&self, start: usize, end: usize) -> &[u8] {
self.data.get(start..end).unwrap_or(&[])
}
#[inline]
pub(crate) fn consume(&mut self, n: usize) {
self.pos = (self.pos + n).min(self.filled);
}
#[inline]
pub(crate) fn pin_at_pos(&mut self) {
self.pin = Some((self.pos, self.pos));
}
#[inline]
pub(crate) fn extend_pin_to_pos(&mut self) {
if let Some((start, _)) = self.pin {
self.pin = Some((start, self.pos));
}
}
#[inline]
pub(crate) fn unpin(&mut self) {
self.pin = None;
}
#[inline]
pub(crate) fn pin(&self) -> usize {
self.pin.map_or(self.pos, |(start, _)| start)
}
fn reclaimable(&self) -> usize {
match self.pin {
Some((start, end)) => start + (self.pos - end),
None => self.pos,
}
}
#[inline]
pub(crate) const fn is_eof(&self) -> bool {
self.eof
}
#[inline]
pub(crate) fn set_eof(&mut self) {
self.eof = true;
}
pub(crate) fn spare(&mut self) -> &mut [u8] {
if self.filled == self.data.len() {
let reclaimable = self.reclaimable();
let len = self.data.len();
if reclaimable > 0 && (len == self.cap || reclaimable >= len / 2) {
self.compact();
} else if len < self.cap {
let grown = len.saturating_mul(2).max(MIN_ALLOC).min(self.cap);
self.data.resize(grown, 0);
} else if reclaimable > 0 {
self.compact();
}
}
let filled = self.filled;
self.data.get_mut(filled..).unwrap_or(&mut [])
}
#[inline]
pub(crate) fn commit(&mut self, n: usize) {
self.filled = (self.filled + n).min(self.data.len());
}
pub(crate) fn feed(&mut self, mut bytes: &[u8]) -> usize {
let mut taken = 0;
while !bytes.is_empty() {
let spare = self.spare();
if spare.is_empty() {
break;
}
let n = spare.len().min(bytes.len());
spare[..n].copy_from_slice(&bytes[..n]);
self.commit(n);
taken += n;
bytes = &bytes[n..];
}
taken
}
fn compact(&mut self) {
let kept = match self.pin {
Some((start, end)) => {
self.data.copy_within(start..end, 0);
self.pin = Some((0, end - start));
end - start
}
None => 0,
};
let live = self.filled - self.pos;
self.data.copy_within(self.pos..self.filled, kept);
self.pos = kept;
self.filled = kept + live;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buffer_never_exceeds_cap() {
let mut b = Buffer::new(10_000);
let big = alloc::vec![7u8; 50_000];
assert_eq!(b.feed(&big), 10_000);
assert!(b.spare().is_empty());
assert_eq!(b.unread().len(), 10_000);
}
#[test]
fn test_buffer_compaction_keeps_pinned_head() {
let mut b = Buffer::new(8192);
assert_eq!(b.feed(b"HEAD"), 4);
b.pin_at_pos();
b.consume(4);
b.extend_pin_to_pos();
let fill = alloc::vec![1u8; 8188];
assert_eq!(b.feed(&fill), 8188);
b.consume(8188);
assert_eq!(b.spare().len(), 8188);
assert_eq!(b.pos(), 4);
assert_eq!(b.range(b.pin(), b.pin() + 4), b"HEAD");
assert_eq!(b.pin(), 0);
b.unpin();
assert_eq!(b.pin(), b.pos());
}
#[test]
fn test_buffer_eof_flag() {
let mut b = Buffer::new(64);
assert!(!b.is_eof());
b.set_eof();
assert!(b.is_eof());
}
}