Skip to main content

tig_core/
pkt.rs

1//! pkt-line (gitprotocol-common) の符号化・復号。
2//!
3//! pkt-line は「4 桁 16 進の長さ + データ」の列で、長さ 0000 (flush) / 0001
4//! (delim) / 0002 (response-end) は区切りとして予約される。protocol v2 の
5//! request / response は全てこの形式で運ばれる。
6
7use alloc::vec::Vec;
8
9use crate::err::{Error, Result};
10
11/// データ部の最大長 (gitprotocol-common: 65520 - 4)。
12pub const MAX_DATA_LEN: usize = 65_516;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Pkt<'a> {
16    Flush,
17    Delim,
18    ResponseEnd,
19    Data(&'a [u8]),
20}
21
22/// 入力を pkt-line の列として読む iterator。
23pub struct PktReader<'a> {
24    rest: &'a [u8],
25}
26
27impl<'a> PktReader<'a> {
28    pub fn new(data: &'a [u8]) -> Self {
29        Self { rest: data }
30    }
31
32    /// 未消費の残り。
33    pub fn rest(&self) -> &'a [u8] {
34        self.rest
35    }
36
37    /// 次の pkt を読む。入力が尽きたら None。
38    #[allow(clippy::should_implement_trait)]
39    pub fn next(&mut self) -> Option<Result<Pkt<'a>>> {
40        if self.rest.is_empty() {
41            return None;
42        }
43        Some(self.read_one())
44    }
45
46    /// 次の pkt を読む。入力が尽きている場合はエラー。
47    pub fn expect_next(&mut self) -> Result<Pkt<'a>> {
48        self.next().unwrap_or(Err(Error::UnexpectedEof))
49    }
50
51    fn read_one(&mut self) -> Result<Pkt<'a>> {
52        let head = self.rest.get(..4).ok_or(Error::UnexpectedEof)?;
53        let mut len = 0usize;
54        for &c in head {
55            let v = match c {
56                b'0'..=b'9' => c - b'0',
57                b'a'..=b'f' => c - b'a' + 10,
58                b'A'..=b'F' => c - b'A' + 10,
59                _ => return Err(Error::Corrupt("pkt-line length")),
60            };
61            len = len * 16 + usize::from(v);
62        }
63        match len {
64            0 => {
65                self.rest = &self.rest[4..];
66                Ok(Pkt::Flush)
67            }
68            1 => {
69                self.rest = &self.rest[4..];
70                Ok(Pkt::Delim)
71            }
72            2 => {
73                self.rest = &self.rest[4..];
74                Ok(Pkt::ResponseEnd)
75            }
76            3 => Err(Error::Corrupt("pkt-line length 0003")),
77            _ => {
78                let data = self.rest.get(4..len).ok_or(Error::UnexpectedEof)?;
79                self.rest = &self.rest[len..];
80                Ok(Pkt::Data(data))
81            }
82        }
83    }
84}
85
86/// データ pkt を書く。
87pub fn write_data(out: &mut Vec<u8>, data: &[u8]) {
88    debug_assert!(data.len() <= MAX_DATA_LEN);
89    let len = data.len() + 4;
90    const HEX: &[u8; 16] = b"0123456789abcdef";
91    out.push(HEX[(len >> 12) & 0xf]);
92    out.push(HEX[(len >> 8) & 0xf]);
93    out.push(HEX[(len >> 4) & 0xf]);
94    out.push(HEX[len & 0xf]);
95    out.extend_from_slice(data);
96}
97
98/// テキスト行の pkt を書く (LF を付加する)。
99pub fn write_line(out: &mut Vec<u8>, line: &[u8]) {
100    debug_assert!(line.len() < MAX_DATA_LEN);
101    let len = line.len() + 5;
102    const HEX: &[u8; 16] = b"0123456789abcdef";
103    out.push(HEX[(len >> 12) & 0xf]);
104    out.push(HEX[(len >> 8) & 0xf]);
105    out.push(HEX[(len >> 4) & 0xf]);
106    out.push(HEX[len & 0xf]);
107    out.extend_from_slice(line);
108    out.push(b'\n');
109}
110
111pub fn write_flush(out: &mut Vec<u8>) {
112    out.extend_from_slice(b"0000");
113}
114
115pub fn write_delim(out: &mut Vec<u8>) {
116    out.extend_from_slice(b"0001");
117}
118
119/// データ pkt の末尾 LF を取り除く (テキスト行の慣例)。
120pub fn trim_line(data: &[u8]) -> &[u8] {
121    data.strip_suffix(b"\n").unwrap_or(data)
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn roundtrip() {
130        let mut buf = Vec::new();
131        write_line(&mut buf, b"version 2");
132        write_delim(&mut buf);
133        write_data(&mut buf, &[0x01, 0xff]);
134        write_flush(&mut buf);
135
136        let mut r = PktReader::new(&buf);
137        assert_eq!(r.expect_next().unwrap(), Pkt::Data(b"version 2\n"));
138        assert_eq!(r.expect_next().unwrap(), Pkt::Delim);
139        assert_eq!(r.expect_next().unwrap(), Pkt::Data(&[0x01, 0xff]));
140        assert_eq!(r.expect_next().unwrap(), Pkt::Flush);
141        assert!(r.next().is_none());
142    }
143
144    #[test]
145    fn known_encoding() {
146        let mut buf = Vec::new();
147        write_line(&mut buf, b"a");
148        assert_eq!(buf, b"0006a\n");
149        buf.clear();
150        write_flush(&mut buf);
151        assert_eq!(buf, b"0000");
152    }
153
154    #[test]
155    fn truncated_rejected() {
156        let mut r = PktReader::new(b"000");
157        assert_eq!(r.expect_next().unwrap_err(), Error::UnexpectedEof);
158        let mut r = PktReader::new(b"0010short");
159        assert_eq!(r.expect_next().unwrap_err(), Error::UnexpectedEof);
160    }
161
162    #[test]
163    fn bad_length_rejected() {
164        let mut r = PktReader::new(b"00xx");
165        assert!(r.expect_next().is_err());
166        let mut r = PktReader::new(b"0003");
167        assert!(r.expect_next().is_err());
168    }
169}