1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
#![no_main]
use fzf_oxide::{chars, Matcher, MatcherConfig, Utf32Str};
use libfuzzer_sys::arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
#[derive(Arbitrary, Debug)]
pub struct Input<'a> {
haystack: &'a str,
needle: &'a str,
ignore_case: bool,
normalize: bool,
}
fuzz_target!(|data: Input<'_>| {
let mut data = data;
let mut config = MatcherConfig::DEFAULT;
config.ignore_case = data.ignore_case;
config.normalize = data.normalize;
let mut matcher = Matcher::new(config);
let mut indices_optimal = Vec::new();
let mut indices_greedy = Vec::new();
let mut needle_buf = Vec::new();
let mut haystack_buf = Vec::new();
let normalize = |mut c: char| {
if config.normalize {
c = chars::normalize(c);
}
if config.ignore_case {
c = chars::to_lower_case(c);
}
c
};
let needle: String = data.needle.chars().map(normalize).collect();
let needle_chars: Vec<_> = needle.chars().collect();
let needle = Utf32Str::new(&needle, &mut needle_buf);
let haystack = Utf32Str::new(data.haystack, &mut haystack_buf);
let greedy_score = matcher.fuzzy_indices_greedy(haystack, needle, &mut indices_greedy);
if greedy_score.is_some() {
let match_chars: Vec<_> = indices_greedy
.iter()
.map(|&i| normalize(haystack.get(i)))
.collect();
assert_eq!(
match_chars, needle_chars,
"failed match, found {indices_greedy:?} {match_chars:?} (greedy)"
);
}
let optimal_score = matcher.fuzzy_indices(haystack, needle, &mut indices_optimal);
if optimal_score.is_some() {
let match_chars: Vec<_> = indices_optimal
.iter()
.map(|&i| normalize(haystack.get(i)))
.collect();
assert_eq!(
match_chars, needle_chars,
"failed match, found {indices_optimal:?} {match_chars:?}"
);
}
match (greedy_score, optimal_score) {
(None, Some(score)) => unreachable!("optimal matched {score} but greedy did not match"),
(Some(score), None) => unreachable!("greedy matched {score} but optimal did not match"),
(Some(greedy), Some(optimal)) => {
assert!(
greedy <= optimal,
"optimal score must be at least the same as greedy score {greedy} {optimal}"
);
if indices_greedy == indices_optimal {
assert_eq!(
greedy, optimal,
"if matching same char greedy and optimal score should be identical"
)
}
}
(None, None) => (),
}
});
|