/src/image-png/fuzz/fuzz_targets/buf_independent.rs
Line | Count | Source |
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 | 14.8k | pub fn new(inner: Vec<u8>) -> Self { |
41 | 14.8k | SmalBuf { |
42 | 14.8k | inner: Cursor::new(inner), |
43 | 14.8k | } |
44 | 14.8k | } |
45 | | } |
46 | | |
47 | | impl Read for SmalBuf { |
48 | 29.1M | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
49 | 29.1M | if buf.is_empty() { |
50 | 0 | return Ok(0); |
51 | 29.1M | } |
52 | 29.1M | self.inner.read(&mut buf[..1]) |
53 | 29.1M | } |
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.41k | pub fn new(inner: R) -> Self { |
93 | 7.41k | Self { |
94 | 7.41k | inner, |
95 | 7.41k | controller: Rc::new(EofController::new()), |
96 | 7.41k | eof_soon: true, |
97 | 7.41k | } |
98 | 7.41k | } |
99 | | |
100 | 7.41k | pub fn controller(&self) -> Rc<EofController> { |
101 | 7.41k | self.controller.clone() |
102 | 7.41k | } |
103 | | } |
104 | | |
105 | | impl<R: BufRead + Seek> Read for IntermittentEofs<R> { |
106 | 22.6M | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
107 | 22.6M | if self.controller.are_intermittent_eofs_enabled() && self.eof_soon { |
108 | 8.18M | self.eof_soon = false; |
109 | 8.18M | return Ok(0); |
110 | 14.4M | } |
111 | | |
112 | 14.4M | self.eof_soon = true; |
113 | 14.4M | let inner_result = self.inner.read(buf); |
114 | 14.4M | if let Ok(0) = &inner_result { |
115 | 4.92k | self.controller.mark_inner_eof(); |
116 | 14.4M | } |
117 | | |
118 | 14.4M | inner_result |
119 | 22.6M | } |
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.41k | fn new() -> Self { |
143 | 7.41k | Self { |
144 | 7.41k | are_intermittent_eofs_enabled: Cell::new(true), |
145 | 7.41k | did_reach_inner_eof: Cell::new(false), |
146 | 7.41k | } |
147 | 7.41k | } |
148 | | |
149 | 4.20k | pub fn enable_intermittent_eofs(&self) { |
150 | 4.20k | self.are_intermittent_eofs_enabled.set(true); |
151 | 4.20k | } |
152 | | |
153 | 7.41k | pub fn disable_intermittent_eofs(&self) { |
154 | 7.41k | self.are_intermittent_eofs_enabled.set(false); |
155 | 7.41k | } |
156 | | |
157 | 22.6M | fn are_intermittent_eofs_enabled(&self) -> bool { |
158 | 22.6M | self.are_intermittent_eofs_enabled.get() |
159 | 22.6M | } |
160 | | |
161 | 4.92k | fn mark_inner_eof(&self) { |
162 | 4.92k | self.did_reach_inner_eof.set(true); |
163 | 4.92k | } |
164 | | |
165 | 8.19M | pub fn did_reach_inner_eof(&self) -> bool { |
166 | 8.19M | self.did_reach_inner_eof.get() |
167 | 8.19M | } |
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.41k | fn test_data<'a>(data: &'a [u8]) -> Result<(), ()> { |
180 | 7.41k | let baseline_reader = Box::new(Cursor::new(data)); |
181 | 7.41k | let byte_by_byte_reader = Box::new(smal_buf::SmalBuf::new(data.to_owned())); |
182 | 7.41k | let intermittent_eofs_reader = Box::new(intermittent_eofs::IntermittentEofs::new( |
183 | 7.41k | smal_buf::SmalBuf::new(data.to_owned()), |
184 | | )); |
185 | 7.41k | let intermittent_eofs_controller = intermittent_eofs_reader.controller(); |
186 | | |
187 | | // `Decoder` used to internally wrap the provided reader with a `BufReader`. Now that it has |
188 | | // been removed, fuzzing would be far too slow if we didn't use a BufReader here. |
189 | 7.41k | let data_readers: Vec<BufReader<Box<dyn BufReadSeek>>> = vec![ |
190 | 7.41k | BufReader::new(baseline_reader), |
191 | 7.41k | BufReader::new(byte_by_byte_reader), |
192 | 7.41k | BufReader::new(intermittent_eofs_reader), |
193 | | ]; |
194 | | |
195 | 7.41k | let decoders = data_readers |
196 | 7.41k | .into_iter() |
197 | 22.2k | .map(|data_reader| { |
198 | | // Small limits, we don't need them hopefully. |
199 | 22.2k | let limits = png::Limits { bytes: 1 << 16 }; |
200 | 22.2k | png::Decoder::new_with_limits(data_reader, limits) |
201 | 22.2k | }) |
202 | 7.41k | .collect::<Vec<_>>(); |
203 | | |
204 | | // `Decoder.read_info` consumes `self` and is therefore not resumable. To work around that |
205 | | // let's temporarily disable intermittent EOFs: |
206 | 7.41k | intermittent_eofs_controller.disable_intermittent_eofs(); |
207 | 7.41k | let mut png_readers = decoders |
208 | 7.41k | .into_iter() |
209 | 22.2k | .map(|decoder| decoder.read_info()) |
210 | 7.41k | .assert_all_results_are_consistent() |
211 | 7.41k | .collect::<Result<Vec<_>, _>>() |
212 | 7.41k | .map_err(|_| ())?; |
213 | 4.20k | intermittent_eofs_controller.enable_intermittent_eofs(); |
214 | | |
215 | 4.20k | let info = png_readers |
216 | 4.20k | .iter() |
217 | 12.6k | .map(|r| r.info().clone()) |
218 | 8.40k | .assert_all_items_are_same(|lhs: &png::Info, rhs: &png::Info| { |
219 | 8.40k | assert_same_info(lhs, rhs); |
220 | | |
221 | | // The assert below is somewhat redundant, but we use `raw_bytes` |
222 | | // later on, so let's double-check that it's the same everywhere. |
223 | 8.40k | assert_eq!(lhs.raw_bytes(), rhs.raw_bytes()); |
224 | 8.40k | }); |
225 | 4.20k | if info.raw_bytes() > 5_000_000 { |
226 | 191 | return Err(()); |
227 | 4.01k | } |
228 | | |
229 | 4.01k | let mut buffers = vec![vec![0; info.raw_bytes()]; png_readers.len()]; |
230 | | loop { |
231 | 4.01k | let output_infos = png_readers |
232 | 4.01k | .iter_mut() |
233 | 4.01k | .zip(buffers.iter_mut()) |
234 | 4.01k | .enumerate() |
235 | 12.0k | .map(|(i, (png_reader, buffer))| { |
236 | 12.0k | let eof_controller = if i == 2 { |
237 | 4.01k | Some(&intermittent_eofs_controller) |
238 | | } else { |
239 | 8.02k | None |
240 | | }; |
241 | 8.20M | retry_after_eofs(eof_controller, || { |
242 | 8.20M | png_reader.next_frame(buffer.as_mut_slice()) |
243 | 8.20M | .and_then(|_| png_reader.finish()) |
244 | 8.20M | }) |
245 | 12.0k | }) |
246 | 4.01k | .assert_all_results_are_consistent() |
247 | 4.01k | .collect::<Result<Vec<_>, _>>() |
248 | 4.01k | .map_err(|_| ())?; |
249 | 0 | output_infos.into_iter().assert_all_items_are_equal(); |
250 | 0 | buffers.iter().assert_all_items_are_equal(); |
251 | | } |
252 | 7.41k | } |
253 | | |
254 | 12.0k | fn retry_after_eofs<T>( |
255 | 12.0k | eof_controller: Option<&std::rc::Rc<intermittent_eofs::EofController>>, |
256 | 12.0k | mut f: impl FnMut() -> Result<T, png::DecodingError>, |
257 | 12.0k | ) -> Result<T, png::DecodingError> { |
258 | | loop { |
259 | 8.20M | let result = f(); |
260 | 8.20M | match result.as_ref() { |
261 | 8.19M | Err(png::DecodingError::IoError(e)) => { |
262 | 8.19M | if e.kind() == std::io::ErrorKind::UnexpectedEof { |
263 | 8.19M | if let Some(ctrl) = eof_controller { |
264 | 8.19M | if !ctrl.did_reach_inner_eof() { |
265 | 8.18M | continue; |
266 | 2.68k | } |
267 | 5.84k | } |
268 | 0 | } |
269 | | } |
270 | 3.50k | _ => (), |
271 | | } |
272 | 12.0k | break result; |
273 | | } |
274 | 12.0k | } |
275 | | |
276 | 8.40k | fn assert_same_info(lhs: &png::Info, rhs: &png::Info) { |
277 | | // Check that all decoders report the same `IHDR` fields. |
278 | 8.40k | assert_eq!(lhs.width, rhs.width); |
279 | 8.40k | assert_eq!(lhs.height, rhs.height); |
280 | 8.40k | assert_eq!(lhs.bit_depth, rhs.bit_depth); |
281 | 8.40k | assert_eq!(lhs.color_type, rhs.color_type); |
282 | 8.40k | assert_eq!(lhs.interlaced, rhs.interlaced); |
283 | | |
284 | | // Check all other `Info` fields that implement `Eq`. |
285 | 8.40k | assert_eq!(lhs.chrm_chunk, rhs.chrm_chunk); |
286 | 8.40k | assert_eq!(lhs.gama_chunk, rhs.gama_chunk); |
287 | 8.40k | assert_eq!(lhs.icc_profile, rhs.icc_profile); |
288 | 8.40k | assert_eq!(lhs.palette, rhs.palette); |
289 | 8.40k | assert_eq!(lhs.source_chromaticities, rhs.source_chromaticities); |
290 | 8.40k | assert_eq!(lhs.source_gamma, rhs.source_gamma); |
291 | 8.40k | assert_eq!(lhs.srgb, rhs.srgb); |
292 | 8.40k | assert_eq!(lhs.trns, rhs.trns); |
293 | 8.40k | } |
294 | | |
295 | | trait IteratorExtensionsForFuzzing: Iterator + Sized { |
296 | | /// Verifies that either 1) all items in the iterator are `Ok(_)` or 2) all items in the |
297 | | /// iterator are `Err(_)`. Passes through unmodified iterator items. |
298 | 11.4k | fn assert_all_results_are_consistent<T>(self) -> impl Iterator<Item = Self::Item> |
299 | 11.4k | where |
300 | 11.4k | Self: Iterator<Item = Result<T, png::DecodingError>>, |
301 | | { |
302 | | // Eagerly collect all the items - this makes sure we check consistency of *all* results, |
303 | | // even if a downstream iterator combinator consumes items lazily and never "pumps" some |
304 | | // items via `next`. (`iter.take(2)` is one example of such lazy consumer; |
305 | | // `iter_of_results.collect::<Result<Vec<_>, _>>()` is another.) |
306 | 11.4k | let all_results = self.collect::<Vec<_>>(); |
307 | | |
308 | 19.8k | 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::<()>::{closure#0}Line | Count | Source | 308 | 4.01k | 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 | 308 | 15.8k | let any_err = all_results.iter().any(|res| res.is_err()); |
|
309 | 25.8k | 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::<()>::{closure#1}Line | Count | Source | 309 | 12.0k | 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 | 309 | 13.8k | let any_ok = all_results.iter().any(|res| res.is_ok()); |
|
310 | 11.4k | if any_err && any_ok { |
311 | | // Replacing `Self::Item` with an "ok" string, because we want to support items |
312 | | // that do not implement `Debug`. |
313 | 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::<()>::{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} |
314 | 0 | for (i, res) in printable_results.enumerate() { |
315 | 0 | eprintln!("Result #{i}: {res:?}"); |
316 | 0 | } |
317 | 0 | panic!("Inconsistent results - some are Ok(_) and some are Err(_)"); |
318 | 11.4k | } |
319 | | |
320 | 11.4k | all_results.into_iter() |
321 | 11.4k | } <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::<()>Line | Count | Source | 298 | 4.01k | fn assert_all_results_are_consistent<T>(self) -> impl Iterator<Item = Self::Item> | 299 | 4.01k | where | 300 | 4.01k | Self: Iterator<Item = Result<T, png::DecodingError>>, | 301 | | { | 302 | | // Eagerly collect all the items - this makes sure we check consistency of *all* results, | 303 | | // even if a downstream iterator combinator consumes items lazily and never "pumps" some | 304 | | // items via `next`. (`iter.take(2)` is one example of such lazy consumer; | 305 | | // `iter_of_results.collect::<Result<Vec<_>, _>>()` is another.) | 306 | 4.01k | let all_results = self.collect::<Vec<_>>(); | 307 | | | 308 | 4.01k | let any_err = all_results.iter().any(|res| res.is_err()); | 309 | 4.01k | let any_ok = all_results.iter().any(|res| res.is_ok()); | 310 | 4.01k | if any_err && any_ok { | 311 | | // Replacing `Self::Item` with an "ok" string, because we want to support items | 312 | | // that do not implement `Debug`. | 313 | 0 | let printable_results = all_results.iter().map(|res| res.as_ref().map(|_| "ok")); | 314 | 0 | for (i, res) in printable_results.enumerate() { | 315 | 0 | eprintln!("Result #{i}: {res:?}"); | 316 | 0 | } | 317 | 0 | panic!("Inconsistent results - some are Ok(_) and some are Err(_)"); | 318 | 4.01k | } | 319 | | | 320 | 4.01k | all_results.into_iter() | 321 | 4.01k | } |
<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 | 298 | 7.41k | fn assert_all_results_are_consistent<T>(self) -> impl Iterator<Item = Self::Item> | 299 | 7.41k | where | 300 | 7.41k | Self: Iterator<Item = Result<T, png::DecodingError>>, | 301 | | { | 302 | | // Eagerly collect all the items - this makes sure we check consistency of *all* results, | 303 | | // even if a downstream iterator combinator consumes items lazily and never "pumps" some | 304 | | // items via `next`. (`iter.take(2)` is one example of such lazy consumer; | 305 | | // `iter_of_results.collect::<Result<Vec<_>, _>>()` is another.) | 306 | 7.41k | let all_results = self.collect::<Vec<_>>(); | 307 | | | 308 | 7.41k | let any_err = all_results.iter().any(|res| res.is_err()); | 309 | 7.41k | let any_ok = all_results.iter().any(|res| res.is_ok()); | 310 | 7.41k | if any_err && any_ok { | 311 | | // Replacing `Self::Item` with an "ok" string, because we want to support items | 312 | | // that do not implement `Debug`. | 313 | 0 | let printable_results = all_results.iter().map(|res| res.as_ref().map(|_| "ok")); | 314 | 0 | for (i, res) in printable_results.enumerate() { | 315 | 0 | eprintln!("Result #{i}: {res:?}"); | 316 | 0 | } | 317 | 0 | panic!("Inconsistent results - some are Ok(_) and some are Err(_)"); | 318 | 7.41k | } | 319 | | | 320 | 7.41k | all_results.into_iter() | 321 | 7.41k | } |
|
322 | | |
323 | | /// Verifies that all items in the iterator are the same (according to their `Eq` |
324 | | /// implementation). Returns one of the items. |
325 | 0 | fn assert_all_items_are_equal(self) -> Self::Item |
326 | 0 | where |
327 | 0 | Self::Item: Debug + Eq, |
328 | | { |
329 | 0 | self.assert_all_items_are_same(|lhs, rhs| assert_eq!(lhs, rhs)) Unexecuted instantiation: <core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}Unexecuted instantiation: <alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0} |
330 | 0 | } Unexecuted instantiation: <core::slice::iter::Iter<alloc::vec::Vec<u8>> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal Unexecuted instantiation: <alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal |
331 | | |
332 | | /// Verifies that all items in the iterator are the same (according to the `assert_same` |
333 | | /// function. Returns one of the items. |
334 | 4.20k | fn assert_all_items_are_same<F>(self, mut assert_same: F) -> <Self as Iterator>::Item |
335 | 4.20k | where |
336 | 4.20k | F: for<'a, 'b> FnMut(&'a Self::Item, &'b Self::Item), |
337 | | { |
338 | 4.20k | self.enumerate() |
339 | 8.40k | .reduce(|(i, lhs), (j, rhs)| { |
340 | 8.40k | let panic = { |
341 | 8.40k | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); |
342 | 8.40k | let lhs = std::panic::AssertUnwindSafe(&lhs); |
343 | 8.40k | let rhs = std::panic::AssertUnwindSafe(&rhs); |
344 | 8.40k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) Unexecuted instantiation: <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}Unexecuted instantiation: <alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#0}::{closure#0}<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 | 344 | 8.40k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) |
|
345 | | }; |
346 | 8.40k | match panic { |
347 | 8.40k | Ok(_) => (), |
348 | 0 | Err(panic) => { |
349 | 0 | eprintln!("Difference found when comparing item #{i} and #{j}."); |
350 | 0 | std::panic::resume_unwind(panic); |
351 | | } |
352 | | } |
353 | 8.40k | ( |
354 | 8.40k | j, lhs, /* Arbitrary - could just as well return `rhs` */ |
355 | 8.40k | ) |
356 | 8.40k | }) Unexecuted instantiation: <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}Unexecuted instantiation: <alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}>::{closure#0}<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 | 339 | 8.40k | .reduce(|(i, lhs), (j, rhs)| { | 340 | 8.40k | let panic = { | 341 | 8.40k | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 342 | 8.40k | let lhs = std::panic::AssertUnwindSafe(&lhs); | 343 | 8.40k | let rhs = std::panic::AssertUnwindSafe(&rhs); | 344 | 8.40k | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 345 | | }; | 346 | 8.40k | match panic { | 347 | 8.40k | Ok(_) => (), | 348 | 0 | Err(panic) => { | 349 | 0 | eprintln!("Difference found when comparing item #{i} and #{j}."); | 350 | 0 | std::panic::resume_unwind(panic); | 351 | | } | 352 | | } | 353 | 8.40k | ( | 354 | 8.40k | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 355 | 8.40k | ) | 356 | 8.40k | }) |
|
357 | 4.20k | .map(|(_index, item)| item) |
358 | 4.20k | .expect("Expecting a non-empty iterator") |
359 | 4.20k | } Unexecuted instantiation: <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}><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 | 334 | 4.20k | fn assert_all_items_are_same<F>(self, mut assert_same: F) -> <Self as Iterator>::Item | 335 | 4.20k | where | 336 | 4.20k | F: for<'a, 'b> FnMut(&'a Self::Item, &'b Self::Item), | 337 | | { | 338 | 4.20k | self.enumerate() | 339 | 4.20k | .reduce(|(i, lhs), (j, rhs)| { | 340 | | let panic = { | 341 | | let mut assert_same = std::panic::AssertUnwindSafe(&mut assert_same); | 342 | | let lhs = std::panic::AssertUnwindSafe(&lhs); | 343 | | let rhs = std::panic::AssertUnwindSafe(&rhs); | 344 | | std::panic::catch_unwind(move || assert_same(*lhs, *rhs)) | 345 | | }; | 346 | | match panic { | 347 | | Ok(_) => (), | 348 | | Err(panic) => { | 349 | | eprintln!("Difference found when comparing item #{i} and #{j}."); | 350 | | std::panic::resume_unwind(panic); | 351 | | } | 352 | | } | 353 | | ( | 354 | | j, lhs, /* Arbitrary - could just as well return `rhs` */ | 355 | | ) | 356 | | }) | 357 | 4.20k | .map(|(_index, item)| item) | 358 | 4.20k | .expect("Expecting a non-empty iterator") | 359 | 4.20k | } |
Unexecuted instantiation: <alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_same::<<alloc::vec::into_iter::IntoIter<()> as buf_independent::IteratorExtensionsForFuzzing>::assert_all_items_are_equal::{closure#0}> |
360 | | } |
361 | | |
362 | | impl<T> IteratorExtensionsForFuzzing for T where T: Iterator + Sized {} |