compression_codecs/gzip/
decoder.rs

1use super::header;
2use crate::{DecodeV2, FlateDecoder};
3use compression_core::util::{PartialBuffer, WriteBuffer};
4use flate2::Crc;
5use std::io::{Error, ErrorKind, Result};
6
7#[derive(Debug)]
8enum State {
9    Header(header::Parser),
10    Decoding,
11    Footer(PartialBuffer<[u8; 8]>),
12    Done,
13}
14
15#[derive(Debug)]
16pub struct GzipDecoder {
17    inner: FlateDecoder,
18    crc: Crc,
19    state: State,
20}
21
22fn check_footer(crc: &Crc, input: &[u8; 8]) -> Result<()> {
23    let crc_sum = crc.sum().to_le_bytes();
24    let bytes_read = crc.amount().to_le_bytes();
25
26    if crc_sum != input[0..4] {
27        return Err(Error::new(
28            ErrorKind::InvalidData,
29            "CRC computed does not match",
30        ));
31    }
32
33    if bytes_read != input[4..8] {
34        return Err(Error::new(
35            ErrorKind::InvalidData,
36            "amount of bytes read does not match",
37        ));
38    }
39
40    Ok(())
41}
42
43impl Default for GzipDecoder {
44    fn default() -> Self {
45        Self {
46            inner: FlateDecoder::new(false),
47            crc: Crc::new(),
48            state: State::Header(header::Parser::default()),
49        }
50    }
51}
52
53impl GzipDecoder {
54    pub fn new() -> Self {
55        Self::default()
56    }
57
58    fn process(
59        &mut self,
60        input: &mut PartialBuffer<&[u8]>,
61        output: &mut WriteBuffer<'_>,
62        inner: impl Fn(&mut Self, &mut PartialBuffer<&[u8]>, &mut WriteBuffer<'_>) -> Result<bool>,
63    ) -> Result<bool> {
64        loop {
65            match &mut self.state {
66                State::Header(parser) => {
67                    if parser.input(input)?.is_some() {
68                        self.state = State::Decoding;
69                    }
70                }
71
72                State::Decoding => {
73                    let prior = output.written_len();
74
75                    let res = inner(self, input, output);
76
77                    if output.written_len() > prior {
78                        // update CRC even if there was an error
79                        self.crc.update(&output.written()[prior..]);
80                    }
81
82                    let done = res?;
83
84                    if done {
85                        self.state = State::Footer([0; 8].into());
86                    }
87                }
88
89                State::Footer(footer) => {
90                    footer.copy_unwritten_from(input);
91
92                    if footer.unwritten().is_empty() {
93                        check_footer(&self.crc, footer.get_mut())?;
94                        self.state = State::Done;
95                    }
96                }
97
98                State::Done => {}
99            };
100
101            if let State::Done = self.state {
102                return Ok(true);
103            }
104
105            if input.unwritten().is_empty() || output.has_no_spare_space() {
106                return Ok(false);
107            }
108        }
109    }
110}
111
112impl DecodeV2 for GzipDecoder {
113    fn reinit(&mut self) -> Result<()> {
114        self.inner.reinit()?;
115        self.crc.reset();
116        self.state = State::Header(header::Parser::default());
117        Ok(())
118    }
119
120    fn decode(
121        &mut self,
122        input: &mut PartialBuffer<&[u8]>,
123        output: &mut WriteBuffer<'_>,
124    ) -> Result<bool> {
125        self.process(input, output, |this, input, output| {
126            this.inner.decode(input, output)
127        })
128    }
129
130    fn flush(&mut self, output: &mut WriteBuffer<'_>) -> Result<bool> {
131        loop {
132            match self.state {
133                State::Header(_) | State::Footer(_) | State::Done => return Ok(true),
134
135                State::Decoding => {
136                    let prior = output.written_len();
137                    let done = self.inner.flush(output)?;
138                    self.crc.update(&output.written()[prior..]);
139                    if done {
140                        return Ok(true);
141                    }
142                }
143            };
144
145            if output.has_no_spare_space() {
146                return Ok(false);
147            }
148        }
149    }
150
151    fn finish(&mut self, _output: &mut WriteBuffer<'_>) -> Result<bool> {
152        // Because of the footer we have to have already flushed all the data out before we get here
153        if let State::Done = self.state {
154            Ok(true)
155        } else {
156            Err(Error::from(ErrorKind::UnexpectedEof))
157        }
158    }
159}