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}