compression_codecs/gzip/
decoder.rs1use 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 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 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}