use core::iter::Enumerate;
use core::num::NonZeroU32;
use crate::bytewise::DoubleArrayAhoCorasick;
use crate::prefilter::{Prefilter, PrefilterGate};
use crate::utils::FromU32;
use crate::{Match, ROOT_STATE_IDX};
#[inline(always)]
fn scan<V>(
pma: &DoubleArrayAhoCorasick<V>,
prefilter: &mut Option<&Prefilter>,
gate: &mut PrefilterGate,
haystack: &[u8],
state_id: &mut u32,
pos: &mut usize,
) -> Option<NonZeroU32> {
if let Some(pf) = *prefilter {
loop {
if *state_id == ROOT_STATE_IDX {
let candidate_pos = pf.next_position(haystack, *pos);
gate.record(candidate_pos - *pos);
*pos = candidate_pos;
if !gate.is_enabled() {
*prefilter = None;
break;
}
}
let &c = haystack.get(*pos)?;
*state_id = unsafe { pma.next_state_id_unchecked(*state_id, c) };
*pos += 1;
if let Some(output_pos) = unsafe { pma.output_pos_unchecked(*state_id) } {
return Some(output_pos);
}
}
}
loop {
let &c = haystack.get(*pos)?;
*state_id = unsafe { pma.next_state_id_unchecked(*state_id, c) };
*pos += 1;
if let Some(output_pos) = unsafe { pma.output_pos_unchecked(*state_id) } {
return Some(output_pos);
}
}
}
pub struct FindIterator<'a, P, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: Enumerate<P>,
pub(crate) first_call: bool,
}
impl<P, V> Iterator for FindIterator<'_, P, V>
where
P: Iterator<Item = u8>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if let Some(value) = unsafe { self.pma.root_output_value() } {
return if self.first_call {
self.first_call = false;
Some(Match {
length: 0,
end: 0,
value,
})
} else {
self.haystack.next().map(|(pos, _)| Match {
length: 0,
end: pos + 1,
value,
})
};
}
let mut state_id = ROOT_STATE_IDX;
for (pos, c) in self.haystack.by_ref() {
state_id = unsafe { self.pma.next_state_id_unchecked(state_id, c) };
if let Some(output_pos) = unsafe { self.pma.output_pos_unchecked(state_id) } {
let out = unsafe { self.pma.output_at(output_pos) };
return Some(out.to_match(pos + 1));
}
}
None
}
}
pub struct FindSliceIterator<'a, P, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: P,
pub(crate) pos: usize,
pub(crate) first_call: bool,
pub(crate) prefilter: Option<&'a Prefilter>,
pub(crate) gate: PrefilterGate,
}
impl<P, V> Iterator for FindSliceIterator<'_, P, V>
where
P: AsRef<[u8]>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let haystack = self.haystack.as_ref();
if let Some(value) = unsafe { self.pma.root_output_value() } {
return if self.first_call {
self.first_call = false;
Some(Match {
length: 0,
end: 0,
value,
})
} else if self.pos < haystack.len() {
self.pos += 1;
Some(Match {
length: 0,
end: self.pos,
value,
})
} else {
None
};
}
let mut state_id = ROOT_STATE_IDX;
let mut pos = self.pos;
{
let c = *haystack.get(pos)?;
state_id = unsafe { self.pma.next_state_id_unchecked(state_id, c) };
pos += 1;
if let Some(output_pos) = unsafe { self.pma.output_pos_unchecked(state_id) } {
self.pos = pos;
let out = unsafe { self.pma.output_at(output_pos) };
return Some(out.to_match(pos));
}
}
let output_pos = scan(
self.pma,
&mut self.prefilter,
&mut self.gate,
haystack,
&mut state_id,
&mut pos,
);
self.pos = pos;
let out = unsafe { self.pma.output_at(output_pos?) };
Some(out.to_match(pos))
}
}
pub struct FindOverlappingIterator<'a, P, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: Enumerate<P>,
pub(crate) state_id: u32,
pub(crate) pos: usize,
pub(crate) output_pos: Option<NonZeroU32>,
}
impl<P, V> Iterator for FindOverlappingIterator<'_, P, V>
where
P: Iterator<Item = u8>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if let Some(output_pos) = self.output_pos {
let out = unsafe { self.pma.output_at(output_pos) };
self.output_pos = out.parent();
return Some(out.to_match(self.pos));
}
for (pos, c) in self.haystack.by_ref() {
self.state_id = unsafe { self.pma.next_state_id_unchecked(self.state_id, c) };
if let Some(output_pos) = unsafe { self.pma.output_pos_unchecked(self.state_id) } {
self.pos = pos + 1;
let out = unsafe { self.pma.output_at(output_pos) };
self.output_pos = out.parent();
return Some(out.to_match(self.pos));
}
}
None
}
}
pub struct FindOverlappingSliceIterator<'a, P, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: P,
pub(crate) state_id: u32,
pub(crate) pos: usize,
pub(crate) output_pos: Option<NonZeroU32>,
pub(crate) prefilter: Option<&'a Prefilter>,
pub(crate) gate: PrefilterGate,
}
impl<P, V> Iterator for FindOverlappingSliceIterator<'_, P, V>
where
P: AsRef<[u8]>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if let Some(output_pos) = self.output_pos {
let out = unsafe { self.pma.output_at(output_pos) };
self.output_pos = out.parent();
return Some(out.to_match(self.pos));
}
let haystack = self.haystack.as_ref();
let mut state_id = self.state_id;
let mut pos = self.pos;
{
let c = *haystack.get(pos)?;
state_id = unsafe { self.pma.next_state_id_unchecked(state_id, c) };
pos += 1;
if let Some(output_pos) = unsafe { self.pma.output_pos_unchecked(state_id) } {
self.state_id = state_id;
self.pos = pos;
let out = unsafe { self.pma.output_at(output_pos) };
self.output_pos = out.parent();
return Some(out.to_match(pos));
}
}
let output_pos = scan(
self.pma,
&mut self.prefilter,
&mut self.gate,
haystack,
&mut state_id,
&mut pos,
);
self.state_id = state_id;
self.pos = pos;
let out = unsafe { self.pma.output_at(output_pos?) };
self.output_pos = out.parent();
Some(out.to_match(pos))
}
}
pub struct FindOverlappingNoSuffixIterator<'a, P, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: Enumerate<P>,
pub(crate) state_id: u32,
pub(crate) first_call: bool,
}
impl<P, V> Iterator for FindOverlappingNoSuffixIterator<'_, P, V>
where
P: Iterator<Item = u8>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.first_call {
self.first_call = false;
if let Some(value) = unsafe { self.pma.root_output_value() } {
return Some(Match {
length: 0,
end: 0,
value,
});
}
}
for (pos, c) in self.haystack.by_ref() {
self.state_id = unsafe { self.pma.next_state_id_unchecked(self.state_id, c) };
if let Some(output_pos) = unsafe { self.pma.output_pos_unchecked(self.state_id) } {
let out = unsafe { self.pma.output_at(output_pos) };
return Some(out.to_match(pos + 1));
}
}
None
}
}
pub struct FindOverlappingNoSuffixSliceIterator<'a, P, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: P,
pub(crate) state_id: u32,
pub(crate) pos: usize,
pub(crate) first_call: bool,
pub(crate) prefilter: Option<&'a Prefilter>,
pub(crate) gate: PrefilterGate,
}
impl<P, V> Iterator for FindOverlappingNoSuffixSliceIterator<'_, P, V>
where
P: AsRef<[u8]>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.first_call {
self.first_call = false;
if let Some(value) = unsafe { self.pma.root_output_value() } {
return Some(Match {
length: 0,
end: 0,
value,
});
}
}
let haystack = self.haystack.as_ref();
let mut state_id = self.state_id;
let mut pos = self.pos;
{
let c = *haystack.get(pos)?;
state_id = unsafe { self.pma.next_state_id_unchecked(state_id, c) };
pos += 1;
if let Some(output_pos) = unsafe { self.pma.output_pos_unchecked(state_id) } {
self.state_id = state_id;
self.pos = pos;
let out = unsafe { self.pma.output_at(output_pos) };
return Some(out.to_match(pos));
}
}
let output_pos = scan(
self.pma,
&mut self.prefilter,
&mut self.gate,
haystack,
&mut state_id,
&mut pos,
);
self.state_id = state_id;
self.pos = pos;
let out = unsafe { self.pma.output_at(output_pos?) };
Some(out.to_match(pos))
}
}
pub struct LeftmostFindIterator<'a, P, V>
where
P: AsRef<[u8]>,
{
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) haystack: P,
pub(crate) pos: usize,
pub(crate) init_output_pos: Option<NonZeroU32>,
pub(crate) skip_empty: bool,
pub(crate) gate: PrefilterGate,
}
impl<P, V> LeftmostFindIterator<'_, P, V>
where
P: AsRef<[u8]>,
V: Copy,
{
#[inline(always)]
fn next_impl<const FILTERED: bool>(&mut self) -> Option<Match<V>> {
let mut state_id = ROOT_STATE_IDX;
let mut last_output_pos = self.init_output_pos;
let haystack = self.haystack.as_ref();
let mut pos = self.pos;
let mut prefilter = if FILTERED {
self.pma.prefilter.as_ref()
} else {
None
};
loop {
if let Some(pf) = prefilter {
if state_id == ROOT_STATE_IDX && last_output_pos.is_none() {
let candidate_pos = pf.next_position(haystack, pos);
self.gate.record(candidate_pos - pos);
pos = candidate_pos;
if !self.gate.is_enabled() {
prefilter = None;
}
}
}
let Some(&c) = haystack.get(pos) else {
break;
};
state_id = unsafe { self.pma.next_state_id_leftmost_unchecked(state_id, c) };
if state_id == ROOT_STATE_IDX {
if let Some(output_pos) = last_output_pos {
let end = self.pos;
if last_output_pos == self.init_output_pos {
self.pos += 1;
if self.skip_empty {
self.skip_empty = false;
pos = self.pos;
continue;
}
} else {
self.skip_empty = true;
}
let out = unsafe { self.pma.output_at(output_pos) };
return Some(out.to_match(end));
}
} else if let Some(output_pos) =
unsafe { self.pma.leftmost_output_pos_unchecked(state_id) }
{
last_output_pos.replace(output_pos);
self.pos = pos + 1;
}
pos += 1;
}
if self.pos == self.haystack.as_ref().len() {
self.init_output_pos.take();
}
if let Some(output_pos) = last_output_pos {
let out = unsafe { self.pma.output_at(output_pos) };
Some(out.to_match(self.pos))
} else {
self.pos = self.haystack.as_ref().len();
None
}
}
}
impl<P, V> Iterator for LeftmostFindIterator<'_, P, V>
where
P: AsRef<[u8]>,
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.pma.prefilter.is_some() && self.gate.is_enabled() {
self.next_impl::<true>()
} else {
self.next_impl::<false>()
}
}
}
pub struct FindStepper<'a, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) state_id: u32,
pub(crate) pos: usize,
pub(crate) output_pos: Option<NonZeroU32>,
}
impl<V> FindStepper<'_, V>
where
V: Copy,
{
#[inline(always)]
pub fn consume(&mut self, c: u8) {
self.pos += 1;
unsafe {
if self.pma.root_output_value().is_some() {
return;
}
self.state_id = self.pma.next_state_id_unchecked(self.state_id, c);
self.output_pos = self.pma.output_pos_unchecked(self.state_id);
}
if self.output_pos.is_some() {
self.state_id = ROOT_STATE_IDX;
}
}
#[must_use]
#[inline(always)]
pub fn matches(&self) -> Option<Match<V>> {
self.output_pos.map(|output_pos| unsafe {
let out = self
.pma
.outputs
.get_unchecked(usize::from_u32(output_pos.get() - 1));
out.to_match(self.pos)
})
}
}
pub struct FindOverlappingStepperIterator<'a, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) pos: usize,
pub(crate) output_pos: Option<NonZeroU32>,
}
impl<V> Iterator for FindOverlappingStepperIterator<'_, V>
where
V: Copy,
{
type Item = Match<V>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if let Some(output_pos) = self.output_pos {
let out = unsafe { self.pma.output_at(output_pos) };
self.output_pos = out.parent();
return Some(out.to_match(self.pos));
}
None
}
}
pub struct FindOverlappingStepper<'a, V> {
pub(crate) pma: &'a DoubleArrayAhoCorasick<V>,
pub(crate) state_id: u32,
pub(crate) pos: usize,
}
impl<'a, V> FindOverlappingStepper<'a, V>
where
V: Copy,
{
#[inline(always)]
pub fn consume(&mut self, c: u8) {
self.state_id = unsafe { self.pma.next_state_id_unchecked(self.state_id, c) };
self.pos += 1;
}
#[must_use]
#[inline(always)]
pub fn matches(&self) -> FindOverlappingStepperIterator<'a, V> {
let output_pos = unsafe {
self.pma
.states
.get_unchecked(usize::from_u32(self.state_id))
.output_pos()
};
FindOverlappingStepperIterator {
pma: self.pma,
pos: self.pos,
output_pos,
}
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use super::*;
#[test]
fn test_overlapping_no_suffix_iter() {
let pma = DoubleArrayAhoCorasick::<u32>::new(["a", "ab", ""]).unwrap();
let result = pma
.find_overlapping_no_suffix_iter("ab")
.collect::<Vec<_>>();
assert_eq!(
vec![
Match {
length: 0,
end: 0,
value: 2
},
Match {
length: 1,
end: 1,
value: 0
},
Match {
length: 2,
end: 2,
value: 1
},
],
result
);
}
#[test]
fn test_overlapping_stepper_lifetime() {
let pma = DoubleArrayAhoCorasick::new(["a", "ab"]).unwrap();
let mut stepper = pma.find_overlapping_stepper();
stepper.consume(b'a');
let mut it1 = stepper.matches();
stepper.consume(b'b');
let mut it2 = stepper.matches();
assert_eq!(
Some(Match {
length: 1,
end: 1,
value: 0
}),
it1.next()
);
assert_eq!(
Some(Match {
length: 2,
end: 2,
value: 1
}),
it2.next()
);
}
}