use std::io::{self, BufRead};
use std::marker::PhantomData;
use memchr::{memchr, memchr2, memchr3};
use super::{ROOT_STATE, StateIdx};
pub trait Automaton<P> {
fn next_state(&self, si: StateIdx, b: u8) -> StateIdx;
fn has_match(&self, si: StateIdx, outi: usize) -> bool;
fn get_match(&self, si: StateIdx, outi: usize, texti: usize) -> Match;
fn start_bytes(&self) -> &[u8];
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.patterns().is_empty()
}
fn find<'a, 's, Q: ?Sized + AsRef<[u8]>>(
&'a self,
s: &'s Q,
) -> Matches<'a, 's, P, Self>
where Self: Sized {
Matches {
aut: self,
text: s.as_ref(),
texti: 0,
si: ROOT_STATE,
_m: PhantomData,
}
}
fn find_overlapping<'a, 's, Q: ?Sized + AsRef<[u8]>>(
&'a self,
s: &'s Q,
) -> MatchesOverlapping<'a, 's, P, Self>
where Self: Sized {
MatchesOverlapping {
aut: self,
text: s.as_ref(),
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>
where Self: Sized {
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>
where Self: Sized {
StreamMatchesOverlapping {
aut: self,
buf: io::BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
outi: 0,
_m: PhantomData,
}
}
}
impl<'a, P: AsRef<[u8]>, A: 'a + Automaton<P> + ?Sized>
Automaton<P> for &'a A {
fn next_state(&self, si: StateIdx, b: u8) -> StateIdx {
(**self).next_state(si, b)
}
fn has_match(&self, si: StateIdx, outi: usize) -> bool {
(**self).has_match(si, outi)
}
fn start_bytes(&self) -> &[u8] {
(**self).start_bytes()
}
fn patterns(&self) -> &[P] {
(**self).patterns()
}
fn pattern(&self, i: usize) -> &P {
(**self).pattern(i)
}
fn get_match(&self, si: StateIdx, outi: usize, texti: usize) -> Match {
(**self).get_match(si, outi, texti)
}
}
#[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> + ?Sized> {
aut: &'a A,
text: &'s [u8],
texti: usize,
si: StateIdx,
_m: PhantomData<P>,
}
#[inline(never)]
fn step_to_match<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
mut texti: usize,
mut si: StateIdx
) -> Option<(usize, StateIdx)> {
while texti < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
if texti + 4 < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
}
}
None
}
fn skip_to_match<P, A: Automaton<P> + ?Sized, F: Fn(&A, &[u8], usize) -> usize>(
aut: &A,
text: &[u8],
mut texti: usize,
mut si: StateIdx,
skip: F,
) -> Option<(usize, StateIdx)> {
if si == ROOT_STATE {
texti = skip(aut, text, texti);
}
while texti < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
if si == ROOT_STATE {
texti = skip(aut, text, texti + 1);
} else {
texti += 1;
if texti + 4 < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
}
}
}
None
}
#[inline]
fn skip1<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
at: usize,
) -> usize {
debug_assert!(aut.start_bytes().len() == 1);
let b = aut.start_bytes()[0];
match memchr(b, &text[at..]) {
None => text.len(),
Some(i) => at + i,
}
}
#[inline]
fn skip2<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
at: usize,
) -> usize {
debug_assert!(aut.start_bytes().len() == 2);
let (b1, b2) = (aut.start_bytes()[0], aut.start_bytes()[1]);
match memchr2(b1, b2, &text[at..]) {
None => text.len(),
Some(i) => at + i,
}
}
#[inline]
fn skip3<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
at: usize,
) -> usize {
debug_assert!(aut.start_bytes().len() == 3);
let (b1, b2, b3) = (
aut.start_bytes()[0], aut.start_bytes()[1], aut.start_bytes()[2],
);
match memchr3(b1, b2, b3, &text[at..]) {
None => text.len(),
Some(i) => at + i,
}
}
impl<'a, 's, P, A: Automaton<P> + ?Sized> Iterator for Matches<'a, 's, P, A> {
type Item = Match;
fn next(&mut self) -> Option<Match> {
if self.aut.start_bytes().len() == 1 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip1);
if let Some((texti, si)) = skip {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
} else if self.aut.start_bytes().len() == 2 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip2);
if let Some((texti, si)) = skip {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
} else if self.aut.start_bytes().len() == 3 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip3);
if let Some((texti, si)) = skip {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
} else {
let step = step_to_match(self.aut, self.text, self.texti, self.si);
if let Some((texti, si)) = step {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
}
None
}
}
#[derive(Debug)]
pub struct StreamMatches<'a, R, P, A: 'a + Automaton<P> + ?Sized> {
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.is_empty() => 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> + ?Sized> {
aut: &'a A,
text: &'s [u8],
texti: usize,
si: StateIdx,
outi: usize,
_m: PhantomData<P>,
}
impl<'a, 's, P, A: Automaton<P> + ?Sized>
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;
if self.aut.start_bytes().len() == 1 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip1);
if let Some((texti, si)) = skip {
self.texti = texti;
self.si = si;
return self.next();
}
} else if self.aut.start_bytes().len() == 2 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip2);
if let Some((texti, si)) = skip {
self.texti = texti;
self.si = si;
return self.next();
}
} else if self.aut.start_bytes().len() == 3 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip3);
if let Some((texti, si)) = skip {
self.texti = texti;
self.si = si;
return self.next();
}
} else {
let step = step_to_match(self.aut, self.text, self.texti, self.si);
if let Some((texti, si)) = step {
self.texti = texti;
self.si = si;
return self.next();
}
}
None
}
}
#[derive(Debug)]
pub struct StreamMatchesOverlapping<'a, R, P, A: 'a + Automaton<P> + ?Sized> {
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> + ?Sized>
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.is_empty() => 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
}
}