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}