use std::io;
pub trait LengthDelimitedReadExt {
fn read_delimited(&mut self) -> io::Result<Vec<u8>>;
fn read_str_delimited(&mut self) -> io::Result<String> {
String::from_utf8(self.read_delimited()?).map_err(io::Error::other)
}
}
impl<T: ?Sized + io::Read> LengthDelimitedReadExt for T {
fn read_delimited(&mut self) -> io::Result<Vec<u8>> {
let mut delimiter = [0; std::mem::size_of::<u32>()];
if self.read(&mut delimiter)? < delimiter.len() {
return Err(io::Error::other("Couldn't read delimiter"));
}
let delimiter: usize = u32::from_le_bytes(delimiter)
.try_into()
.map_err(io::Error::other)?;
const CHUNK_SIZE: usize = 0x4000;
let mut result = Vec::with_capacity(std::cmp::min(delimiter, CHUNK_SIZE));
while result.len() < delimiter {
let prev_len = result.len();
result.resize(std::cmp::min(delimiter, prev_len + CHUNK_SIZE), 0);
let size = self.read(&mut result[prev_len..])?;
let expected = result.len() - prev_len;
if size < expected {
return Err(io::Error::other(format!(
"Expected {} bytes but only {} were read",
expected, size
)));
}
}
Ok(result)
}
}
#[cfg(test)]
mod test {
use super::LengthDelimitedReadExt;
use crate::ext::LengthDelimitedWriteExt;
#[test]
fn write_and_read_small_bytes() {
let mut writer = Vec::new();
assert_eq!(writer.write_delimited(&[1, 2, 3, 4, 5]).unwrap(), 9);
let mut reader = &writer[..];
assert_eq!(reader.read_delimited().unwrap(), &[1, 2, 3, 4, 5]);
}
#[test]
fn write_and_read_small_string() {
let mut writer = Vec::new();
assert_eq!(writer.write_str_delimited("hello!").unwrap(), 10);
let mut reader = &writer[..];
assert_eq!(reader.read_str_delimited().unwrap(), "hello!");
}
#[test]
fn write_and_read_large_bytes() {
let mut data = [0; 0x5000];
for (i, byte) in data.iter_mut().enumerate() {
*byte = (i % 255).try_into().unwrap();
}
let mut writer = Vec::new();
assert_eq!(writer.write_delimited(&data[..]).unwrap(), 0x5004);
let mut reader = &writer[..];
assert_eq!(reader.read_delimited().unwrap(), &data);
}
}