use std::io::{self, BufRead, Read};
use std::marker::PhantomData;
use super::{ROOT_STATE, PatIdx, StateIdx};
pub trait Automaton<P>: Sized {
fn next_state(&self, si: StateIdx, b: u8) -> StateIdx;
fn has_match(&self, si: StateIdx, outi: PatIdx) -> bool;
fn get_match(&self, si: StateIdx, outi: PatIdx, texti: usize) -> Match;
fn skip_to(&self, si: StateIdx, text: &[u8], at: usize) -> usize;
fn is_skippable(&self) -> bool;
fn patterns(&self) -> &[P];
fn pattern(&self, i: usize) -> &P;
#[inline]
fn len(&self) -> usize {
self.patterns().len()
}
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
fn find<'a, 's>(
&'a self,
s: &'s str,
) -> Matches<'a, 's, P, Self> {
Matches {
aut: self,
text: s.as_bytes(),
texti: 0,
si: ROOT_STATE,
_m: PhantomData,
}
}
fn find_overlapping<'a, 's>(
&'a self,
s: &'s str,
) -> MatchesOverlapping<'a, 's, P, Self> {
MatchesOverlapping {
aut: self,
text: s.as_bytes(),
texti: 0,
si: ROOT_STATE,
outi: 0,
_m: PhantomData,
}
}
fn stream_find<'a, R: io::Read>(
&'a self,
rdr: R,
) -> StreamMatches<'a, R, P, Self> {
StreamMatches {
aut: self,
buf: io::BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
_m: PhantomData,
}
}
fn stream_find_overlapping<'a, R: io::Read>(
&'a self,
rdr: R,
) -> StreamMatchesOverlapping<'a, R, P, Self> {
StreamMatchesOverlapping {
aut: self,
buf: io::BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
outi: 0,
_m: PhantomData,
}
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Match {
pub pati: usize,
pub start: usize,
pub end: usize,
}
#[derive(Debug)]
pub struct Matches<'a, 's, P, A: 'a + Automaton<P>> {
aut: &'a A,
text: &'s [u8],
texti: usize,
si: StateIdx,
_m: PhantomData<P>,
}
impl<'a, 's, P, A: Automaton<P>> Iterator for Matches<'a, 's, P, A> {
type Item = Match;
fn next(&mut self) -> Option<Match> {
if self.aut.is_skippable() {
self.texti = self.aut.skip_to(self.si, self.text, self.texti);
while self.texti < self.text.len() {
self.si = self.aut.next_state(self.si, self.text[self.texti]);
if self.aut.has_match(self.si, 0) {
let m = self.aut.get_match(self.si, 0, self.texti);
self.si = ROOT_STATE;
self.texti += 1;
return Some(m);
}
self.texti = self.aut.skip_to(
self.si, self.text, self.texti + 1);
}
} else {
while self.texti < self.text.len() {
self.si = self.aut.next_state(self.si, self.text[self.texti]);
if self.aut.has_match(self.si, 0) {
let m = self.aut.get_match(self.si, 0, self.texti);
self.si = ROOT_STATE;
self.texti += 1;
return Some(m);
}
self.texti += 1;
}
}
None
}
}
#[derive(Debug)]
pub struct StreamMatches<'a, R, P, A: 'a + Automaton<P>> {
aut: &'a A,
buf: io::BufReader<R>,
texti: usize,
si: StateIdx,
_m: PhantomData<P>,
}
impl<'a, R: io::Read, P, A: Automaton<P>> Iterator for StreamMatches<'a, R, P, A> {
type Item = io::Result<Match>;
fn next(&mut self) -> Option<io::Result<Match>> {
let mut m = None;
let mut consumed = 0;
'LOOP: loop {
self.buf.consume(consumed);
let bs = match self.buf.fill_buf() {
Err(err) => return Some(Err(err)),
Ok(bs) if bs.len() == 0 => break,
Ok(bs) => bs,
};
consumed = bs.len(); for (i, &b) in bs.iter().enumerate() {
self.si = self.aut.next_state(self.si, b);
if self.aut.has_match(self.si, 0) {
m = Some(Ok(self.aut.get_match(self.si, 0, self.texti)));
consumed = i + 1;
self.texti += 1;
self.si = ROOT_STATE;
break 'LOOP;
}
self.texti += 1;
}
}
self.buf.consume(consumed);
m
}
}
#[derive(Debug)]
pub struct MatchesOverlapping<'a, 's, P, A: 'a + Automaton<P>> {
aut: &'a A,
text: &'s [u8],
texti: usize,
si: StateIdx,
outi: usize,
_m: PhantomData<P>,
}
impl<'a, 's, P, A: Automaton<P>> Iterator for MatchesOverlapping<'a, 's, P, A> {
type Item = Match;
fn next(&mut self) -> Option<Match> {
if self.aut.has_match(self.si, self.outi) {
let m = self.aut.get_match(self.si, self.outi, self.texti);
self.outi += 1;
if !self.aut.has_match(self.si, self.outi) {
self.texti += 1;
}
return Some(m);
}
self.outi = 0;
self.texti = self.aut.skip_to(self.si, self.text, self.texti);
while self.texti < self.text.len() {
let b = self.text[self.texti];
self.si = self.aut.next_state(self.si, b);
if self.aut.has_match(self.si, self.outi) {
return self.next();
}
self.texti = self.aut.skip_to(self.si, self.text, self.texti + 1);
}
None
}
}
#[derive(Debug)]
pub struct StreamMatchesOverlapping<'a, R, P, A: 'a + Automaton<P>> {
aut: &'a A,
buf: io::BufReader<R>,
texti: usize,
si: StateIdx,
outi: usize,
_m: PhantomData<P>,
}
impl<
'a,
R: io::Read,
P,
A: Automaton<P>,
> Iterator for StreamMatchesOverlapping<'a, R, P, A> {
type Item = io::Result<Match>;
fn next(&mut self) -> Option<io::Result<Match>> {
if self.aut.has_match(self.si, self.outi) {
let m = self.aut.get_match(self.si, self.outi, self.texti);
self.outi += 1;
if !self.aut.has_match(self.si, self.outi) {
self.texti += 1;
}
return Some(Ok(m));
}
let mut m = None;
let mut consumed = 0;
self.outi = 0;
'LOOP: loop {
self.buf.consume(consumed);
let bs = match self.buf.fill_buf() {
Err(err) => return Some(Err(err)),
Ok(bs) if bs.len() == 0 => break,
Ok(bs) => bs,
};
consumed = bs.len(); for (i, &b) in bs.iter().enumerate() {
self.si = self.aut.next_state(self.si, b);
if self.aut.has_match(self.si, self.outi) {
m = Some(Ok(self.aut.get_match(self.si, self.outi,
self.texti)));
consumed = i + 1;
self.outi += 1;
if !self.aut.has_match(self.si, self.outi) {
self.texti += 1;
}
break 'LOOP;
}
self.texti += 1;
}
}
self.buf.consume(consumed);
m
}
}