use std::io::{Read, Write};
use crate::{Error, Result};
pub const MAX_PAYLOAD: usize = 65516;
#[derive(Debug, PartialEq, Eq)]
pub enum Packet {
Data(Vec<u8>),
Flush,
}
pub fn read_packet(input: &mut impl Read) -> Result<Packet> {
let mut length = [0u8; 4];
input.read_exact(&mut length)?;
if !length.iter().all(u8::is_ascii_hexdigit) {
return Err(Error::Format(
"the filter protocol sent a non-hex packet length".into(),
));
}
let text = std::str::from_utf8(&length)
.map_err(|_| Error::Format("the filter protocol sent a non-hex packet length".into()))?;
let length = usize::from_str_radix(text, 16)
.map_err(|_| Error::Format(format!("the filter protocol sent a bad length `{text}`")))?;
if length == 0 {
return Ok(Packet::Flush);
}
if length < 4 {
return Err(Error::Format(format!(
"the filter protocol sent an impossible packet length {length}"
)));
}
let mut payload = vec![0u8; length - 4];
input.read_exact(&mut payload)?;
Ok(Packet::Data(payload))
}
pub fn write_data(output: &mut impl Write, payload: &[u8]) -> Result<()> {
for chunk in payload.chunks(MAX_PAYLOAD) {
write!(output, "{:04x}", chunk.len() + 4)?;
output.write_all(chunk)?;
}
Ok(())
}
pub fn write_flush(output: &mut impl Write) -> Result<()> {
output.write_all(b"0000")?;
output.flush()?;
Ok(())
}
pub fn read_until_flush(input: &mut impl Read) -> Result<Vec<Vec<u8>>> {
let mut items = Vec::new();
loop {
match read_packet(input)? {
Packet::Flush => return Ok(items),
Packet::Data(payload) => items.push(payload),
}
}
}
pub fn read_content(input: &mut impl Read) -> Result<Vec<u8>> {
let mut content = Vec::new();
loop {
match read_packet(input)? {
Packet::Flush => return Ok(content),
Packet::Data(payload) => content.extend_from_slice(&payload),
}
}
}