use memchr::memmem::Finder;
#[inline]
pub(crate) fn lit_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
if a.len() <= SHORT_LIT {
return a.iter().zip(b).all(|(x, y)| x == y);
}
a == b
}
const SHORT_LIT: usize = 4;
const SHORT_HAYSTACK: usize = 16;
#[inline]
fn few_starts(h: &[u8], len: usize) -> bool {
h.len() < len + SHORT_HAYSTACK
}
pub(crate) fn find(h: &[u8], needle: &[u8]) -> Option<usize> {
let Some(&first) = needle.first() else {
return Some(0);
};
let starts = h.len().checked_sub(needle.len() - 1)?;
let hay = &h[..starts];
if few_starts(h, needle.len()) {
for (i, &b) in hay.iter().enumerate() {
if b == first && lit_eq(&h[i..i + needle.len()], needle) {
return Some(i);
}
}
return None;
}
let mut off = 0;
while let Some(k) = memchr::memchr(first, &hay[off..]) {
let i = off + k;
if lit_eq(&h[i..i + needle.len()], needle) {
return Some(i);
}
off = i + 1;
}
None
}
#[inline]
pub(crate) fn find_lit(h: &[u8], lit: &Finder<'static>) -> Option<usize> {
let l = lit.needle();
if few_starts(h, l.len()) {
find(h, l)
} else {
lit.find(h)
}
}
pub(crate) fn fields<'a>(s: &'a [u8], d: &'a [u8]) -> impl Iterator<Item = (usize, usize)> + 'a {
debug_assert!(!d.is_empty(), "an empty delimiter splits nothing");
let mut next = Some(0usize);
std::iter::from_fn(move || {
let start = next?;
let end = match find(&s[start..], d) {
Some(k) => start + k,
None => s.len(),
};
next = (end < s.len()).then(|| end + d.len());
Some((start, end))
})
}
#[cfg(test)]
#[path = "tests/search.rs"]
mod tests;