async_compression/codec/gzip/
header.rs

1use crate::util::PartialBuffer;
2use std::io;
3
4#[derive(Debug, Default)]
5struct Flags {
6    ascii: bool,
7    crc: bool,
8    extra: bool,
9    filename: bool,
10    comment: bool,
11}
12
13#[derive(Debug, Default)]
14pub(super) struct Header {
15    flags: Flags,
16}
17
18#[derive(Debug)]
19enum State {
20    Fixed(PartialBuffer<[u8; 10]>),
21    ExtraLen(PartialBuffer<[u8; 2]>),
22    Extra(PartialBuffer<Vec<u8>>),
23    Filename(Vec<u8>),
24    Comment(Vec<u8>),
25    Crc(PartialBuffer<[u8; 2]>),
26    Done,
27}
28
29impl Default for State {
30    fn default() -> Self {
31        State::Fixed(<_>::default())
32    }
33}
34
35#[derive(Debug, Default)]
36pub(super) struct Parser {
37    state: State,
38    header: Header,
39}
40
41impl Header {
42    fn parse(input: &[u8; 10]) -> io::Result<Self> {
43        if input[0..3] != [0x1f, 0x8b, 0x08] {
44            return Err(io::Error::new(
45                io::ErrorKind::InvalidData,
46                "Invalid gzip header",
47            ));
48        }
49
50        let flag = input[3];
51
52        let flags = Flags {
53            ascii: (flag & 0b0000_0001) != 0,
54            crc: (flag & 0b0000_0010) != 0,
55            extra: (flag & 0b0000_0100) != 0,
56            filename: (flag & 0b0000_1000) != 0,
57            comment: (flag & 0b0001_0000) != 0,
58        };
59
60        Ok(Header { flags })
61    }
62}
63
64impl Parser {
65    pub(super) fn input(
66        &mut self,
67        input: &mut PartialBuffer<impl AsRef<[u8]>>,
68    ) -> io::Result<Option<Header>> {
69        loop {
70            match &mut self.state {
71                State::Fixed(data) => {
72                    data.copy_unwritten_from(input);
73
74                    if data.unwritten().is_empty() {
75                        self.header = Header::parse(&data.take().into_inner())?;
76                        self.state = State::ExtraLen(<_>::default());
77                    } else {
78                        return Ok(None);
79                    }
80                }
81
82                State::ExtraLen(data) => {
83                    if !self.header.flags.extra {
84                        self.state = State::Filename(<_>::default());
85                        continue;
86                    }
87
88                    data.copy_unwritten_from(input);
89
90                    if data.unwritten().is_empty() {
91                        let len = u16::from_le_bytes(data.take().into_inner());
92                        self.state = State::Extra(vec![0; usize::from(len)].into());
93                    } else {
94                        return Ok(None);
95                    }
96                }
97
98                State::Extra(data) => {
99                    data.copy_unwritten_from(input);
100
101                    if data.unwritten().is_empty() {
102                        self.state = State::Filename(<_>::default());
103                    } else {
104                        return Ok(None);
105                    }
106                }
107
108                State::Filename(data) => {
109                    if !self.header.flags.filename {
110                        self.state = State::Comment(<_>::default());
111                        continue;
112                    }
113
114                    if let Some(len) = memchr::memchr(0, input.unwritten()) {
115                        data.extend_from_slice(&input.unwritten()[..len]);
116                        input.advance(len + 1);
117                        self.state = State::Comment(<_>::default());
118                    } else {
119                        data.extend_from_slice(input.unwritten());
120                        input.advance(input.unwritten().len());
121                        return Ok(None);
122                    }
123                }
124
125                State::Comment(data) => {
126                    if !self.header.flags.comment {
127                        self.state = State::Crc(<_>::default());
128                        continue;
129                    }
130
131                    if let Some(len) = memchr::memchr(0, input.unwritten()) {
132                        data.extend_from_slice(&input.unwritten()[..len]);
133                        input.advance(len + 1);
134                        self.state = State::Crc(<_>::default());
135                    } else {
136                        data.extend_from_slice(input.unwritten());
137                        input.advance(input.unwritten().len());
138                        return Ok(None);
139                    }
140                }
141
142                State::Crc(data) => {
143                    if !self.header.flags.crc {
144                        self.state = State::Done;
145                        return Ok(Some(std::mem::take(&mut self.header)));
146                    }
147
148                    data.copy_unwritten_from(input);
149
150                    if data.unwritten().is_empty() {
151                        self.state = State::Done;
152                        return Ok(Some(std::mem::take(&mut self.header)));
153                    } else {
154                        return Ok(None);
155                    }
156                }
157
158                State::Done => {
159                    return Err(io::Error::new(
160                        io::ErrorKind::Other,
161                        "parser used after done",
162                    ));
163                }
164            };
165        }
166    }
167}