png/decoder/zlib.rs
1use super::{stream::FormatErrorInner, unfiltering_buffer::UnfilteringBuffer, DecodingError};
2
3use fdeflate::Decompressor;
4
5/// An inplace buffer for decompression and filtering of PNG rowlines.
6///
7/// The underlying data structure is a vector, with additional markers denoting a region of bytes
8/// that are utilized by the decompression but not yet available to arbitrary modifications. The
9/// caller can still shift around data between calls to the stream decompressor as long as the data
10/// in the marked region is not modified and the indices adjusted accordingly. See
11/// [`UnfilterRegion`] that contains these markers.
12///
13/// Violating the invariants, i.e. modifying bytes in the marked region, results in absurdly wacky
14/// decompression output or panics but not undefined behavior.
15pub struct UnfilterBuf<'data> {
16 /// The data container. Starts with arbitrary data unrelated to the decoder, a slice of decoder
17 /// private data followed by free space for further decoder output. The regions are delimited
18 /// by `filled` and `available` which must be updated accordingly.
19 pub(crate) buffer: &'data mut Vec<u8>,
20 /// Where we record changes to the out position.
21 pub(crate) filled: &'data mut usize,
22 /// Where we record changes to the available byte.
23 pub(crate) available: &'data mut usize,
24}
25
26/// A region into a buffer utilized as a [`UnfilterBuf`].
27///
28/// The span of data denoted by `filled..available` is the region of bytes that must be preserved
29/// for use by the decompression algorithm. It may be moved, e.g. by subtracting the same amount
30/// from both of these fields. Always ensure that `filled <= available`, the library does not
31/// violate this invariant when modifying this struct as an [`UnfilterBuf`].
32#[derive(Default, Clone, Copy)]
33pub struct UnfilterRegion {
34 /// The past-the-end index of byte that are allowed to be modified.
35 pub available: usize,
36 /// The past-the-end of bytes that have been written to.
37 pub filled: usize,
38}
39
40/// Ergonomics wrapper around `miniz_oxide::inflate::stream` for zlib compressed data.
41pub(super) struct ZlibStream {
42 /// Current decoding state.
43 state: Box<fdeflate::Decompressor>,
44 /// If there has been a call to decompress already.
45 started: bool,
46 /// Ignore and do not calculate the Adler-32 checksum. Defaults to `true`.
47 ///
48 /// This flag overrides `TINFL_FLAG_COMPUTE_ADLER32`.
49 ///
50 /// This flag should not be modified after decompression has started.
51 ignore_adler32: bool,
52}
53
54impl ZlibStream {
55 // [PNG spec](https://www.w3.org/TR/2003/REC-PNG-20031110/#10Compression) says that
56 // "deflate/inflate compression with a sliding window (which is an upper bound on the
57 // distances appearing in the deflate stream) of at most 32768 bytes".
58 //
59 // `fdeflate` requires that we keep this many most recently decompressed bytes in the
60 // `out_buffer` - this allows referring back to them when handling "length and distance
61 // codes" in the deflate stream).
62 const LOOKBACK_SIZE: usize = 32768;
63
64 pub(crate) fn new() -> Self {
65 ZlibStream {
66 state: Box::new(Decompressor::new()),
67 started: false,
68 ignore_adler32: true,
69 }
70 }
71
72 pub(crate) fn reset(&mut self) {
73 self.started = false;
74 *self.state = Decompressor::new();
75 }
76
77 /// Set the `ignore_adler32` flag and return `true` if the flag was
78 /// successfully set.
79 ///
80 /// The default is `true`.
81 ///
82 /// This flag cannot be modified after decompression has started until the
83 /// [ZlibStream] is reset.
84 pub(crate) fn set_ignore_adler32(&mut self, flag: bool) -> bool {
85 if !self.started {
86 self.ignore_adler32 = flag;
87 true
88 } else {
89 false
90 }
91 }
92
93 /// Return the `ignore_adler32` flag.
94 pub(crate) fn ignore_adler32(&self) -> bool {
95 self.ignore_adler32
96 }
97
98 /// Fill the decoded buffer as far as possible from `data`.
99 /// On success returns the number of consumed input bytes.
100 pub(crate) fn decompress(
101 &mut self,
102 data: &[u8],
103 image_data: &mut UnfilterBuf<'_>,
104 ) -> Result<usize, DecodingError> {
105 // There may be more data past the adler32 checksum at the end of the deflate stream. We
106 // match libpng's default behavior and ignore any trailing data. In the future we may want
107 // to add a flag to control this behavior.
108 if self.state.is_done() {
109 return Ok(data.len());
110 }
111
112 if !self.started && self.ignore_adler32 {
113 self.state.ignore_adler32();
114 }
115
116 let (buffer, filled) = image_data.borrow_mut();
117 let output_limit = (filled + UnfilteringBuffer::GROWTH_BYTES).min(buffer.len());
118 let (in_consumed, out_consumed) = self
119 .state
120 .read(data, &mut buffer[..output_limit], filled, false)
121 .map_err(|err| {
122 DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into())
123 })?;
124
125 self.started = true;
126 let filled = filled + out_consumed;
127 image_data.filled(filled);
128
129 if self.state.is_done() {
130 image_data.commit(filled);
131 } else {
132 // See [`Self::LOOKBACK_SIZE`].
133 image_data.commit(filled.saturating_sub(Self::LOOKBACK_SIZE));
134 }
135
136 Ok(in_consumed)
137 }
138
139 /// Called after all consecutive IDAT chunks were handled.
140 ///
141 /// The compressed stream can be split on arbitrary byte boundaries. This enables some cleanup
142 /// within the decompressor and flushing additional data which may have been kept back in case
143 /// more data were passed to it.
144 pub(crate) fn finish_compressed_chunks(
145 &mut self,
146 image_data: &mut UnfilterBuf<'_>,
147 ) -> Result<(), DecodingError> {
148 if !self.started {
149 return Ok(());
150 }
151
152 if self.state.is_done() {
153 // We can end up here only after the [`decompress`] call above has detected the state
154 // to be done, too. In this case the filled and committed amount of data are already
155 // equal to each other. So neither of them needs to be touched in any way.
156 return Ok(());
157 }
158
159 let (_, mut filled) = image_data.borrow_mut();
160 while !self.state.is_done() {
161 let (buffer, _) = image_data.borrow_mut();
162 let (_in_consumed, out_consumed) =
163 self.state.read(&[], buffer, filled, true).map_err(|err| {
164 DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into())
165 })?;
166
167 filled += out_consumed;
168
169 if !self.state.is_done() {
170 image_data.flush_allocate();
171 }
172 }
173
174 image_data.filled(filled);
175 image_data.commit(filled);
176
177 Ok(())
178 }
179}
180
181impl UnfilterRegion {
182 /// Use this region to decompress new filtered rowline data.
183 ///
184 /// Pass the wrapped buffer to
185 /// [`StreamingDecoder::update`][`super::stream::StreamingDecoder::update`] to fill it with
186 /// data and update the region indices.
187 pub fn as_buf<'data>(&'data mut self, buffer: &'data mut Vec<u8>) -> UnfilterBuf<'data> {
188 UnfilterBuf {
189 buffer,
190 filled: &mut self.filled,
191 available: &mut self.available,
192 }
193 }
194}
195
196impl UnfilterBuf<'_> {
197 pub(crate) fn borrow_mut(&mut self) -> (&mut [u8], usize) {
198 (self.buffer, *self.filled)
199 }
200
201 pub(crate) fn filled(&mut self, filled: usize) {
202 *self.filled = filled;
203 }
204
205 pub(crate) fn commit(&mut self, howmany: usize) {
206 *self.available = howmany;
207 }
208
209 pub(crate) fn flush_allocate(&mut self) {
210 let len = self.buffer.len() + 32 * 1024;
211 self.buffer.resize(len, 0);
212 }
213}