use backtrack::{self, Backtrack};
use dfa::{self, Dfa, DfaResult};
use input::{ByteInput, CharInput};
use nfa::Nfa;
use program::{Program, ProgramBuilder};
use re::CaptureIdxs;
use {Regex, Error};
#[derive(Clone, Debug)]
pub struct Exec {
prog: Program,
dfa: Program,
dfa_reverse: Program,
can_dfa: bool,
match_engine: MatchEngine,
}
pub struct ExecBuilder<'r> {
re: &'r str,
match_engine: MatchEngine,
size_limit: usize,
bytes: bool,
}
impl<'r> ExecBuilder<'r> {
pub fn new(re: &'r str) -> Self {
ExecBuilder {
re: re,
match_engine: MatchEngine::Automatic,
size_limit: 10 * (1 << 20),
bytes: false,
}
}
pub fn automatic(mut self) -> Self {
self.match_engine = MatchEngine::Automatic;
self
}
pub fn nfa(mut self) -> Self {
self.match_engine = MatchEngine::Nfa;
self
}
pub fn bounded_backtracking(mut self) -> Self {
self.match_engine = MatchEngine::Backtrack;
self
}
pub fn size_limit(mut self, bytes: usize) -> Self {
self.size_limit = bytes;
self
}
pub fn bytes(mut self, yes: bool) -> Self {
self.bytes = yes;
self
}
pub fn build(self) -> Result<Exec, Error> {
let prog = try!(
ProgramBuilder::new(self.re)
.size_limit(self.size_limit)
.bytes(self.bytes)
.compile());
let mut dfa = try!(
ProgramBuilder::new(self.re)
.size_limit(self.size_limit)
.dfa(true)
.compile());
dfa.prefixes = prog.prefixes.clone();
let dfa_reverse = try!(
ProgramBuilder::new(self.re)
.size_limit(self.size_limit)
.dfa(true)
.reverse(true)
.compile());
let can_dfa = dfa::can_exec(&dfa.insts);
Ok(Exec {
prog: prog,
dfa: dfa,
dfa_reverse: dfa_reverse,
can_dfa: can_dfa,
match_engine: self.match_engine,
})
}
}
impl Exec {
pub fn exec(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
match self.match_engine {
MatchEngine::Automatic => self.exec_auto(caps, text, start),
MatchEngine::Backtrack => self.exec_backtrack(caps, text, start),
MatchEngine::Nfa => self.exec_nfa(caps, text, start),
}
}
pub fn exec_auto(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
if caps.len() <= 2 && self.prog.is_prefix_match() {
self.exec_literals(caps, text, start)
} else if self.can_dfa {
self.exec_dfa(caps, text, start)
} else {
self.exec_auto_nfa(caps, text, start)
}
}
fn exec_dfa(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
debug_assert!(self.can_dfa);
let btext = text.as_bytes();
let search = Dfa::exec(&self.dfa, btext, start, caps.is_empty());
let match_end = match search {
DfaResult::Match(match_end) => match_end,
DfaResult::EarlyMatch => return true,
DfaResult::NoMatch => return false,
};
if caps.is_empty() {
return true;
}
if start == match_end {
if caps.len() == 2 {
caps[0] = Some(start);
caps[1] = Some(start);
return true;
}
return self.exec_auto_nfa(caps, text, start);
}
let search = Dfa::exec(
&self.dfa_reverse, &btext[start..], match_end - start, false);
let match_start = match search {
DfaResult::Match(match_start) => start + match_start,
DfaResult::EarlyMatch => {
panic!("BUG: early matches can't happen on reverse search")
}
DfaResult::NoMatch => {
panic!("BUG: forward match implies backward match")
}
};
if caps.len() == 2 {
caps[0] = Some(match_start);
caps[1] = Some(match_end);
return true;
}
self.exec_auto_nfa(caps, text, match_start)
}
fn exec_auto_nfa(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
if backtrack::should_exec(self.prog.insts.len(), text.len()) {
self.exec_backtrack(caps, text, start)
} else {
self.exec_nfa(caps, text, start)
}
}
fn exec_nfa(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
if self.prog.insts.is_bytes() {
Nfa::exec(&self.prog, caps, ByteInput::new(text), start)
} else {
Nfa::exec(&self.prog, caps, CharInput::new(text), start)
}
}
fn exec_backtrack(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
if self.prog.insts.is_bytes() {
Backtrack::exec(&self.prog, caps, ByteInput::new(text), start)
} else {
Backtrack::exec(&self.prog, caps, CharInput::new(text), start)
}
}
fn exec_literals(
&self,
caps: &mut CaptureIdxs,
text: &str,
start: usize,
) -> bool {
debug_assert!(self.prog.is_prefix_match());
match self.prog.prefixes.find(&text.as_bytes()[start..]) {
None => false,
Some((s, e)) => {
if caps.len() == 2 {
caps[0] = Some(start + s);
caps[1] = Some(start + e);
}
true
}
}
}
pub fn into_regex(self) -> Regex {
Regex::Dynamic(self)
}
pub fn regex_str(&self) -> &str {
&self.prog.original
}
pub fn capture_names(&self) -> &[Option<String>] {
&self.prog.cap_names
}
pub fn alloc_captures(&self) -> Vec<Option<usize>> {
self.prog.alloc_captures()
}
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
enum MatchEngine {
Automatic,
Backtrack,
Nfa,
}