Skip to main content

png/decoder/
unfiltering_buffer.rs

1use super::stream::{DecodingError, FormatErrorInner};
2use super::zlib::UnfilterBuf;
3use crate::common::BytesPerPixel;
4use crate::filter::{unfilter, RowFilter};
5use crate::Info;
6
7// Buffer for temporarily holding decompressed, not-yet-`unfilter`-ed rows.
8pub(crate) struct UnfilteringBuffer {
9    /// Vec containing the uncompressed image data currently being processed.
10    data_stream: Vec<u8>,
11    prev_row: PrevRow,
12    /// Index in `data_stream` where the current row starts.
13    /// This points at the filter type byte of the current row (i.e. the actual pixel data starts at `current_start + 1`)
14    /// The pixel data is not-yet-`unfilter`-ed.
15    ///
16    /// `current_start` can wrap around the length.
17    current_start: usize,
18    /// Logical length of data that must be preserved.
19    filled: usize,
20    /// Length of data that can be modified.
21    available: usize,
22    /// The number of bytes before we shift the buffer back.
23    shift_back_limit: usize,
24    /// How many bytes are left to decompress into this buffer for the current frame.
25    remaining_bytes: u64,
26    /// To avoid always allocating a new vector in `fn unfilter_curr_row_using_scratch_buffer`.
27    scratch_buffer: Vec<u8>,
28}
29
30impl UnfilteringBuffer {
31    pub const GROWTH_BYTES: usize = 8 * 1024;
32
33    /// Asserts in debug builds that all the invariants hold.  No-op in release
34    /// builds.  Intended to be called after creating or mutating `self` to
35    /// ensure that the final state preserves the invariants.
36    #[cfg(not(debug_assertions))]
37    fn debug_assert_invariants(&self) {}
38    #[cfg(debug_assertions)]
39    fn debug_assert_invariants(&self) {
40        if let PrevRow::InPlace(prev_start) = &self.prev_row {
41            debug_assert!(*prev_start <= self.current_start);
42        }
43        debug_assert!(self.current_start <= self.filled);
44        debug_assert!(self.available <= self.filled);
45        debug_assert!(self.filled <= self.data_stream.len());
46    }
47
48    /// Create a buffer tuned for filtering rows of the image type.
49    pub fn new(info: &Info<'_>) -> Self {
50        // We don't need all of `info` here so if that becomes a structural problem then these
51        // derived constants can be extracted into a parameter struct. For instance they may be
52        // adjusted according to platform hardware such as cache sizes.
53        let data_stream_capacity = {
54            let max_data = info
55                .checked_raw_row_length()
56                // In the current state this is really dependent on IDAT sizes and the compression
57                // settings. We aim to avoid overallocation here, but that occurs in part due to
58                // the algorithm for draining the buffer, which at the time of writing is at each
59                // individual IDAT chunk boundary. So this is set for a quadratic image roughly
60                // fitting into a single 4k chunk at compression.. A very arbitrary choice made
61                // from (probably overfitting) a benchmark of that image size. With a different
62                // algorithm we may come to different buffer uses and have to re-evaluate.
63                .and_then(|v| v.checked_mul(info.height.min(128) as usize))
64                // In the worst case this is additional room for use of unmasked SIMD moves. But
65                // the other idea here is that the allocator generally aligns the buffer.
66                .and_then(|v| checked_next_multiple_of(v, 256))
67                .unwrap_or(usize::MAX);
68            // We do not want to pre-allocate too much in case of a faulty image (no DOS by
69            // pretending to be very very large) and also we want to avoid allocating more data
70            // than we need for the image itself.
71            max_data.min(128 * 1024)
72        };
73
74        let shift_back_limit = {
75            // Prefer shifting by powers of two and only after having done some number of
76            // lines that then become free at the end of the buffer.
77            let rowlen_pot = info
78                .checked_raw_row_length()
79                // Ensure some number of rows are actually present before shifting back, i.e. next
80                // time around we want to be able to decode them without reallocating the buffer.
81                .and_then(|v| v.checked_mul(4))
82                // And also, we should be able to use aligned memcopy on the whole thing. Well at
83                // least that is the idea but the parameter is just benchmarking. Higher numbers
84                // did not result in performance gains but lowers also, so this is fickle. Maybe
85                // our shift back behavior can not be tuned very well.
86                .and_then(|v| checked_next_multiple_of(v, 64))
87                .unwrap_or(isize::MAX as usize);
88            // But never shift back before we have a number of pages freed.
89            rowlen_pot.max(128 * 1024)
90        };
91
92        let result = Self {
93            data_stream: Vec::with_capacity(data_stream_capacity),
94            prev_row: PrevRow::None,
95            current_start: 0,
96            filled: 0,
97            available: 0,
98            shift_back_limit,
99            remaining_bytes: u64::MAX,
100            scratch_buffer: Vec::new(),
101        };
102
103        result.debug_assert_invariants();
104        result
105    }
106
107    /// Called to indicate that there is no previous row (e.g. when the current
108    /// row is the first scanline of a given Adam7 pass).
109    pub fn reset_prev_row(&mut self) {
110        // Stash a previously allocated buffer (for potential reuse later)
111        // rather than throwing it away when resetting `self.prev_row`.
112        if let PrevRow::Scratch(buf) = &mut self.prev_row {
113            self.scratch_buffer = std::mem::take(buf);
114        }
115
116        self.prev_row = PrevRow::None;
117        self.debug_assert_invariants();
118    }
119
120    pub fn start_frame(&mut self, frame_bytes: u64) {
121        self.data_stream.clear();
122        self.prev_row = PrevRow::None;
123        self.current_start = 0;
124        self.filled = 0;
125        self.available = 0;
126        self.remaining_bytes = frame_bytes;
127    }
128
129    pub fn remaining_bytes(&self) -> u64 {
130        self.remaining_bytes
131    }
132
133    /// Returns the previous (already `unfilter`-ed) row.
134    pub fn prev_row(&self) -> &[u8] {
135        self.prev_row
136            .as_slice(&self.data_stream[..self.current_start])
137    }
138
139    /// Returns how many bytes of the current row are present in the mutable
140    /// part of the buffer (32kB most recently decompressed bytes are read-only
141    /// to retain the "lookback" window as needed for inflate algorithm).  If a
142    /// full row is mutable, then it may be unfiltered using
143    /// `unfilter_curr_row_in_place`.
144    ///
145    /// See also `readable_len_of_curr_row`.
146    pub fn mutable_len_of_curr_row(&self) -> usize {
147        self.available.saturating_sub(self.current_start)
148    }
149
150    /// Returns how many bytes of the current row have been already
151    /// decompressed.  If a full row is available, then it may be unfiltered
152    /// using `unfilter_curr_row_using_scratch_buffer`.
153    ///
154    /// See also `mutable_len_of_curr_row`.
155    pub fn readable_len_of_curr_row(&self) -> usize {
156        self.filled - self.current_start
157    }
158
159    /// Runs `f` on the underlying buffer.
160    ///
161    /// Invariants of `self` depend on the assumption that the caller will only
162    /// append new bytes to the returned vector (which is indeed the behavior of
163    /// `ReadDecoder` and `StreamingDecoder`).  TODO: Consider protecting the
164    /// invariants by returning an append-only view of the vector
165    /// (`FnMut(&[u8])`??? or maybe `std::io::Write`???).
166    pub fn with_unfilled_buffer<F, T>(&mut self, f: F) -> T
167    where
168        F: FnOnce(&mut UnfilterBuf<'_>) -> T,
169    {
170        // Potentially shift the buffer left to avoid unbounded growth.
171        let discard_size = self.available.min(match &self.prev_row {
172            PrevRow::None | PrevRow::Scratch(_) => self.current_start,
173            PrevRow::InPlace(prev_start) => *prev_start,
174        });
175        if discard_size >= self.shift_back_limit
176            // Avoid the shift back if the buffer is still very empty. Consider how we got here: a
177            // previous decompression filled the buffer, then we unfiltered, we're now refilling
178            // the buffer again. The condition implies, the previous decompression filled at most
179            // half the buffer. Likely the same will happen again so the following decompression
180            // attempt will not yet be limited by the buffer length.
181            && self.filled >= self.data_stream.len() / 2
182        {
183            self.shift_buffer_left(discard_size);
184        }
185
186        if self.filled + Self::GROWTH_BYTES > self.data_stream.len() {
187            self.data_stream.resize(self.filled + Self::GROWTH_BYTES, 0);
188        }
189
190        if self.remaining_bytes < usize::MAX as u64
191            && self.filled.saturating_add(self.remaining_bytes as usize) < self.data_stream.len()
192        {
193            self.data_stream
194                .resize(self.filled + self.remaining_bytes as usize, 0);
195        }
196
197        let old_filled = self.filled;
198        let ret = f(&mut UnfilterBuf {
199            buffer: &mut self.data_stream,
200            filled: &mut self.filled,
201            available: &mut self.available,
202        });
203        assert!(self.filled >= old_filled);
204        self.remaining_bytes -= (self.filled - old_filled) as u64;
205
206        if self.remaining_bytes == 0 {
207            self.available = self.filled;
208        }
209
210        self.debug_assert_invariants();
211        ret
212    }
213
214    /// Shifts the contents of `self.data_stream` left,
215    /// discarding the first `discard_size` bytes.
216    fn shift_buffer_left(&mut self, discard_size: usize) {
217        // Violating this assertion will clobber the immutable "lookback"
218        // window that needs to be maintained for decompressor.
219        assert!(discard_size <= self.available);
220
221        // We have to relocate the data to the start of the buffer. Benchmarking suggests that
222        // the codegen for an unbounded range is better / different than the one for a bounded
223        // range. We prefer the former if the data overhead is not too high. `16` was
224        // determined experimentally and might be system (memory) dependent. There's also the
225        // question if we could be a little smarter and avoid crossing page boundaries when
226        // that is not required. Alas, microbenchmarking TBD.
227        if let Some(16..) = self.data_stream.len().checked_sub(self.filled) {
228            self.data_stream.copy_within(discard_size..self.filled, 0);
229        } else {
230            self.data_stream.copy_within(discard_size.., 0);
231        }
232
233        // The data kept its relative position to `filled` which now lands exactly at
234        // the distance between prev_start and filled.
235        self.current_start -= discard_size;
236        self.available -= discard_size;
237        self.filled -= discard_size;
238        match &mut self.prev_row {
239            PrevRow::None | PrevRow::Scratch(_) => (),
240            PrevRow::InPlace(prev_start) => *prev_start -= discard_size,
241        }
242    }
243
244    fn curr_row_filter(&self) -> Result<RowFilter, DecodingError> {
245        let filter = self.data_stream[self.current_start];
246        RowFilter::from_u8(filter).ok_or(DecodingError::Format(
247            FormatErrorInner::UnknownFilterMethod(filter).into(),
248        ))
249    }
250
251    /// Runs `unfilter` on the current row, and then shifts rows so that the
252    /// current row becomes the previous row.
253    ///
254    /// `unfilter` will mutate the current row in-place, and therefore the
255    /// caller should first consult `mutable_len_of_curr_row` to check if all
256    /// bytes of the current row are indeed mutable.
257    pub fn unfilter_curr_row_in_place(
258        &mut self,
259        rowlen: usize,
260        bpp: BytesPerPixel,
261    ) -> Result<(), DecodingError> {
262        debug_assert!(rowlen >= 2); // 1 byte for `RowFilter` and at least 1 byte of pixel data.
263
264        // Violating the assertion below would clobber the bytes in the
265        // "lookback" window.
266        debug_assert!(self.mutable_len_of_curr_row() >= rowlen);
267
268        let filter = self.curr_row_filter()?;
269        let (prev, row) = self.data_stream.split_at_mut(self.current_start);
270        let prev: &[u8] = self.prev_row.as_slice(prev);
271        let row = &mut row[1..rowlen]; // Skip the `RowFilter` byte.
272        debug_assert!(prev.is_empty() || prev.len() == row.len());
273
274        unfilter(filter, bpp, prev, row);
275
276        self.reset_prev_row();
277        self.prev_row = PrevRow::InPlace(self.current_start + 1);
278        self.current_start += rowlen;
279        self.debug_assert_invariants();
280
281        Ok(())
282    }
283
284    /// Runs `unfilter` on the current row, and then shifts rows so that the
285    /// current row becomes the previous row.
286    ///
287    /// Before running `unfilter`, the contents of the current row will be
288    /// copied into a scratch buffer.  This allows unfiltering to happen even
289    /// if `mutable_len_of_curr_row < rowlen` (e.g. when handling partial
290    /// or not-yet-complete input streams).
291    pub fn unfilter_curr_row_using_scratch_buffer(
292        &mut self,
293        rowlen: usize,
294        bpp: BytesPerPixel,
295    ) -> Result<(), DecodingError> {
296        debug_assert!(rowlen >= 2); // 1 byte for `RowFilter` and at least 1 byte of pixel data.
297        debug_assert!(self.readable_len_of_curr_row() >= rowlen);
298
299        // If `mutable_len_of_curr_row >= rowlen`, then `unfilter_curr_row_in_place`
300        // should have been called instead (to avoid the cost of `copy_from_slice` below).
301        debug_assert!(self.mutable_len_of_curr_row() < rowlen);
302
303        let filter = self.curr_row_filter()?;
304
305        let mut row = std::mem::take(&mut self.scratch_buffer);
306        row.resize(rowlen - 1, 0);
307        row.as_mut_slice()
308            .copy_from_slice(&self.data_stream[self.current_start + 1..][..rowlen - 1]);
309
310        let prev = self.prev_row();
311        debug_assert!(prev.is_empty() || prev.len() == (rowlen - 1));
312
313        unfilter(filter, bpp, prev, &mut row);
314
315        self.reset_prev_row();
316        self.prev_row = PrevRow::Scratch(row);
317        self.current_start += rowlen;
318
319        self.debug_assert_invariants();
320
321        Ok(())
322    }
323}
324
325/// An already `unfilter`-ed, previous row.
326///
327/// The data excludes the `RowFilter` byte - it only includes the actual pixel data.
328enum PrevRow {
329    /// No unfiltered row.
330    ///
331    /// `None` is the value of `UnfilteringBuffer::prev_row` before any row has
332    /// been unfiltered (or at the start of a new interlace pass).
333    None,
334
335    /// Offset of `UnfilteringBuffer::data_stream` where the unfiltered row
336    /// starts.
337    ///
338    /// `UnfilteringBuffer::InPlace(_)` is used by `unfilter_curr_row_in_place`
339    /// when setting `UnfilteringBuffer::prev_row`.
340    InPlace(usize),
341
342    /// Separate scratch buffer containing the unfiltered row data.
343    ///
344    /// `UnfilteringBuffer::Scratch(_)` is used by
345    /// `unfilter_curr_row_using_scratch_buffer`
346    /// when setting `UnfilteringBuffer::prev_row`.
347    Scratch(Vec<u8>),
348}
349
350impl PrevRow {
351    /// Returns the previous unfiltered row as a slice of bytes.
352    ///
353    /// `buf` should refer to the `..current_start` portion of
354    /// `UnfilteringBuffer::data_stream`.
355    fn as_slice<'a>(&'a self, buf: &'a [u8]) -> &'a [u8] {
356        match self {
357            PrevRow::None => &[],
358            PrevRow::InPlace(prev_start) => &buf[*prev_start..],
359            PrevRow::Scratch(scratch) => scratch.as_slice(),
360        }
361    }
362}
363
364fn checked_next_multiple_of(val: usize, factor: usize) -> Option<usize> {
365    if factor == 0 {
366        return None;
367    }
368
369    let remainder = val % factor;
370
371    if remainder > 0 {
372        val.checked_add(factor - remainder)
373    } else {
374        Some(val)
375    }
376}
377
378#[test]
379fn next_multiple_of_backport_testsuite() {
380    assert_eq!(checked_next_multiple_of(1, 0), None);
381    assert_eq!(checked_next_multiple_of(2, 0), None);
382    assert_eq!(checked_next_multiple_of(1, 2), Some(2));
383    assert_eq!(checked_next_multiple_of(2, 2), Some(2));
384    assert_eq!(checked_next_multiple_of(2, 5), Some(5));
385    assert_eq!(checked_next_multiple_of(1, usize::MAX), Some(usize::MAX));
386    assert_eq!(checked_next_multiple_of(usize::MAX, 2), None);
387}