Coverage Report

Created: 2025-12-05 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/png-0.18.0/src/decoder/zlib.rs
Line
Count
Source
1
use super::{stream::FormatErrorInner, unfiltering_buffer::UnfilteringBuffer, DecodingError};
2
3
use 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.
15
pub 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)]
33
pub 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.
41
pub(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
54
impl 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
11.6k
    pub(crate) fn new() -> Self {
65
11.6k
        ZlibStream {
66
11.6k
            state: Box::new(Decompressor::new()),
67
11.6k
            started: false,
68
11.6k
            ignore_adler32: true,
69
11.6k
        }
70
11.6k
    }
71
72
300
    pub(crate) fn reset(&mut self) {
73
300
        self.started = false;
74
300
        *self.state = Decompressor::new();
75
300
    }
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
11.6k
    pub(crate) fn set_ignore_adler32(&mut self, flag: bool) -> bool {
85
11.6k
        if !self.started {
86
11.6k
            self.ignore_adler32 = flag;
87
11.6k
            true
88
        } else {
89
0
            false
90
        }
91
11.6k
    }
92
93
    /// Return the `ignore_adler32` flag.
94
0
    pub(crate) fn ignore_adler32(&self) -> bool {
95
0
        self.ignore_adler32
96
0
    }
97
98
    /// Fill the decoded buffer as far as possible from `data`.
99
    /// On success returns the number of consumed input bytes.
100
2.47M
    pub(crate) fn decompress(
101
2.47M
        &mut self,
102
2.47M
        data: &[u8],
103
2.47M
        image_data: &mut UnfilterBuf<'_>,
104
2.47M
    ) -> 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
2.47M
        if self.state.is_done() {
109
1.08k
            return Ok(data.len());
110
2.47M
        }
111
112
2.47M
        if !self.started && self.ignore_adler32 {
113
6.19k
            self.state.ignore_adler32();
114
2.46M
        }
115
116
2.47M
        let (buffer, filled) = image_data.borrow_mut();
117
2.47M
        let output_limit = (filled + UnfilteringBuffer::GROWTH_BYTES).min(buffer.len());
118
2.47M
        let (in_consumed, out_consumed) = self
119
2.47M
            .state
120
2.47M
            .read(data, &mut buffer[..output_limit], filled, false)
121
2.47M
            .map_err(|err| {
122
1.16k
                DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into())
123
1.16k
            })?;
124
125
2.47M
        self.started = true;
126
2.47M
        let filled = filled + out_consumed;
127
2.47M
        image_data.filled(filled);
128
129
2.47M
        if self.state.is_done() {
130
2.32k
            image_data.commit(filled);
131
2.47M
        } else {
132
2.47M
            // See [`Self::LOOKBACK_SIZE`].
133
2.47M
            image_data.commit(filled.saturating_sub(Self::LOOKBACK_SIZE));
134
2.47M
        }
135
136
2.47M
        Ok(in_consumed)
137
2.47M
    }
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
376
    pub(crate) fn finish_compressed_chunks(
145
376
        &mut self,
146
376
        image_data: &mut UnfilterBuf<'_>,
147
376
    ) -> Result<(), DecodingError> {
148
376
        if !self.started {
149
0
            return Ok(());
150
376
        }
151
152
376
        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
45
            return Ok(());
157
331
        }
158
159
331
        let (_, mut filled) = image_data.borrow_mut();
160
337
        while !self.state.is_done() {
161
331
            let (buffer, _) = image_data.borrow_mut();
162
6
            let (_in_consumed, out_consumed) =
163
331
                self.state.read(&[], buffer, filled, true).map_err(|err| {
164
325
                    DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into())
165
325
                })?;
166
167
6
            filled += out_consumed;
168
169
6
            if !self.state.is_done() {
170
0
                image_data.flush_allocate();
171
6
            }
172
        }
173
174
6
        image_data.filled(filled);
175
6
        image_data.commit(filled);
176
177
6
        Ok(())
178
376
    }
179
}
180
181
impl 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
0
    pub fn as_buf<'data>(&'data mut self, buffer: &'data mut Vec<u8>) -> UnfilterBuf<'data> {
188
0
        UnfilterBuf {
189
0
            buffer,
190
0
            filled: &mut self.filled,
191
0
            available: &mut self.available,
192
0
        }
193
0
    }
194
}
195
196
impl UnfilterBuf<'_> {
197
2.47M
    pub(crate) fn borrow_mut(&mut self) -> (&mut [u8], usize) {
198
2.47M
        (self.buffer, *self.filled)
199
2.47M
    }
200
201
2.47M
    pub(crate) fn filled(&mut self, filled: usize) {
202
2.47M
        *self.filled = filled;
203
2.47M
    }
204
205
2.47M
    pub(crate) fn commit(&mut self, howmany: usize) {
206
2.47M
        *self.available = howmany;
207
2.47M
    }
208
209
0
    pub(crate) fn flush_allocate(&mut self) {
210
0
        let len = self.buffer.len() + 32 * 1024;
211
0
        self.buffer.resize(len, 0);
212
0
    }
213
}