Added smoke tests for new methods.
Fixed bug in existing StrSearcher impl
This commit is contained in:
parent
c29559d28a
commit
fbba28e246
@ -1720,6 +1720,31 @@ mod pattern {
|
||||
if rev {
|
||||
v.reverse();
|
||||
}
|
||||
|
||||
let mut first_index = 0;
|
||||
let mut err = None;
|
||||
|
||||
for (i, e) in right.iter().enumerate() {
|
||||
match *e {
|
||||
Match(a, b) | Reject(a, b)
|
||||
if a <= b && a == first_index => {
|
||||
first_index = b;
|
||||
}
|
||||
_ => {
|
||||
err = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(err) = err {
|
||||
panic!("Input skipped range at {}", err);
|
||||
}
|
||||
|
||||
if first_index != haystack.len() {
|
||||
panic!("Did not cover whole input");
|
||||
}
|
||||
|
||||
assert_eq!(v, right);
|
||||
}
|
||||
|
||||
@ -1731,14 +1756,21 @@ mod pattern {
|
||||
Reject(6, 7),
|
||||
]);
|
||||
make_test!(str_searcher_empty_needle_ascii_haystack, "", "abbcbbd", [
|
||||
Match(0, 0),
|
||||
Match(1, 1),
|
||||
Match(2, 2),
|
||||
Match(3, 3),
|
||||
Match(4, 4),
|
||||
Match(5, 5),
|
||||
Match(6, 6),
|
||||
Match(7, 7),
|
||||
Match (0, 0),
|
||||
Reject(0, 1),
|
||||
Match (1, 1),
|
||||
Reject(1, 2),
|
||||
Match (2, 2),
|
||||
Reject(2, 3),
|
||||
Match (3, 3),
|
||||
Reject(3, 4),
|
||||
Match (4, 4),
|
||||
Reject(4, 5),
|
||||
Match (5, 5),
|
||||
Reject(5, 6),
|
||||
Match (6, 6),
|
||||
Reject(6, 7),
|
||||
Match (7, 7),
|
||||
]);
|
||||
make_test!(str_searcher_mulibyte_haystack, " ", "├──", [
|
||||
Reject(0, 3),
|
||||
@ -1746,10 +1778,13 @@ mod pattern {
|
||||
Reject(6, 9),
|
||||
]);
|
||||
make_test!(str_searcher_empty_needle_mulibyte_haystack, "", "├──", [
|
||||
Match(0, 0),
|
||||
Match(3, 3),
|
||||
Match(6, 6),
|
||||
Match(9, 9),
|
||||
Match (0, 0),
|
||||
Reject(0, 3),
|
||||
Match (3, 3),
|
||||
Reject(3, 6),
|
||||
Match (6, 6),
|
||||
Reject(6, 9),
|
||||
Match (9, 9),
|
||||
]);
|
||||
make_test!(str_searcher_empty_needle_empty_haystack, "", "", [
|
||||
Match(0, 0),
|
||||
@ -1778,6 +1813,96 @@ mod pattern {
|
||||
|
||||
}
|
||||
|
||||
macro_rules! generate_iterator_test {
|
||||
{
|
||||
$name:ident {
|
||||
$(
|
||||
($($arg:expr),*) -> [$($t:tt)*];
|
||||
)*
|
||||
}
|
||||
with $fwd:expr, $bwd:expr;
|
||||
} => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
$(
|
||||
{
|
||||
let res = vec![$($t)*];
|
||||
|
||||
let fwd_vec: Vec<_> = ($fwd)($($arg),*).collect();
|
||||
assert_eq!(fwd_vec, res);
|
||||
|
||||
let mut bwd_vec: Vec<_> = ($bwd)($($arg),*).collect();
|
||||
bwd_vec.reverse();
|
||||
assert_eq!(bwd_vec, res);
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
{
|
||||
$name:ident {
|
||||
$(
|
||||
($($arg:expr),*) -> [$($t:tt)*];
|
||||
)*
|
||||
}
|
||||
with $fwd:expr;
|
||||
} => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
$(
|
||||
{
|
||||
let res = vec![$($t)*];
|
||||
|
||||
let fwd_vec: Vec<_> = ($fwd)($($arg),*).collect();
|
||||
assert_eq!(fwd_vec, res);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_iterator_test! {
|
||||
double_ended_split {
|
||||
("foo.bar.baz", '.') -> ["foo", "bar", "baz"];
|
||||
("foo::bar::baz", "::") -> ["foo", "bar", "baz"];
|
||||
}
|
||||
with str::split, str::rsplit;
|
||||
}
|
||||
|
||||
generate_iterator_test! {
|
||||
double_ended_split_terminator {
|
||||
("foo;bar;baz;", ';') -> ["foo", "bar", "baz"];
|
||||
}
|
||||
with str::split_terminator, str::rsplit_terminator;
|
||||
}
|
||||
|
||||
generate_iterator_test! {
|
||||
double_ended_matches {
|
||||
("a1b2c3", char::is_numeric) -> ["1", "2", "3"];
|
||||
}
|
||||
with str::matches, str::rmatches;
|
||||
}
|
||||
|
||||
generate_iterator_test! {
|
||||
double_ended_match_indices {
|
||||
("a1b2c3", char::is_numeric) -> [(1, 2), (3, 4), (5, 6)];
|
||||
}
|
||||
with str::match_indices, str::rmatch_indices;
|
||||
}
|
||||
|
||||
generate_iterator_test! {
|
||||
not_double_ended_splitn {
|
||||
("foo::bar::baz", 2, "::") -> ["foo", "bar::baz"];
|
||||
}
|
||||
with str::splitn;
|
||||
}
|
||||
|
||||
generate_iterator_test! {
|
||||
not_double_ended_rsplitn {
|
||||
("foo::bar::baz", 2, "::") -> ["baz", "foo::bar"];
|
||||
}
|
||||
with str::rsplitn;
|
||||
}
|
||||
|
||||
mod bench {
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
|
@ -351,7 +351,14 @@ pub struct StrSearcher<'a, 'b> {
|
||||
needle: &'b str,
|
||||
start: usize,
|
||||
end: usize,
|
||||
done: bool,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
enum State { Done, NotDone, Reject(usize, usize) }
|
||||
impl State {
|
||||
#[inline] fn done(&self) -> bool { *self == State::Done }
|
||||
#[inline] fn take(&mut self) -> State { ::mem::replace(self, State::NotDone) }
|
||||
}
|
||||
|
||||
/// Non-allocating substring search.
|
||||
@ -368,7 +375,7 @@ impl<'a, 'b> Pattern<'a> for &'b str {
|
||||
needle: self,
|
||||
start: 0,
|
||||
end: haystack.len(),
|
||||
done: false,
|
||||
state: State::NotDone,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,8 +392,9 @@ unsafe impl<'a, 'b> Searcher<'a> for StrSearcher<'a, 'b> {
|
||||
|m: &mut StrSearcher| {
|
||||
// Forward step for empty needle
|
||||
let current_start = m.start;
|
||||
if !m.done {
|
||||
if !m.state.done() {
|
||||
m.start = m.haystack.char_range_at(current_start).next;
|
||||
m.state = State::Reject(current_start, m.start);
|
||||
}
|
||||
SearchStep::Match(current_start, current_start)
|
||||
},
|
||||
@ -415,8 +423,9 @@ unsafe impl<'a, 'b> ReverseSearcher<'a> for StrSearcher<'a, 'b> {
|
||||
|m: &mut StrSearcher| {
|
||||
// Backward step for empty needle
|
||||
let current_end = m.end;
|
||||
if !m.done {
|
||||
if !m.state.done() {
|
||||
m.end = m.haystack.char_range_at_reverse(current_end).next;
|
||||
m.state = State::Reject(m.end, current_end);
|
||||
}
|
||||
SearchStep::Match(current_end, current_end)
|
||||
},
|
||||
@ -446,23 +455,27 @@ fn str_search_step<F, G>(mut m: &mut StrSearcher,
|
||||
where F: FnOnce(&mut StrSearcher) -> SearchStep,
|
||||
G: FnOnce(&mut StrSearcher) -> SearchStep
|
||||
{
|
||||
if m.done {
|
||||
if m.state.done() {
|
||||
SearchStep::Done
|
||||
} else if m.needle.len() == 0 && m.start <= m.end {
|
||||
// Case for needle == ""
|
||||
if m.start == m.end {
|
||||
m.done = true;
|
||||
if let State::Reject(a, b) = m.state.take() {
|
||||
SearchStep::Reject(a, b)
|
||||
} else {
|
||||
if m.start == m.end {
|
||||
m.state = State::Done;
|
||||
}
|
||||
empty_needle_step(&mut m)
|
||||
}
|
||||
empty_needle_step(&mut m)
|
||||
} else if m.start + m.needle.len() <= m.end {
|
||||
// Case for needle != ""
|
||||
nonempty_needle_step(&mut m)
|
||||
} else if m.start < m.end {
|
||||
// Remaining slice shorter than needle, reject it
|
||||
m.done = true;
|
||||
m.state = State::Done;
|
||||
SearchStep::Reject(m.start, m.end)
|
||||
} else {
|
||||
m.done = true;
|
||||
m.state = State::Done;
|
||||
SearchStep::Done
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user