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}