use alloc::boxed::Box;
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use core::cmp::Reverse;
use memchr::memmem::FinderRev;
use crate::node::Node;
use crate::state::StaticState;
#[derive(Clone, Debug)]
pub(crate) struct Suffix {
bytes: Box<[u8]>,
finder: FinderRev<'static>,
}
impl Suffix {
fn new(bytes: Vec<u8>) -> Self {
let finder = FinderRev::new(&bytes).into_owned();
Self {
bytes: bytes.into_boxed_slice(),
finder,
}
}
fn positions<'a>(
&'a self,
remaining: &'a [u8],
cap: usize,
) -> impl Iterator<Item = usize> + 'a {
let initial = (cap + self.bytes.len()).min(remaining.len());
let first = self.finder.rfind(&remaining[..initial]);
core::iter::successors(first, move |&end| self.finder.rfind(&remaining[..end]))
}
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Suffixes(Box<[Suffix]>);
impl Suffixes {
pub(crate) fn longest(&self) -> usize {
self.0.first().map_or(0, |suffix| suffix.bytes.len())
}
pub(crate) fn accepts(&self, after: &[u8]) -> bool {
self.0.iter().any(|suffix| {
after.len() >= suffix.bytes.len() && suffix.bytes.iter().zip(after).all(|(a, b)| a == b)
})
}
pub(crate) fn positions<'a>(
&'a self,
path: &'a str,
offset: usize,
cap: usize,
) -> impl Iterator<Item = usize> + 'a {
let remaining = &path.as_bytes()[offset..];
self.0
.iter()
.flat_map(move |suffix| suffix.positions(remaining, cap))
.filter(|&position| position > 0)
.filter(move |&position| path.is_char_boundary(offset + position))
}
pub(crate) fn compute<S, T>(
node: &Node<S, T>,
prefix: &mut Vec<u8>,
seen: &mut BTreeSet<Vec<u8>>,
) -> Self {
seen.clear();
for child in &node.static_children {
Self::walk_static(child, prefix, seen);
}
let mut suffixes: Vec<Suffix> = seen.iter().cloned().map(Suffix::new).collect();
suffixes.sort_by_key(|suffix| Reverse(suffix.bytes.len()));
Self(suffixes.into_boxed_slice())
}
fn walk_static<T>(
node: &Node<StaticState, T>,
prefix: &mut Vec<u8>,
seen: &mut BTreeSet<Vec<u8>>,
) {
let start = prefix.len();
prefix.extend_from_slice(&node.state.prefix);
let is_terminal = node.data.is_some() || node.has_parameters();
if is_terminal {
seen.insert(prefix.clone());
}
for child in &node.static_children {
Self::walk_static(child, prefix, seen);
}
prefix.truncate(start);
}
}