use std::io::{
BufRead,
Error,
ErrorKind,
Result,
};
use std::string::FromUtf8Error;
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>> {
read_until_limited_impl(self, delimiter, max_len)
}
#[inline]
fn read_until_limited_into(
&mut self,
delimiter: u8,
output: &mut Vec<u8>,
max_len: usize,
) -> Result<usize> {
read_until_limited_into_impl(self, delimiter, output, max_len)
}
#[inline]
fn read_line_limited(&mut self, max_len: usize) -> Result<String> {
read_line_limited_impl(self, max_len)
}
#[inline]
fn read_line_limited_into(&mut self, output: &mut String, max_len: usize) -> Result<usize> {
read_line_limited_into_impl(self, output, max_len)
}
#[inline]
fn discard_until_limited(&mut self, delimiter: u8, max_len: usize) -> Result<usize> {
discard_until_limited_impl(self, delimiter, max_len)
}
}
fn read_until_limited_impl<T>(reader: &mut T, delimiter: u8, max_len: usize) -> Result<Vec<u8>>
where
T: BufRead + ?Sized,
{
let mut output = Vec::with_capacity(max_len.min(8192));
read_until_limited_into_impl(reader, delimiter, &mut output, max_len)?;
Ok(output)
}
fn read_until_limited_into_impl<T>(
reader: &mut T,
delimiter: u8,
output: &mut Vec<u8>,
max_len: usize,
) -> Result<usize>
where
T: BufRead + ?Sized,
{
let mut appended = 0;
loop {
let available = reader.fill_buf()?;
if available.is_empty() {
return Ok(appended);
}
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(appended);
if requested > remaining {
if remaining > 0 {
output.extend_from_slice(&available[..remaining]);
reader.consume(remaining);
}
return Err(limit_exceeded_error(max_len, delimiter));
}
output.extend_from_slice(&available[..requested]);
reader.consume(requested);
appended += requested;
if delimiter_position.is_some() {
return Ok(appended);
}
}
}
fn read_line_limited_impl<T>(reader: &mut T, max_len: usize) -> Result<String>
where
T: BufRead + ?Sized,
{
let mut output = String::new();
read_line_limited_into_impl(reader, &mut output, max_len)?;
Ok(output)
}
fn read_line_limited_into_impl<T>(
reader: &mut T,
output: &mut String,
max_len: usize,
) -> Result<usize>
where
T: BufRead + ?Sized,
{
let mut bytes = Vec::with_capacity(max_len.min(8192));
let count = read_until_limited_into_impl(reader, b'\n', &mut bytes, max_len)?;
let line = String::from_utf8(bytes).map_err(invalid_utf8_error)?;
output.push_str(&line);
Ok(count)
}
fn discard_until_limited_impl<T>(reader: &mut T, delimiter: u8, max_len: usize) -> Result<usize>
where
T: BufRead + ?Sized,
{
let mut discarded = 0;
loop {
let available = reader.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 {
reader.consume(remaining);
}
return Err(limit_exceeded_error(max_len, delimiter));
}
reader.consume(requested);
discarded += requested;
if delimiter_position.is_some() {
return Ok(discarded);
}
}
}
fn limit_exceeded_error(max_len: usize, delimiter: u8) -> Error {
Error::new(
ErrorKind::InvalidData,
format!("input exceeds maximum length of {max_len} bytes before delimiter {delimiter}"),
)
}
fn invalid_utf8_error(error: FromUtf8Error) -> Error {
Error::new(
ErrorKind::InvalidData,
format!("limited line is not valid UTF-8: {error}"),
)
}