use std::fmt;
use aho_corasick::{AcAutomaton, Sparse};
use memchr::memchr;
#[derive(Clone)]
pub enum Prefix {
Empty,
Single(u8),
Singles {
chars: Vec<u8>,
sparse: Vec<bool>,
},
Automaton(AcAutomaton<Sparse>),
}
impl Prefix {
pub fn new(pfxs: Vec<String>) -> Prefix {
if pfxs.len() == 0 || pfxs[0].len() == 0 {
Prefix::Empty
} else if pfxs.len() == 1 && pfxs[0].len() == 1 {
Prefix::Single(pfxs[0].as_bytes()[0])
} else if pfxs.len() >= 2 && pfxs.iter().all(|s| s.len() == 1) {
let mut set = vec![false; 256];
let mut chars = vec![];
for p in pfxs {
chars.push(p.as_bytes()[0]);
set[p.as_bytes()[0] as usize] = true;
}
Prefix::Singles { chars: chars, sparse: set }
} else {
Prefix::Automaton(AcAutomaton::with_transitions(pfxs))
}
}
pub fn find(&self, haystack: &str) -> Option<(usize, usize)> {
use self::Prefix::*;
match *self {
Empty => Some((0, 0)),
Single(b) => memchr(b, haystack.as_bytes()).map(|i| (i, i+1)),
Singles { ref sparse, .. } => {
find_singles(sparse, haystack.as_bytes())
}
Automaton(ref aut) => {
aut.find(haystack).next().map(|m| (m.start, m.end))
}
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
match *self {
Prefix::Empty => 0,
Prefix::Single(_) => 1,
Prefix::Singles { ref chars, .. } => chars.len(),
Prefix::Automaton(ref aut) => aut.len(),
}
}
pub fn preserves_priority(&self) -> bool {
match *self {
Prefix::Empty => true,
Prefix::Single(_) => true,
Prefix::Singles{..} => true,
Prefix::Automaton(ref aut) => {
aut.patterns().iter().all(|p| p.len() == aut.pattern(0).len())
}
}
}
}
fn find_singles(sparse: &[bool], haystack: &[u8]) -> Option<(usize, usize)> {
for (hi, &b) in haystack.iter().enumerate() {
if sparse[b as usize] {
return Some((hi, hi+1));
}
}
None
}
impl fmt::Debug for Prefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Prefix::Empty => write!(f, "Empty"),
Prefix::Single(b) => write!(f, "{:?}", b as char),
Prefix::Singles { ref chars, .. } => {
let chars: Vec<String> =
chars.iter()
.map(|&c| format!("{:?}", c as char))
.collect();
write!(f, "{}", chars.connect(", "))
}
Prefix::Automaton(ref aut) => write!(f, "{:?}", aut),
}
}
}