Skip to content

Commit 308448d

Browse files
committed
onepass diff
1 parent d8603f5 commit 308448d

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

fuzz/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ path = "fuzz_targets/ast_diff_default.rs"
7575
name = "ast_diff_dense_dfa"
7676
path = "fuzz_targets/ast_diff_dense_dfa.rs"
7777

78+
[[bin]]
79+
name = "ast_diff_onepass_dfa"
80+
path = "fuzz_targets/ast_diff_onepass_dfa.rs"
81+
7882
[[bin]]
7983
name = "ast_diff_sparse_dfa"
8084
path = "fuzz_targets/ast_diff_sparse_dfa.rs"
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#![no_main]
2+
3+
use {
4+
libfuzzer_sys::{fuzz_target, Corpus},
5+
regex_automata::{
6+
dfa::{onepass::Builder as RegexBuilder, onepass::DFA},
7+
nfa::thompson::{pikevm::PikeVM as NfaRegex, NFA},
8+
},
9+
regex_syntax::ast::Ast,
10+
};
11+
12+
#[derive(Eq, PartialEq, arbitrary::Arbitrary)]
13+
struct FuzzData {
14+
ast: Ast,
15+
haystack: String,
16+
}
17+
18+
impl std::fmt::Debug for FuzzData {
19+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20+
let mut builder = f.debug_struct("FuzzData");
21+
builder.field("ast", &format!("{}", self.ast));
22+
builder.field("haystack", &self.haystack);
23+
builder.finish()
24+
}
25+
}
26+
27+
fn do_fuzz(data: FuzzData) -> Corpus {
28+
let _ = env_logger::try_init();
29+
30+
let pattern = format!("{}", data.ast);
31+
let config = NFA::config().nfa_size_limit(Some(1 << 20));
32+
let Ok(nfa) = NFA::compiler().configure(config).build(&pattern) else {
33+
return Corpus::Reject;
34+
};
35+
let Ok(baseline) = NfaRegex::new_from_nfa(nfa) else {
36+
return Corpus::Reject;
37+
};
38+
let mut baseline_cache = baseline.create_cache();
39+
40+
let config = DFA::config().size_limit(Some(1 << 20));
41+
let Ok(re) = RegexBuilder::new().configure(config).build(&pattern) else {
42+
return Corpus::Reject;
43+
};
44+
let mut onepass_cache = re.create_cache();
45+
46+
assert_eq!(
47+
re.is_match(&mut onepass_cache, &data.haystack),
48+
baseline.is_match(&mut baseline_cache, &data.haystack)
49+
);
50+
let found1 = re.find(&mut onepass_cache, &data.haystack);
51+
let found2 = baseline.find(&mut baseline_cache, &data.haystack);
52+
if let Some(found1) = found1 {
53+
let found2 = found2.expect("Found in target, but not in baseline!");
54+
assert_eq!(found1.start(), found2.start());
55+
assert_eq!(found1.end(), found2.end());
56+
}
57+
58+
let mut onepass_captures = re.create_captures();
59+
re.captures(&mut onepass_cache, &data.haystack, &mut onepass_captures);
60+
61+
let mut baseline_captures = baseline.create_captures();
62+
baseline.captures(
63+
&mut baseline_cache,
64+
&data.haystack,
65+
&mut baseline_captures,
66+
);
67+
drop(baseline_cache);
68+
assert_eq!(onepass_captures.group_len(), baseline_captures.group_len());
69+
for (c1, c2) in onepass_captures.iter().zip(baseline_captures.iter()) {
70+
if let Some(c1) = c1 {
71+
let c2 = c2.expect("Matched in target, but not baseline!");
72+
assert_eq!(c1.start, c2.start);
73+
assert_eq!(c1.end, c2.end);
74+
} else {
75+
assert!(c2.is_none(), "Matched in baseline, but not target!");
76+
}
77+
}
78+
79+
Corpus::Keep
80+
}
81+
82+
fuzz_target!(|data: FuzzData| -> Corpus { do_fuzz(data) });

0 commit comments

Comments
 (0)