Skip to content

Commit d8603f5

Browse files
committed
add sparse dfa
1 parent eb2dbaa commit d8603f5

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-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_sparse_dfa"
80+
path = "fuzz_targets/ast_diff_sparse_dfa.rs"
81+
7882
[[bin]]
7983
name = "ast_diff_nfas"
8084
path = "fuzz_targets/ast_diff_nfas.rs"
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#![no_main]
2+
3+
use {
4+
libfuzzer_sys::{fuzz_target, Corpus},
5+
regex_automata::{
6+
dfa::{dense::DFA, regex::Builder as RegexBuilder},
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 cache = baseline.create_cache();
39+
40+
let config = DFA::config().dfa_size_limit(Some(1 << 20));
41+
let Ok(dense_re) = RegexBuilder::new().dense(config).build(&pattern) else {
42+
return Corpus::Reject;
43+
};
44+
let (Ok(fwd), Ok(rev)) = (dense_re.forward().to_sparse(), dense_re.reverse().to_sparse()) else {
45+
return Corpus::Reject;
46+
};
47+
let re = RegexBuilder::new().build_from_dfas(fwd, rev);
48+
49+
assert_eq!(
50+
re.is_match(&data.haystack),
51+
baseline.is_match(&mut cache, &data.haystack)
52+
);
53+
let found1 = re.find(&data.haystack);
54+
let found2 = baseline.find(&mut cache, &data.haystack);
55+
if let Some(found1) = found1 {
56+
let found2 = found2.expect("Found in target, but not in baseline!");
57+
assert_eq!(found1.start(), found2.start());
58+
assert_eq!(found1.end(), found2.end());
59+
}
60+
61+
// no captures
62+
63+
Corpus::Keep
64+
}
65+
66+
fuzz_target!(|data: FuzzData| -> Corpus { do_fuzz(data) });

0 commit comments

Comments
 (0)