about summary refs log tree commit diff
diff options
context:
space:
mode:
authornham <hamann.nick@gmail.com>2014-08-19 15:20:51 -0400
committernham <hamann.nick@gmail.com>2014-08-20 02:51:22 -0400
commit9419e9265950a16f873dbed49c715fd7ea4e08e7 (patch)
tree5e0cde488f12bf4c89d7776390977b6a352b2020
parent3f5d0b5b6cd4994c719d57a778697124348a4c1c (diff)
downloadrust-9419e9265950a16f873dbed49c715fd7ea4e08e7.tar.gz
rust-9419e9265950a16f873dbed49c715fd7ea4e08e7.zip
Fix TwoWaySearcher to work when used with periodic needles.
There is a check in TwoWaySearcher::new to determine whether the needle
is periodic. This is needed because during searching when a match fails,
we cannot advance the position by the entire length of the needle when
it is periodic, but can only advance by the length of the period.

The reason "bananas".contains("nana") (and similar searches) were
returning false was because the periodicity check was wrong.

Closes #16589
-rw-r--r--src/libcore/str.rs7
-rw-r--r--src/libcoretest/str.rs20
2 files changed, 26 insertions, 1 deletions
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 800e2dcc278..3c489719496 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -419,6 +419,8 @@ struct TwoWaySearcher {
     memory: uint
 }
 
+// This is the Two-Way search algorithm, which was introduced in the paper:
+// Crochemore, M., Perrin, D., 1991, Two-way string-matching, Journal of the ACM 38(3):651-675.
 impl TwoWaySearcher {
     fn new(needle: &[u8]) -> TwoWaySearcher {
         let (critPos1, period1) = TwoWaySearcher::maximal_suffix(needle, false);
@@ -437,7 +439,10 @@ impl TwoWaySearcher {
         let byteset = needle.iter()
                             .fold(0, |a, &b| (1 << ((b & 0x3f) as uint)) | a);
 
-        if needle.slice_to(critPos) == needle.slice_from(needle.len() - critPos) {
+        // Check if the needle is periodic. If so, during searching when we
+        // find a mismatch, we must only advance the position by the length
+        // of the period, not the length of the entire needle
+        if needle.slice_to(critPos) == needle.slice(period, period + critPos) {
             TwoWaySearcher {
                 critPos: critPos,
                 period: period,
diff --git a/src/libcoretest/str.rs b/src/libcoretest/str.rs
index bac8d509b13..be2275dcd4a 100644
--- a/src/libcoretest/str.rs
+++ b/src/libcoretest/str.rs
@@ -8,7 +8,27 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+fn check_contains_all_substrings(s: &str) {
+    assert!(s.contains(""));
+    for i in range(0, s.len()) {
+        for j in range(i+1, s.len() + 1) {
+            assert!(s.contains(s.slice(i, j)));
+        }
+    }
+}
+
 #[test]
 fn strslice_issue_16589() {
     assert!("bananas".contains("nana"));
+
+    // prior to the fix for #16589, x.contains("abcdabcd") returned false
+    // test all substrings for good measure
+    check_contains_all_substrings("012345678901234567890123456789bcdabcdabcd");
+}
+
+
+#[test]
+fn test_strslice_contains() {
+    let x = "There are moments, Jeeves, when one asks oneself, 'Do trousers matter?'";
+    check_contains_all_substrings(x);
 }