Coverage Report

Created: 2026-01-17 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 {}