Coverage Report

Created: 2026-08-31 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/imgref-1.12.3/src/iter.rs
Line
Count
Source
1
use core::iter::FusedIterator;
2
use core::marker::PhantomData;
3
use core::num::NonZeroUsize;
4
use core::slice;
5
6
#[cfg(test)]
7
use alloc::vec;
8
9
/// Rows of the image. Call `Img.rows()` to create it.
10
///
11
/// Each element is a slice `width` pixels wide. Ignores padding, if there's any.
12
#[derive(Debug)]
13
#[must_use]
14
pub struct RowsIter<'a, T> {
15
    pub(crate) inner: slice::Chunks<'a, T>,
16
    pub(crate) width: usize,
17
}
18
19
impl<'a, T: 'a> Iterator for RowsIter<'a, T> {
20
    type Item = &'a [T];
21
22
    #[inline]
23
0
    fn next(&mut self) -> Option<Self::Item> {
24
0
        match self.inner.next() {
25
0
            Some(s) => {
26
                // guaranteed during creation of chunks iterator
27
0
                debug_assert!(s.len() >= self.width);
28
                unsafe {
29
0
                    Some(s.get_unchecked(0..self.width))
30
                }
31
            },
32
0
            None => None,
33
        }
34
0
    }
35
36
    #[inline]
37
0
    fn size_hint(&self) -> (usize, Option<usize>) {
38
0
        self.inner.size_hint()
39
0
    }
40
41
    #[inline]
42
0
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
43
0
        match self.inner.nth(n) {
44
0
            Some(s) => {
45
                // guaranteed during creation of chunks iterator
46
0
                debug_assert!(s.len() >= self.width);
47
                unsafe {
48
0
                    Some(s.get_unchecked(0..self.width))
49
                }
50
            },
51
0
            None => None,
52
        }
53
0
    }
54
55
    #[inline]
56
0
    fn count(self) -> usize {
57
0
        self.inner.count()
58
0
    }
59
}
60
61
impl<T> ExactSizeIterator for RowsIter<'_, T> {
62
    #[inline]
63
0
    fn len(&self) -> usize {
64
0
        self.inner.len()
65
0
    }
66
}
67
68
impl<T> FusedIterator for RowsIter<'_, T> {}
69
70
impl<'a, T: 'a> DoubleEndedIterator for RowsIter<'a, T> {
71
    #[inline]
72
0
    fn next_back(&mut self) -> Option<Self::Item> {
73
0
        match self.inner.next_back() {
74
0
            Some(s) => {
75
                // guaranteed during creation of chunks iterator
76
0
                debug_assert!(s.len() >= self.width);
77
                unsafe {
78
0
                    Some(s.get_unchecked(0..self.width))
79
                }
80
            },
81
0
            None => None,
82
        }
83
0
    }
84
}
85
86
/// Rows of the image. Call `Img.rows_mut()` to create it.
87
///
88
/// Each element is a slice `width` pixels wide. Ignores padding, if there's any.
89
#[derive(Debug)]
90
#[must_use]
91
pub struct RowsIterMut<'a, T> {
92
    pub(crate) width: usize,
93
    pub(crate) inner: slice::ChunksMut<'a, T>,
94
}
95
96
impl<'a, T: 'a> Iterator for RowsIterMut<'a, T> {
97
    type Item = &'a mut [T];
98
99
    #[inline]
100
0
    fn next(&mut self) -> Option<Self::Item> {
101
0
        match self.inner.next() {
102
0
            Some(s) => Some(&mut s[0..self.width]),
103
0
            None => None,
104
        }
105
0
    }
106
107
    #[inline]
108
0
    fn size_hint(&self) -> (usize, Option<usize>) {
109
0
        self.inner.size_hint()
110
0
    }
111
112
    #[inline]
113
0
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
114
0
        match self.inner.nth(n) {
115
0
            Some(s) => Some(&mut s[0..self.width]),
116
0
            None => None,
117
        }
118
0
    }
119
120
    #[inline]
121
0
    fn count(self) -> usize {
122
0
        self.inner.count()
123
0
    }
124
}
125
126
impl<T> ExactSizeIterator for RowsIterMut<'_, T> {}
127
impl<T> FusedIterator for RowsIterMut<'_, T> {}
128
129
impl<'a, T: 'a> DoubleEndedIterator for RowsIterMut<'a, T> {
130
    #[inline]
131
0
    fn next_back(&mut self) -> Option<Self::Item> {
132
0
        match self.inner.next_back() {
133
0
            Some(s) => Some(&mut s[0..self.width]),
134
0
            None => None,
135
        }
136
0
    }
137
}
138
139
/// Iterates over pixels in the (sub)image. Call `Img.pixels()` to create it.
140
///
141
/// Ignores padding, if there's any.
142
#[must_use]
143
pub struct PixelsIter<'a, T: Copy> {
144
    inner: PixelsRefIter<'a, T>,
145
}
146
147
impl<'a, T: Copy + 'a> PixelsIter<'a, T> {
148
    #[inline(always)]
149
    #[track_caller]
150
0
    pub(crate) fn new(img: super::ImgRef<'a, T>) -> Self {
151
0
        Self {
152
0
            inner: PixelsRefIter::new(img)
153
0
        }
154
0
    }
Unexecuted instantiation: <imgref::iter::PixelsIter<rgb::formats::rgba::Rgba<u8>>>::new
Unexecuted instantiation: <imgref::iter::PixelsIter<_>>::new
Unexecuted instantiation: <imgref::iter::PixelsIter<rgb::formats::rgb::Rgb<u8>>>::new
155
}
156
157
impl<'a, T: Copy + 'a> Iterator for PixelsIter<'a, T> {
158
    type Item = T;
159
160
    #[inline(always)]
161
0
    fn next(&mut self) -> Option<Self::Item> {
162
0
        self.inner.next().copied()
163
0
    }
Unexecuted instantiation: <imgref::iter::PixelsIter<rgb::formats::rgba::Rgba<u8>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <imgref::iter::PixelsIter<_> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <imgref::iter::PixelsIter<rgb::formats::rgb::Rgb<u8>> as core::iter::traits::iterator::Iterator>::next
164
}
165
166
impl<T: Copy> ExactSizeIterator for PixelsIter<'_, T> {
167
    #[inline]
168
0
    fn len(&self) -> usize {
169
0
        self.inner.len()
170
0
    }
171
}
172
173
impl<T: Copy> FusedIterator for PixelsIter<'_, T> {}
174
175
/// Iterates over pixels in the (sub)image. Call `Img.pixels_ref()` to create it.
176
///
177
/// Ignores padding, if there's any.
178
#[derive(Debug)]
179
#[must_use]
180
pub struct PixelsRefIter<'a, T> {
181
    current: *const T,
182
    current_line_end: *const T,
183
    rows_left: usize,
184
    width: NonZeroUsize,
185
    pad: usize,
186
    _dat: PhantomData<&'a [T]>,
187
}
188
189
unsafe impl<T> Send for PixelsRefIter<'_, T> where T: Sync {}
190
unsafe impl<T> Sync for PixelsRefIter<'_, T> where T: Sync {}
191
192
impl<'a, T: 'a> PixelsRefIter<'a, T> {
193
    #[inline]
194
    #[track_caller]
195
0
    pub(crate) fn new(img: super::ImgRef<'a, T>) -> Self {
196
0
        let buf = img.valid_buf();
197
0
        let height = img.height();
198
0
        match NonZeroUsize::new(img.width()) {
199
0
            Some(width) if height > 0 => {
200
0
                let stride = img.stride();
201
0
                let pad = stride - width.get();
202
0
                Self {
203
0
                    current: buf.as_ptr(),
204
0
                    current_line_end: buf[width.get()..].as_ptr(),
205
0
                    width,
206
0
                    rows_left: height - 1,
207
0
                    pad,
208
0
                    _dat: PhantomData,
209
0
                }
210
            },
211
            _ => {
212
0
                Self {
213
0
                    current: buf.as_ptr(),
214
0
                    current_line_end: buf.as_ptr(),
215
0
                    width: NonZeroUsize::new(1).unwrap(),
216
0
                    rows_left: 0,
217
0
                    pad: 0,
218
0
                    _dat: PhantomData,
219
0
                }
220
            }
221
        }
222
0
    }
Unexecuted instantiation: <imgref::iter::PixelsRefIter<rgb::formats::rgba::Rgba<u8>>>::new
Unexecuted instantiation: <imgref::iter::PixelsRefIter<_>>::new
Unexecuted instantiation: <imgref::iter::PixelsRefIter<rgb::formats::rgb::Rgb<u8>>>::new
223
}
224
225
impl<'a, T: 'a> Iterator for PixelsRefIter<'a, T> {
226
    type Item = &'a T;
227
228
    #[inline(always)]
229
0
    fn next(&mut self) -> Option<Self::Item> {
230
        unsafe {
231
0
            if self.current >= self.current_line_end {
232
0
                if self.rows_left == 0 {
233
0
                    return None;
234
0
                }
235
0
                self.rows_left -= 1;
236
0
                self.current = self.current_line_end.add(self.pad);
237
0
                self.current_line_end = self.current.add(self.width.get());
238
0
            }
239
0
            let px = &*self.current;
240
0
            self.current = self.current.add(1);
241
0
            Some(px)
242
        }
243
0
    }
Unexecuted instantiation: <imgref::iter::PixelsRefIter<rgb::formats::rgba::Rgba<u8>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <imgref::iter::PixelsRefIter<_> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <imgref::iter::PixelsRefIter<rgb::formats::rgb::Rgb<u8>> as core::iter::traits::iterator::Iterator>::next
244
245
    #[inline]
246
    #[cfg_attr(debug_assertions, track_caller)]
247
0
    fn size_hint(&self) -> (usize, Option<usize>) {
248
0
        let this_line = unsafe {
249
0
            self.current_line_end.offset_from(self.current)
250
        };
251
0
        debug_assert!(this_line >= 0);
252
0
        let len = this_line as usize + self.rows_left * self.width.get();
253
0
        (len, Some(len))
254
0
    }
255
}
256
257
impl<T: Copy> ExactSizeIterator for PixelsRefIter<'_, T> {
258
}
259
260
impl<T> FusedIterator for PixelsRefIter<'_, T> {}
261
262
/// Iterates over pixels in the (sub)image. Call `Img.pixels_mut()` to create it.
263
///
264
/// Ignores padding, if there's any.
265
#[derive(Debug)]
266
#[must_use]
267
pub struct PixelsIterMut<'a, T> {
268
    current: *mut T,
269
    current_line_end: *mut T,
270
    rows_left: usize,
271
    width: NonZeroUsize,
272
    pad: usize,
273
    _dat: PhantomData<&'a mut [T]>,
274
}
275
276
unsafe impl<T> Send for PixelsIterMut<'_, T> where T: Send {}
277
unsafe impl<T> Sync for PixelsIterMut<'_, T> where T: Sync {}
278
279
impl<'a, T: 'a> PixelsIterMut<'a, T> {
280
    #[inline]
281
    #[track_caller]
282
0
    pub(crate) fn new(mut img: super::ImgRefMut<'a, T>) -> Self {
283
0
        let width = img.width();
284
0
        let height = img.height();
285
0
        let stride = img.stride();
286
0
        let buf = img.valid_buf_mut();
287
0
        let ptr = buf.as_mut_ptr();
288
0
        match NonZeroUsize::new(width) {
289
0
            Some(width) if height > 0 => {
290
0
                Self {
291
0
                    current: ptr,
292
0
                    current_line_end: unsafe { ptr.add(width.get()) },
293
0
                    width,
294
0
                    rows_left: height - 1,
295
0
                    pad: stride - width.get(),
296
0
                    _dat: PhantomData,
297
0
                }
298
            },
299
            _ => {
300
0
                Self {
301
0
                    current: ptr,
302
0
                    current_line_end: ptr,
303
0
                    width: NonZeroUsize::new(1).unwrap(),
304
0
                    rows_left: 0,
305
0
                    pad: 0,
306
0
                    _dat: PhantomData,
307
0
                }
308
            }
309
        }
310
0
    }
311
}
312
313
impl<'a, T: 'a> Iterator for PixelsIterMut<'a, T> {
314
    type Item = &'a mut T;
315
316
    #[inline(always)]
317
0
    fn next(&mut self) -> Option<Self::Item> {
318
        unsafe {
319
0
            if self.current >= self.current_line_end {
320
0
                if self.rows_left == 0 {
321
0
                    return None;
322
0
                }
323
0
                self.rows_left -= 1;
324
0
                self.current = self.current_line_end.add(self.pad);
325
0
                self.current_line_end = self.current.add(self.width.get());
326
0
            }
327
0
            let px = &mut *self.current;
328
0
            self.current = self.current.add(1);
329
0
            Some(px)
330
        }
331
0
    }
332
333
    #[inline]
334
    #[cfg_attr(debug_assertions, track_caller)]
335
0
    fn size_hint(&self) -> (usize, Option<usize>) {
336
0
        let this_line = unsafe {
337
0
            self.current_line_end.offset_from(self.current)
338
        };
339
0
        debug_assert!(this_line >= 0);
340
0
        let len = this_line as usize + self.rows_left * self.width.get();
341
0
        (len, Some(len))
342
0
    }
343
}
344
345
impl<T: Copy> ExactSizeIterator for PixelsIterMut<'_, T> {
346
}
347
348
impl<T> FusedIterator for PixelsIterMut<'_, T> {}
349
350
#[test]
351
fn iter() {
352
    let img = super::Img::new(vec![1u8, 2], 1, 2);
353
    let mut it = img.pixels();
354
    assert_eq!(Some(1), it.next());
355
    assert_eq!(Some(2), it.next());
356
    assert_eq!(None, it.next());
357
358
    let buf = [1u8; (16 + 3) * (8 + 1)];
359
    for width in 1..16 {
360
        for height in 1..8 {
361
            for pad in 0..3 {
362
                let stride = width + pad;
363
                let img = super::Img::new_stride(&buf[..stride * height + stride - width], width, height, stride);
364
                assert_eq!(width * height, img.pixels().map(|a| a as usize).sum(), "{width}x{height}");
365
                assert_eq!(width * height, img.pixels().count(), "{width}x{height}");
366
                assert_eq!(height, img.rows().count());
367
368
                let mut iter1 = img.pixels();
369
                let mut left = width * height;
370
                while let Some(_px) = iter1.next() {
371
                    left -= 1;
372
                    assert_eq!(left, iter1.len());
373
                }
374
                assert_eq!(0, iter1.len());
375
                assert_eq!(0, left);
376
                iter1.next();
377
                assert_eq!(0, iter1.len());
378
379
                let mut iter2 = img.rows();
380
                match iter2.next() {
381
                    Some(_) => {
382
                        assert_eq!(height - 1, iter2.size_hint().0);
383
                        assert_eq!(height - 1, iter2.filter(|_| true).count());
384
                    },
385
                    None => {
386
                        assert_eq!(height, 0);
387
                    },
388
                }
389
            }
390
        }
391
    }
392
}
393
394
#[test]
395
#[should_panic(expected = "Invalid ImgRef params")]
396
fn rows_iter_len_overflow_can_create_oob_slice() {
397
    // `ImgRef::valid_min_len()` computes `stride * height + width - stride`
398
    // using checked arithmetic. It must panic on overflow instead of accepting
399
    // a one-element buffer for a 2x2 image with an impossible stride and later
400
    // reaching `RowsIter::next()`'s unsafe `get_unchecked(0..2)`.
401
    let buf = [0u8; 1];
402
    let img = super::Img::new_stride(&buf[..], 2, 2, usize::MAX);
403
404
    let _ = img.rows().next();
405
}
406
407
#[test]
408
#[should_panic(expected = "Invalid ImgRef params")]
409
fn pixels_ref_len_overflow_can_walk_oob() {
410
    // The same checked length calculation must panic on overflow for a 1x3
411
    // image, rather than letting `PixelsRefIter` start from a one-element slice
412
    // and later move the raw pointer far outside the allocation.
413
    let buf = [0u8; 1];
414
    let img = super::Img::new_stride(&buf[..], 1, 3, usize::MAX / 2 + 1);
415
    let mut pixels = img.pixels_ref();
416
417
    let _ = pixels.next();
418
    let _ = pixels.next();
419
}
420
421
#[test]
422
fn pixels_ref_iter_send_requires_sync_pixels() {
423
    // `PixelsRefIter<'_, T>` yields `&T`, so sending it to another thread is
424
    // only sound when `T: Sync`. `Cell<u32>` is `Send` but not `Sync`; if the
425
    // iterator were `Send` for `T: Send`, safe code could create a data race by
426
    // sending the iterator to another thread while retaining local shared
427
    // access to the same cell.
428
    use core::cell::Cell;
429
430
    macro_rules! assert_not_impl_any {
431
        ($x:ty: $($t:path),+ $(,)?) => {
432
            const _: fn() = || {
433
                trait AmbiguousIfImpl<A> { fn some_item() {} }
434
                impl<T: ?Sized> AmbiguousIfImpl<()> for T {}
435
                impl<T: ?Sized $(+ $t)+> AmbiguousIfImpl<u8> for T {}
436
                <$x as AmbiguousIfImpl<_>>::some_item()
437
            };
438
        };
439
    }
440
441
    fn assert_send<T: Send>() {}
442
443
    assert_send::<PixelsRefIter<'static, u32>>();
444
    assert_not_impl_any!(PixelsRefIter<'static, Cell<u32>>: Send);
445
}
446
447
#[test]
448
#[should_panic(expected = "Invalid ImgRef params")]
449
fn pixels_mut_len_overflow_can_create_oob_line_end() {
450
    // `PixelsIterMut::new()` computes `ptr.add(width)` for the first row's
451
    // line end. Checked validation must reject this wrapped 2x2 image before
452
    // creating a one-element mutable slice where that pointer is out of bounds.
453
    let mut img = super::Img::new_stride(vec![0u8; 1], 2, 2, usize::MAX);
454
455
    let _ = img.pixels_mut();
456
}
457
458
#[test]
459
#[should_panic(expected = "Invalid ImgRef params")]
460
fn pixels_mut_len_overflow_can_walk_oob() {
461
    // Mutable pixel iteration has the same invariant: checked validation must
462
    // reject this wrapped 1x3 image before the second row would require raw
463
    // pointer arithmetic far outside the one-element allocation.
464
    let mut buf = [0u8; 1];
465
    let mut img = super::Img::new_stride(&mut buf[..], 1, 3, usize::MAX / 2 + 1);
466
    let mut pixels = img.pixels_mut();
467
468
    let _ = pixels.next();
469
    let _ = pixels.next();
470
}