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                        // update CRC even if there was an error
88                        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        // Because of the footer we have to have already flushed all the data out before we get here
169        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}