use std::io::{self, Read, Write};
pub const FLUSH: &str = "0000";
pub const DELIM: &str = "0001";
pub const RESPONSE_END: &str = "0002";
pub fn write_line(w: &mut impl Write, data: &str) -> io::Result<()> {
let len = 4 + data.len() + 1;
writeln!(w, "{len:04x}{data}")
}
pub fn write_line_to_vec(buf: &mut Vec<u8>, data: &str) -> io::Result<()> {
let len = 4 + data.len() + 1;
let line = format!("{len:04x}{data}\n");
buf.extend_from_slice(line.as_bytes());
Ok(())
}
pub fn write_flush(w: &mut impl Write) -> io::Result<()> {
write!(w, "0000")
}
pub fn write_packet_raw(w: &mut impl Write, payload: &[u8]) -> io::Result<()> {
let total = payload
.len()
.checked_add(4)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "pkt-line payload too large"))?;
if total > 65520 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"pkt-line exceeds maximum size",
));
}
write!(w, "{total:04x}")?;
w.write_all(payload)?;
Ok(())
}
pub fn write_delim(w: &mut impl Write) -> io::Result<()> {
write!(w, "0001")
}
#[derive(Debug, PartialEq, Eq)]
pub enum Packet {
Data(String),
Flush,
Delim,
ResponseEnd,
}
pub fn read_packet(r: &mut impl Read) -> io::Result<Option<Packet>> {
let mut len_buf = [0u8; 4];
match r.read_exact(&mut len_buf) {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e),
}
let len_str =
std::str::from_utf8(&len_buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let len = usize::from_str_radix(len_str, 16)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
match len {
0 => Ok(Some(Packet::Flush)),
1 => Ok(Some(Packet::Delim)),
2 => Ok(Some(Packet::ResponseEnd)),
n if n <= 4 => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid pkt-line length: {n}"),
)),
n => {
let payload_len = n - 4;
let mut buf = vec![0u8; payload_len];
r.read_exact(&mut buf)?;
let s = String::from_utf8_lossy(&buf).into_owned();
Ok(Some(Packet::Data(
s.strip_suffix('\n').unwrap_or(&s).to_owned(),
)))
}
}
}
pub fn read_until_flush_or_delim(r: &mut impl Read) -> io::Result<(Vec<String>, Option<Packet>)> {
let mut lines = Vec::new();
loop {
match read_packet(r)? {
None => return Ok((lines, None)),
Some(Packet::Flush) => return Ok((lines, Some(Packet::Flush))),
Some(Packet::Delim) => return Ok((lines, Some(Packet::Delim))),
Some(Packet::ResponseEnd) => return Ok((lines, Some(Packet::ResponseEnd))),
Some(Packet::Data(s)) => lines.push(s),
}
}
}
pub fn read_data_lines_until_flush(
r: &mut impl Read,
err_not_flush: &str,
) -> io::Result<Vec<String>> {
let mut lines = Vec::new();
loop {
match read_packet(r)? {
None => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
err_not_flush.to_string(),
));
}
Some(Packet::Flush) => return Ok(lines),
Some(Packet::Delim) | Some(Packet::ResponseEnd) => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
err_not_flush.to_string(),
));
}
Some(Packet::Data(s)) => lines.push(s),
}
}
}
pub fn write_sideband_packet(w: &mut impl Write, band: u8, payload: &[u8]) -> io::Result<()> {
let len = 4 + 1 + payload.len();
write!(w, "{len:04x}")?;
w.write_all(&[band])?;
w.write_all(payload)?;
Ok(())
}
pub fn write_sideband_channel1_64k(w: &mut impl Write, payload: &[u8]) -> io::Result<()> {
const MAX_PAYLOAD: usize = 65515;
for chunk in payload.chunks(MAX_PAYLOAD) {
let len = 4 + 1 + chunk.len();
write!(w, "{len:04x}")?;
w.write_all(&[1u8])?;
w.write_all(chunk)?;
}
Ok(())
}
pub fn parse_hex_len(prefix: &[u8]) -> io::Result<usize> {
let s = std::str::from_utf8(prefix)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{e}")))?;
usize::from_str_radix(s, 16)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("invalid length: {e}")))
}
pub fn decode_sideband_primary(mut input: &[u8]) -> io::Result<Vec<u8>> {
let mut out = Vec::new();
while !input.is_empty() {
if input.len() < 4 {
break;
}
let len = parse_hex_len(&input[..4])?;
input = &input[4..];
if len == 0 {
break;
}
if len <= 4 || input.len() < len - 4 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"truncated sideband packet",
));
}
let payload_len = len - 4;
let payload = &input[..payload_len];
input = &input[payload_len..];
if payload.is_empty() {
continue;
}
let band = payload[0];
let data = &payload[1..];
if band == 1 {
out.extend_from_slice(data);
}
}
Ok(out)
}