Rollup merge of #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 https://github.com/rust-lang/rust/issues/47115

I'll add unit tests for all this if the general idea and behaviour makes sense for everybody.
Also see https://github.com/rust-lang/rust/issues/47115#issuecomment-354715511 for an example what this improves.
This commit is contained in:
kennytm 2018-01-15 16:55:28 +08:00
commit 5d0474ad73
No known key found for this signature in database
GPG Key ID: FEF6C8051D0E013C
7 changed files with 472 additions and 10 deletions

View File

@ -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))]

View File

@ -123,6 +123,8 @@
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
@ -611,6 +613,9 @@ pub fn windows(&self, size: usize) -> Windows<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.
@ -631,11 +636,44 @@ pub fn chunks(&self, chunk_size: usize) -> Chunks<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.
@ -660,6 +698,42 @@ pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<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

View File

@ -30,6 +30,7 @@
#![feature(string_retain)]
#![feature(unboxed_closures)]
#![feature(unicode)]
#![feature(exact_chunks)]
extern crate alloc_system;
extern crate std_unicode;

View File

@ -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];
@ -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]
@ -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]
@ -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];

View File

@ -104,6 +104,9 @@ fn rsplitn<P>(&self, n: usize, pred: P) -> RSplitN<Self::Item, P>
#[stable(feature = "core", since = "1.6.0")]
fn chunks(&self, size: usize) -> Chunks<Self::Item>;
#[unstable(feature = "exact_chunks", issue = "47115")]
fn exact_chunks(&self, size: usize) -> ExactChunks<Self::Item>;
#[stable(feature = "core", since = "1.6.0")]
fn get<I>(&self, index: I) -> Option<&I::Output>
where I: SliceIndex<Self>;
@ -181,6 +184,9 @@ fn rsplitn_mut<P>(&mut self, n: usize, pred: P) -> RSplitNMut<Self::Item, P>
#[stable(feature = "core", since = "1.6.0")]
fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<Self::Item>;
#[unstable(feature = "exact_chunks", issue = "47115")]
fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut<Self::Item>;
#[stable(feature = "core", since = "1.6.0")]
fn swap(&mut self, a: usize, b: usize);
@ -356,6 +362,14 @@ fn chunks(&self, chunk_size: usize) -> Chunks<T> {
Chunks { v: self, chunk_size: chunk_size }
}
#[inline]
fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
assert!(chunk_size != 0);
let rem = self.len() % chunk_size;
let len = self.len() - rem;
ExactChunks { v: &self[..len], chunk_size: chunk_size}
}
#[inline]
fn get<I>(&self, index: I) -> Option<&I::Output>
where I: SliceIndex<[T]>
@ -539,6 +553,14 @@ fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<T> {
ChunksMut { v: self, chunk_size: chunk_size }
}
#[inline]
fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
assert!(chunk_size != 0);
let rem = self.len() % chunk_size;
let len = self.len() - rem;
ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size}
}
#[inline]
fn swap(&mut self, a: usize, b: usize) {
unsafe {
@ -2378,6 +2400,209 @@ unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] {
fn may_have_side_effect() -> bool { false }
}
/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time).
///
/// When the slice len is not evenly divided by the chunk size, the last
/// up to `chunk_size-1` elements will be omitted.
///
/// This struct is created by the [`exact_chunks`] method on [slices].
///
/// [`exact_chunks`]: ../../std/primitive.slice.html#method.exact_chunks
/// [slices]: ../../std/primitive.slice.html
#[derive(Debug)]
#[unstable(feature = "exact_chunks", issue = "47115")]
pub struct ExactChunks<'a, T:'a> {
v: &'a [T],
chunk_size: usize
}
// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> Clone for ExactChunks<'a, T> {
fn clone(&self) -> ExactChunks<'a, T> {
ExactChunks {
v: self.v,
chunk_size: self.chunk_size,
}
}
}
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> Iterator for ExactChunks<'a, T> {
type Item = &'a [T];
#[inline]
fn next(&mut self) -> Option<&'a [T]> {
if self.v.len() < self.chunk_size {
None
} else {
let (fst, snd) = self.v.split_at(self.chunk_size);
self.v = snd;
Some(fst)
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.v.len() / self.chunk_size;
(n, Some(n))
}
#[inline]
fn count(self) -> usize {
self.len()
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let (start, overflow) = n.overflowing_mul(self.chunk_size);
if start >= self.v.len() || overflow {
self.v = &[];
None
} else {
let (_, snd) = self.v.split_at(start);
self.v = snd;
self.next()
}
}
#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> {
#[inline]
fn next_back(&mut self) -> Option<&'a [T]> {
if self.v.len() < self.chunk_size {
None
} else {
let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size);
self.v = fst;
Some(snd)
}
}
}
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> {
fn is_empty(&self) -> bool {
self.v.is_empty()
}
}
#[unstable(feature = "fused", issue = "35602")]
impl<'a, T> FusedIterator for ExactChunks<'a, T> {}
#[doc(hidden)]
unsafe impl<'a, T> TrustedRandomAccess for ExactChunks<'a, T> {
unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] {
let start = i * self.chunk_size;
from_raw_parts(self.v.as_ptr().offset(start as isize), self.chunk_size)
}
fn may_have_side_effect() -> bool { false }
}
/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size`
/// elements at a time). When the slice len is not evenly divided by the chunk
/// size, the last up to `chunk_size-1` elements will be omitted.
///
/// This struct is created by the [`exact_chunks_mut`] method on [slices].
///
/// [`exact_chunks_mut`]: ../../std/primitive.slice.html#method.exact_chunks_mut
/// [slices]: ../../std/primitive.slice.html
#[derive(Debug)]
#[unstable(feature = "exact_chunks", issue = "47115")]
pub struct ExactChunksMut<'a, T:'a> {
v: &'a mut [T],
chunk_size: usize
}
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> Iterator for ExactChunksMut<'a, T> {
type Item = &'a mut [T];
#[inline]
fn next(&mut self) -> Option<&'a mut [T]> {
if self.v.len() < self.chunk_size {
None
} else {
let tmp = mem::replace(&mut self.v, &mut []);
let (head, tail) = tmp.split_at_mut(self.chunk_size);
self.v = tail;
Some(head)
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.v.len() / self.chunk_size;
(n, Some(n))
}
#[inline]
fn count(self) -> usize {
self.len()
}
#[inline]
fn nth(&mut self, n: usize) -> Option<&'a mut [T]> {
let (start, overflow) = n.overflowing_mul(self.chunk_size);
if start >= self.v.len() || overflow {
self.v = &mut [];
None
} else {
let tmp = mem::replace(&mut self.v, &mut []);
let (_, snd) = tmp.split_at_mut(start);
self.v = snd;
self.next()
}
}
#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> {
#[inline]
fn next_back(&mut self) -> Option<&'a mut [T]> {
if self.v.len() < self.chunk_size {
None
} else {
let tmp = mem::replace(&mut self.v, &mut []);
let tmp_len = tmp.len();
let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size);
self.v = head;
Some(tail)
}
}
}
#[unstable(feature = "exact_chunks", issue = "47115")]
impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> {
fn is_empty(&self) -> bool {
self.v.is_empty()
}
}
#[unstable(feature = "fused", issue = "35602")]
impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {}
#[doc(hidden)]
unsafe impl<'a, T> TrustedRandomAccess for ExactChunksMut<'a, T> {
unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] {
let start = i * self.chunk_size;
from_raw_parts_mut(self.v.as_mut_ptr().offset(start as isize), self.chunk_size)
}
fn may_have_side_effect() -> bool { false }
}
//
// Free functions
//

View File

@ -42,6 +42,7 @@
#![feature(try_from)]
#![feature(try_trait)]
#![feature(unique)]
#![feature(exact_chunks)]
extern crate core;
extern crate test;

View File

@ -117,12 +117,12 @@ fn test_chunks_count() {
fn test_chunks_nth() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let mut c = v.chunks(2);
assert_eq!(c.nth(1).unwrap()[1], 3);
assert_eq!(c.next().unwrap()[0], 4);
assert_eq!(c.nth(1).unwrap(), &[2, 3]);
assert_eq!(c.next().unwrap(), &[4, 5]);
let v2: &[i32] = &[0, 1, 2, 3, 4];
let mut c2 = v2.chunks(3);
assert_eq!(c2.nth(1).unwrap()[1], 4);
assert_eq!(c2.nth(1).unwrap(), &[3, 4]);
assert_eq!(c2.next(), None);
}
@ -168,12 +168,12 @@ fn test_chunks_mut_count() {
fn test_chunks_mut_nth() {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
let mut c = v.chunks_mut(2);
assert_eq!(c.nth(1).unwrap()[1], 3);
assert_eq!(c.next().unwrap()[0], 4);
assert_eq!(c.nth(1).unwrap(), &[2, 3]);
assert_eq!(c.next().unwrap(), &[4, 5]);
let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
let mut c2 = v2.chunks_mut(3);
assert_eq!(c2.nth(1).unwrap()[1], 4);
assert_eq!(c2.nth(1).unwrap(), &[3, 4]);
assert_eq!(c2.next(), None);
}
@ -181,11 +181,11 @@ fn test_chunks_mut_nth() {
fn test_chunks_mut_last() {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
let c = v.chunks_mut(2);
assert_eq!(c.last().unwrap()[1], 5);
assert_eq!(c.last().unwrap(), &[4, 5]);
let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
let c2 = v2.chunks_mut(2);
assert_eq!(c2.last().unwrap()[0], 4);
assert_eq!(c2.last().unwrap(), &[4]);
}
#[test]
@ -202,6 +202,110 @@ fn test_chunks_mut_zip() {
assert_eq!(v1, [13, 14, 19, 20, 14]);
}
#[test]
fn test_exact_chunks_count() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let c = v.exact_chunks(3);
assert_eq!(c.count(), 2);
let v2: &[i32] = &[0, 1, 2, 3, 4];
let c2 = v2.exact_chunks(2);
assert_eq!(c2.count(), 2);
let v3: &[i32] = &[];
let c3 = v3.exact_chunks(2);
assert_eq!(c3.count(), 0);
}
#[test]
fn test_exact_chunks_nth() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let mut c = v.exact_chunks(2);
assert_eq!(c.nth(1).unwrap(), &[2, 3]);
assert_eq!(c.next().unwrap(), &[4, 5]);
let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
let mut c2 = v2.exact_chunks(3);
assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
assert_eq!(c2.next(), None);
}
#[test]
fn test_exact_chunks_last() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let c = v.exact_chunks(2);
assert_eq!(c.last().unwrap(), &[4, 5]);
let v2: &[i32] = &[0, 1, 2, 3, 4];
let c2 = v2.exact_chunks(2);
assert_eq!(c2.last().unwrap(), &[2, 3]);
}
#[test]
fn test_exact_chunks_zip() {
let v1: &[i32] = &[0, 1, 2, 3, 4];
let v2: &[i32] = &[6, 7, 8, 9, 10];
let res = v1.exact_chunks(2)
.zip(v2.exact_chunks(2))
.map(|(a, b)| a.iter().sum::<i32>() + b.iter().sum::<i32>())
.collect::<Vec<_>>();
assert_eq!(res, vec![14, 22]);
}
#[test]
fn test_exact_chunks_mut_count() {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
let c = v.exact_chunks_mut(3);
assert_eq!(c.count(), 2);
let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
let c2 = v2.exact_chunks_mut(2);
assert_eq!(c2.count(), 2);
let v3: &mut [i32] = &mut [];
let c3 = v3.exact_chunks_mut(2);
assert_eq!(c3.count(), 0);
}
#[test]
fn test_exact_chunks_mut_nth() {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
let mut c = v.exact_chunks_mut(2);
assert_eq!(c.nth(1).unwrap(), &[2, 3]);
assert_eq!(c.next().unwrap(), &[4, 5]);
let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
let mut c2 = v2.exact_chunks_mut(3);
assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
assert_eq!(c2.next(), None);
}
#[test]
fn test_exact_chunks_mut_last() {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
let c = v.exact_chunks_mut(2);
assert_eq!(c.last().unwrap(), &[4, 5]);
let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
let c2 = v2.exact_chunks_mut(2);
assert_eq!(c2.last().unwrap(), &[2, 3]);
}
#[test]
fn test_exact_chunks_mut_zip() {
let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
let v2: &[i32] = &[6, 7, 8, 9, 10];
for (a, b) in v1.exact_chunks_mut(2).zip(v2.exact_chunks(2)) {
let sum = b.iter().sum::<i32>();
for v in a {
*v += sum;
}
}
assert_eq!(v1, [13, 14, 19, 20, 4]);
}
#[test]
fn test_windows_count() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];