use super::hash_index::{HashIndex, hash_bytes, same_bytes};
const LINEAR_STRINGS: usize = 16;
const RECENT: usize = 64;
#[derive(Debug, Clone, Copy, Default)]
struct Recent {
at: usize,
len: u32,
number: u32,
}
#[derive(Debug, Default)]
pub(crate) struct Interner {
spans: Vec<(usize, usize)>,
index: HashIndex,
recent: Vec<Recent>,
}
impl Interner {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn clear(&mut self) {
self.spans.clear();
self.index.clear();
self.recent.clear();
}
#[inline]
pub(crate) fn find(&mut self, buf: &[u8], s: &str) -> Result<usize, Option<u32>> {
if self.spans.len() <= LINEAR_STRINGS {
return self.lookup(buf, s);
}
let at = s.as_ptr() as usize;
let slot = ((at >> ADDRESS_SHIFT) ^ s.len()) & (RECENT - 1);
let guess = self.recent.get(slot).copied().unwrap_or_default();
if guess.at == at
&& guess.number > 0
&& guess.len as usize == s.len()
&& self.is(buf, guess.number as usize - 1, s)
{
return Ok(guess.number as usize - 1);
}
let found = self.lookup(buf, s)?;
if self.recent.is_empty() {
self.recent.resize(RECENT, Recent::default());
}
self.recent[slot] = Recent {
at,
len: u32::try_from(s.len()).unwrap_or(0),
number: u32::try_from(found + 1).unwrap_or(0),
};
Ok(found)
}
#[inline(never)]
pub(crate) fn lookup(&self, buf: &[u8], s: &str) -> Result<usize, Option<u32>> {
if self.spans.len() <= LINEAR_STRINGS {
let bytes = s.as_bytes();
return self
.spans
.iter()
.position(|&(at, len)| len == bytes.len() && same_bytes(&buf[at..at + len], bytes))
.ok_or(None);
}
let hash = hash_bytes(s.as_bytes());
self.index
.find(hash, |i| self.is(buf, i, s))
.ok_or(Some(hash))
}
pub(crate) fn is(&self, buf: &[u8], i: usize, s: &str) -> bool {
let (at, len) = self.spans[i];
len == s.len() && same_bytes(&buf[at..at + len], s.as_bytes())
}
pub(crate) fn add(&mut self, buf: &[u8], s: &str, at: usize, hash: Option<u32>) -> usize {
let i = self.spans.len();
self.spans.push((at, s.len()));
if self.spans.len() > LINEAR_STRINGS {
if self.index.is_empty() {
for &(start, len) in &self.spans {
self.index.push(hash_bytes(&buf[start..start + len]));
}
} else {
self.index
.push(hash.unwrap_or_else(|| hash_bytes(s.as_bytes())));
}
}
i
}
}
const ADDRESS_SHIFT: u32 = 3;