Skip to main content

git_zero_core/
pktline.rs

1use crate::{Error, Result, io::Write};
2
3pub const MAX_LENGTH: usize = 65520;
4
5pub fn write(output: &mut impl Write, data: &[u8]) -> Result<()> {
6    write_parts(output, &[data])
7}
8
9/// Write one packet from borrowed parts without a contiguous scratch buffer.
10pub fn write_parts(output: &mut impl Write, parts: &[&[u8]]) -> Result<()> {
11    let length = parts
12        .iter()
13        .try_fold(4_usize, |length, part| length.checked_add(part.len()))
14        .filter(|&n| n <= MAX_LENGTH)
15        .ok_or(Error::InvalidPacket)?;
16    let digits = b"0123456789abcdef";
17    output.write_all(&[
18        digits[(length >> 12) & 15],
19        digits[(length >> 8) & 15],
20        digits[(length >> 4) & 15],
21        digits[length & 15],
22    ])?;
23    for part in parts {
24        output.write_all(part)?;
25    }
26    Ok(())
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum Packet<'a> {
31    Data(&'a [u8]),
32    Flush,
33}
34
35/// Decode one complete four-byte pkt-line prefix without requiring its body.
36/// Zero denotes a flush; reserved control values 1 through 3 are rejected.
37pub fn length(prefix: &[u8]) -> Result<usize> {
38    if prefix.len() != 4 || !prefix.iter().all(u8::is_ascii_hexdigit) {
39        return Err(Error::InvalidPacket);
40    }
41    let prefix = core::str::from_utf8(prefix).map_err(|_| Error::InvalidPacket)?;
42    let length = usize::from_str_radix(prefix, 16).map_err(|_| Error::InvalidPacket)?;
43    if length != 0 && !(4..=MAX_LENGTH).contains(&length) {
44        return Err(Error::InvalidPacket);
45    }
46    Ok(length)
47}
48
49pub fn read<'a>(input: &mut &'a [u8]) -> Result<Option<Packet<'a>>> {
50    if input.is_empty() {
51        return Ok(None);
52    }
53    let prefix = input.get(..4).ok_or(Error::InvalidPacket)?;
54    let length = length(prefix)?;
55    if length == 0 {
56        *input = &input[4..];
57        return Ok(Some(Packet::Flush));
58    }
59    let bytes = input.get(4..length).ok_or(Error::InvalidPacket)?;
60    *input = &input[length..];
61    Ok(Some(Packet::Data(bytes)))
62}