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}