git_xcrypt/git/
pktline.rs1use std::io::{Read, Write};
8
9use crate::{Error, Result};
10
11pub const MAX_PAYLOAD: usize = 65516;
13
14#[derive(Debug, PartialEq, Eq)]
16pub enum Packet {
17 Data(Vec<u8>),
19 Flush,
21}
22
23pub fn read_packet(input: &mut impl Read) -> Result<Packet> {
29 let mut length = [0u8; 4];
30 input.read_exact(&mut length)?;
31
32 if !length.iter().all(u8::is_ascii_hexdigit) {
36 return Err(Error::Format(
37 "the filter protocol sent a non-hex packet length".into(),
38 ));
39 }
40 let text = std::str::from_utf8(&length)
41 .map_err(|_| Error::Format("the filter protocol sent a non-hex packet length".into()))?;
42 let length = usize::from_str_radix(text, 16)
43 .map_err(|_| Error::Format(format!("the filter protocol sent a bad length `{text}`")))?;
44
45 if length == 0 {
46 return Ok(Packet::Flush);
47 }
48 if length < 4 {
49 return Err(Error::Format(format!(
50 "the filter protocol sent an impossible packet length {length}"
51 )));
52 }
53
54 let mut payload = vec![0u8; length - 4];
55 input.read_exact(&mut payload)?;
56 Ok(Packet::Data(payload))
57}
58
59pub fn write_data(output: &mut impl Write, payload: &[u8]) -> Result<()> {
65 for chunk in payload.chunks(MAX_PAYLOAD) {
66 write!(output, "{:04x}", chunk.len() + 4)?;
67 output.write_all(chunk)?;
68 }
69 Ok(())
70}
71
72pub fn write_flush(output: &mut impl Write) -> Result<()> {
81 output.write_all(b"0000")?;
82 output.flush()?;
83 Ok(())
84}
85
86pub fn read_until_flush(input: &mut impl Read) -> Result<Vec<Vec<u8>>> {
92 let mut items = Vec::new();
93 loop {
94 match read_packet(input)? {
95 Packet::Flush => return Ok(items),
96 Packet::Data(payload) => items.push(payload),
97 }
98 }
99}
100
101pub fn read_content(input: &mut impl Read) -> Result<Vec<u8>> {
107 let mut content = Vec::new();
108 loop {
109 match read_packet(input)? {
110 Packet::Flush => return Ok(content),
111 Packet::Data(payload) => content.extend_from_slice(&payload),
112 }
113 }
114}