use input::{Input, InputAt, CharInput};
use program::{Inst, InstIdx, Program};
use re::CaptureIdxs;
type Bits = u32;
const BIT_SIZE: usize = 32;
const MAX_PROG_SIZE: usize = 100;
const MAX_INPUT_SIZE: usize = 256 * (1 << 10);
#[derive(Debug)]
pub struct Backtrack<'a, 'r, 't, 'c> {
prog: &'r Program,
input: CharInput<'t>,
caps: &'c mut CaptureIdxs,
m: &'a mut BackMachine,
}
#[derive(Debug)]
pub struct BackMachine {
jobs: Vec<Job>,
visited: Vec<Bits>,
}
impl BackMachine {
pub fn new() -> BackMachine {
BackMachine {
jobs: vec![],
visited: vec![],
}
}
}
#[derive(Clone, Copy, Debug)]
enum Job {
Inst { pc: InstIdx, at: InputAt },
SaveRestore { slot: usize, old_pos: Option<usize> },
}
impl<'a, 'r, 't, 'c> Backtrack<'a, 'r, 't, 'c> {
pub fn exec(
prog: &'r Program,
mut caps: &mut CaptureIdxs,
text: &'t str,
start: usize,
) -> bool {
let input = CharInput::new(text);
let start = input.at(start);
let mut m = prog.backtrack.get();
let mut b = Backtrack {
prog: prog,
input: input,
caps: caps,
m: &mut m,
};
let matched = b.exec_(start);
matched
}
pub fn should_exec(prog: &'r Program, input: &str) -> bool {
prog.insts.len() <= MAX_PROG_SIZE && input.len() <= MAX_INPUT_SIZE
}
fn clear(&mut self) {
self.m.jobs.truncate(0);
let visited_len =
(self.prog.insts.len() * (self.input.len() + 1) + BIT_SIZE - 1)
/
BIT_SIZE;
for v in &mut self.m.visited {
*v = 0;
}
let cur_visited_cap = self.m.visited.capacity();
if visited_len > cur_visited_cap {
self.m.visited.reserve_exact(visited_len - cur_visited_cap);
for _ in 0..(visited_len - cur_visited_cap) {
self.m.visited.push(0);
}
}
}
fn exec_(&mut self, mut at: InputAt) -> bool {
self.clear();
if self.prog.anchored_begin {
return if !at.is_beginning() {
false
} else {
match self.input.prefix_at(&self.prog.prefixes, at) {
None => false,
Some(at) => self.backtrack(at),
}
};
}
loop {
if !self.prog.prefixes.is_empty() {
at = match self.input.prefix_at(&self.prog.prefixes, at) {
None => return false,
Some(at) => at,
};
}
if self.backtrack(at) {
return true;
}
if at.char().is_none() {
return false;
}
at = self.input.at(at.next_pos());
}
}
#[inline(always)]
fn backtrack(&mut self, start: InputAt) -> bool {
self.push(0, start);
while let Some(job) = self.m.jobs.pop() {
match job {
Job::Inst { pc, at } => {
if self.step(pc, at) {
return true;
}
}
Job::SaveRestore { slot, old_pos } => {
self.caps[slot] = old_pos;
}
}
}
false
}
fn step(&mut self, mut pc: InstIdx, mut at: InputAt) -> bool {
use program::Inst::*;
loop {
match self.prog.insts[pc] {
Match => return true,
Save(slot) => {
if slot < self.caps.len() {
let old_pos = self.caps[slot];
self.push_save_restore(slot, old_pos);
self.caps[slot] = Some(at.pos());
}
pc += 1;
}
Jump(pc2) => pc = pc2,
Split(x, y) => {
self.push(y, at);
pc = x;
}
EmptyLook(ref inst) => {
let prev = self.input.previous_at(at.pos());
if inst.matches(prev.char(), at.char()) {
pc += 1;
} else {
return false;
}
}
Char(c) => {
if c == at.char() {
pc += 1;
at = self.input.at(at.next_pos());
} else {
return false;
}
}
Ranges(ref inst) => {
if inst.matches(at.char()) {
pc += 1;
at = self.input.at(at.next_pos());
} else {
return false;
}
}
}
if self.has_visited(pc, at) {
return false;
}
}
}
fn push(&mut self, pc: InstIdx, at: InputAt) {
self.m.jobs.push(Job::Inst { pc: pc, at: at });
}
fn push_save_restore(&mut self, slot: usize, old_pos: Option<usize>) {
self.m.jobs.push(Job::SaveRestore { slot: slot, old_pos: old_pos });
}
fn has_visited(&mut self, pc: InstIdx, at: InputAt) -> bool {
let k = pc * (self.input.len() + 1) + at.pos();
let k1 = k / BIT_SIZE;
let k2 = (1 << (k & (BIT_SIZE - 1))) as Bits;
if self.m.visited[k1] & k2 == 0 {
self.m.visited[k1] |= k2;
false
} else {
true
}
}
}