pub const DEFAULT_LOOKAHEAD_SIZE: usize = 5 * 1024 * 1024;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LookaheadState {
Collecting,
Ready,
Overflow,
}
pub struct LookaheadBuffer {
data: Vec<u8>,
max_size: usize,
state: LookaheadState,
}
impl LookaheadBuffer {
pub fn new(max_size: usize) -> Self {
Self {
data: Vec::with_capacity(max_size.min(DEFAULT_LOOKAHEAD_SIZE)),
max_size,
state: LookaheadState::Collecting,
}
}
pub fn push(&mut self, chunk: &[u8]) -> LookaheadState {
if self.state != LookaheadState::Collecting {
return self.state;
}
if self.data.len() + chunk.len() > self.max_size {
self.state = LookaheadState::Overflow;
return self.state;
}
self.data.extend_from_slice(chunk);
self.state
}
pub fn mark_ready(&mut self) {
if self.state == LookaheadState::Collecting {
self.state = LookaheadState::Ready;
}
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn drain(&mut self) -> Vec<u8> {
self.state = LookaheadState::Collecting;
std::mem::take(&mut self.data)
}
pub fn state(&self) -> LookaheadState {
self.state
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn max_size(&self) -> usize {
self.max_size
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_flow() {
let mut buf = LookaheadBuffer::new(100);
assert_eq!(buf.push(b"hello"), LookaheadState::Collecting);
assert_eq!(buf.push(b"world"), LookaheadState::Collecting);
assert_eq!(buf.len(), 10);
buf.mark_ready();
assert_eq!(buf.state(), LookaheadState::Ready);
assert_eq!(buf.data(), b"helloworld");
}
#[test]
fn test_overflow() {
let mut buf = LookaheadBuffer::new(5);
assert_eq!(buf.push(b"abc"), LookaheadState::Collecting);
assert_eq!(buf.push(b"def"), LookaheadState::Overflow);
}
#[test]
fn test_zero_size() {
let mut buf = LookaheadBuffer::new(0);
assert_eq!(buf.push(b"a"), LookaheadState::Overflow);
}
}