pub(crate) struct FirstBigramSet {
bits: Box<[u64; 1024]>,
fail_open: bool,
}
impl FirstBigramSet {
pub(crate) fn from_literals<'a>(
lits: impl IntoIterator<Item = &'a [u8]>,
ascii_case_insensitive: bool,
) -> Self {
let mut bits = Box::new([0u64; 1024]);
for lit in lits {
if lit.len() < 2 {
return Self {
bits: Box::new([u64::MAX; 1024]),
fail_open: true,
};
}
let a = lit[0];
let b = lit[1];
let mut a_variants = [a; 2];
let a_len = if ascii_case_insensitive && a.is_ascii_alphabetic() {
a_variants = [a.to_ascii_lowercase(), a.to_ascii_uppercase()];
2
} else {
1
};
let mut b_variants = [b; 2];
let b_len = if ascii_case_insensitive && b.is_ascii_alphabetic() {
b_variants = [b.to_ascii_lowercase(), b.to_ascii_uppercase()];
2
} else {
1
};
for &ca in &a_variants[..a_len] {
for &cb in &b_variants[..b_len] {
let idx = (ca as usize) << 8 | cb as usize;
bits[idx >> 6] |= 1u64 << (idx & 63);
}
}
}
Self {
bits,
fail_open: false,
}
}
#[inline]
pub(crate) fn may_have_match(&self, match_text: &str) -> bool {
if self.fail_open {
return true;
}
let bytes = match_text.as_bytes();
let bits = self.bits.as_ref();
let len = bytes.len();
if len < 2 {
return false;
}
let last_start = len - 2;
#[inline(always)]
fn probe(bits: &[u64; 1024], bytes: &[u8], i: usize) -> bool {
let idx = (bytes[i] as usize) << 8 | bytes[i + 1] as usize;
bits[idx >> 6] & (1u64 << (idx & 63)) != 0
}
let mut i = 0usize;
while i + 4 <= last_start + 1 {
if probe(bits, bytes, i)
| probe(bits, bytes, i + 1)
| probe(bits, bytes, i + 2)
| probe(bits, bytes, i + 3)
{
return true;
}
i += 4;
}
while i <= last_start {
if probe(bits, bytes, i) {
return true;
}
i += 1;
}
false
}
}