async_compression/codec/gzip/
decoder.rs
1use crate::{
2 codec::{
3 gzip::header::{self, Header},
4 Decode,
5 },
6 util::PartialBuffer,
7};
8use std::io::{Error, ErrorKind, Result};
9
10use flate2::Crc;
11
12#[derive(Debug)]
13enum State {
14 Header(header::Parser),
15 Decoding,
16 Footer(PartialBuffer<Vec<u8>>),
17 Done,
18}
19
20#[derive(Debug)]
21pub struct GzipDecoder {
22 inner: crate::codec::FlateDecoder,
23 crc: Crc,
24 state: State,
25 header: Header,
26}
27
28fn check_footer(crc: &Crc, input: &[u8]) -> Result<()> {
29 if input.len() < 8 {
30 return Err(Error::new(
31 ErrorKind::InvalidData,
32 "Invalid gzip footer length",
33 ));
34 }
35
36 let crc_sum = crc.sum().to_le_bytes();
37 let bytes_read = crc.amount().to_le_bytes();
38
39 if crc_sum != input[0..4] {
40 return Err(Error::new(
41 ErrorKind::InvalidData,
42 "CRC computed does not match",
43 ));
44 }
45
46 if bytes_read != input[4..8] {
47 return Err(Error::new(
48 ErrorKind::InvalidData,
49 "amount of bytes read does not match",
50 ));
51 }
52
53 Ok(())
54}
55
56impl GzipDecoder {
57 pub(crate) fn new() -> Self {
58 Self {
59 inner: crate::codec::FlateDecoder::new(false),
60 crc: Crc::new(),
61 state: State::Header(header::Parser::default()),
62 header: Header::default(),
63 }
64 }
65
66 fn process<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
67 &mut self,
68 input: &mut PartialBuffer<I>,
69 output: &mut PartialBuffer<O>,
70 inner: impl Fn(&mut Self, &mut PartialBuffer<I>, &mut PartialBuffer<O>) -> Result<bool>,
71 ) -> Result<bool> {
72 loop {
73 match &mut self.state {
74 State::Header(parser) => {
75 if let Some(header) = parser.input(input)? {
76 self.header = header;
77 self.state = State::Decoding;
78 }
79 }
80
81 State::Decoding => {
82 let prior = output.written().len();
83
84 let res = inner(self, input, output);
85
86 if (output.written().len() > prior) {
87 self.crc.update(&output.written()[prior..]);
89 }
90
91 let done = res?;
92
93 if done {
94 self.state = State::Footer(vec![0; 8].into())
95 }
96 }
97
98 State::Footer(footer) => {
99 footer.copy_unwritten_from(input);
100
101 if footer.unwritten().is_empty() {
102 check_footer(&self.crc, footer.written())?;
103 self.state = State::Done
104 }
105 }
106
107 State::Done => {}
108 };
109
110 if let State::Done = self.state {
111 return Ok(true);
112 }
113
114 if input.unwritten().is_empty() || output.unwritten().is_empty() {
115 return Ok(false);
116 }
117 }
118 }
119}
120
121impl Decode for GzipDecoder {
122 fn reinit(&mut self) -> Result<()> {
123 self.inner.reinit()?;
124 self.crc = Crc::new();
125 self.state = State::Header(header::Parser::default());
126 self.header = Header::default();
127 Ok(())
128 }
129
130 fn decode(
131 &mut self,
132 input: &mut PartialBuffer<impl AsRef<[u8]>>,
133 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
134 ) -> Result<bool> {
135 self.process(input, output, |this, input, output| {
136 this.inner.decode(input, output)
137 })
138 }
139
140 fn flush(
141 &mut self,
142 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
143 ) -> Result<bool> {
144 loop {
145 match self.state {
146 State::Header(_) | State::Footer(_) | State::Done => return Ok(true),
147
148 State::Decoding => {
149 let prior = output.written().len();
150 let done = self.inner.flush(output)?;
151 self.crc.update(&output.written()[prior..]);
152 if done {
153 return Ok(true);
154 }
155 }
156 };
157
158 if output.unwritten().is_empty() {
159 return Ok(false);
160 }
161 }
162 }
163
164 fn finish(
165 &mut self,
166 _output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
167 ) -> Result<bool> {
168 if let State::Done = self.state {
170 Ok(true)
171 } else {
172 Err(Error::new(
173 ErrorKind::UnexpectedEof,
174 "unexpected end of file",
175 ))
176 }
177 }
178}