/src/image-png/src/decoder/zlib.rs
Line | Count | Source |
1 | | use super::{stream::FormatErrorInner, unfiltering_buffer::UnfilteringBuffer, DecodingError}; |
2 | | |
3 | | use fdeflate::Decompressor; |
4 | | |
5 | | /// [PNG spec](https://www.w3.org/TR/2003/REC-PNG-20031110/#10Compression) says that |
6 | | /// "deflate/inflate compression with a sliding window (which is an upper bound on the |
7 | | /// distances appearing in the deflate stream) of at most 32768 bytes". |
8 | | /// |
9 | | /// `fdeflate` requires that we keep this many most recently decompressed bytes in the |
10 | | /// `out_buffer` - this allows referring back to them when handling "length and distance |
11 | | /// codes" in the deflate stream). |
12 | | const LOOKBACK_SIZE: usize = 32768; |
13 | | |
14 | | /// A buffer for decompression and in-place filtering of PNG rowlines. |
15 | | /// |
16 | | /// The underlying data structure is a vector, with additional markers dividing |
17 | | /// the vector into specific regions of bytes - see [`UnfilterRegion`] for more |
18 | | /// details. |
19 | | pub struct UnfilterBuf<'data> { |
20 | | /// The data container. |
21 | | pub(crate) buffer: &'data mut [u8], |
22 | | /// The past-the-end index of the region that is allowed to be modified. |
23 | | pub(crate) available: &'data mut usize, |
24 | | /// The past-the-end index of the region with decompressed bytes. |
25 | | pub(crate) filled: &'data mut usize, |
26 | | } |
27 | | |
28 | | /// `UnfilterRegion` divides a `Vec<u8>` buffer into three consecutive regions: |
29 | | /// |
30 | | /// * `vector[0..available]` - bytes that may be mutated (this typically means |
31 | | /// bytes that were decompressed earlier, but user of the buffer may also use |
32 | | /// this region for storing other data) |
33 | | /// * `vector[available..filled]` - already decompressed bytes that need to be |
34 | | /// preserved. (Future decompressor calls may reference and copy bytes from |
35 | | /// this region. The maximum `filled - available` "look back" distance for |
36 | | /// [PNG compression method 0](https://www.w3.org/TR/png-3/#10CompressionCM0) |
37 | | /// is 32768 bytes) |
38 | | /// * `vector[filled..]` - buffer where future decompressor calls can write |
39 | | /// additional decompressed bytes |
40 | | /// |
41 | | /// Even though only `vector[0..available]` bytes can be mutated, it is allowed |
42 | | /// to "shift" or "move" the contents of vector, as long as the: |
43 | | /// |
44 | | /// * `vector[available..filled]` bytes are preserved |
45 | | /// * `available` and `filled` offsets are updated |
46 | | /// |
47 | | /// Violating the invariants described above (e.g. mutating the bytes in the |
48 | | /// `vector[available..filled]` region) may result in absurdly wacky |
49 | | /// decompression output or panics, but not undefined behavior. |
50 | | #[derive(Default, Clone, Copy)] |
51 | | pub struct UnfilterRegion { |
52 | | /// The past-the-end index of the region that is allowed to be modified. |
53 | | pub available: usize, |
54 | | /// The past-the-end index of the region with decompressed bytes. |
55 | | pub filled: usize, |
56 | | } |
57 | | |
58 | | /// Ergonomics wrapper around `miniz_oxide::inflate::stream` for zlib compressed data. |
59 | | pub(super) struct ZlibStream { |
60 | | /// Current decoding state. |
61 | | state: Box<fdeflate::Decompressor>, |
62 | | /// If there has been a call to decompress already. |
63 | | started: bool, |
64 | | /// Ignore and do not calculate the Adler-32 checksum. Defaults to `true`. |
65 | | /// |
66 | | /// This flag overrides `TINFL_FLAG_COMPUTE_ADLER32`. |
67 | | /// |
68 | | /// This flag should not be modified after decompression has started. |
69 | | ignore_adler32: bool, |
70 | | } |
71 | | |
72 | | impl ZlibStream { |
73 | 42.5k | pub(crate) fn new() -> Self { |
74 | 42.5k | ZlibStream { |
75 | 42.5k | state: Box::new(Decompressor::new()), |
76 | 42.5k | started: false, |
77 | 42.5k | ignore_adler32: true, |
78 | 42.5k | } |
79 | 42.5k | } |
80 | | |
81 | 4.14k | pub(crate) fn reset(&mut self) { |
82 | 4.14k | self.started = false; |
83 | 4.14k | *self.state = Decompressor::new(); |
84 | 4.14k | } |
85 | | |
86 | | /// Set the `ignore_adler32` flag and return `true` if the flag was |
87 | | /// successfully set. |
88 | | /// |
89 | | /// The default is `true`. |
90 | | /// |
91 | | /// This flag cannot be modified after decompression has started until the |
92 | | /// [ZlibStream] is reset. |
93 | 42.5k | pub(crate) fn set_ignore_adler32(&mut self, flag: bool) -> bool { |
94 | 42.5k | if !self.started { |
95 | 42.5k | self.ignore_adler32 = flag; |
96 | 42.5k | true |
97 | | } else { |
98 | 0 | false |
99 | | } |
100 | 42.5k | } |
101 | | |
102 | | /// Return the `ignore_adler32` flag. |
103 | 0 | pub(crate) fn ignore_adler32(&self) -> bool { |
104 | 0 | self.ignore_adler32 |
105 | 0 | } |
106 | | |
107 | | /// Fill the decoded buffer as far as possible from `data`. |
108 | | /// On success returns the number of consumed input bytes. |
109 | 10.5M | pub(crate) fn decompress( |
110 | 10.5M | &mut self, |
111 | 10.5M | data: &[u8], |
112 | 10.5M | image_data: &mut UnfilterBuf<'_>, |
113 | 10.5M | ) -> Result<usize, DecodingError> { |
114 | | // There may be more data past the adler32 checksum at the end of the deflate stream. We |
115 | | // match libpng's default behavior and ignore any trailing data. In the future we may want |
116 | | // to add a flag to control this behavior. |
117 | 10.5M | if self.state.is_done() { |
118 | 4.04M | return Ok(data.len()); |
119 | 6.53M | } |
120 | | |
121 | 6.53M | if !self.started && self.ignore_adler32 { |
122 | 29.3k | self.state.ignore_adler32(); |
123 | 6.50M | } |
124 | | |
125 | 6.53M | let in_consumed = image_data.decompress(&mut self.state, data)?; |
126 | 6.53M | self.started = true; |
127 | | |
128 | 6.53M | Ok(in_consumed) |
129 | 10.5M | } |
130 | | |
131 | | /// Output any remaining buffered data within the decompressor. |
132 | | /// |
133 | | /// Returns `Ok(true)` if all data has been decompressed and there is no |
134 | | /// more data that will be produced, or `Ok(false)` if there's potentially |
135 | | /// more output. |
136 | | /// |
137 | | /// Returns `Err` if the zlib stream is corrupt or truncated too early. |
138 | 760 | pub(crate) fn finish( |
139 | 760 | &mut self, |
140 | 760 | image_data: &mut UnfilterBuf<'_>, |
141 | 760 | ) -> Result<bool, DecodingError> { |
142 | 760 | if !self.started || self.state.is_done() { |
143 | 102 | return Ok(true); |
144 | 658 | } |
145 | | |
146 | | // If the zlib stream isn't done but we've already output all the pixel |
147 | | // data needed, then either there's too much compressed data or the |
148 | | // checksum is missing. Those aren't allowed by the spec, but libpng |
149 | | // generally doesn't treat them as fatal. |
150 | 658 | if *image_data.filled == image_data.buffer.len() { |
151 | 0 | return Ok(true); |
152 | 658 | } |
153 | | |
154 | 658 | let (_, out_consumed) = self |
155 | 658 | .state |
156 | 658 | .read( |
157 | 658 | &[], |
158 | 658 | &mut image_data.buffer[*image_data.available..], |
159 | 658 | *image_data.filled - *image_data.available, |
160 | | false, |
161 | | ) |
162 | 658 | .map_err(|err| { |
163 | 2 | DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into()) |
164 | 2 | })?; |
165 | 656 | *image_data.filled += out_consumed; |
166 | | |
167 | 656 | if self.state.is_done() { |
168 | 130 | *image_data.available = *image_data.filled; |
169 | 130 | return Ok(true); |
170 | 526 | } |
171 | | |
172 | | // More output is only possible if zlib stream hasn't finished and the |
173 | | // output buffer *is* full. (Empty space in the output buffer tells us |
174 | | // there wasn't more data to write into it.) |
175 | 526 | if *image_data.filled == image_data.buffer.len() { |
176 | 92 | *image_data.available = |
177 | 92 | (*image_data.available).max(image_data.filled.saturating_sub(LOOKBACK_SIZE)); |
178 | 92 | return Ok(false); |
179 | 434 | } |
180 | | |
181 | | // The zlib stream was truncated before the end of the pixel data. This |
182 | | // would ordinarily be caught within fdeflate if we'd passed |
183 | | // end_of_input=true. But we intentionally don't pass that flag so that |
184 | | // we're able to drain all available pixel data first. |
185 | 434 | Err(DecodingError::Format( |
186 | 434 | FormatErrorInner::CorruptFlateStream { |
187 | 434 | err: fdeflate::DecompressionError::InsufficientInput, |
188 | 434 | } |
189 | 434 | .into(), |
190 | 434 | )) |
191 | 760 | } |
192 | | } |
193 | | |
194 | | impl UnfilterRegion { |
195 | | /// Use this region to decompress new filtered rowline data. |
196 | | /// |
197 | | /// Pass the wrapped buffer to |
198 | | /// [`StreamingDecoder::update`][`super::stream::StreamingDecoder::update`] to fill it with |
199 | | /// data and update the region indices. |
200 | | /// |
201 | | /// May panic if invariants of [`UnfilterRegion`] are violated. |
202 | 0 | pub fn as_buf<'data>(&'data mut self, buffer: &'data mut Vec<u8>) -> UnfilterBuf<'data> { |
203 | 0 | assert!(self.available <= self.filled); |
204 | 0 | assert!(self.filled <= buffer.len()); |
205 | 0 | UnfilterBuf { |
206 | 0 | buffer, |
207 | 0 | filled: &mut self.filled, |
208 | 0 | available: &mut self.available, |
209 | 0 | } |
210 | 0 | } |
211 | | } |
212 | | |
213 | | impl UnfilterBuf<'_> { |
214 | | /// Pushes `input` into `fdeflate` crate and appends decompressed bytes to `self.buffer` |
215 | | /// (adjusting `self.filled` and `self.available` depending on how many bytes have been |
216 | | /// decompressed). |
217 | | /// |
218 | | /// Returns how many bytes of `input` have been consumed. |
219 | | #[inline] |
220 | 6.53M | fn decompress( |
221 | 6.53M | &mut self, |
222 | 6.53M | decompressor: &mut fdeflate::Decompressor, |
223 | 6.53M | input: &[u8], |
224 | 6.53M | ) -> Result<usize, DecodingError> { |
225 | 6.53M | let output_limit = (*self.filled + UnfilteringBuffer::GROWTH_BYTES).min(self.buffer.len()); |
226 | 6.53M | let (in_consumed, out_consumed) = decompressor |
227 | 6.53M | .read( |
228 | 6.53M | input, |
229 | 6.53M | &mut self.buffer[*self.available..output_limit], |
230 | 6.53M | *self.filled - *self.available, |
231 | | false, |
232 | | ) |
233 | 6.53M | .map_err(|err| { |
234 | 3.31k | DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into()) |
235 | 3.31k | })?; |
236 | | |
237 | 6.53M | *self.filled += out_consumed; |
238 | 6.53M | if decompressor.is_done() { |
239 | 12.9k | *self.available = *self.filled; |
240 | 6.52M | } else if let Some(new_available) = self.filled.checked_sub(LOOKBACK_SIZE) { |
241 | | // The decompressed data may have started in the middle of the buffer, |
242 | | // so ensure that `self.available` never goes backward. This is needed |
243 | | // to avoid miscommunicating the size of the "look-back" window when calling |
244 | | // `fdeflate::Decompressor::read` a bit earlier and passing |
245 | | // `&mut self.buffer[*self.available..output_limit]`. |
246 | 4.51M | if new_available > *self.available { |
247 | 2.70M | *self.available = new_available; |
248 | 2.70M | } |
249 | 2.00M | } |
250 | | |
251 | 6.53M | Ok(in_consumed) |
252 | 6.53M | } |
253 | | } |