From 51d546f4aa8a94b81d2a580518d95d1ab12a3655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 02:13:20 +0200 Subject: [PATCH 01/11] 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. See https://github.com/rust-lang/rust/issues/47115 --- src/liballoc/lib.rs | 1 + src/liballoc/slice.rs | 56 ++++++++++ src/libcore/slice/mod.rs | 230 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3cc3ea467966b..d8ce28695ab6f 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -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))] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index fa73197885be7..bae366736379c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -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 @@ -631,6 +633,31 @@ impl [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. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.chunks(2); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert_eq!(iter.next().unwrap(), &['m']); + /// assert!(iter.next().is_none()); + /// ``` + #[unstable(feature = "exact_chunks", issue = "47115")] + #[inline] + pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { + 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 @@ -660,6 +687,35 @@ impl [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. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// 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, 3]); + /// ``` + #[unstable(feature = "exact_chunks", issue = "47115")] + #[inline] + pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { + 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 diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 72036d6d3a248..5791b1d6e79c6 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -104,6 +104,9 @@ pub trait SliceExt { #[stable(feature = "core", since = "1.6.0")] fn chunks(&self, size: usize) -> Chunks; + #[unstable(feature = "exact_chunks", issue = "47115")] + fn exact_chunks(&self, size: usize) -> ExactChunks; + #[stable(feature = "core", since = "1.6.0")] fn get(&self, index: I) -> Option<&I::Output> where I: SliceIndex; @@ -181,6 +184,9 @@ pub trait SliceExt { #[stable(feature = "core", since = "1.6.0")] fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut; + #[unstable(feature = "exact_chunks", issue = "47115")] + fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut; + #[stable(feature = "core", since = "1.6.0")] fn swap(&mut self, a: usize, b: usize); @@ -353,6 +359,14 @@ impl SliceExt for [T] { Chunks { v: self, chunk_size: chunk_size } } + #[inline] + fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { + 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(&self, index: I) -> Option<&I::Output> where I: SliceIndex<[T]> @@ -536,6 +550,14 @@ impl SliceExt for [T] { ChunksMut { v: self, chunk_size: chunk_size } } + #[inline] + fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { + 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 { @@ -2365,6 +2387,214 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, 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) { + 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 { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &[]; + None + } else { + let end = match start.checked_add(self.chunk_size) { + Some(sum) => cmp::min(self.v.len(), sum), + None => self.v.len(), + }; + + if end - start != self.chunk_size { + self.v = &[]; + None + } else { + let nth = &self.v[start..end]; + self.v = &self.v[end..]; + Some(nth) + } + } + } + + #[inline] + fn last(self) -> Option { + if self.v.len() < self.chunk_size { + None + } else { + let start = self.v.len() - self.chunk_size; + Some(&self.v[start..]) + } + } +} + +#[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> {} + +#[unstable(feature = "fused", issue = "35602")] +impl<'a, T> FusedIterator for ExactChunks<'a, T> {} + +/// 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) { + 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 end = match start.checked_add(self.chunk_size) { + Some(sum) => cmp::min(self.v.len(), sum), + None => self.v.len(), + }; + + if end - start != self.chunk_size { + self.v = &mut []; + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(end); + let (_, nth) = head.split_at_mut(start); + self.v = tail; + Some(nth) + } + } + } + + #[inline] + fn last(self) -> Option { + if self.v.len() < self.chunk_size { + None + } else { + let start = (self.v.len() - self.chunk_size) / self.chunk_size * self.chunk_size; + Some(&mut self.v[start..]) + } + } +} + +#[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> {} + +#[unstable(feature = "fused", issue = "35602")] +impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {} + // // Free functions // From 83396fc712aefd24531f7925d377b148da8ed04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 14:54:18 +0200 Subject: [PATCH 02/11] Add #![feature(exact_chunks)] to the documentation examples to fix the doc tests --- src/liballoc/slice.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index bae366736379c..6a3970f37285c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -645,6 +645,8 @@ impl [T] { /// # Examples /// /// ``` + /// #![feature(exact_chunks)] + /// /// let slice = ['l', 'o', 'r', 'e', 'm']; /// let mut iter = slice.chunks(2); /// assert_eq!(iter.next().unwrap(), &['l', 'o']); @@ -699,6 +701,8 @@ impl [T] { /// # Examples /// /// ``` + /// #![feature(exact_chunks)] + /// /// let v = &mut [0, 0, 0, 0, 0]; /// let mut count = 1; /// From 802ba9ea5b432d36444d8da8646bd2a1624b9dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 14:55:25 +0200 Subject: [PATCH 03/11] Fix assertions in examples of the exact_chunk() documentation --- src/liballoc/slice.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 6a3970f37285c..9ff39363fd7ef 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -651,7 +651,6 @@ impl [T] { /// let mut iter = slice.chunks(2); /// assert_eq!(iter.next().unwrap(), &['l', 'o']); /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert_eq!(iter.next().unwrap(), &['m']); /// assert!(iter.next().is_none()); /// ``` #[unstable(feature = "exact_chunks", issue = "47115")] @@ -712,7 +711,7 @@ impl [T] { /// } /// count += 1; /// } - /// assert_eq!(v, &[1, 1, 2, 2, 3]); + /// assert_eq!(v, &[1, 1, 2, 2]); /// ``` #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] From e51a89a0ad35f42af9c39d4ada25a5ad9746cd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 19:21:39 +0200 Subject: [PATCH 04/11] Fix doctests for slice::exact_chunks() for real --- src/liballoc/slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 9ff39363fd7ef..182e9b0a00ed6 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -648,7 +648,7 @@ impl [T] { /// #![feature(exact_chunks)] /// /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.chunks(2); + /// 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()); @@ -711,7 +711,7 @@ impl [T] { /// } /// count += 1; /// } - /// assert_eq!(v, &[1, 1, 2, 2]); + /// assert_eq!(v, &[1, 1, 2, 2, 0]); /// ``` #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] From aa0c08a22c32da5cc1db16fba2e5e6b762a47de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 23:54:42 +0200 Subject: [PATCH 05/11] Apply review comments from @bluss - Simplify nth() by making use of the fact that the slice is evenly divisible by the chunk size, and calling next() instead of duplicating it - Call next_back() in last(), they are equivalent - Implement ExactSizeIterator::is_empty() --- src/libcore/slice/mod.rs | 67 ++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 5791b1d6e79c6..2d606f43084ba 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2448,30 +2448,16 @@ impl<'a, T> Iterator for ExactChunks<'a, T> { self.v = &[]; None } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - - if end - start != self.chunk_size { - self.v = &[]; - None - } else { - let nth = &self.v[start..end]; - self.v = &self.v[end..]; - Some(nth) - } + let (_, snd) = self.v.split_at(start); + self.v = snd; + assert!(self.v.len() == self.chunk_size); + self.next() } } #[inline] - fn last(self) -> Option { - if self.v.len() < self.chunk_size { - None - } else { - let start = self.v.len() - self.chunk_size; - Some(&self.v[start..]) - } + fn last(mut self) -> Option { + self.next_back() } } @@ -2490,7 +2476,11 @@ impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> { } #[unstable(feature = "exact_chunks", issue = "47115")] -impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> {} +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> {} @@ -2544,32 +2534,17 @@ impl<'a, T> Iterator for ExactChunksMut<'a, T> { self.v = &mut []; None } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - - if end - start != self.chunk_size { - self.v = &mut []; - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(end); - let (_, nth) = head.split_at_mut(start); - self.v = tail; - Some(nth) - } + let tmp = mem::replace(&mut self.v, &mut []); + let (_, snd) = tmp.split_at_mut(start); + self.v = snd; + assert!(self.v.len() == self.chunk_size); + self.next() } } #[inline] - fn last(self) -> Option { - if self.v.len() < self.chunk_size { - None - } else { - let start = (self.v.len() - self.chunk_size) / self.chunk_size * self.chunk_size; - Some(&mut self.v[start..]) - } + fn last(mut self) -> Option { + self.next_back() } } @@ -2590,7 +2565,11 @@ impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> { } #[unstable(feature = "exact_chunks", issue = "47115")] -impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> {} +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> {} From cea36f447ea746397003f1ee98ee41e858b1951b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 3 Jan 2018 00:05:13 +0200 Subject: [PATCH 06/11] Remove useless assertion --- src/libcore/slice/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 2d606f43084ba..c61d17290c89d 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2450,7 +2450,6 @@ impl<'a, T> Iterator for ExactChunks<'a, T> { } else { let (_, snd) = self.v.split_at(start); self.v = snd; - assert!(self.v.len() == self.chunk_size); self.next() } } @@ -2537,7 +2536,6 @@ impl<'a, T> Iterator for ExactChunksMut<'a, T> { let tmp = mem::replace(&mut self.v, &mut []); let (_, snd) = tmp.split_at_mut(start); self.v = snd; - assert!(self.v.len() == self.chunk_size); self.next() } } From 6bf1dfdd76625ba753de0d1b87a2825606cf6f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 3 Jan 2018 01:01:40 +0200 Subject: [PATCH 07/11] Implement TrustedRandomAccess for slice::{ExactChunks, ExactChunksMut} --- src/libcore/slice/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index c61d17290c89d..9379a064a6c11 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2484,6 +2484,15 @@ impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> { #[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. @@ -2572,6 +2581,15 @@ impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> { #[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 // From 8a82e8e89f6eec3744f12d45c12ce05d48233d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 9 Jan 2018 22:58:41 +0200 Subject: [PATCH 08/11] Mention in the exact_chunks docs that this can often be optimized better by the compiler And also link from the normal chunks iterator to the exact_chunks one. --- src/liballoc/slice.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 182e9b0a00ed6..f8980b8777e93 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -613,6 +613,9 @@ impl [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. @@ -638,6 +641,10 @@ impl [T] { /// 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. @@ -664,6 +671,9 @@ impl [T] { /// 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. @@ -693,6 +703,11 @@ impl [T] { /// 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. From baa81dc77f0a46b35c6b103cba79334d87622f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jan 2018 12:11:32 +0200 Subject: [PATCH 09/11] Use assert_eq!() instead of assert!(a == b) in slice chunks_mut() unit test This way more useful information is printed if the test ever fails. --- src/liballoc/tests/slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 85d5ce304b88d..c2f7522c5f054 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -1124,7 +1124,7 @@ fn test_mut_chunks() { } } let result = [0, 0, 0, 1, 1, 1, 2]; - assert!(v == result); + assert_eq!(v, result); } #[test] @@ -1136,7 +1136,7 @@ fn test_mut_chunks_rev() { } } let result = [2, 2, 2, 1, 1, 1, 0]; - assert!(v == result); + assert_eq!(v, result); } #[test] From ed774838b372bf532a3fb221601bdcd1c8856277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jan 2018 12:12:55 +0200 Subject: [PATCH 10/11] Test the whole chunks instead of just an element in the chunks/chunks_mut tests Easy enough to do and ensures that the whole chunk is as expected instead of just the element that was looked at before. --- src/libcore/tests/slice.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index d6230e93f998d..4f9fc30785c73 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -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] From 5f4fc8214279f2ffadbf2cc3a4c22b748c17bd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jan 2018 12:13:45 +0200 Subject: [PATCH 11/11] Add unit tests for exact_chunks/exact_chunks_mut These are basically modified copies of the chunks/chunks_mut tests. --- src/liballoc/tests/lib.rs | 1 + src/liballoc/tests/slice.rs | 56 +++++++++++++++++++ src/libcore/tests/lib.rs | 1 + src/libcore/tests/slice.rs | 104 ++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+) diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index f1e95883b3827..eee229bc6fdfa 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -30,6 +30,7 @@ #![feature(string_retain)] #![feature(unboxed_closures)] #![feature(unicode)] +#![feature(exact_chunks)] extern crate alloc_system; extern crate std_unicode; diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index c2f7522c5f054..5a3e5e6cb780d 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -910,6 +910,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::>(), chunks); + let chunks: &[&[_]] = &[&[1, 2, 3]]; + assert_eq!(v.exact_chunks(3).collect::>(), chunks); + let chunks: &[&[_]] = &[]; + assert_eq!(v.exact_chunks(6).collect::>(), chunks); + + let chunks: &[&[_]] = &[&[3, 4], &[1, 2]]; + assert_eq!(v.exact_chunks(2).rev().collect::>(), 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]; @@ -1146,6 +1170,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]; diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index c4b85b829812c..2c0009569d75d 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -42,6 +42,7 @@ #![feature(try_from)] #![feature(try_trait)] #![feature(unique)] +#![feature(exact_chunks)] extern crate core; extern crate test; diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 4f9fc30785c73..a89a88ac0af1e 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -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::() + b.iter().sum::()) + .collect::>(); + 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::(); + 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];