use dfa::{Dfa, RetTrait};
use dfa::trie::Trie;
use nfa::{Accept, StateIdx};
use std::cmp::{Ordering, PartialOrd};
use std::collections::{HashSet, VecDeque};
use std::mem::swap;
const NUM_PREFIX_LIMIT: usize = 30;
const PREFIX_LEN_LIMIT: usize = 15;
#[derive(Clone, Debug, PartialEq)]
pub struct PrefixPart(pub Vec<u8>, pub StateIdx);
pub struct PrefixSearcher {
active: VecDeque<PrefixPart>,
current: PrefixPart,
suffixes: Trie,
finished: Vec<PrefixPart>,
complete: bool,
max_prefixes: usize,
max_len: usize,
}
impl PrefixSearcher {
pub fn extract<T: RetTrait>(dfa: &Dfa<T>, state: StateIdx) -> Vec<PrefixPart> {
let mut searcher = PrefixSearcher::new();
searcher.search(dfa, state);
searcher.finished
}
fn new() -> PrefixSearcher {
PrefixSearcher {
active: VecDeque::new(),
current: PrefixPart(Vec::new(), 0),
suffixes: Trie::new(),
finished: Vec::new(),
complete: true,
max_prefixes: NUM_PREFIX_LIMIT,
max_len: PREFIX_LEN_LIMIT,
}
}
fn bail_out(&mut self) {
let mut current = PrefixPart(Vec::new(), 0);
let mut active = VecDeque::new();
swap(&mut current, &mut self.current);
swap(&mut active, &mut self.active);
self.finished.extend(active.into_iter());
self.finished.push(current);
self.complete = false;
}
fn add(&mut self, new_prefs: Vec<PrefixPart>) {
debug_assert!(new_prefs.len() + self.active.len() + self.finished.len() <= self.max_prefixes);
for p in new_prefs.into_iter() {
if p.0.len() >= self.max_len {
self.finished.push(p);
} else {
self.active.push_back(p);
}
}
}
fn too_many(&mut self, more: usize) -> bool {
self.active.len() + self.finished.len() + more > self.max_prefixes
}
fn search<T: RetTrait>(&mut self, dfa: &Dfa<T>, state: StateIdx) {
self.active.push_back(PrefixPart(Vec::new(), state));
self.suffixes.insert(vec![].into_iter(), state);
while !self.active.is_empty() {
self.current = self.active.pop_front().unwrap();
let trans = dfa.transitions(self.current.1);
let mut next_prefs = Vec::new();
for (ch, next_state) in trans.keys_values() {
let mut next_pref = self.current.0.clone();
next_pref.push(ch);
next_prefs.push(PrefixPart(next_pref, *next_state));
}
next_prefs.retain(|pref| {
let rev_bytes = pref.0.iter().cloned().rev();
!self.suffixes
.prefixes(rev_bytes)
.any(|s| s == pref.1)
});
for pref in &next_prefs {
self.suffixes.insert(pref.0.iter().cloned().rev(), pref.1);
}
if self.too_many(next_prefs.len())
|| *dfa.accept(self.current.1) != Accept::Never {
self.bail_out();
break;
}
self.add(next_prefs);
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CriticalSegment {
bytes: Vec<u8>,
paths: HashSet<Vec<StateIdx>>,
}
fn find(haystack: &[u8], needle: &[u8]) -> Option<usize> {
haystack.windows(needle.len())
.enumerate()
.find(|x| x.1 == needle)
.map(|y| y.0)
}
impl PartialOrd for CriticalSegment {
fn partial_cmp(&self, other: &CriticalSegment) -> Option<Ordering> {
fn less(a: &CriticalSegment, b: &CriticalSegment) -> bool {
let a_len = a.bytes.len();
let b_len = b.bytes.len();
(a_len > b_len && find(&a.bytes, &b.bytes).is_some())
|| (a.bytes == b.bytes && a.paths.is_subset(&b.paths))
}
if less(self, other) {
Some(Ordering::Less)
} else if less(other, self) {
Some(Ordering::Greater)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use dfa;
use look::Look;
use quickcheck::{QuickCheck, quickcheck, StdGen, TestResult};
use rand;
use super::*;
fn qc(size: usize) -> QuickCheck<StdGen<rand::ThreadRng>> {
QuickCheck::new().gen(StdGen::new(rand::thread_rng(), size))
}
macro_rules! test_prefix {
($name:ident, $re_str:expr, $answer:expr, $max_num:expr, $max_len:expr) => {
#[test]
fn $name() {
let dfa = dfa::tests::make_dfa($re_str).unwrap();
println!("{:?}", dfa);
let mut pref = PrefixSearcher::new();
pref.max_prefixes = $max_num;
pref.max_len = $max_len;
pref.search(&dfa, dfa.init_state(Look::Full).unwrap());
let mut prefs = pref.finished.into_iter().map(|x| x.0).collect::<Vec<_>>();
prefs.sort();
let answer: Vec<Vec<u8>> = $answer.iter()
.map(|s| s.as_bytes().to_owned())
.collect();
assert_eq!(prefs, answer);
}
};
}
test_prefix!(long,
"[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ",
vec!["XABCDEFGHIJKLMNOPQRSTUVWXYZ",
"YABCDEFGHIJKLMNOPQRSTUVWXYZ",
"ZABCDEFGHIJKLMNOPQRSTUVWXYZ",],
3, 30);
test_prefix!(case_insensitive,
"(?i)abc[a-z]",
vec!["ABC", "ABc", "AbC", "Abc", "aBC", "aBc", "abC", "abc"],
30, 5);
test_prefix!(byte_set,
"[ac]",
vec!["a", "c"],
30, 5);
test_prefix!(pruned_repetition,
"a+bc",
vec!["abc"],
10, 10);
test_prefix!(pruned_empty_repetition,
"[a-zA-Z]*bc",
vec!["bc"],
10, 10);
}