Repository: rayon-rs/rayon
Pull request #869 (2021-06-16 21:44:35)

impl Pattern for &[char]
The standard library has had this since 1.0, so I don't know why we didn't add it here.


Patch:
diff --git a/rayon-demo/src/str_split.rs b/rayon-demo/src/str_split.rs
index 7416b17..46e232c 100644
--- a/rayon-demo/src/str_split.rs
+++ b/rayon-demo/src/str_split.rs
@@ -14,6 +14,9 @@ lazy_static::lazy_static! {
     static ref COUNT: usize = HAYSTACK.split(' ').count();
 }
 
+// Try multiple kinds of whitespace, but HAYSTACK only contains plain spaces.
+const WHITESPACE: &[char] = &['\r', '\n', ' ', '\t'];
+
 fn get_string_count() -> (&'static str, usize) {
     (&HAYSTACK, *COUNT)
 }
@@ -24,6 +27,12 @@ fn parallel_space_char(b: &mut Bencher) {
     b.iter(|| assert_eq!(string.par_split(' ').count(), count))
 }
 
+#[bench]
+fn parallel_space_chars(b: &mut Bencher) {
+    let (string, count) = get_string_count();
+    b.iter(|| assert_eq!(string.par_split(WHITESPACE).count(), count))
+}
+
 #[bench]
 fn parallel_space_fn(b: &mut Bencher) {
     let (string, count) = get_string_count();
@@ -36,6 +45,12 @@ fn serial_space_char(b: &mut Bencher) {
     b.iter(|| assert_eq!(string.split(' ').count(), count))
 }
 
+#[bench]
+fn serial_space_chars(b: &mut Bencher) {
+    let (string, count) = get_string_count();
+    b.iter(|| assert_eq!(string.split(WHITESPACE).count(), count))
+}
+
 #[bench]
 fn serial_space_fn(b: &mut Bencher) {
     let (string, count) = get_string_count();
diff --git a/src/str.rs b/src/str.rs
index 2fdaaa7..7afda30 100644
--- a/src/str.rs
+++ b/src/str.rs
@@ -6,7 +6,8 @@
 //! Note: [`ParallelString::par_split()`] and [`par_split_terminator()`]
 //! reference a `Pattern` trait which is not visible outside this crate.
 //! This trait is intentionally kept private, for use only by Rayon itself.
-//! It is implemented for `char` and any `F: Fn(char) -> bool + Sync + Send`.
+//! It is implemented for `char`, `&[char]`, and any function or closure
+//! `F: Fn(char) -> bool + Sync + Send`.
 //!
 //! [`ParallelString::par_split()`]: trait.ParallelString.html#method.par_split
 //! [`par_split_terminator()`]: trait.ParallelString.html#method.par_split_terminator
@@ -34,11 +35,11 @@ fn find_char_midpoint(chars: &str) -> usize {
     // character boundary.  So we look at the raw bytes, first scanning
     // forward from the midpoint for a boundary, then trying backward.
     let (left, right) = chars.as_bytes().split_at(mid);
-    match right.iter().cloned().position(is_char_boundary) {
+    match right.iter().copied().position(is_char_boundary) {
         Some(i) => mid + i,
         None => left
             .iter()
-            .cloned()
+            .copied()
             .rposition(is_char_boundary)
             .unwrap_or(0),
     }
@@ -96,7 +97,7 @@ pub trait ParallelString {
     /// Note that multi-byte sequences (for code points greater than `U+007F`)
     /// are produced as separate items, but will not be split across threads.
     /// If you would prefer an indexed iterator without that guarantee, consider
-    /// `string.as_bytes().par_iter().cloned()` instead.
+    /// `string.as_bytes().par_iter().copied()` instead.
     ///
     /// # Examples
     ///
@@ -139,7 +140,8 @@ pub trait ParallelString {
     /// given character or predicate, similar to `str::split`.
     ///
     /// Note: the `Pattern` trait is private, for use only by Rayon itself.
-    /// It is implemented for `char` and any `F: Fn(char) -> bool + Sync + Send`.
+    /// It is implemented for `char`, `&[char]`, and any function or closure
+    /// `F: Fn(char) -> bool + Sync + Send`.
     ///
     /// # Examples
     ///
@@ -161,7 +163,8 @@ pub trait ParallelString {
     /// substring after a trailing terminator.
     ///
     /// Note: the `Pattern` trait is private, for use only by Rayon itself.
-    /// It is implemented for `char` and any `F: Fn(char) -> bool + Sync + Send`.
+    /// It is implemented for `char`, `&[char]`, and any function or closure
+    /// `F: Fn(char) -> bool + Sync + Send`.
     ///
     /// # Examples
     ///
@@ -218,7 +221,8 @@ pub trait ParallelString {
     /// given character or predicate, similar to `str::matches`.
     ///
     /// Note: the `Pattern` trait is private, for use only by Rayon itself.
-    /// It is implemented for `char` and any `F: Fn(char) -> bool + Sync + Send`.
+    /// It is implemented for `char`, `&[char]`, and any function or closure
+    /// `F: Fn(char) -> bool + Sync + Send`.
     ///
     /// # Examples
     ///
@@ -241,7 +245,8 @@ pub trait ParallelString {
     /// or predicate, with their positions, similar to `str::match_indices`.
     ///
     /// Note: the `Pattern` trait is private, for use only by Rayon itself.
-    /// It is implemented for `char` and any `F: Fn(char) -> bool + Sync + Send`.
+    /// It is implemented for `char`, `&[char]`, and any function or closure
+    /// `F: Fn(char) -> bool + Sync + Send`.
     ///
     /// # Examples
     ///
@@ -303,89 +308,62 @@ fn offset<T>(base: usize) -> impl Fn((usize, T)) -> (usize, T) {
     move |(i, x)| (base + i, x)
 }
 
-impl Pattern for char {
-    private_impl! {}
+macro_rules! impl_pattern {
+    (&$self:ident => $pattern:expr) => {
+        private_impl! {}
 
-    #[inline]
-    fn find_in(&self, chars: &str) -> Option<usize> {
-        chars.find(*self)
-    }
+        #[inline]
+        fn find_in(&$self, chars: &str) -> Option<usize> {
+            chars.find($pattern)
+        }
 
-    #[inline]
-    fn rfind_in(&self, chars: &str) -> Option<usize> {
-        chars.rfind(*self)
-    }
+        #[inline]
+        fn rfind_in(&$self, chars: &str) -> Option<usize> {
+            chars.rfind($pattern)
+        }
 
-    #[inline]
-    fn is_suffix_of(&self, chars: &str) -> bool {
-        chars.ends_with(*self)
-    }
+        #[inline]
+        fn is_suffix_of(&$self, chars: &str) -> bool {
+            chars.ends_with($pattern)
+        }
 
-    fn fold_splits<'ch, F>(&self, chars: &'ch str, folder: F, skip_last: bool) -> F
-    where
-        F: Folder<&'ch str>,
-    {
-        let mut split = chars.split(*self);
-        if skip_last {
-            split.next_back();
+        fn fold_splits<'ch, F>(&$self, chars: &'ch str, folder: F, skip_last: bool) -> F
+        where
+            F: Folder<&'ch str>,
+        {
+            let mut split = chars.split($pattern);
+            if skip_last {
+                split.next_back();
+            }
+            folder.consume_iter(split)
         }
-        folder.consume_iter(split)
-    }
 
-    fn fold_matches<'ch, F>(&self, chars: &'ch str, folder: F) -> F
-    where
-        F: Folder<&'ch str>,
-    {
-        folder.consume_iter(chars.matches(*self))
-    }
+        fn fold_matches<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
+        where
+            F: Folder<&'ch str>,
+        {
+            folder.consume_iter(chars.matches($pattern))
+        }
 
-    fn fold_match_indices<'ch, F>(&self, chars: &'ch str, folder: F, base: usize) -> F
-    where
-        F: Folder<(usize, &'ch str)>,
-    {
-        folder.consume_iter(chars.match_indices(*self).map(offset(base)))
+        fn fold_match_indices<'ch, F>(&$self, chars: &'ch str, folder: F, base: usize) -> F
+        where
+            F: Folder<(usize, &'ch str)>,
+        {
+            folder.consume_iter(chars.match_indices($pattern).map(offset(base)))
+        }
     }
 }
 
-impl<FN: Sync + Send + Fn(char) -> bool> Pattern for FN {
-    private_impl! {}
-
-    fn find_in(&self, chars: &str) -> Option<usize> {
-        chars.find(self)
-    }
-
-    fn rfind_in(&self, chars: &str) -> Option<usize> {
-        chars.rfind(self)
-    }
-
-    fn is_suffix_of(&self, chars: &str) -> bool {
-        chars.ends_with(self)
-    }
-
-    fn fold_splits<'ch, F>(&self, chars: &'ch str, folder: F, skip_last: bool) -> F
-    where
-        F: Folder<&'ch str>,
-    {
-        let mut split = chars.split(self);
-        if skip_last {
-            split.next_back();
-        }
-        folder.consume_iter(split)
-    }
+impl Pattern for char {
+    impl_pattern!(&self => *self);
+}
 
-    fn fold_matches<'ch, F>(&self, chars: &'ch str, folder: F) -> F
-    where
-        F: Folder<&'ch str>,
-    {
-        folder.consume_iter(chars.matches(self))
-    }
+impl Pattern for &[char] {
+    impl_pattern!(&self => *self);
+}
 
-    fn fold_match_indices<'ch, F>(&self, chars: &'ch str, folder: F, base: usize) -> F
-    where
-        F: Folder<(usize, &'ch str)>,
-    {
-        folder.consume_iter(chars.match_indices(self).map(offset(base)))
-    }
+impl<FN: Sync + Send + Fn(char) -> bool> Pattern for FN {
+    impl_pattern!(&self => self);
 }
 
 // /////////////////////////////////////////////////////////////////////////


Test patch:
diff --git a/tests/str.rs b/tests/str.rs
index 0e1e35e..f73a261 100644
--- a/tests/str.rs
+++ b/tests/str.rs
@@ -64,6 +64,11 @@ pub fn execute_strings_split() {
         let parallel: Vec<_> = string.par_split(separator).collect();
         assert_eq!(serial, parallel);
 
+        let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
+        let serial: Vec<_> = string.split(pattern).collect();
+        let parallel: Vec<_> = string.par_split(pattern).collect();
+        assert_eq!(serial, parallel);
+
         let serial_fn: Vec<_> = string.split(|c| c == separator).collect();
         let parallel_fn: Vec<_> = string.par_split(|c| c == separator).collect();
         assert_eq!(serial_fn, parallel_fn);
@@ -73,9 +78,12 @@ pub fn execute_strings_split() {
         let serial: Vec<_> = string.split_terminator(separator).collect();
         let parallel: Vec<_> = string.par_split_terminator(separator).collect();
         assert_eq!(serial, parallel);
-    }
 
-    for &(string, separator) in &tests {
+        let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
+        let serial: Vec<_> = string.split_terminator(pattern).collect();
+        let parallel: Vec<_> = string.par_split_terminator(pattern).collect();
+        assert_eq!(serial, parallel);
+
         let serial: Vec<_> = string.split_terminator(|c| c == separator).collect();
         let parallel: Vec<_> = string.par_split_terminator(|c| c == separator).collect();
         assert_eq!(serial, parallel);
@@ -99,6 +107,11 @@ pub fn execute_strings_split() {
         let parallel: Vec<_> = string.par_matches(separator).collect();
         assert_eq!(serial, parallel);
 
+        let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
+        let serial: Vec<_> = string.matches(pattern).collect();
+        let parallel: Vec<_> = string.par_matches(pattern).collect();
+        assert_eq!(serial, parallel);
+
         let serial_fn: Vec<_> = string.matches(|c| c == separator).collect();
         let parallel_fn: Vec<_> = string.par_matches(|c| c == separator).collect();
         assert_eq!(serial_fn, parallel_fn);
@@ -109,6 +122,11 @@ pub fn execute_strings_split() {
         let parallel: Vec<_> = string.par_match_indices(separator).collect();
         assert_eq!(serial, parallel);
 
+        let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
+        let serial: Vec<_> = string.match_indices(pattern).collect();
+        let parallel: Vec<_> = string.par_match_indices(pattern).collect();
+        assert_eq!(serial, parallel);
+
         let serial_fn: Vec<_> = string.match_indices(|c| c == separator).collect();
         let parallel_fn: Vec<_> = string.par_match_indices(|c| c == separator).collect();
         assert_eq!(serial_fn, parallel_fn);