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    /// Index in `data_stream` where the previous row starts.
12    /// This excludes the filter type byte - it points at the first byte of actual pixel data.
13    /// The pixel data is already-`unfilter`-ed.
14    ///
15    /// If `prev_start == current_start` then it means that there is no previous row.
16    prev_start: usize,
17    /// Index in `data_stream` where the current row starts.
18    /// This points at the filter type byte of the current row (i.e. the actual pixel data starts at `current_start + 1`)
19    /// The pixel data is not-yet-`unfilter`-ed.
20    ///
21    /// `current_start` can wrap around the length.
22    current_start: usize,
23    /// Logical length of data that must be preserved.
24    filled: usize,
25    /// Length of data that can be modified.
26    available: usize,
27    /// The number of bytes before we shift the buffer back.
28    shift_back_limit: usize,
29}
30
31impl UnfilteringBuffer {
32    pub const GROWTH_BYTES: usize = 8 * 1024;
33
34    /// Asserts in debug builds that all the invariants hold.  No-op in release
35    /// builds.  Intended to be called after creating or mutating `self` to
36    /// ensure that the final state preserves the invariants.
37    fn debug_assert_invariants(&self) {
38        debug_assert!(self.prev_start <= self.current_start);
39        debug_assert!(self.current_start <= self.available);
40        debug_assert!(self.available <= self.filled);
41        debug_assert!(self.filled <= self.data_stream.len());
42    }
43
44    /// Create a buffer tuned for filtering rows of the image type.
45    pub fn new(info: &Info<'_>) -> Self {
46        // We don't need all of `info` here so if that becomes a structural problem then these
47        // derived constants can be extracted into a parameter struct. For instance they may be
48        // adjusted according to platform hardware such as cache sizes.
49        let data_stream_capacity = {
50            let max_data = info
51                .checked_raw_row_length()
52                // In the current state this is really dependent on IDAT sizes and the compression
53                // settings. We aim to avoid overallocation here, but that occurs in part due to
54                // the algorithm for draining the buffer, which at the time of writing is at each
55                // individual IDAT chunk boundary. So this is set for a quadratic image roughly
56                // fitting into a single 4k chunk at compression.. A very arbitrary choice made
57                // from (probably overfitting) a benchmark of that image size. With a different
58                // algorithm we may come to different buffer uses and have to re-evaluate.
59                .and_then(|v| v.checked_mul(info.height.min(128) as usize))
60                // In the worst case this is additional room for use of unmasked SIMD moves. But
61                // the other idea here is that the allocator generally aligns the buffer.
62                .and_then(|v| checked_next_multiple_of(v, 256))
63                .unwrap_or(usize::MAX);
64            // We do not want to pre-allocate too much in case of a faulty image (no DOS by
65            // pretending to be very very large) and also we want to avoid allocating more data
66            // than we need for the image itself.
67            max_data.min(128 * 1024)
68        };
69
70        let shift_back_limit = {
71            // Prefer shifting by powers of two and only after having done some number of
72            // lines that then become free at the end of the buffer.
73            let rowlen_pot = info
74                .checked_raw_row_length()
75                // Ensure some number of rows are actually present before shifting back, i.e. next
76                // time around we want to be able to decode them without reallocating the buffer.
77                .and_then(|v| v.checked_mul(4))
78                // And also, we should be able to use aligned memcopy on the whole thing. Well at
79                // least that is the idea but the parameter is just benchmarking. Higher numbers
80                // did not result in performance gains but lowers also, so this is fickle. Maybe
81                // our shift back behavior can not be tuned very well.
82                .and_then(|v| checked_next_multiple_of(v, 64))
83                .unwrap_or(isize::MAX as usize);
84            // But never shift back before we have a number of pages freed.
85            rowlen_pot.max(128 * 1024)
86        };
87
88        let result = Self {
89            data_stream: Vec::with_capacity(data_stream_capacity),
90            prev_start: 0,
91            current_start: 0,
92            filled: 0,
93            available: 0,
94            shift_back_limit,
95        };
96
97        result.debug_assert_invariants();
98        result
99    }
100
101    /// Called to indicate that there is no previous row (e.g. when the current
102    /// row is the first scanline of a given Adam7 pass).
103    pub fn reset_prev_row(&mut self) {
104        self.prev_start = self.current_start;
105        self.debug_assert_invariants();
106    }
107
108    pub fn reset_all(&mut self) {
109        self.data_stream.clear();
110        self.prev_start = 0;
111        self.current_start = 0;
112        self.filled = 0;
113        self.available = 0;
114    }
115
116    /// Returns the previous (already `unfilter`-ed) row.
117    pub fn prev_row(&self) -> &[u8] {
118        &self.data_stream[self.prev_start..self.current_start]
119    }
120
121    /// Returns how many bytes of the current row are present in the buffer.
122    pub fn curr_row_len(&self) -> usize {
123        self.available - self.current_start
124    }
125
126    /// Returns a `&mut Vec<u8>` suitable for passing to
127    /// `ReadDecoder.decode_image_data` or `StreamingDecoder.update`.
128    ///
129    /// Invariants of `self` depend on the assumption that the caller will only
130    /// append new bytes to the returned vector (which is indeed the behavior of
131    /// `ReadDecoder` and `StreamingDecoder`).  TODO: Consider protecting the
132    /// invariants by returning an append-only view of the vector
133    /// (`FnMut(&[u8])`??? or maybe `std::io::Write`???).
134    pub fn as_unfilled_buffer(&mut self) -> UnfilterBuf<'_> {
135        if self.prev_start >= self.shift_back_limit
136            // Avoid the shift back if the buffer is still very empty. Consider how we got here: a
137            // previous decompression filled the buffer, then we unfiltered, we're now refilling
138            // the buffer again. The condition implies, the previous decompression filled at most
139            // half the buffer. Likely the same will happen again so the following decompression
140            // attempt will not yet be limited by the buffer length.
141            && self.filled >= self.data_stream.len() / 2
142        {
143            // We have to relocate the data to the start of the buffer. Benchmarking suggests that
144            // the codegen for an unbounded range is better / different than the one for a bounded
145            // range. We prefer the former if the data overhead is not too high. `16` was
146            // determined experimentally and might be system (memory) dependent. There's also the
147            // question if we could be a little smarter and avoid crossing page boundaries when
148            // that is not required. Alas, microbenchmarking TBD.
149            if let Some(16..) = self.data_stream.len().checked_sub(self.filled) {
150                self.data_stream
151                    .copy_within(self.prev_start..self.filled, 0);
152            } else {
153                self.data_stream.copy_within(self.prev_start.., 0);
154            }
155
156            // The data kept its relative position to `filled` which now lands exactly at
157            // the distance between prev_start and filled.
158            self.current_start -= self.prev_start;
159            self.available -= self.prev_start;
160            self.filled -= self.prev_start;
161            self.prev_start = 0;
162        }
163
164        if self.filled + Self::GROWTH_BYTES > self.data_stream.len() {
165            self.data_stream.resize(self.filled + Self::GROWTH_BYTES, 0);
166        }
167
168        UnfilterBuf {
169            buffer: &mut self.data_stream,
170            filled: &mut self.filled,
171            available: &mut self.available,
172        }
173    }
174
175    /// Runs `unfilter` on the current row, and then shifts rows so that the current row becomes the previous row.
176    ///
177    /// Will panic if `self.curr_row_len() < rowlen`.
178    pub fn unfilter_curr_row(
179        &mut self,
180        rowlen: usize,
181        bpp: BytesPerPixel,
182    ) -> Result<(), DecodingError> {
183        debug_assert!(rowlen >= 2); // 1 byte for `FilterType` and at least 1 byte of pixel data.
184
185        let (prev, row) = self.data_stream.split_at_mut(self.current_start);
186        let prev: &[u8] = &prev[self.prev_start..];
187
188        debug_assert!(prev.is_empty() || prev.len() == (rowlen - 1));
189
190        // Get the filter type.
191        let filter = RowFilter::from_u8(row[0]).ok_or(DecodingError::Format(
192            FormatErrorInner::UnknownFilterMethod(row[0]).into(),
193        ))?;
194        let row = &mut row[1..rowlen];
195
196        unfilter(filter, bpp, prev, row);
197
198        self.prev_start = self.current_start + 1;
199        self.current_start += rowlen;
200
201        self.debug_assert_invariants();
202
203        Ok(())
204    }
205}
206
207fn checked_next_multiple_of(val: usize, factor: usize) -> Option<usize> {
208    if factor == 0 {
209        return None;
210    }
211
212    let remainder = val % factor;
213
214    if remainder > 0 {
215        val.checked_add(factor - remainder)
216    } else {
217        Some(val)
218    }
219}
220
221#[test]
222fn next_multiple_of_backport_testsuite() {
223    assert_eq!(checked_next_multiple_of(1, 0), None);
224    assert_eq!(checked_next_multiple_of(2, 0), None);
225    assert_eq!(checked_next_multiple_of(1, 2), Some(2));
226    assert_eq!(checked_next_multiple_of(2, 2), Some(2));
227    assert_eq!(checked_next_multiple_of(2, 5), Some(5));
228    assert_eq!(checked_next_multiple_of(1, usize::MAX), Some(usize::MAX));
229    assert_eq!(checked_next_multiple_of(usize::MAX, 2), None);
230}