use std::fmt::Debug;
use dfa::PrefixPart;
use itertools::Itertools;
use memchr::memchr;
use runner::Engine;
use runner::program::TableInsts;
#[derive(Clone, Debug)]
pub struct ForwardBackwardEngine<Ret> {
forward: TableInsts<(usize, u8)>,
backward: TableInsts<Ret>,
prefix: Prefix,
}
impl<Ret: Copy + Debug> ForwardBackwardEngine<Ret> {
pub fn new(forward: TableInsts<(usize, u8)>, prefix: Prefix, backward: TableInsts<Ret>) -> Self {
ForwardBackwardEngine {
forward: forward,
backward: backward,
prefix: prefix,
}
}
fn find_with_searcher<SearchFn>(&self, input: &[u8], search: SearchFn)
-> Option<(usize, usize, Ret)>
where SearchFn: Fn(&[u8], usize) -> Option<usize> {
let mut pos = 0;
while let Some(start) = search(input, pos) {
match self.forward.find_from(input, start, 0) {
Ok((end, (rev_state, look_ahead))) => {
let rev_pos = end.saturating_sub(look_ahead as usize);
let (start_pos, ret) = self.backward
.longest_backward_find_from(input, rev_pos, rev_state)
.expect("BUG: matched forward but failed to match backward");
return Some((start_pos, rev_pos, ret));
},
Err(end) => {
pos = end + 1;
},
}
}
None
}
}
impl<Ret: Copy + Debug + 'static> Engine<Ret> for ForwardBackwardEngine<Ret> {
fn find(&self, s: &str) -> Option<(usize, usize, Ret)> {
let input = s.as_bytes();
if self.forward.is_empty() {
return None;
}
match self.prefix {
Prefix::Empty => self.find_with_searcher(
input,
|s, pos| if pos <= s.len() { Some(pos) } else { None }
),
Prefix::ByteSet { ref bytes, offset } => self.find_with_searcher(
input,
|s, pos| if pos + offset <= s.len() {
s[(pos + offset)..].iter().position(|c| bytes[*c as usize]).map(|x| x + pos)
} else {
None
}
),
Prefix::Byte { byte, offset } => self.find_with_searcher(
input,
|s, pos| if pos + offset <= s.len() {
memchr(byte, &input[(pos + offset)..]).map(|x| x + pos)
} else {
None
}
),
}
}
fn clone_box(&self) -> Box<Engine<Ret>> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub enum Prefix {
Empty,
ByteSet { bytes: Vec<bool>, offset: usize },
Byte { byte: u8, offset: usize },
}
const MAX_BYTE_SET_SIZE: usize = 16;
impl Prefix {
fn byte_prefix(parts: &[PrefixPart]) -> Option<Prefix> {
fn common_prefix<'a>(s1: &'a [u8], s2: &'a [u8]) -> &'a [u8] {
let prefix_len = s1.iter().zip(s2.iter())
.take_while(|pair| pair.0 == pair.1)
.count();
&s1[0..prefix_len]
}
let mut parts = parts.iter();
if let Some(first) = parts.next() {
let lit = parts.fold(&first.0[..], |acc, p| common_prefix(acc, &p.0));
if !lit.is_empty() {
let cp_last_byte = ((!lit[0]).leading_zeros() as usize).saturating_sub(1);
if cp_last_byte < lit.len() {
return Some(Prefix::Byte { byte: lit[cp_last_byte], offset: cp_last_byte });
}
}
}
None
}
fn byte_set_prefix(parts: &[PrefixPart]) -> Option<Prefix> {
let crit_byte_pos = |p: &PrefixPart| ((!p.0[0]).leading_zeros() as usize).saturating_sub(1);
let crit_byte_posns: Vec<usize> = parts.iter().map(crit_byte_pos).dedup().collect();
if crit_byte_posns.len() == 1 {
let crit_byte = crit_byte_posns[0];
if parts.iter().all(|x| x.0.len() > crit_byte) {
let mut crit_bytes: Vec<u8> = parts.iter().map(|x| x.0[crit_byte]).collect();
crit_bytes.sort();
crit_bytes.dedup();
if crit_bytes.len() <= MAX_BYTE_SET_SIZE {
let mut ret = vec![false; 256];
for &b in &crit_bytes {
ret[b as usize] = true;
}
return Some(Prefix::ByteSet { bytes: ret, offset: crit_byte });
}
}
}
None
}
pub fn from_parts(mut parts: Vec<PrefixPart>) -> Prefix {
parts.retain(|x| !x.0.is_empty());
if let Some(pref) = Prefix::byte_prefix(&parts) {
pref
} else if let Some(pref) = Prefix::byte_set_prefix(&parts) {
pref
} else {
Prefix::Empty
}
}
}
#[cfg(test)]
mod tests {
use dfa::PrefixPart;
use super::*;
fn pref(strs: Vec<&str>) -> Prefix {
Prefix::from_parts(
strs.into_iter()
.enumerate()
.map(|(i, s)| PrefixPart(s.as_bytes().to_vec(), i))
.collect())
}
#[test]
fn test_prefix_choice() {
use super::Prefix::*;
assert!(matches!(pref(vec![]), Empty));
assert!(matches!(pref(vec![""]), Empty));
assert!(matches!(pref(vec!["a"]), Byte {..}));
assert!(matches!(pref(vec!["", "a", ""]), Byte {..}));
assert!(matches!(pref(vec!["abc"]), Byte {..}));
assert!(matches!(pref(vec!["abc", ""]), Byte {..}));
assert!(matches!(pref(vec!["a", "b", "c"]), ByteSet {..}));
assert!(matches!(pref(vec!["a", "b", "", "c"]), ByteSet {..}));
assert!(matches!(pref(vec!["a", "baa", "", "c"]), ByteSet {..}));
assert!(matches!(pref(vec!["ab", "baa", "", "cb"]), ByteSet {..}));
assert!(matches!(pref(vec!["ab", "aaa", "", "acb"]), Byte {..}));
assert!(matches!(pref(vec!["ab", "abc", "abd"]), Byte {..}));
}
}