Skip to content

Commit

Permalink
Rollup merge of rust-lang#47126 - sdroege:exact-chunks, r=bluss
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kennytm committed Jan 15, 2018
2 parents 06112ab + 5f4fc82 commit 5d0474a
Show file tree
Hide file tree
Showing 7 changed files with 472 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
#![feature(unsize)]
#![feature(allocator_internals)]
#![feature(on_unimplemented)]
#![feature(exact_chunks)]

#![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol, swap_with_slice, i128))]
#![cfg_attr(test, feature(test, box_heap))]
Expand Down
74 changes: 74 additions & 0 deletions src/liballoc/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ pub use core::slice::{from_raw_parts, from_raw_parts_mut};
pub use core::slice::{from_ref, from_ref_mut};
#[unstable(feature = "slice_get_slice", issue = "35729")]
pub use core::slice::SliceIndex;
#[unstable(feature = "exact_chunks", issue = "47115")]
pub use core::slice::{ExactChunks, ExactChunksMut};

////////////////////////////////////////////////////////////////////////////////
// Basic slice extension methods
Expand Down Expand Up @@ -611,6 +613,9 @@ impl<T> [T] {
/// not divide the length of the slice, then the last chunk will
/// not have length `chunk_size`.
///
/// See [`exact_chunks`] for a variant of this iterator that returns chunks
/// of always exactly `chunk_size` elements.
///
/// # Panics
///
/// Panics if `chunk_size` is 0.
Expand All @@ -631,11 +636,44 @@ impl<T> [T] {
core_slice::SliceExt::chunks(self, chunk_size)
}

/// Returns an iterator over `chunk_size` elements of the slice at a
/// time. The chunks are slices and do not overlap. If `chunk_size` does
/// not divide the length of the slice, then the last up to `chunk_size-1`
/// elements will be omitted.
///
/// Due to each chunk having exactly `chunk_size` elements, the compiler
/// can often optimize the resulting code better than in the case of
/// [`chunks`].
///
/// # Panics
///
/// Panics if `chunk_size` is 0.
///
/// # Examples
///
/// ```
/// #![feature(exact_chunks)]
///
/// let slice = ['l', 'o', 'r', 'e', 'm'];
/// let mut iter = slice.exact_chunks(2);
/// assert_eq!(iter.next().unwrap(), &['l', 'o']);
/// assert_eq!(iter.next().unwrap(), &['r', 'e']);
/// assert!(iter.next().is_none());
/// ```
#[unstable(feature = "exact_chunks", issue = "47115")]
#[inline]
pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
core_slice::SliceExt::exact_chunks(self, chunk_size)
}

/// Returns an iterator over `chunk_size` elements of the slice at a time.
/// The chunks are mutable slices, and do not overlap. If `chunk_size` does
/// not divide the length of the slice, then the last chunk will not
/// have length `chunk_size`.
///
/// See [`exact_chunks_mut`] for a variant of this iterator that returns chunks
/// of always exactly `chunk_size` elements.
///
/// # Panics
///
/// Panics if `chunk_size` is 0.
Expand All @@ -660,6 +698,42 @@ impl<T> [T] {
core_slice::SliceExt::chunks_mut(self, chunk_size)
}

/// Returns an iterator over `chunk_size` elements of the slice at a time.
/// The chunks are mutable slices, and do not overlap. If `chunk_size` does
/// not divide the length of the slice, then the last up to `chunk_size-1`
/// elements will be omitted.
///
///
/// Due to each chunk having exactly `chunk_size` elements, the compiler
/// can often optimize the resulting code better than in the case of
/// [`chunks_mut`].
///
/// # Panics
///
/// Panics if `chunk_size` is 0.
///
/// # Examples
///
/// ```
/// #![feature(exact_chunks)]
///
/// let v = &mut [0, 0, 0, 0, 0];
/// let mut count = 1;
///
/// for chunk in v.exact_chunks_mut(2) {
/// for elem in chunk.iter_mut() {
/// *elem += count;
/// }
/// count += 1;
/// }
/// assert_eq!(v, &[1, 1, 2, 2, 0]);
/// ```
#[unstable(feature = "exact_chunks", issue = "47115")]
#[inline]
pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
core_slice::SliceExt::exact_chunks_mut(self, chunk_size)
}

/// Divides one slice into two at an index.
///
/// The first will contain all indices from `[0, mid)` (excluding
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#![feature(string_retain)]
#![feature(unboxed_closures)]
#![feature(unicode)]
#![feature(exact_chunks)]

extern crate alloc_system;
extern crate std_unicode;
Expand Down
60 changes: 58 additions & 2 deletions src/liballoc/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,30 @@ fn test_chunksator_0() {
let _it = v.chunks(0);
}

#[test]
fn test_exact_chunksator() {
let v = &[1, 2, 3, 4, 5];

assert_eq!(v.exact_chunks(2).len(), 2);

let chunks: &[&[_]] = &[&[1, 2], &[3, 4]];
assert_eq!(v.exact_chunks(2).collect::<Vec<_>>(), chunks);
let chunks: &[&[_]] = &[&[1, 2, 3]];
assert_eq!(v.exact_chunks(3).collect::<Vec<_>>(), chunks);
let chunks: &[&[_]] = &[];
assert_eq!(v.exact_chunks(6).collect::<Vec<_>>(), chunks);

let chunks: &[&[_]] = &[&[3, 4], &[1, 2]];
assert_eq!(v.exact_chunks(2).rev().collect::<Vec<_>>(), chunks);
}

#[test]
#[should_panic]
fn test_exact_chunksator_0() {
let v = &[1, 2, 3, 4];
let _it = v.exact_chunks(0);
}

#[test]
fn test_reverse_part() {
let mut values = [1, 2, 3, 4, 5];
Expand Down Expand Up @@ -1159,7 +1183,7 @@ fn test_mut_chunks() {
}
}
let result = [0, 0, 0, 1, 1, 1, 2];
assert!(v == result);
assert_eq!(v, result);
}

#[test]
Expand All @@ -1171,7 +1195,7 @@ fn test_mut_chunks_rev() {
}
}
let result = [2, 2, 2, 1, 1, 1, 0];
assert!(v == result);
assert_eq!(v, result);
}

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

#[test]
fn test_mut_exact_chunks() {
let mut v = [0, 1, 2, 3, 4, 5, 6];
assert_eq!(v.exact_chunks_mut(2).len(), 3);
for (i, chunk) in v.exact_chunks_mut(3).enumerate() {
for x in chunk {
*x = i as u8;
}
}
let result = [0, 0, 0, 1, 1, 1, 6];
assert_eq!(v, result);
}

#[test]
fn test_mut_exact_chunks_rev() {
let mut v = [0, 1, 2, 3, 4, 5, 6];
for (i, chunk) in v.exact_chunks_mut(3).rev().enumerate() {
for x in chunk {
*x = i as u8;
}
}
let result = [1, 1, 1, 0, 0, 0, 6];
assert_eq!(v, result);
}

#[test]
#[should_panic]
fn test_mut_exact_chunks_0() {
let mut v = [1, 2, 3, 4];
let _it = v.exact_chunks_mut(0);
}

#[test]
fn test_mut_last() {
let mut x = [1, 2, 3, 4, 5];
Expand Down
Loading

0 comments on commit 5d0474a

Please sign in to comment.