about summary refs log tree commit diff
diff options
context:
space:
mode:
authorClément Renault <clement@meilisearch.com>2020-12-10 10:16:29 +0100
committerClément Renault <clement@meilisearch.com>2020-12-10 10:16:29 +0100
commita891f6edfeb4d7b061a215ba160fca0e4804ffd2 (patch)
tree268ac9128fb52fcce238b6f87261252d147beace
parente413d89aa706060ddc347e1e06d551ec86d3f471 (diff)
downloadrust-a891f6edfeb4d7b061a215ba160fca0e4804ffd2.tar.gz
rust-a891f6edfeb4d7b061a215ba160fca0e4804ffd2.zip
Introduce the GroupBy and GroupByMut Iterators
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/slice.rs2
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/alloc/tests/slice.rs27
-rw-r--r--library/core/src/slice/iter.rs180
-rw-r--r--library/core/src/slice/mod.rs68
-rw-r--r--library/core/tests/lib.rs1
7 files changed, 280 insertions, 0 deletions
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 3ac34c9ae28..34102d9c403 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -140,6 +140,7 @@
 #![feature(try_trait)]
 #![feature(type_alias_impl_trait)]
 #![feature(associated_type_bounds)]
+#![feature(slice_group_by)]
 // Allow testing this library
 
 #[cfg(test)]
diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs
index 064700fc72c..bfa317ffd73 100644
--- a/library/alloc/src/slice.rs
+++ b/library/alloc/src/slice.rs
@@ -118,6 +118,8 @@ pub use core::slice::{RChunks, RChunksExact, RChunksExactMut, RChunksMut};
 pub use core::slice::{RSplit, RSplitMut};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::slice::{RSplitN, RSplitNMut, SplitN, SplitNMut};
+#[unstable(feature = "slice_group_by", issue = "0")]
+pub use core::slice::{GroupBy, GroupByMut};
 
 ////////////////////////////////////////////////////////////////////////////////
 // Basic slice extension methods
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index b7cc03f8eb9..268153242c7 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -21,6 +21,7 @@
 #![feature(iter_map_while)]
 #![feature(int_bits_const)]
 #![feature(vecdeque_binary_search)]
+#![feature(slice_group_by)]
 
 use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs
index a4f0fb415fb..e2fdb5a6b5a 100644
--- a/library/alloc/tests/slice.rs
+++ b/library/alloc/tests/slice.rs
@@ -1898,3 +1898,30 @@ fn subslice_patterns() {
     m!(&mut v, [..] => ());
     m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4))));
 }
+
+#[test]
+fn test_group_by() {
+    let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+    let mut iter = slice.group_by(|a, b| a == b);
+
+    assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+
+    assert_eq!(iter.remaining(), &[3, 3, 2, 2, 2]);
+
+    assert_eq!(iter.next(), Some(&[3, 3][..]));
+    assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+    assert_eq!(iter.next(), None);
+}
+
+#[test]
+fn test_group_by_rev() {
+    let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+    let mut iter = slice.group_by(|a, b| a == b);
+
+    assert_eq!(iter.next_back(), Some(&[2, 2, 2][..]));
+    assert_eq!(iter.next_back(), Some(&[3, 3][..]));
+    assert_eq!(iter.next_back(), Some(&[1, 1, 1][..]));
+    assert_eq!(iter.next_back(), None);
+}
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index e373936a6c7..917997f902a 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -2967,3 +2967,183 @@ unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> {
         false
     }
 }
+
+macro_rules! group_by {
+    (struct $name:ident, $elem:ty, $mkslice:ident) => {
+        #[unstable(feature = "slice_group_by", issue = "0")]
+        impl<'a, T: 'a, P> $name<'a, T, P> {
+            #[inline]
+            fn is_empty(&self) -> bool {
+                self.ptr == self.end
+            }
+
+            #[inline]
+            fn remaining_len(&self) -> usize {
+                unsafe { self.end.offset_from(self.ptr) as usize }
+            }
+        }
+
+        #[unstable(feature = "slice_group_by", issue = "0")]
+        impl<'a, T: 'a, P> Iterator for $name<'a, T, P>
+        where P: FnMut(&T, &T) -> bool,
+        {
+            type Item = $elem;
+
+            fn next(&mut self) -> Option<Self::Item> {
+                // we use an unsafe block to avoid bounds checking here.
+                // this is safe because the only thing we do here is to get
+                // two elements at `ptr` and `ptr + 1`, bounds checking is done by hand.
+                unsafe {
+                    if self.is_empty() { return None }
+
+                    let mut i = 0;
+                    let mut ptr = self.ptr;
+
+                    // we need to get *two* contiguous elements so we check that:
+                    //  - the first element is at the `end - 1` position because
+                    //  - the second one will be read from `ptr + 1` that must
+                    //    be lower or equal to `end`
+                    while ptr != self.end.sub(1) {
+                        let a = &*ptr;
+                        ptr = ptr.add(1);
+                        let b = &*ptr;
+
+                        i += 1;
+
+                        if !(self.predicate)(a, b) {
+                            let slice = $mkslice(self.ptr, i);
+                            self.ptr = ptr;
+                            return Some(slice)
+                        }
+                    }
+
+                    // `i` is either `0` or the slice `length - 1` because either:
+                    //  - we have not entered the loop and so `i` is equal to `0`
+                    //    the slice length is necessarily `1` because we ensure it is not empty
+                    //  - we have entered the loop and we have not early returned
+                    //    so `i` is equal to the slice `length - 1`
+                    let slice = $mkslice(self.ptr, i + 1);
+                    self.ptr = self.end;
+                    Some(slice)
+                }
+            }
+
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                if self.is_empty() { return (0, Some(0)) }
+                let len = self.remaining_len();
+                (1, Some(len))
+            }
+
+            fn last(mut self) -> Option<Self::Item> {
+                self.next_back()
+            }
+        }
+
+        #[unstable(feature = "slice_group_by", issue = "0")]
+        impl<'a, T: 'a, P> DoubleEndedIterator for $name<'a, T, P>
+        where P: FnMut(&T, &T) -> bool,
+        {
+            fn next_back(&mut self) -> Option<Self::Item> {
+                // during the loop we retrieve two elements at `ptr` and `ptr - 1`.
+                unsafe {
+                    if self.is_empty() { return None }
+
+                    let mut i = 0;
+                    // we ensure that the first element that will be read
+                    // is not under `end` because `end` is out of bound.
+                    let mut ptr = self.end.sub(1);
+
+                    while ptr != self.ptr {
+                        // we first get `a` that is at the left of `ptr`
+                        // then `b` that is under the `ptr` position.
+                        let a = &*ptr.sub(1);
+                        let b = &*ptr;
+
+                        i += 1;
+
+                        if !(self.predicate)(a, b) {
+                            // the slice to return starts at the `ptr` position
+                            // and `i` is the length of it.
+                            let slice = $mkslice(ptr, i);
+
+                            // because `end` is always an invalid bound
+                            // we use `ptr` as `end` for the future call to `next`.
+                            self.end = ptr;
+                            return Some(slice)
+                        }
+
+                        ptr = ptr.sub(1);
+                    }
+
+                    let slice = $mkslice(self.ptr, i + 1);
+                    self.ptr = self.end;
+                    Some(slice)
+                }
+            }
+        }
+
+        #[unstable(feature = "slice_group_by", issue = "0")]
+        impl<'a, T: 'a, P> FusedIterator for $name<'a, T, P>
+        where P: FnMut(&T, &T) -> bool,
+        { }
+    }
+}
+
+/// An iterator over slice in (non-overlapping) chunks separated by a predicate.
+///
+/// This struct is created by the [`group_by`] method on [slices].
+///
+/// [`group_by`]: ../../std/primitive.slice.html#method.group_by
+/// [slices]: ../../std/primitive.slice.html
+#[unstable(feature = "slice_group_by", issue = "0")]
+#[derive(Debug)] // FIXME implement Debug to be more user friendly
+pub struct GroupBy<'a, T: 'a, P> {
+    ptr: *const T,
+    end: *const T,
+    predicate: P,
+    _phantom: marker::PhantomData<&'a T>,
+}
+
+#[unstable(feature = "slice_group_by", issue = "0")]
+impl<'a, T: 'a, P> GroupBy<'a, T, P>
+where P: FnMut(&T, &T) -> bool,
+{
+    /// Returns the remainder of the original slice that is going to be
+    /// returned by the iterator.
+    pub fn remaining(&self) -> &[T] {
+        let len = self.remaining_len();
+        unsafe { from_raw_parts(self.ptr, len) }
+    }
+}
+
+group_by!{ struct GroupBy, &'a [T], from_raw_parts }
+
+/// An iterator over slice in (non-overlapping) mutable chunks separated
+/// by a predicate.
+///
+/// This struct is created by the [`group_by_mut`] method on [slices].
+///
+/// [`group_by_mut`]: ../../std/primitive.slice.html#method.group_by_mut
+/// [slices]: ../../std/primitive.slice.html
+#[unstable(feature = "slice_group_by", issue = "0")]
+#[derive(Debug)] // FIXME implement Debug to be more user friendly
+pub struct GroupByMut<'a, T: 'a, P> {
+    ptr: *mut T,
+    end: *mut T,
+    predicate: P,
+    _phantom: marker::PhantomData<&'a T>,
+}
+
+#[unstable(feature = "slice_group_by", issue = "0")]
+impl<'a, T: 'a, P> GroupByMut<'a, T, P>
+where P: FnMut(&T, &T) -> bool,
+{
+    /// Returns the remainder of the original slice that is going to be
+    /// returned by the iterator.
+    pub fn into_remaining(self) -> &'a mut [T] {
+        let len = self.remaining_len();
+        unsafe { from_raw_parts_mut(self.ptr, len) }
+    }
+}
+
+group_by!{ struct GroupByMut, &'a mut [T], from_raw_parts_mut }
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 44fe2ca8859..e366baa34c6 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -1207,6 +1207,74 @@ impl<T> [T] {
         RChunksExactMut::new(self, chunk_size)
     }
 
+    /// Returns an iterator over the slice producing non-overlapping runs
+    /// of elements using the predicate to separate them.
+    ///
+    /// The predicate is called on two elements following themselves,
+    /// it means the predicate is called on `slice[0]` and `slice[1]`
+    /// then on `slice[1]` and `slice[2]` and so on.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_group_by)]
+    ///
+    /// let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+    ///
+    /// let mut iter = slice.group_by(|a, b| a == b);
+    ///
+    /// assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+    /// assert_eq!(iter.next(), Some(&[3, 3][..]));
+    /// assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+    /// assert_eq!(iter.next(), None);
+    /// ```
+    #[unstable(feature = "slice_group_by", issue = "0")]
+    #[inline]
+    pub fn group_by<F>(&self, pred: F) -> GroupBy<T, F>
+    where F: FnMut(&T, &T) -> bool
+    {
+        GroupBy {
+            ptr: self.as_ptr(),
+            end: unsafe { self.as_ptr().add(self.len()) },
+            predicate: pred,
+            _phantom: marker::PhantomData,
+        }
+    }
+
+    /// Returns an iterator over the slice producing non-overlapping mutable
+    /// runs of elements using the predicate to separate them.
+    ///
+    /// The predicate is called on two elements following themselves,
+    /// it means the predicate is called on `slice[0]` and `slice[1]`
+    /// then on `slice[1]` and `slice[2]` and so on.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_group_by)]
+    ///
+    /// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
+    ///
+    /// let mut iter = slice.group_by_mut(|a, b| a == b);
+    ///
+    /// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
+    /// assert_eq!(iter.next(), Some(&mut [3, 3][..]));
+    /// assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
+    /// assert_eq!(iter.next(), None);
+    /// ```
+    #[unstable(feature = "slice_group_by", issue = "0")]
+    #[inline]
+    pub fn group_by_mut<F>(&mut self, pred: F) -> GroupByMut<T, F>
+    where F: FnMut(&T, &T) -> bool
+    {
+        GroupByMut {
+            ptr: self.as_mut_ptr(),
+            end: unsafe { self.as_mut_ptr().add(self.len()) },
+            predicate: pred,
+            _phantom: marker::PhantomData,
+        }
+    }
+
     /// Divides one slice into two at an index.
     ///
     /// The first will contain all indices from `[0, mid)` (excluding
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 106c9fe5da3..1199fa4abbc 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -65,6 +65,7 @@
 #![feature(nonzero_leading_trailing_zeros)]
 #![feature(const_option)]
 #![feature(integer_atomics)]
+#![feature(slice_group_by)]
 #![deny(unsafe_op_in_unsafe_fn)]
 
 extern crate test;