use std::collections::HashMap;
use super::parse::{Node, NodeKind, UNBOUNDED};
const DFA_DEAD: u8 = 0;
const DFA_ACCEPT: u8 = 1;
const DFA_BAIL: u8 = 2;
const DFA_FIRST: usize = 3;
const MAX_DFA_STATES: usize = 256;
const MAX_NFA_STATES: usize = 512;
const MAX_DFA_STEPS: i64 = 400_000;
#[derive(Clone, Copy, PartialEq, Eq)]
enum NfaKind {
Set,
Split,
Bol,
Eol,
Match,
}
#[derive(Clone, Copy)]
struct NfaState {
kind: NfaKind,
set: [u64; 2],
out: usize,
alt: usize,
}
impl NfaState {
fn new(kind: NfaKind, out: usize) -> NfaState {
NfaState { kind, set: [0; 2], out, alt: 0 }
}
}
#[derive(Default)]
struct NfaBuilder {
states: Vec<NfaState>,
failed: bool,
}
impl NfaBuilder {
fn add(&mut self, s: NfaState) -> usize {
if self.states.len() >= MAX_NFA_STATES {
self.failed = true;
return 0;
}
self.states.push(s);
self.states.len() - 1
}
fn compile(&mut self, n: &Node, mut next: usize) -> usize {
if self.failed {
return 0;
}
match n.kind {
NodeKind::Empty => next,
NodeKind::Char => {
let mut set = [0u64; 2];
if (0..128).contains(&n.r) {
set[(n.r >> 6) as usize] = 1 << (n.r & 63);
}
self.add(NfaState { set, ..NfaState::new(NfaKind::Set, next) })
}
NodeKind::Set => self.add(NfaState { set: n.class().ascii, ..NfaState::new(NfaKind::Set, next) }),
NodeKind::Cat => {
for sub in n.subs.iter().rev() {
next = self.compile(sub, next);
}
next
}
NodeKind::Alt => {
let mut start = self.compile(&n.subs[n.subs.len() - 1], next);
for sub in n.subs[..n.subs.len() - 1].iter().rev() {
let out = self.compile(sub, next);
start = self.add(NfaState { alt: start, ..NfaState::new(NfaKind::Split, out) });
}
start
}
NodeKind::Group => self.compile(&n.subs[0], next),
NodeKind::Repeat => {
let sub = &n.subs[0];
if n.max == UNBOUNDED {
let loop_ = self.add(NfaState { alt: next, ..NfaState::new(NfaKind::Split, 0) });
if self.failed {
return 0;
}
let out = self.compile(sub, loop_);
self.states[loop_].out = out;
next = loop_;
} else {
let mut i = n.min;
while i < n.max && !self.failed {
let out = self.compile(sub, next);
next = self.add(NfaState { alt: next, ..NfaState::new(NfaKind::Split, out) });
i += 1;
}
}
let mut i = 0;
while i < n.min && !self.failed {
next = self.compile(sub, next);
i += 1;
}
next
}
NodeKind::Bol => self.add(NfaState::new(NfaKind::Bol, next)),
NodeKind::Eol => self.add(NfaState::new(NfaKind::Eol, next)),
_ => {
self.failed = true;
0
}
}
}
}
pub(crate) struct Dfa {
class: [u8; 256],
trans: Vec<u8>,
shift: u32,
start: u8,
at_end: Vec<bool>,
empty: bool,
}
impl Dfa {
#[inline]
pub(crate) fn is_match(&self, text: &[u8]) -> Option<bool> {
if text.is_empty() {
return Some(self.empty);
}
let mut s = self.start;
if s == DFA_ACCEPT {
return Some(true);
}
let (trans, shift) = (&self.trans[..], self.shift);
for &c in text {
s = trans[(s as usize) << shift | self.class[c as usize] as usize];
if (s as usize) < DFA_FIRST {
return if s == DFA_BAIL { None } else { Some(s == DFA_ACCEPT) };
}
}
Some(self.at_end[s as usize])
}
}
struct DfaBuilder<'a> {
nfa: &'a [NfaState],
mark: Vec<u32>,
generation: u32,
stack: Vec<usize>,
steps: i64,
}
impl DfaBuilder<'_> {
fn closure(&mut self, set: &mut Vec<usize>, from: &[usize], at_start: bool, at_end: bool) {
self.generation += 1;
self.stack.clear();
self.stack.extend_from_slice(from);
while let Some(i) = self.stack.pop() {
self.steps -= 1;
if self.mark[i] == self.generation {
continue;
}
self.mark[i] = self.generation;
let s = &self.nfa[i];
match s.kind {
NfaKind::Split => {
self.stack.push(s.out);
self.stack.push(s.alt);
}
NfaKind::Bol => {
if at_start {
self.stack.push(s.out);
}
}
NfaKind::Eol => {
if at_end {
self.stack.push(s.out);
} else {
set.push(i);
}
}
_ => set.push(i),
}
}
}
fn has_match(&self, set: &[usize]) -> bool {
set.iter().any(|&i| self.nfa[i].kind == NfaKind::Match)
}
}
fn set_key(set: &mut [usize]) -> Vec<u16> {
set.sort_unstable();
set.iter().map(|&s| s as u16).collect()
}
pub(crate) fn build_dfa(root: &Node) -> Option<Dfa> {
let mut nb = NfaBuilder::default();
let match_ = nb.add(NfaState::new(NfaKind::Match, 0));
let start = nb.compile(root, match_);
if nb.failed {
return None;
}
let nfa = &nb.states[..];
let mut b = DfaBuilder { nfa, mark: vec![0; nfa.len()], generation: 0, stack: Vec::new(), steps: MAX_DFA_STEPS };
let mut d = Dfa { class: [0; 256], trans: Vec::new(), shift: 0, start: 0, at_end: Vec::new(), empty: false };
let mut classes = 1usize;
let mut signatures: HashMap<Vec<u8>, u8> = HashMap::new();
for c in 0..128usize {
let mut signature = Vec::with_capacity(nfa.len() / 8 + 1);
let (mut acc, mut n) = (0u8, 0u8);
for s in nfa {
if s.kind != NfaKind::Set {
continue;
}
acc = acc << 1 | ((s.set[c >> 6] >> (c & 63)) & 1) as u8;
n += 1;
if n == 8 {
signature.push(acc);
(acc, n) = (0, 0);
}
}
signature.push(acc);
let class = *signatures.entry(signature).or_insert_with(|| {
classes += 1;
(classes - 1) as u8
});
d.class[c] = class;
}
while 1usize << d.shift < classes {
d.shift += 1;
}
let mut sample = [0usize; 129];
for c in (0..128usize).rev() {
sample[d.class[c] as usize] = c;
}
let mut restart = Vec::new();
b.closure(&mut restart, &[start], false, false);
let mut at_both = Vec::new();
b.closure(&mut at_both, &[start], true, true);
d.empty = b.has_match(&at_both);
let mut ids: HashMap<Vec<u16>, u8> = HashMap::new();
let mut sets: Vec<Vec<usize>> = vec![Vec::new(); DFA_FIRST];
let stride = 1usize << d.shift;
d.trans = vec![0; DFA_FIRST * stride];
d.at_end = vec![false; DFA_FIRST];
for class in 0..stride {
d.trans[DFA_ACCEPT as usize * stride + class] = DFA_ACCEPT;
d.trans[DFA_BAIL as usize * stride + class] = DFA_BAIL;
}
fn intern(
b: &mut DfaBuilder<'_>,
d: &mut Dfa,
ids: &mut HashMap<Vec<u16>, u8>,
sets: &mut Vec<Vec<usize>>,
stride: usize,
set: &mut [usize],
) -> Option<u8> {
if b.has_match(set) {
return Some(DFA_ACCEPT);
}
if set.is_empty() {
return Some(DFA_DEAD);
}
let key = set_key(set);
if let Some(&id) = ids.get(&key) {
return Some(id);
}
if sets.len() >= MAX_DFA_STATES {
return None;
}
let id = sets.len() as u8;
ids.insert(key, id);
sets.push(set.to_vec());
d.trans.resize(d.trans.len() + stride, 0);
let ends: Vec<usize> = set.iter().filter(|&&i| b.nfa[i].kind == NfaKind::Eol).map(|&i| b.nfa[i].out).collect();
let mut closed = Vec::new();
if !ends.is_empty() {
b.closure(&mut closed, &ends, false, true);
}
d.at_end.push(!ends.is_empty() && b.has_match(&closed));
Some(id)
}
let mut first = Vec::new();
b.closure(&mut first, &[start], true, false);
d.start = intern(&mut b, &mut d, &mut ids, &mut sets, stride, &mut first)?;
let (mut moved, mut next) = (Vec::new(), Vec::new());
let mut id = DFA_FIRST;
while id < sets.len() {
for class in 0..stride {
if class == 0 || class >= classes {
d.trans[id * stride + class] = DFA_BAIL;
continue;
}
let c = *sample.get(class).expect("a class has a byte");
moved.clear();
for &i in &sets[id] {
let s = &nfa[i];
if s.kind == NfaKind::Set && (s.set[c >> 6] >> (c & 63)) & 1 != 0 {
moved.push(s.out);
}
}
moved.extend_from_slice(&restart);
next.clear();
b.closure(&mut next, &moved, false, false);
let to = intern(&mut b, &mut d, &mut ids, &mut sets, stride, &mut next)?;
if b.steps < 0 {
return None;
}
d.trans[id * stride + class] = to;
}
id += 1;
}
Some(d)
}