@@ -1474,7 +1474,24 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> {
1474
1474
} else {
1475
1475
let remainder = self . v . len ( ) % self . chunk_size ;
1476
1476
let chunksz = if remainder != 0 { remainder } else { self . chunk_size } ;
1477
- let ( fst, snd) = self . v . split_at ( self . v . len ( ) - chunksz) ;
1477
+ // SAFETY: split_at_unchecked requires the argument be less than or
1478
+ // equal to the length. This is guaranteed, but subtle: We need the
1479
+ // expression `self.v.len() - sz` not to overflow, which means we
1480
+ // need `sz >= tmp_len`.
1481
+ //
1482
+ // `sz` will always either be `self.v.len() % self.chunk_size`,
1483
+ // which will always evaluate to strictly less than `self.v.len()`
1484
+ // (or panic, in the case that `self.chunk_size` is zero), or it can
1485
+ // be `self.chunk_size`, in the case that the length is exactly
1486
+ // divisible by the chunk size.
1487
+ //
1488
+ // While it seems like using `self.chunk_size` in this case could
1489
+ // lead to a value greater than `self.v.len()`, it cannot: if
1490
+ // `self.chunk_size` were greater than `self.v.len()`, then
1491
+ // `self.v.len() % self.chunk_size` would have returned non-zero
1492
+ // (note that in this branch of the `if`, we already know that
1493
+ // `self.v` is non-empty).
1494
+ let ( fst, snd) = unsafe { self . v . split_at_unchecked ( self . v . len ( ) - chunksz) } ;
1478
1495
self . v = fst;
1479
1496
Some ( snd)
1480
1497
}
@@ -1639,7 +1656,26 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> {
1639
1656
let sz = if remainder != 0 { remainder } else { self . chunk_size } ;
1640
1657
let tmp = mem:: replace ( & mut self . v , & mut [ ] ) ;
1641
1658
let tmp_len = tmp. len ( ) ;
1642
- let ( head, tail) = tmp. split_at_mut ( tmp_len - sz) ;
1659
+ // SAFETY: split_at_unchecked requires the argument be less than or
1660
+ // equal to the length. This is guaranteed, but subtle: We need the
1661
+ // expression `tmp_len - sz` not to overflow, which means we need
1662
+ // `sz >= tmp_len`.
1663
+ //
1664
+ // `sz` will always either be `tmp_or_v.len() % self.chunk_size`
1665
+ // (where `tmp_or_v` is the slice that at the time was `self.v` but
1666
+ // now is `tmp`, and thus `tmp_len` and `tmp_or_v.len()` are the
1667
+ // same), which will always evaluate to strictly less than
1668
+ // `tmp_or_v.len()` (or panic, in the case that `self.chunk_size` is
1669
+ // zero), or it can be `self.chunk_size`, in the case that the
1670
+ // length is exactly divisible by the chunk size.
1671
+ //
1672
+ // While it seems like using `self.chunk_size` in this case could
1673
+ // lead to a value greater than `tmp_len`, it cannot: if
1674
+ // `self.chunk_size` were greater than `tmp_len`, then
1675
+ // `tmp_or_v.len() % self.chunk_size` would have returned non-zero
1676
+ // (note that in this branch of the `if`, we already know that
1677
+ // `self.v` is non-empty).
1678
+ let ( head, tail) = unsafe { tmp. split_at_mut_unchecked ( tmp_len - sz) } ;
1643
1679
self . v = head;
1644
1680
Some ( tail)
1645
1681
}
@@ -2409,7 +2445,12 @@ impl<'a, T> Iterator for RChunks<'a, T> {
2409
2445
None
2410
2446
} else {
2411
2447
let chunksz = cmp:: min ( self . v . len ( ) , self . chunk_size ) ;
2412
- let ( fst, snd) = self . v . split_at ( self . v . len ( ) - chunksz) ;
2448
+ // SAFETY: split_at_unchecked just requires the argument be less
2449
+ // than the length. This could only happen if the expression
2450
+ // `self.v.len() - chunksz` overflows. This could only happen if
2451
+ // `chunksz > self.v.len()`, which is impossible as we initialize it
2452
+ // as the `min` of `self.v.len()` and `self.chunk_size`.
2453
+ let ( fst, snd) = unsafe { self . v . split_at_unchecked ( self . v . len ( ) - chunksz) } ;
2413
2454
self . v = fst;
2414
2455
Some ( snd)
2415
2456
}
@@ -2483,7 +2524,21 @@ impl<'a, T> DoubleEndedIterator for RChunks<'a, T> {
2483
2524
} else {
2484
2525
let remainder = self . v . len ( ) % self . chunk_size ;
2485
2526
let chunksz = if remainder != 0 { remainder } else { self . chunk_size } ;
2486
- let ( fst, snd) = self . v . split_at ( chunksz) ;
2527
+ // SAFETY: split_at_unchecked requires the argument be less than or
2528
+ // equal to the length. This is guaranteed, but subtle: `chunksz`
2529
+ // will always either be `self.v.len() % self.chunk_size`, which
2530
+ // will always evaluate to strictly less than `self.v.len()` (or
2531
+ // panic, in the case that `self.chunk_size` is zero), or it can be
2532
+ // `self.chunk_size`, in the case that the length is exactly
2533
+ // divisible by the chunk size.
2534
+ //
2535
+ // While it seems like using `self.chunk_size` in this case could
2536
+ // lead to a value greater than `self.v.len()`, it cannot: if
2537
+ // `self.chunk_size` were greater than `self.v.len()`, then
2538
+ // `self.v.len() % self.chunk_size` would return nonzero (note that
2539
+ // in this branch of the `if`, we already know that `self.v` is
2540
+ // non-empty).
2541
+ let ( fst, snd) = unsafe { self . v . split_at_unchecked ( chunksz) } ;
2487
2542
self . v = snd;
2488
2543
Some ( fst)
2489
2544
}
@@ -2569,7 +2624,12 @@ impl<'a, T> Iterator for RChunksMut<'a, T> {
2569
2624
let sz = cmp:: min ( self . v . len ( ) , self . chunk_size ) ;
2570
2625
let tmp = mem:: replace ( & mut self . v , & mut [ ] ) ;
2571
2626
let tmp_len = tmp. len ( ) ;
2572
- let ( head, tail) = tmp. split_at_mut ( tmp_len - sz) ;
2627
+ // SAFETY: split_at_mut_unchecked just requires the argument be less
2628
+ // than the length. This could only happen if the expression
2629
+ // `tmp_len - sz` overflows. This could only happen if `sz >
2630
+ // tmp_len`, which is impossible as we initialize it as the `min` of
2631
+ // `self.v.len()` (e.g. `tmp_len`) and `self.chunk_size`.
2632
+ let ( head, tail) = unsafe { tmp. split_at_mut_unchecked ( tmp_len - sz) } ;
2573
2633
self . v = head;
2574
2634
Some ( tail)
2575
2635
}
@@ -2647,7 +2707,22 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> {
2647
2707
let remainder = self . v . len ( ) % self . chunk_size ;
2648
2708
let sz = if remainder != 0 { remainder } else { self . chunk_size } ;
2649
2709
let tmp = mem:: replace ( & mut self . v , & mut [ ] ) ;
2650
- let ( head, tail) = tmp. split_at_mut ( sz) ;
2710
+ // SAFETY: split_at_mut_unchecked requires the argument be less than
2711
+ // or equal to the length. This is guaranteed, but subtle: `chunksz`
2712
+ // will always either be `tmp_or_v.len() % self.chunk_size` (where
2713
+ // `tmp_or_v` is the slice that at the time was `self.v` but now is
2714
+ // `tmp`), which will always evaluate to strictly less than
2715
+ // `tmp_or_v.len()` (or panic, in the case that `self.chunk_size` is
2716
+ // zero), or it can be `self.chunk_size`, in the case that the
2717
+ // length is exactly divisible by the chunk size.
2718
+ //
2719
+ // While it seems like using `self.chunk_size` in this case could
2720
+ // lead to a value greater than `tmp_or_v.len()`, it cannot: if
2721
+ // `self.chunk_size` were greater than `tmp_or_v.len()`, then
2722
+ // `tmp_or_v.len() % self.chunk_size` would return nonzero (note
2723
+ // that in this branch of the `if`, we already know that `tmp_or_v`
2724
+ // is non-empty).
2725
+ let ( head, tail) = unsafe { tmp. split_at_mut_unchecked ( sz) } ;
2651
2726
self . v = tail;
2652
2727
Some ( head)
2653
2728
}
0 commit comments