diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 3ac34c9ae28af271afe810bc5b357c5cd4604ddb..34102d9c4036a4354f2c3b029624047f7f13a07a 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 064700fc72c95154a98209ce4bb88d75749ddb43..bfa317ffd73c8cc4d867302e395a2d81764b3a9c 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -118,6 +118,8 @@ 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 b7cc03f8eb999403f45f2a70939bbf92f960c569..268153242c778fe11eeb2a69e94eadb880f0a8a7 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 a4f0fb415fb3cc79b8045c98471371fd28c2e6da..e2fdb5a6b5a73edb2511f0aa87a3acbe60474221 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 e373936a6c74d848a2c527f6791146578251b34a..917997f902aec7e8f4b406049da77f777ca82e7c 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2967,3 +2967,183 @@ fn may_have_side_effect() -> bool { 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 { + // 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) { + if self.is_empty() { return (0, Some(0)) } + let len = self.remaining_len(); + (1, Some(len)) + } + + fn last(mut self) -> Option { + 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 { + // 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 44fe2ca88596f8dc63654f09eab4a7ac2cd6aa98..e366baa34c6655d9abb5f2172cc06a5be78a47b9 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1207,6 +1207,74 @@ pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, 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(&self, pred: F) -> GroupBy + 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(&mut self, pred: F) -> GroupByMut + 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 106c9fe5da3e6255a5964abe96a1df7a496365fe..1199fa4abbce996850a9ef2e81a6fba22a0ca7bf 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;