use crate::NetError;
pub const DEFAULT_MAX_LINE_BYTES: usize = 64 * 1024;
#[derive(Debug)]
pub struct LineDecoder {
buf: Vec<u8>,
max_line_bytes: Option<usize>,
}
impl Default for LineDecoder {
fn default() -> Self {
Self::with_max_line_bytes(DEFAULT_MAX_LINE_BYTES)
}
}
impl LineDecoder {
pub fn new() -> Self {
Self::default()
}
pub fn unbounded() -> Self {
Self {
buf: Vec::new(),
max_line_bytes: None,
}
}
pub fn with_max_line_bytes(max_line_bytes: usize) -> Self {
Self {
buf: Vec::new(),
max_line_bytes: Some(max_line_bytes),
}
}
pub fn push(&mut self, bytes: &[u8]) -> Vec<Vec<u8>> {
self.push_checked(bytes)
.expect("line decoder cap exceeded; use push_checked for fallible handling")
}
pub fn push_checked(&mut self, bytes: &[u8]) -> Result<Vec<Vec<u8>>, NetError> {
self.check_append(bytes)?;
self.buf.extend_from_slice(bytes);
Ok(self.drain_complete_lines())
}
pub fn flush(&mut self) -> Option<Vec<u8>> {
self.flush_checked()
.expect("line decoder cap exceeded; use flush_checked for fallible handling")
}
pub fn flush_checked(&mut self) -> Result<Option<Vec<u8>>, NetError> {
self.check_len(self.buf.len())?;
if self.buf.is_empty() {
return Ok(None);
}
let mut line = std::mem::take(&mut self.buf);
if line.last() == Some(&b'\r') {
line.pop();
}
Ok(Some(line))
}
pub fn buffered_len(&self) -> usize {
self.buf.len()
}
pub fn buffered(&self) -> &[u8] {
&self.buf
}
pub fn has_buffered(&self) -> bool {
!self.buf.is_empty()
}
fn check_append(&self, bytes: &[u8]) -> Result<(), NetError> {
let Some(max) = self.max_line_bytes else {
return Ok(());
};
self.check_len(self.buf.len())?;
let mut line_len = self.buf.len();
for byte in bytes {
if *byte == b'\n' {
line_len = 0;
} else {
line_len = line_len.saturating_add(1);
if line_len > max {
return Err(NetError::LineTooLong { max });
}
}
}
Ok(())
}
fn check_len(&self, len: usize) -> Result<(), NetError> {
if let Some(max) = self.max_line_bytes
&& len > max
{
return Err(NetError::LineTooLong { max });
}
Ok(())
}
fn drain_complete_lines(&mut self) -> Vec<Vec<u8>> {
let mut lines = Vec::new();
while let Some(index) = self.buf.iter().position(|byte| *byte == b'\n') {
let mut line = self.buf.drain(..=index).collect::<Vec<u8>>();
line.pop(); if line.last() == Some(&b'\r') {
line.pop();
}
lines.push(line);
}
lines
}
}