/src/image-png/fuzz/fuzz_targets/buf_independent.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! This fuzzer tests that decoding results are the same regardless of the |
2 | | //! details of how the `Read` trait exposes the underlying input via |
3 | | //! the `fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>` |
4 | | //! method: |
5 | | //! |
6 | | //! * Whole slice - `impl Read for &[u8]`: |
7 | | //! - The whole slice is available for reading (if it fits into `buf`) |
8 | | //! - No IO errors are expected |
9 | | //! - Motivation: This is the baseline |
10 | | //! * Byte-by-byte - `SmalBuf<R>`: |
11 | | //! - At most 1 byte can be read in a single call to `read` |
12 | | //! - Motivation: Testing that decoding works regardless of how the input is split |
13 | | //! into multiple `read` calls. (The test checks every possible `read` boundary in the input |
14 | | //! buffer, even though in practice file or network buffers would split the input into only a |
15 | | //! handful of chunks.) |
16 | | //! * TODO: Intermittent EOFs: |
17 | | //! - Intermittently `read` report 0 available bytes. |
18 | | //! - Still no IO errors at the `Read` trait level |
19 | | //! - Motivation: Testing support for decoding a streaming or partial input |
20 | | //! (i.e. scenarios where initially only the first few interlaced passes |
21 | | //! can be decoded, and where decoding is resumed after getting more complete |
22 | | //! input). |
23 | | |
24 | | #![no_main] |
25 | | |
26 | | use libfuzzer_sys::fuzz_target; |
27 | | |
28 | | use std::fmt::Debug; |
29 | | use std::io::{BufRead, BufReader, Cursor, Seek}; |
30 | | |
31 | | mod smal_buf { |
32 | | use std::io::{BufRead, Cursor, Read, Seek}; |
33 | | |
34 | | /// A reader that returns at most 1 byte in a single call to `read`. |
35 | | pub struct SmalBuf { |
36 | | inner: Cursor<Vec<u8>>, |
37 | | } |
38 | | |
39 | | impl SmalBuf { |
40 | 15.8k | pub fn new(inner: Vec<u8>) -> Self { |
41 | 15.8k | SmalBuf { |
42 | 15.8k | inner: Cursor::new(inner), |
43 | 15.8k | } |
44 | 15.8k | } |
45 | | } |
46 | | |
47 | | impl Read for SmalBuf { |
48 | 32.4M | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
49 | 32.4M | if buf.is_empty() { |
50 | 0 | return Ok(0); |
51 | 32.4M | } |
52 | 32.4M | self.inner.read(&mut buf[..1]) |
53 | 32.4M | } |
54 | | } |
55 | | impl BufRead for SmalBuf { |
56 | 0 | fn fill_buf(&mut self) -> std::io::Result<&[u8]> { |
57 | 0 | let buf = self.inner.fill_buf()?; |
58 | 0 | Ok(&buf[..buf.len().min(1)]) |
59 | 0 | } |
60 | | |
61 | 0 | fn consume(&mut self, amt: usize) { |
62 | 0 | self.inner.consume(amt); |
63 | 0 | } |
64 | | } |
65 | | impl Seek for SmalBuf { |
66 | 0 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> { |
67 | 0 | self.inner.seek(pos) |
68 | 0 | } |
69 | | } |
70 | | } |
71 | | |
72 | | mod intermittent_eofs { |
73 | | |
74 | | use std::cell::Cell; |
75 | | use std::io::{BufRead, Read, Seek}; |
76 | | use std::rc::Rc; |
77 | | |
78 | | /// A reader that returns `std::io::ErrorKind::UnexpectedEof` errors in every other read. |
79 | | /// EOFs can be temporarily disabled and re-enabled later using the associated `EofController`. |
80 | | pub struct IntermittentEofs<R: BufRead + Seek> { |
81 | | inner: R, |
82 | | |
83 | | /// Controls whether intermittent EOFs happen at all. |
84 | | controller: Rc<EofController>, |
85 | | |
86 | | /// Controls whether an intermittent EOF will happen during the next `read` |
87 | | /// (when enabled, intermittent EOFs happen every other `read`). |
88 | | eof_soon: bool, |
89 | | } |
90 | | |
91 | | impl<R: BufRead + Seek> IntermittentEofs<R> { |
92 | 7.92k | pub fn new(inner: R) -> Self { |
93 | 7.92k | Self { |
94 | 7.92k | inner, |
95 | 7.92k | controller: Rc::new(EofController::new()), |
96 | 7.92k | eof_soon: true, |
97 | 7.92k | } |
98 | 7.92k | } |
99 | | |
100 | 7.92k | pub fn controller(&self) -> Rc<EofController> { |
101 | 7.92k | self.controller.clone() |
102 | 7.92k | } |
103 | | } |
104 | | |
105 | | impl<R: BufRead + Seek> Read for IntermittentEofs<R> { |
106 | 26.2M | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
107 | 26.2M | if self.controller.are_intermittent_eofs_enabled() && self.eof_soon { |
108 | 10.0M | self.eof_soon = false; |
109 | 10.0M | return Ok(0); |
110 | 16.2M | } |
111 | 16.2M | |
112 | 16.2M | self.eof_soon = true; |
113 | 16.2M | let inner_result = self.inner.read(buf); |
114 | 16.2M | if let Ok(0) = &inner_result { |
115 | 6.14k | self.controller.mark_inner_eof(); |
116 | 16.2M | } |
117 | | |
118 | 16.2M | inner_result |
119 | 26.2M | } |
120 | | } |
121 | | impl<R: BufRead + Seek> BufRead for IntermittentEofs<R> { |
122 | 0 | fn fill_buf(&mut self) -> std::io::Result<&[u8]> { |
123 | 0 | self.inner.fill_buf() |
124 | 0 | } |
125 | | |
126 | 0 | fn consume(&mut self, amt: usize) { |
127 | 0 | self.inner.consume(amt); |
128 | 0 | } |
129 | | } |
130 | | impl<R: BufRead + Seek> Seek for IntermittentEofs<R> { |
131 | 0 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> { |
132 | 0 | self.inner.seek(pos) |
133 | 0 | } |
134 | | } |
135 | | |
136 | | pub struct EofController { |
137 | | are_intermittent_eofs_enabled: Cell<bool>, |
138 | | did_reach_inner_eof: Cell<bool>, |
139 | | } |
140 | | |
141 | | impl EofController { |
142 | 7.92k | fn new() -> Self { |
143 | 7.92k | Self { |
144 | 7.92k | are_intermittent_eofs_enabled: Cell::new(true), |
145 | 7.92k | did_reach_inner_eof: Cell::new(false), |
146 | 7.92k | } |
147 | 7.92k | } |
148 | | |
149 | 3.79k | pub fn enable_intermittent_eofs(&self) { |
150 | 3.79k | self.are_intermittent_eofs_enabled.set(true); |
151 | 3.79k | } |
152 | | |
153 | 7.92k | pub fn disable_intermittent_eofs(&self) { |
154 | 7.92k | self.are_intermittent_eofs_enabled.set(false); |
155 | 7.92k | } |
156 | | |
157 | 26.2M | fn are_intermittent_eofs_enabled(&self) -> bool { |
158 | 26.2M | self.are_intermittent_eofs_enabled.get() |
159 | 26.2M | } |
160 | | |
161 | 6.14k | fn mark_inner_eof(&self) { |
162 | 6.14k | self.did_reach_inner_eof.set(true); |
163 | 6.14k | } |
164 | | |
165 | 10.0M | pub fn did_reach_inner_eof(&self) -> bool { |
166 | 10.0M | self.did_reach_inner_eof.get() |
167 | 10.0M | } |
168 | | } |
169 | | } |
170 | | |
171 | | fuzz_target!(|data: &[u8]| { |
172 | | let _ = test_data(data); |
173 | | }); |
174 | | |
175 | | trait BufReadSeek: BufRead + Seek {} |
176 | | impl<T> BufReadSeek for T where T: BufRead + Seek {} |
177 | | |
178 | | #[inline(always)] |
179 | 7.92k | fn test_data<'a>(data: &'a [u8]) -> Result<(), ()> { |
180 | 7.92k | let baseline_reader = Box::new(Cursor::new(data)); |
181 | 7.92k | let byte_by_byte_reader = Box::new(smal_buf::SmalBuf::new(data.to_owned())); |
182 | 7.92k | let intermittent_eofs_reader = Box::new(intermittent_eofs::IntermittentEofs::new( |
183 | 7.92k | smal_buf::SmalBuf::new(data.to_owned()), |
184 | 7.92k | )); |
185 | 7.92k | let intermittent_eofs_controller = intermittent_eofs_reader.controller(); |
186 | 7.92k | |
187 | 7.92k | // `Decoder` used to internally wrap the provided reader with a `BufReader`. Now that it has |
188 | 7.92k | // been removed, fuzzing would be far too slow if we didn't use a BufReader here. |
189 | 7.92k | let data_readers: Vec<BufReader<Box<dyn BufReadSeek>>> = vec![ |
190 | 7.92k | BufReader::new(baseline_reader), |
191 | 7.92k | BufReader::new(byte_by_byte_reader), |
192 | 7.92k | BufReader::new(intermittent_eofs_reader), |
193 | 7.92k | ]; |
194 | 7.92k | |
195 | 7.92k | let decoders = data_readers |
196 | 7.92k | .into_iter() |
197 | 23.7k | .map(|data_reader| { |
198 | 23.7k | // Small limits, we don't need them hopefully. |
199 | 23.7k | let limits = png::Limits { bytes: 1 << 16 }; |
200 | 23.7k | png::Decoder::new_with_limits(data_reader, limits) |
201 | 23.7k | }) |
202 | 7.92k | .collect::<Vec<_>>(); |
203 | 7.92k | |
204 | 7.92k | // `Decoder.read_info` consumes `self` and is therefore not resumable. To work around that |
205 | 7.92k | // let's temporarily disable intermittent EOFs: |
206 | 7.92k | intermittent_eofs_controller.disable_intermittent_eofs(); |
207 | 7.92k | let mut png_readers = decoders |
208 | 7.92k | .into_iter() |
209 | 23.7k | .map(|decoder| decoder.read_info()) |
210 | 7.92k | .assert_all_results_are_consistent() |
211 | 7.92k | .collect::<Result<Vec<_>, _>>() |
212 | 7.92k | .map_err(|_| ())?; |
213 | 3.79k | intermittent_eofs_controller.enable_intermittent_eofs(); |
214 | 3.79k | |
215 | 3.79k | let info = png_readers |
216 | 3.79k | .iter() |
217 | 11.3k | .map(|r| r.info().clone()) |
218 | 7.59k | .assert_all_items_are_same(|lhs: &png::Info, rhs: &png::Info| { |
219 | 7.59k | assert_same_info(lhs, rhs); |
220 | 7.59k | |
221 | 7.59k | // The assert below is somewhat redundant, but we use `raw_bytes` |
222 | 7.59k | // later on, so let's double-check that it's the same everywhere. |
223 | 7.59k | assert_eq!(lhs.raw_bytes(), rhs.raw_bytes()); |
224 | 7.59k | }); |
225 | 3.79k | if info.raw_bytes() > 5_000_000 { |
226 | 144 | return Err(()); |
227 | 3.65k | } |
228 | 3.65k | |
229 | 3.65k | let mut buffers = vec![vec![0; info.raw_bytes()]; png_readers.len()]; |
230 | | loop { |
231 | 4.28k | let output_infos = png_readers |
232 | 4.28k | .iter_mut() |
233 | 4.28k | .zip(buffers.iter_mut()) |
234 | 4.28k | .enumerate() |
235 | 12.8k | .map(|(i, (png_reader, buffer))| { |
236 | 12.8k | let eof_controller = if i == 2 { |
237 | 4.28k | Some(&intermittent_eofs_controller) |
238 | | } else { |
239 | 8.57k | None |
240 | | }; |
241 | 10.0M | retry_after_eofs(eof_controller, || { |
242 | 10.0M | png_reader.next_frame(buffer.as_mut_slice()) |
243 | 10.0M | }) |
244 | 12.8k | }) |
245 | 4.28k | .assert_all_results_are_consistent() |
246 | 4.28k | .collect::<Result<Vec<_>, _>>() |
247 | 4.28k | .map_err(|_| ())?; |
248 | 635 | output_infos.into_iter().assert_all_items_are_equal(); |
249 | 635 | buffers.iter().assert_all_items_are_equal(); |
250 | | } |
251 | 7.92k | } |
252 | | |
253 | 12.8k | fn retry_after_eofs<T>( |
254 | 12.8k | eof_controller: Option<&std::rc::Rc<intermittent_eofs::EofController>>, |
255 | 12.8k | mut f: impl FnMut() -> Result<T, png::DecodingError>, |
256 | 12.8k | ) -> Result<T, png::DecodingError> { |
257 | | loop { |
258 | 10.0M | let result = f(); |
259 | 10.0M | match result.as_ref() { |
260 | 10.0M | Err(png::DecodingError::IoError(e)) => { |
261 | 10.0M | if e.kind() == std::io::ErrorKind::UnexpectedEof { |
262 | 10.0M | if let Some(ctrl) = eof_controller { |
263 | 10.0M | if !ctrl.did_reach_inner_eof() { |
264 | 10.0M | continue; |
265 | 2.72k | } |
266 | 5.45k | } |
267 | 0 | } |
268 | | } |
269 | 4.67k | _ => (), |
270 | | } |
271 | 12.8k | break result; |
272 | 12.8k | } |
273 | 12.8k | } |
274 | | |
275 | 7.59k | fn assert_same_info(lhs: &png::Info, rhs: &png::Info) { |
276 | 7.59k | // Check that all decoders report the same `IHDR` fields. |
277 | 7.59k | assert_eq!(lhs.width, rhs.width); |
278 | 7.59k | assert_eq!(lhs.height, rhs.height); |
279 | 7.59k | assert_eq!(lhs.bit_depth, rhs.bit_depth); |
280 | 7.59k | assert_eq!(lhs.color_type, rhs.color_type); |
281 | 7.59k | assert_eq!(lhs.interlaced, rhs.interlaced); |
282 | | |
283 | | // Check all other `Info` fields that implement `Eq`. |
284 | 7.59k | assert_eq!(lhs.chrm_chunk, rhs.chrm_chunk); |
285 | 7.59k | assert_eq!(lhs.gama_chunk, rhs.gama_chunk); |
286 | 7.59k | assert_eq!(lhs.icc_profile, rhs.icc_profile); |
287 | 7.59k | assert_eq!(lhs.palette, rhs.palette); |
288 | 7.59k | assert_eq!(lhs.source_chromaticities, rhs.source_chromaticities); |
289 | 7.59k | assert_eq!(lhs.source_gamma, rhs.source_gamma); |
290 | 7.59k | assert_eq!(lhs.srgb, rhs.srgb); |
291 | 7.59k | assert_eq!(lhs.trns, rhs.trns); |
292 | 7.59k | } |
293 | | |
294 | | trait IteratorExtensionsForFuzzing: Iterator + Sized { |
295 | | /// Verifies that either 1) all items in the iterator are `Ok(_)` or 2) all items in the |
296 | | /// iterator are `Err(_)`. Passes through unmodified iterator items. |
297 | 12.2k | fn assert_all_results_are_consistent<T>(self) -> impl Iterator<Item = Self::Item> |
298 | 12.2k | where |
299 | 12.2k | Self: Iterator<Item = Result<T, png::DecodingError>>, |
300 | 12.2k | { |
301 | 12.2k | // Eagerly collect all the items - this makes sure we check consistency of *all* results, |
302 | 12.2k | // even if a downstream iterator combinator consumes items lazily and never "pumps" some |
303 | 12.2k | // items via `next`. (`iter.take(2)` is one example of such lazy consumer; |
304 | 12.2k | // `iter_of_results.collect::<Result<Vec<_>, _>>()` is another.) |
305 | 12.2k | let all_results = self.collect::<Vec<_>>(); |
306 | 12.2k | |
307 | 21.0k | let any_err = all_results.iter().any(|res| res.is_err()); <core::iter::adapters::map::Map<core::iter::adapters::enumerate::Enumerate<core::iter::adapters::zip::Zip<core::slice::iter::IterMut<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, core::slice::iter::IterMut<alloc::vec::Vec<u8>>>>, buf_independent::test_data::{closure#5}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::OutputInfo>::{closure#0} Line | Count | Source | 307 | 5.55k | let any_err = all_results.iter().any(|res| res.is_err()); |
<core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<png::decoder::Decoder<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#1}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>::{closure#0} Line | Count | Source | 307 | 15.5k | let any_err = all_results.iter().any(|res| res.is_err()); |
|
308 | 27.7k | let any_ok = all_results.iter().any(|res| res.is_ok()); <core::iter::adapters::map::Map<core::iter::adapters::enumerate::Enumerate<core::iter::adapters::zip::Zip<core::slice::iter::IterMut<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, core::slice::iter::IterMut<alloc::vec::Vec<u8>>>>, buf_independent::test_data::{closure#5}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::OutputInfo>::{closure#1} Line | Count | Source | 308 | 11.5k | let any_ok = all_results.iter().any(|res| res.is_ok()); |
<core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<png::decoder::Decoder<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#1}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>::{closure#1} Line | Count | Source | 308 | 16.1k | let any_ok = all_results.iter().any(|res| res.is_ok()); |
|
309 | 12.2k | if any_err && any_ok { |
310 | | // Replacing `Self::Item` with an "ok" string, because we want to support items |
311 | | // that do not implement `Debug`. |
312 | 0 | let printable_results = all_results.iter().map(|res| res.as_ref().map(|_| "ok")); Unexecuted instantiation: <core::iter::adapters::map::Map<core::iter::adapters::enumerate::Enumerate<core::iter::adapters::zip::Zip<core::slice::iter::IterMut<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, core::slice::iter::IterMut<alloc::vec::Vec<u8>>>>, buf_independent::test_data::{closure#5}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::OutputInfo>::{closure#2} Unexecuted instantiation: <core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<png::decoder::Decoder<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#1}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>::{closure#2} Unexecuted instantiation: <core::iter::adapters::map::Map<core::iter::adapters::enumerate::Enumerate<core::iter::adapters::zip::Zip<core::slice::iter::IterMut<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, core::slice::iter::IterMut<alloc::vec::Vec<u8>>>>, buf_independent::test_data::{closure#5}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::OutputInfo>::{closure#2}::{closure#0} Unexecuted instantiation: <core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<png::decoder::Decoder<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#1}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>::{closure#2}::{closure#0} |
313 | 0 | for (i, res) in printable_results.enumerate() { |
314 | 0 | eprintln!("Result #{i}: {res:?}"); |
315 | 0 | } |
316 | 0 | panic!("Inconsistent results - some are Ok(_) and some are Err(_)"); |
317 | 12.2k | } |
318 | 12.2k | |
319 | 12.2k | all_results.into_iter() |
320 | 12.2k | } <core::iter::adapters::map::Map<core::iter::adapters::enumerate::Enumerate<core::iter::adapters::zip::Zip<core::slice::iter::IterMut<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, core::slice::iter::IterMut<alloc::vec::Vec<u8>>>>, buf_independent::test_data::{closure#5}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::OutputInfo> Line | Count | Source | 297 | 4.28k | fn assert_all_results_are_consistent<T>(self) -> impl Iterator<Item = Self::Item> | 298 | 4.28k | where | 299 | 4.28k | Self: Iterator<Item = Result<T, png::DecodingError>>, | 300 | 4.28k | { | 301 | 4.28k | // Eagerly collect all the items - this makes sure we check consistency of *all* results, | 302 | 4.28k | // even if a downstream iterator combinator consumes items lazily and never "pumps" some | 303 | 4.28k | // items via `next`. (`iter.take(2)` is one example of such lazy consumer; | 304 | 4.28k | // `iter_of_results.collect::<Result<Vec<_>, _>>()` is another.) | 305 | 4.28k | let all_results = self.collect::<Vec<_>>(); | 306 | 4.28k | | 307 | 4.28k | let any_err = all_results.iter().any(|res| res.is_err()); | 308 | 4.28k | let any_ok = all_results.iter().any(|res| res.is_ok()); | 309 | 4.28k | if any_err && any_ok { | 310 | | // Replacing `Self::Item` with an "ok" string, because we want to support items | 311 | | // that do not implement `Debug`. | 312 | 0 | let printable_results = all_results.iter().map(|res| res.as_ref().map(|_| "ok")); | 313 | 0 | for (i, res) in printable_results.enumerate() { | 314 | 0 | eprintln!("Result #{i}: {res:?}"); | 315 | 0 | } | 316 | 0 | panic!("Inconsistent results - some are Ok(_) and some are Err(_)"); | 317 | 4.28k | } | 318 | 4.28k | | 319 | 4.28k | all_results.into_iter() | 320 | 4.28k | } |
<core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<png::decoder::Decoder<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#1}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_results_are_consistent::<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>> Line | Count | Source | 297 | 7.92k | fn assert_all_results_are_consistent<T>(self) -> impl Iterator<Item = Self::Item> | 298 | 7.92k | where | 299 | 7.92k | Self: Iterator<Item = Result<T, png::DecodingError>>, | 300 | 7.92k | { | 301 | 7.92k | // Eagerly collect all the items - this makes sure we check consistency of *all* results, | 302 | 7.92k | // even if a downstream iterator combinator consumes items lazily and never "pumps" some | 303 | 7.92k | // items via `next`. (`iter.take(2)` is one example of such lazy consumer; | 304 | 7.92k | // `iter_of_results.collect::<Result<Vec<_>, _>>()` is another.) | 305 | 7.92k | let all_results = self.collect::<Vec<_>>(); | 306 | 7.92k | | 307 | 7.92k | let any_err = all_results.iter().any(|res| res.is_err()); | 308 | 7.92k | let any_ok = all_results.iter().any(|res| res.is_ok()); | 309 | 7.92k | if any_err && any_ok { | 310 | | // Replacing `Self::Item` with an "ok" string, because we want to support items | 311 | | // that do not implement `Debug`. | 312 | 0 | let printable_results = all_results.iter().map(|res| res.as_ref().map(|_| "ok")); | 313 | 0 | for (i, res) in printable_results.enumerate() { | 314 | 0 | eprintln!("Result #{i}: {res:?}"); | 315 | 0 | } | 316 | 0 | panic!("Inconsistent results - some are Ok(_) and some are Err(_)"); | 317 | 7.92k | } | 318 | 7.92k | | 319 | 7.92k | all_results.into_iter() | 320 | 7.92k | } |
|
321 | | |
322 | | /// Verifies that all items in the iterator are the same (according to their `Eq` |
323 | | /// implementation). Returns one of the items. |
324 | 1.27k | fn assert_all_items_are_equal(self) -> Self::Item |
325 | 1.27k | where |
326 | 1.27k | Self::Item: Debug + Eq, |
327 | 1.27k | { |
328 | 2.54k | self.assert_all_items_are_same(|lhs, rhs| assert_eq!(lhs, rhs)) <alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0} Line | Count | Source | 328 | 1.27k | self.assert_all_items_are_same(|lhs, rhs| assert_eq!(lhs, rhs)) |
<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0} Line | Count | Source | 328 | 1.27k | self.assert_all_items_are_same(|lhs, rhs| assert_eq!(lhs, rhs)) |
|
329 | 1.27k | } <core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal Line | Count | Source | 324 | 635 | fn assert_all_items_are_equal(self) -> Self::Item | 325 | 635 | where | 326 | 635 | Self::Item: Debug + Eq, | 327 | 635 | { | 328 | 635 | self.assert_all_items_are_same(|lhs, rhs| assert_eq!(lhs, rhs)) | 329 | 635 | } |
<alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal Line | Count | Source | 324 | 635 | fn assert_all_items_are_equal(self) -> Self::Item | 325 | 635 | where | 326 | 635 | Self::Item: Debug + Eq, | 327 | 635 | { | 328 | 635 | self.assert_all_items_are_same(|lhs, rhs| assert_eq!(lhs, rhs)) | 329 | 635 | } |
|
330 | | |
331 | | /// Verifies that all items in the iterator are the same (according to the `assert_same` |
332 | | /// function. Returns one of the items. |
333 | 5.06k | fn assert_all_items_are_same<F>(self, mut assert_same: F) -> <Self as Iterator>::Item |
334 | 5.06k | where |
335 | 5.06k | F: for<'a, 'b> FnMut(&'a Self::Item, &'b Self::Item), |
336 | 5.06k | { |
337 | 5.06k | self.enumerate() |
338 | 10.1k | .reduce(|(i, lhs), (j, rhs)| { |
339 | 10.1k | let panic = { |
340 | 10.1k | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); |
341 | 10.1k | let lhs = std::panic::AssertUnwindSafe(&lhs); |
342 | 10.1k | let rhs = std::panic::AssertUnwindSafe(&rhs); |
343 | 10.1k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) <alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#0}::{closure#0} Line | Count | Source | 343 | 1.27k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) |
<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#0}::{closure#0} Line | Count | Source | 343 | 1.27k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) |
<core::iter::adapters::map::Map<core::slice::iter::Iter<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#3}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<buf_independent::test_data::{closure#4}>::{closure#0}::{closure#0} Line | Count | Source | 343 | 7.59k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) |
|
344 | 10.1k | }; |
345 | 10.1k | match panic { |
346 | 10.1k | Ok(_) => (), |
347 | 0 | Err(panic) => { |
348 | 0 | eprintln!("Difference found when comparing item #{i} and #{j}."); |
349 | 0 | std::panic::resume_unwind(panic); |
350 | | } |
351 | | } |
352 | 10.1k | ( |
353 | 10.1k | j, lhs, /* Arbitrary - could just as well return `rhs` */ |
354 | 10.1k | ) |
355 | 10.1k | }) <alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#0} Line | Count | Source | 338 | 1.27k | .reduce(|(i, lhs), (j, rhs)| { | 339 | 1.27k | let panic = { | 340 | 1.27k | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 341 | 1.27k | let lhs = std::panic::AssertUnwindSafe(&lhs); | 342 | 1.27k | let rhs = std::panic::AssertUnwindSafe(&rhs); | 343 | 1.27k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 344 | 1.27k | }; | 345 | 1.27k | match panic { | 346 | 1.27k | Ok(_) => (), | 347 | 0 | Err(panic) => { | 348 | 0 | eprintln!("Difference found when comparing item #{i} and #{j}."); | 349 | 0 | std::panic::resume_unwind(panic); | 350 | | } | 351 | | } | 352 | 1.27k | ( | 353 | 1.27k | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 354 | 1.27k | ) | 355 | 1.27k | }) |
<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#0} Line | Count | Source | 338 | 1.27k | .reduce(|(i, lhs), (j, rhs)| { | 339 | 1.27k | let panic = { | 340 | 1.27k | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 341 | 1.27k | let lhs = std::panic::AssertUnwindSafe(&lhs); | 342 | 1.27k | let rhs = std::panic::AssertUnwindSafe(&rhs); | 343 | 1.27k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 344 | 1.27k | }; | 345 | 1.27k | match panic { | 346 | 1.27k | Ok(_) => (), | 347 | 0 | Err(panic) => { | 348 | 0 | eprintln!("Difference found when comparing item #{i} and #{j}."); | 349 | 0 | std::panic::resume_unwind(panic); | 350 | | } | 351 | | } | 352 | 1.27k | ( | 353 | 1.27k | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 354 | 1.27k | ) | 355 | 1.27k | }) |
<core::iter::adapters::map::Map<core::slice::iter::Iter<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#3}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<buf_independent::test_data::{closure#4}>::{closure#0} Line | Count | Source | 338 | 7.59k | .reduce(|(i, lhs), (j, rhs)| { | 339 | 7.59k | let panic = { | 340 | 7.59k | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 341 | 7.59k | let lhs = std::panic::AssertUnwindSafe(&lhs); | 342 | 7.59k | let rhs = std::panic::AssertUnwindSafe(&rhs); | 343 | 7.59k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 344 | 7.59k | }; | 345 | 7.59k | match panic { | 346 | 7.59k | Ok(_) => (), | 347 | 0 | Err(panic) => { | 348 | 0 | eprintln!("Difference found when comparing item #{i} and #{j}."); | 349 | 0 | std::panic::resume_unwind(panic); | 350 | | } | 351 | | } | 352 | 7.59k | ( | 353 | 7.59k | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 354 | 7.59k | ) | 355 | 7.59k | }) |
|
356 | 5.06k | .map(|(_index, item)| item) <alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#1} Line | Count | Source | 356 | 635 | .map(|(_index, item)| item) |
<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#1} Line | Count | Source | 356 | 635 | .map(|(_index, item)| item) |
<core::iter::adapters::map::Map<core::slice::iter::Iter<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#3}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<buf_independent::test_data::{closure#4}>::{closure#1} Line | Count | Source | 356 | 3.79k | .map(|(_index, item)| item) |
|
357 | 5.06k | .expect("Expecting a non-empty iterator") |
358 | 5.06k | } <core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}> Line | Count | Source | 333 | 635 | fn assert_all_items_are_same<F>(self, mut assert_same: F) -> <Self as Iterator>::Item | 334 | 635 | where | 335 | 635 | F: for<'a, 'b> FnMut(&'a Self::Item, &'b Self::Item), | 336 | 635 | { | 337 | 635 | self.enumerate() | 338 | 635 | .reduce(|(i, lhs), (j, rhs)| { | 339 | | let panic = { | 340 | | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 341 | | let lhs = std::panic::AssertUnwindSafe(&lhs); | 342 | | let rhs = std::panic::AssertUnwindSafe(&rhs); | 343 | | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 344 | | }; | 345 | | match panic { | 346 | | Ok(_) => (), | 347 | | Err(panic) => { | 348 | | eprintln!("Difference found when comparing item #{i} and #{j}."); | 349 | | std::panic::resume_unwind(panic); | 350 | | } | 351 | | } | 352 | | ( | 353 | | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 354 | | ) | 355 | 635 | }) | 356 | 635 | .map(|(_index, item)| item) | 357 | 635 | .expect("Expecting a non-empty iterator") | 358 | 635 | } |
<alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<png::decoder::OutputInfo> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}> Line | Count | Source | 333 | 635 | fn assert_all_items_are_same<F>(self, mut assert_same: F) -> <Self as Iterator>::Item | 334 | 635 | where | 335 | 635 | F: for<'a, 'b> FnMut(&'a Self::Item, &'b Self::Item), | 336 | 635 | { | 337 | 635 | self.enumerate() | 338 | 635 | .reduce(|(i, lhs), (j, rhs)| { | 339 | | let panic = { | 340 | | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 341 | | let lhs = std::panic::AssertUnwindSafe(&lhs); | 342 | | let rhs = std::panic::AssertUnwindSafe(&rhs); | 343 | | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 344 | | }; | 345 | | match panic { | 346 | | Ok(_) => (), | 347 | | Err(panic) => { | 348 | | eprintln!("Difference found when comparing item #{i} and #{j}."); | 349 | | std::panic::resume_unwind(panic); | 350 | | } | 351 | | } | 352 | | ( | 353 | | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 354 | | ) | 355 | 635 | }) | 356 | 635 | .map(|(_index, item)| item) | 357 | 635 | .expect("Expecting a non-empty iterator") | 358 | 635 | } |
<core::iter::adapters::map::Map<core::slice::iter::Iter<png::decoder::Reader<std::io::buffered::bufreader::BufReader<alloc::boxed::Box<dyn buf_independent::BufReadSeek>>>>, buf_independent::test_data::{closure#3}> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<buf_independent::test_data::{closure#4}> Line | Count | Source | 333 | 3.79k | fn assert_all_items_are_same<F>(self, mut assert_same: F) -> <Self as Iterator>::Item | 334 | 3.79k | where | 335 | 3.79k | F: for<'a, 'b> FnMut(&'a Self::Item, &'b Self::Item), | 336 | 3.79k | { | 337 | 3.79k | self.enumerate() | 338 | 3.79k | .reduce(|(i, lhs), (j, rhs)| { | 339 | | let panic = { | 340 | | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 341 | | let lhs = std::panic::AssertUnwindSafe(&lhs); | 342 | | let rhs = std::panic::AssertUnwindSafe(&rhs); | 343 | | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 344 | | }; | 345 | | match panic { | 346 | | Ok(_) => (), | 347 | | Err(panic) => { | 348 | | eprintln!("Difference found when comparing item #{i} and #{j}."); | 349 | | std::panic::resume_unwind(panic); | 350 | | } | 351 | | } | 352 | | ( | 353 | | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 354 | | ) | 355 | 3.79k | }) | 356 | 3.79k | .map(|(_index, item)| item) | 357 | 3.79k | .expect("Expecting a non-empty iterator") | 358 | 3.79k | } |
|
359 | | } |
360 | | |
361 | | impl<T> IteratorExtensionsForFuzzing for T where T: Iterator + Sized {} |