use std::io::{
BufRead,
Error,
ErrorKind,
Result,
};
use crate::ext::internal::read_ext_impl;
use crate::util::{
try_reserve_string,
try_reserve_vec,
};
pub trait BufReadExt: BufRead {
fn read_until_limited(
&mut self,
delimiter: u8,
max_len: usize,
) -> Result<Vec<u8>>;
fn read_until_limited_into(
&mut self,
delimiter: u8,
output: &mut Vec<u8>,
max_len: usize,
) -> Result<usize>;
fn read_line_limited(&mut self, max_len: usize) -> Result<String>;
fn read_line_limited_into(
&mut self,
output: &mut String,
max_len: usize,
) -> Result<usize>;
fn discard_until_limited(
&mut self,
delimiter: u8,
max_len: usize,
) -> Result<usize>;
}
impl<T> BufReadExt for T
where
T: BufRead + ?Sized,
{
#[inline]
fn read_until_limited(
&mut self,
delimiter: u8,
max_len: usize,
) -> Result<Vec<u8>> {
let mut output = Vec::new();
try_reserve_vec(&mut output, max_len.min(8192))?;
read_ext_impl::read_until_limited_into(
self,
delimiter,
&mut output,
max_len,
)?;
Ok(output)
}
#[inline]
fn read_until_limited_into(
&mut self,
delimiter: u8,
output: &mut Vec<u8>,
max_len: usize,
) -> Result<usize> {
read_ext_impl::read_until_limited_into(self, delimiter, output, max_len)
}
#[inline]
fn read_line_limited(&mut self, max_len: usize) -> Result<String> {
let mut output = String::new();
self.read_line_limited_into(&mut output, max_len)?;
Ok(output)
}
fn read_line_limited_into(
&mut self,
output: &mut String,
max_len: usize,
) -> Result<usize> {
let original_len = output.len();
let mut bytes = Vec::new();
try_reserve_vec(&mut bytes, max_len.min(8192))?;
let result = (|| {
let count = read_ext_impl::read_until_limited_into(
self, b'\n', &mut bytes, max_len,
)?;
let line = String::from_utf8(bytes).map_err(|error| {
Error::new(
ErrorKind::InvalidData,
format!("limited line is not valid UTF-8: {error}"),
)
})?;
try_reserve_string(output, line.len())?;
output.push_str(&line);
Ok(count)
})();
if result.is_err() {
output.truncate(original_len);
}
result
}
fn discard_until_limited(
&mut self,
delimiter: u8,
max_len: usize,
) -> Result<usize> {
let mut discarded = 0;
loop {
let available = self.fill_buf()?;
if available.is_empty() {
return Ok(discarded);
}
let delimiter_position =
available.iter().position(|byte| *byte == delimiter);
let requested = delimiter_position
.map_or(available.len(), |position| position + 1);
let remaining = max_len.saturating_sub(discarded);
if requested > remaining {
if remaining > 0 {
self.consume(remaining);
}
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"input exceeds maximum length of {max_len} bytes before delimiter {delimiter}"
),
));
}
self.consume(requested);
discarded += requested;
if delimiter_position.is_some() {
return Ok(discarded);
}
}
}
}