Skip to content

Commit

Permalink
Implement split_inclusive for slice and str, an splitting iterator th…
Browse files Browse the repository at this point in the history
…at includes the matched part in the iterated substrings as a terminator.
  • Loading branch information
golddranks committed Feb 9, 2020
1 parent 8647aa1 commit 86bf962
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/liballoc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#![feature(binary_heap_into_iter_sorted)]
#![feature(binary_heap_drain_sorted)]
#![feature(vec_remove_item)]
#![feature(split_inclusive)]

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
Expand Down
20 changes: 20 additions & 0 deletions src/liballoc/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,26 @@ fn test_splitator() {
assert_eq!(xs.split(|x| *x == 5).collect::<Vec<&[i32]>>(), splits);
}

#[test]
fn test_splitator_inclusive() {
let xs = &[1, 2, 3, 4, 5];

let splits: &[&[_]] = &[&[1, 2], &[3, 4], &[5]];
assert_eq!(xs.split_inclusive(|x| *x % 2 == 0).collect::<Vec<_>>(), splits);
let splits: &[&[_]] = &[&[1], &[2, 3, 4, 5]];
assert_eq!(xs.split_inclusive(|x| *x == 1).collect::<Vec<_>>(), splits);
let splits: &[&[_]] = &[&[1, 2, 3, 4, 5], &[]];
assert_eq!(xs.split_inclusive(|x| *x == 5).collect::<Vec<_>>(), splits);
let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]];
assert_eq!(xs.split_inclusive(|x| *x == 10).collect::<Vec<_>>(), splits);
let splits: &[&[_]] = &[&[1], &[2], &[3], &[4], &[5], &[]];
assert_eq!(xs.split_inclusive(|_| true).collect::<Vec<&[i32]>>(), splits);

let xs: &[i32] = &[];
let splits: &[&[i32]] = &[&[]];
assert_eq!(xs.split_inclusive(|x| *x == 5).collect::<Vec<&[i32]>>(), splits);
}

#[test]
fn test_splitnator() {
let xs = &[1, 2, 3, 4, 5];
Expand Down
18 changes: 18 additions & 0 deletions src/liballoc/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,24 @@ fn test_split_char_iterator_no_trailing() {
assert_eq!(split, ["", "Märy häd ä little lämb", "Little lämb"]);
}

#[test]
fn test_split_char_iterator_inclusive() {
let data = "\nMäry häd ä little lämb\nLittle lämb\n";

let split: Vec<&str> = data.split_inclusive('\n').collect();
assert_eq!(split, ["\n", "Märy häd ä little lämb\n", "Little lämb\n", ""]);

let uppercase_separated = "SheePSharKTurtlECaT";
let mut first_char = true;
let split: Vec<&str> = uppercase_separated.split_inclusive(|c: char| {
let split = !first_char && c.is_uppercase();
first_char = split;
split
}).collect();
assert_eq!(split, ["SheeP", "SharK", "TurtlE", "CaT", ""]);
}


#[test]
fn test_rsplit() {
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
Expand Down
261 changes: 260 additions & 1 deletion src/libcore/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,72 @@ impl<T> [T] {
SplitMut { v: self, pred, finished: false }
}

/// Returns an iterator over subslices separated by elements that match
/// `pred`. The matched element is contained in the end of the previous
/// subslice as a terminator.
///
/// # Examples
///
/// ```
/// #![feature(split_inclusive)]
/// let slice = [10, 40, 33, 20];
/// let mut iter = slice.split_inclusive(|num| num % 3 == 0);
///
/// assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
/// assert_eq!(iter.next().unwrap(), &[20]);
/// assert!(iter.next().is_none());
/// ```
///
/// If the first element is matched, an empty slice will be the first item
/// returned by the iterator. Similarly, if the last element in the slice
/// is matched, an empty slice will be the last item returned by the
/// iterator:
///
/// ```
/// #![feature(split_inclusive)]
/// let slice = [10, 40, 33];
/// let mut iter = slice.split_inclusive(|num| num % 3 == 0);
///
/// assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
/// assert_eq!(iter.next().unwrap(), &[]);
/// assert!(iter.next().is_none());
/// ```
#[unstable(feature = "split_inclusive", issue = "0")]
#[inline]
pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, T, F>
where F: FnMut(&T) -> bool
{
SplitInclusive {
v: self,
pred,
finished: false
}
}

/// Returns an iterator over mutable subslices separated by elements that
/// match `pred`. The matched element is contained in the previous
/// subslice as a terminator.
///
/// # Examples
///
/// ```
/// #![feature(split_inclusive)]
/// let mut v = [10, 40, 30, 20, 60, 50];
///
/// for group in v.split_inclusive_mut(|num| *num % 3 == 0) {
/// let terminator_idx = group.len()-1;
/// group[terminator_idx] = 1;
/// }
/// assert_eq!(v, [10, 40, 1, 20, 1, 1]);
/// ```
#[unstable(feature = "split_inclusive", issue = "0")]
#[inline]
pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F>
where F: FnMut(&T) -> bool
{
SplitInclusiveMut { v: self, pred, finished: false }
}

/// Returns an iterator over subslices separated by elements that match
/// `pred`, starting at the end of the slice and working backwards.
/// The matched element is not contained in the subslices.
Expand Down Expand Up @@ -3675,7 +3741,100 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<T, P> FusedIterator for Split<'_, T, P> where P: FnMut(&T) -> bool {}

/// An iterator over the subslices of the vector which are separated
/// An iterator over subslices separated by elements that match a predicate
/// function. Unlike `Split`, it contains the matched part as a terminator
/// of the subslice.
///
/// This struct is created by the [`split_inclusive`] method on [slices].
///
/// [`split_inclusive`]: ../../std/primitive.slice.html#method.split_inclusive
/// [slices]: ../../std/primitive.slice.html
#[unstable(feature = "split_inclusive", issue = "0")]
pub struct SplitInclusive<'a, T:'a, P> where P: FnMut(&T) -> bool {
v: &'a [T],
pred: P,
finished: bool
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<T: fmt::Debug, P> fmt::Debug for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SplitInclusive")
.field("v", &self.v)
.field("finished", &self.finished)
.finish()
}
}

// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
#[unstable(feature = "split_inclusive", issue = "0")]
impl<T, P> Clone for SplitInclusive<'_, T, P> where P: Clone + FnMut(&T) -> bool {
fn clone(&self) -> Self {
SplitInclusive {
v: self.v,
pred: self.pred.clone(),
finished: self.finished,
}
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<'a, T, P> Iterator for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool {
type Item = &'a [T];

#[inline]
fn next(&mut self) -> Option<&'a [T]> {
if self.finished { return None; }

match self.v.iter().position(|x| (self.pred)(x)) {
None => self.finish(),
Some(idx) => {
let ret = Some(&self.v[..idx + 1]);
self.v = &self.v[idx + 1..];
ret
}
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.finished {
(0, Some(0))
} else {
(1, Some(self.v.len() + 1))
}
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<'a, T, P> DoubleEndedIterator for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool {
#[inline]
fn next_back(&mut self) -> Option<&'a [T]> {
if self.finished { return None; }

match self.v.iter().rposition(|x| (self.pred)(x)) {
None => self.finish(),
Some(idx) => {
let ret = Some(&self.v[idx + 1..]);
self.v = &self.v[..idx];
ret
}
}
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<'a, T, P> SplitIter for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool {
#[inline]
fn finish(&mut self) -> Option<&'a [T]> {
if self.finished { None } else { self.finished = true; Some(self.v) }
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<T, P> FusedIterator for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool {}

/// An iterator over the mutable subslices of the vector which are separated
/// by elements that match `pred`.
///
/// This struct is created by the [`split_mut`] method on [slices].
Expand Down Expand Up @@ -3789,6 +3948,106 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<T, P> FusedIterator for SplitMut<'_, T, P> where P: FnMut(&T) -> bool {}

/// An iterator over the mutable subslices of the vector which are separated
/// by elements that match `pred`. Unlike `SplitMut`, it contains the matched
/// parts in the ends of the subslices.
///
/// This struct is created by the [`split_inclusive_mut`] method on [slices].
///
/// [`split_inclusive_mut`]: ../../std/primitive.slice.html#method.split_inclusive_mut
/// [slices]: ../../std/primitive.slice.html
#[unstable(feature = "split_inclusive", issue = "0")]
pub struct SplitInclusiveMut<'a, T:'a, P> where P: FnMut(&T) -> bool {
v: &'a mut [T],
pred: P,
finished: bool
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<T: fmt::Debug, P> fmt::Debug for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SplitInclusiveMut")
.field("v", &self.v)
.field("finished", &self.finished)
.finish()
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<'a, T, P> SplitIter for SplitInclusiveMut<'a, T, P> where P: FnMut(&T) -> bool {
#[inline]
fn finish(&mut self) -> Option<&'a mut [T]> {
if self.finished {
None
} else {
self.finished = true;
Some(mem::replace(&mut self.v, &mut []))
}
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<'a, T, P> Iterator for SplitInclusiveMut<'a, T, P> where P: FnMut(&T) -> bool {
type Item = &'a mut [T];

#[inline]
fn next(&mut self) -> Option<&'a mut [T]> {
if self.finished { return None; }

let idx_opt = { // work around borrowck limitations
let pred = &mut self.pred;
self.v.iter().position(|x| (*pred)(x))
};
match idx_opt {
None => self.finish(),
Some(idx) => {
let tmp = mem::replace(&mut self.v, &mut []);
let (head, tail) = tmp.split_at_mut(idx+1);
self.v = tail;
Some(head)
}
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.finished {
(0, Some(0))
} else {
// if the predicate doesn't match anything, we yield one slice
// if it matches every element, we yield len+1 empty slices.
(1, Some(self.v.len() + 1))
}
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<'a, T, P> DoubleEndedIterator for SplitInclusiveMut<'a, T, P> where
P: FnMut(&T) -> bool,
{
#[inline]
fn next_back(&mut self) -> Option<&'a mut [T]> {
if self.finished { return None; }

let idx_opt = { // work around borrowck limitations
let pred = &mut self.pred;
self.v.iter().rposition(|x| (*pred)(x))
};
match idx_opt {
None => self.finish(),
Some(idx) => {
let tmp = mem::replace(&mut self.v, &mut []);
let (head, tail) = tmp.split_at_mut(idx+1);
self.v = head;
Some(tail)
}
}
}
}

#[unstable(feature = "split_inclusive", issue = "0")]
impl<T, P> FusedIterator for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool {}

/// An iterator over subslices separated by elements that match a predicate
/// function, starting from the end of the slice.
///
Expand Down
Loading

0 comments on commit 86bf962

Please sign in to comment.