compression_codecs/gzip/
decoder.rs

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