Skip to content

Commit 5d0474a

Browse files
committed
Rollup merge of rust-lang#47126 - sdroege:exact-chunks, r=bluss
Add slice::ExactChunks and ::ExactChunksMut iterators These guarantee that always the requested slice size will be returned and any leftoever elements at the end will be ignored. It allows llvm to get rid of bounds checks in the code using the iterator. This is inspired by the same iterators provided by ndarray. Fixes rust-lang#47115 I'll add unit tests for all this if the general idea and behaviour makes sense for everybody. Also see rust-lang#47115 (comment) for an example what this improves.
2 parents 06112ab + 5f4fc82 commit 5d0474a

File tree

7 files changed

+472
-10
lines changed

7 files changed

+472
-10
lines changed

src/liballoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
#![feature(unsize)]
125125
#![feature(allocator_internals)]
126126
#![feature(on_unimplemented)]
127+
#![feature(exact_chunks)]
127128

128129
#![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol, swap_with_slice, i128))]
129130
#![cfg_attr(test, feature(test, box_heap))]

src/liballoc/slice.rs

+74
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ pub use core::slice::{from_raw_parts, from_raw_parts_mut};
123123
pub use core::slice::{from_ref, from_ref_mut};
124124
#[unstable(feature = "slice_get_slice", issue = "35729")]
125125
pub use core::slice::SliceIndex;
126+
#[unstable(feature = "exact_chunks", issue = "47115")]
127+
pub use core::slice::{ExactChunks, ExactChunksMut};
126128

127129
////////////////////////////////////////////////////////////////////////////////
128130
// Basic slice extension methods
@@ -611,6 +613,9 @@ impl<T> [T] {
611613
/// not divide the length of the slice, then the last chunk will
612614
/// not have length `chunk_size`.
613615
///
616+
/// See [`exact_chunks`] for a variant of this iterator that returns chunks
617+
/// of always exactly `chunk_size` elements.
618+
///
614619
/// # Panics
615620
///
616621
/// Panics if `chunk_size` is 0.
@@ -631,11 +636,44 @@ impl<T> [T] {
631636
core_slice::SliceExt::chunks(self, chunk_size)
632637
}
633638

639+
/// Returns an iterator over `chunk_size` elements of the slice at a
640+
/// time. The chunks are slices and do not overlap. If `chunk_size` does
641+
/// not divide the length of the slice, then the last up to `chunk_size-1`
642+
/// elements will be omitted.
643+
///
644+
/// Due to each chunk having exactly `chunk_size` elements, the compiler
645+
/// can often optimize the resulting code better than in the case of
646+
/// [`chunks`].
647+
///
648+
/// # Panics
649+
///
650+
/// Panics if `chunk_size` is 0.
651+
///
652+
/// # Examples
653+
///
654+
/// ```
655+
/// #![feature(exact_chunks)]
656+
///
657+
/// let slice = ['l', 'o', 'r', 'e', 'm'];
658+
/// let mut iter = slice.exact_chunks(2);
659+
/// assert_eq!(iter.next().unwrap(), &['l', 'o']);
660+
/// assert_eq!(iter.next().unwrap(), &['r', 'e']);
661+
/// assert!(iter.next().is_none());
662+
/// ```
663+
#[unstable(feature = "exact_chunks", issue = "47115")]
664+
#[inline]
665+
pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
666+
core_slice::SliceExt::exact_chunks(self, chunk_size)
667+
}
668+
634669
/// Returns an iterator over `chunk_size` elements of the slice at a time.
635670
/// The chunks are mutable slices, and do not overlap. If `chunk_size` does
636671
/// not divide the length of the slice, then the last chunk will not
637672
/// have length `chunk_size`.
638673
///
674+
/// See [`exact_chunks_mut`] for a variant of this iterator that returns chunks
675+
/// of always exactly `chunk_size` elements.
676+
///
639677
/// # Panics
640678
///
641679
/// Panics if `chunk_size` is 0.
@@ -660,6 +698,42 @@ impl<T> [T] {
660698
core_slice::SliceExt::chunks_mut(self, chunk_size)
661699
}
662700

701+
/// Returns an iterator over `chunk_size` elements of the slice at a time.
702+
/// The chunks are mutable slices, and do not overlap. If `chunk_size` does
703+
/// not divide the length of the slice, then the last up to `chunk_size-1`
704+
/// elements will be omitted.
705+
///
706+
///
707+
/// Due to each chunk having exactly `chunk_size` elements, the compiler
708+
/// can often optimize the resulting code better than in the case of
709+
/// [`chunks_mut`].
710+
///
711+
/// # Panics
712+
///
713+
/// Panics if `chunk_size` is 0.
714+
///
715+
/// # Examples
716+
///
717+
/// ```
718+
/// #![feature(exact_chunks)]
719+
///
720+
/// let v = &mut [0, 0, 0, 0, 0];
721+
/// let mut count = 1;
722+
///
723+
/// for chunk in v.exact_chunks_mut(2) {
724+
/// for elem in chunk.iter_mut() {
725+
/// *elem += count;
726+
/// }
727+
/// count += 1;
728+
/// }
729+
/// assert_eq!(v, &[1, 1, 2, 2, 0]);
730+
/// ```
731+
#[unstable(feature = "exact_chunks", issue = "47115")]
732+
#[inline]
733+
pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
734+
core_slice::SliceExt::exact_chunks_mut(self, chunk_size)
735+
}
736+
663737
/// Divides one slice into two at an index.
664738
///
665739
/// The first will contain all indices from `[0, mid)` (excluding

src/liballoc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#![feature(string_retain)]
3131
#![feature(unboxed_closures)]
3232
#![feature(unicode)]
33+
#![feature(exact_chunks)]
3334

3435
extern crate alloc_system;
3536
extern crate std_unicode;

src/liballoc/tests/slice.rs

+58-2
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,30 @@ fn test_chunksator_0() {
945945
let _it = v.chunks(0);
946946
}
947947

948+
#[test]
949+
fn test_exact_chunksator() {
950+
let v = &[1, 2, 3, 4, 5];
951+
952+
assert_eq!(v.exact_chunks(2).len(), 2);
953+
954+
let chunks: &[&[_]] = &[&[1, 2], &[3, 4]];
955+
assert_eq!(v.exact_chunks(2).collect::<Vec<_>>(), chunks);
956+
let chunks: &[&[_]] = &[&[1, 2, 3]];
957+
assert_eq!(v.exact_chunks(3).collect::<Vec<_>>(), chunks);
958+
let chunks: &[&[_]] = &[];
959+
assert_eq!(v.exact_chunks(6).collect::<Vec<_>>(), chunks);
960+
961+
let chunks: &[&[_]] = &[&[3, 4], &[1, 2]];
962+
assert_eq!(v.exact_chunks(2).rev().collect::<Vec<_>>(), chunks);
963+
}
964+
965+
#[test]
966+
#[should_panic]
967+
fn test_exact_chunksator_0() {
968+
let v = &[1, 2, 3, 4];
969+
let _it = v.exact_chunks(0);
970+
}
971+
948972
#[test]
949973
fn test_reverse_part() {
950974
let mut values = [1, 2, 3, 4, 5];
@@ -1159,7 +1183,7 @@ fn test_mut_chunks() {
11591183
}
11601184
}
11611185
let result = [0, 0, 0, 1, 1, 1, 2];
1162-
assert!(v == result);
1186+
assert_eq!(v, result);
11631187
}
11641188

11651189
#[test]
@@ -1171,7 +1195,7 @@ fn test_mut_chunks_rev() {
11711195
}
11721196
}
11731197
let result = [2, 2, 2, 1, 1, 1, 0];
1174-
assert!(v == result);
1198+
assert_eq!(v, result);
11751199
}
11761200

11771201
#[test]
@@ -1181,6 +1205,38 @@ fn test_mut_chunks_0() {
11811205
let _it = v.chunks_mut(0);
11821206
}
11831207

1208+
#[test]
1209+
fn test_mut_exact_chunks() {
1210+
let mut v = [0, 1, 2, 3, 4, 5, 6];
1211+
assert_eq!(v.exact_chunks_mut(2).len(), 3);
1212+
for (i, chunk) in v.exact_chunks_mut(3).enumerate() {
1213+
for x in chunk {
1214+
*x = i as u8;
1215+
}
1216+
}
1217+
let result = [0, 0, 0, 1, 1, 1, 6];
1218+
assert_eq!(v, result);
1219+
}
1220+
1221+
#[test]
1222+
fn test_mut_exact_chunks_rev() {
1223+
let mut v = [0, 1, 2, 3, 4, 5, 6];
1224+
for (i, chunk) in v.exact_chunks_mut(3).rev().enumerate() {
1225+
for x in chunk {
1226+
*x = i as u8;
1227+
}
1228+
}
1229+
let result = [1, 1, 1, 0, 0, 0, 6];
1230+
assert_eq!(v, result);
1231+
}
1232+
1233+
#[test]
1234+
#[should_panic]
1235+
fn test_mut_exact_chunks_0() {
1236+
let mut v = [1, 2, 3, 4];
1237+
let _it = v.exact_chunks_mut(0);
1238+
}
1239+
11841240
#[test]
11851241
fn test_mut_last() {
11861242
let mut x = [1, 2, 3, 4, 5];

0 commit comments

Comments
 (0)