From 45209a9cf0b20c9f19af1c0b827324c47fb5aa23 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 18 May 2020 20:06:00 +0200 Subject: [PATCH 1/4] improve chunks + windows err on size 0 --- src/libcore/slice/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 3386f83ec810f..602ae3e230f28 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -674,7 +674,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn windows(&self, size: usize) -> Windows<'_, T> { - assert!(size != 0); + assert_ne!(size, 0); Windows { v: self, size } } @@ -708,7 +708,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { - assert!(chunk_size != 0); + assert_ne!(chunk_size, 0); Chunks { v: self, chunk_size } } @@ -746,7 +746,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { - assert!(chunk_size != 0); + assert_ne!(chunk_size, 0); ChunksMut { v: self, chunk_size } } @@ -783,7 +783,7 @@ impl [T] { #[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { - assert!(chunk_size != 0); + assert_ne!(chunk_size, 0); let rem = self.len() % chunk_size; let len = self.len() - rem; let (fst, snd) = self.split_at(len); @@ -828,7 +828,7 @@ impl [T] { #[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { - assert!(chunk_size != 0); + assert_ne!(chunk_size, 0); let rem = self.len() % chunk_size; let len = self.len() - rem; let (fst, snd) = self.split_at_mut(len); From eb594a2974a2ec2a0e68aebdc2a4f08bb4f02464 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 18 May 2020 20:40:37 +0200 Subject: [PATCH 2/4] adds `slice::array_chunks` --- src/libcore/slice/mod.rs | 180 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 602ae3e230f28..ac5bd67a1a243 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -835,6 +835,41 @@ impl [T] { ChunksExactMut { v: fst, rem: snd, chunk_size } } + /// Returns an iterator over `N` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are slices and do not overlap. If `N` does not divide the length of the + /// slice, then the last up to `N-1` elements will be omitted and can be retrieved + /// from the `remainder` function of the iterator. + /// + /// # Panics + /// + /// Panics if `N` is 0. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_chunks)] + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.array_chunks(); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert!(iter.next().is_none()); + /// assert_eq!(iter.remainder(), &['m']); + /// ``` + /// + /// [`chunks`]: #method.chunks + /// [`rchunks_exact`]: #method.rchunks_exact + #[unstable(feature = "array_chunks", issue = "none")] + #[inline] + pub fn array_chunks(&self) -> ArrayChunks<'_, T, N> { + assert_ne!(N, 0); + let rem = self.len() % N; + let len = self.len() - rem; + let (fst, snd) = self.split_at(len); + ArrayChunks { v: fst, rem: snd } + } + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end /// of the slice. /// @@ -5152,6 +5187,151 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> { } } +/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a +/// time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last +/// up to `chunk_size-1` elements will be omitted but can be retrieved from +/// the [`remainder`] function from the iterator. +/// +/// This struct is created by the [`array_chunks`] method on [slices]. +/// +/// [`array_chunks`]: ../../std/primitive.slice.html#method.array_chunks +/// [`remainder`]: ../../std/slice/struct.ArrayChunks.html#method.remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[unstable(feature = "array_chunks", issue = "none")] +pub struct ArrayChunks<'a, T: 'a, const N: usize> { + v: &'a [T], + rem: &'a [T], +} + +impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `chunk_size-1` + /// elements. + #[unstable(feature = "array_chunks", issue = "none")] + pub fn remainder(&self) -> &'a [T] { + self.rem + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[unstable(feature = "array_chunks", issue = "none")] +impl Clone for ArrayChunks<'_, T, N> { + fn clone(&self) -> Self { + ArrayChunks { v: self.v, rem: self.rem } + } +} + +#[unstable(feature = "array_chunks", issue = "none")] +impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> { + type Item = &'a [T; N]; + + #[inline] + fn next(&mut self) -> Option<&'a [T; N]> { + if self.v.len() < N { + None + } else { + let (fst, snd) = self.v.split_at(N); + self.v = snd; + // SAFETY: This is safe as fst is exactly N elements long. + let ptr = fst.as_ptr() as *const [T; N]; + unsafe { Some(&*ptr) } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / N; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (start, overflow) = n.overflowing_mul(N); + 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.next_back() + } +} + +#[unstable(feature = "array_chunks", issue = "none")] +impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T; N]> { + if self.v.len() < N { + None + } else { + let (fst, snd) = self.v.split_at(self.v.len() - N); + self.v = fst; + // SAFETY: This is safe as snd is exactly N elements long. + let ptr = snd.as_ptr() as *const [T; N]; + unsafe { Some(&*ptr) } + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &[]; + None + } else { + let start = (len - 1 - n) * N; + let end = start + N; + let nth_back = &self.v[start..end]; + self.v = &self.v[..start]; + // SAFETY: This is safe as snd is exactly N elements long. + let ptr = nth_back.as_ptr() as *const [T; N]; + unsafe { Some(&*ptr) } + } + } +} + +#[unstable(feature = "array_chunks", issue = "none")] +impl ExactSizeIterator for ArrayChunks<'_, T, N> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayChunks<'_, T, N> {} + +#[unstable(feature = "array_chunks", issue = "none")] +impl FusedIterator for ArrayChunks<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "array_chunks", issue = "none")] +unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> { + unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T; N] { + let start = i * N; + let segment = from_raw_parts(self.v.as_ptr().add(start), N); + // SAFETY: This is safe as segment is exactly N elements long. + let ptr = segment.as_ptr() as *const [T; N]; + &*ptr + } + fn may_have_side_effect() -> bool { + false + } +} + /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a /// time), starting at the end of the slice. /// From 255f2854cf47460faf78c553c31fc95365ee7a90 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 18 May 2020 21:24:37 +0200 Subject: [PATCH 3/4] add tests for array_chunks --- src/libcore/slice/mod.rs | 1 - src/libcore/tests/lib.rs | 1 + src/libcore/tests/slice.rs | 22 ++++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index ac5bd67a1a243..b68eafcbe787a 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -859,7 +859,6 @@ impl [T] { /// ``` /// /// [`chunks`]: #method.chunks - /// [`rchunks_exact`]: #method.rchunks_exact #[unstable(feature = "array_chunks", issue = "none")] #[inline] pub fn array_chunks(&self) -> ArrayChunks<'_, T, N> { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index d636542d699f9..57261924fc0f8 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -1,4 +1,5 @@ #![feature(alloc_layout_extra)] +#![feature(array_chunks)] #![feature(bool_to_option)] #![feature(bound_cloned)] #![feature(box_syntax)] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 54a585415bce2..2585019c13ea6 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -433,6 +433,28 @@ fn test_chunks_exact_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 4]); } +// FIXME(#71154) +// FIXME(const_generics) +// We can't yet use `v.array_chunks::<3>()`, so until either #71154 or a +// different PR implementing const arguments in type dependent paths lands, +// we can't yet test many uses. +#[test] +fn test_array_chunks_simple() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = <[i32]>::array_chunks(&v); + assert!(matches!(c.next(), Some(&[0, 1, 2]))); + assert!(matches!(c.next(), Some(&[3, 4, 5]))); + assert!(matches!(c.next(), None)); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = <[i32]>::array_chunks(&v2); + for &[a, b, c] in c2 { + assert_eq!(a, 0i32); + assert_eq!(b, 1i32); + assert_eq!(c, 2i32); + } +} + #[test] fn test_rchunks_count() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; From bdcd488fea7b5bbbac524f4a241ee6d502eace68 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 18 May 2020 23:14:55 +0200 Subject: [PATCH 4/4] liballoc export ArrayChunks --- src/liballoc/lib.rs | 1 + src/liballoc/slice.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 7aaa91ee10d97..a4cd5fd49e83a 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -75,6 +75,7 @@ #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(test))] #![feature(allocator_api)] +#![feature(array_chunks)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] #![feature(box_patterns)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 53477288b59ee..8d07d9284234a 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -95,6 +95,8 @@ use crate::borrow::ToOwned; use crate::boxed::Box; use crate::vec::Vec; +#[unstable(feature = "array_chunks", issue = "none")] +pub use core::slice::ArrayChunks; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[stable(feature = "from_ref", since = "1.28.0")]