use alloc::{vec, vec::Vec};
#[derive(Debug)]
pub(crate) struct SliceMap {
data: Vec<u32>,
ends: Vec<usize>,
hashes: Vec<u64>,
index: Vec<u32>,
}
impl SliceMap {
pub(crate) fn new() -> Self {
Self {
data: Vec::new(),
ends: Vec::new(),
hashes: Vec::new(),
index: vec![0; 64],
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.ends.len()
}
#[inline]
pub(crate) fn words(&self) -> usize {
self.data.len()
}
#[inline]
pub(crate) fn get(&self, id: usize) -> &[u32] {
let start = if id == 0 { 0 } else { self.ends[id - 1] };
&self.data[start..self.ends[id]]
}
pub(crate) fn insert(&mut self, key: &[u32]) -> (u32, bool) {
let hash = hash(key);
let mask = self.index.len() - 1;
let mut slot = (hash as usize) & mask;
loop {
match self.index[slot] {
0 => break,
entry => {
let id = (entry - 1) as usize;
if self.hashes[id] == hash && self.get(id) == key {
return (entry - 1, false);
}
}
}
slot = (slot + 1) & mask;
}
let id = self.ends.len() as u32;
self.data.extend_from_slice(key);
self.ends.push(self.data.len());
self.hashes.push(hash);
self.index[slot] = id + 1;
if self.ends.len() * 2 > self.index.len() {
self.grow();
}
(id, true)
}
fn grow(&mut self) {
let size = self.index.len() * 2;
let mask = size - 1;
let mut index = vec![0u32; size];
for (id, &hash) in self.hashes.iter().enumerate() {
let mut slot = (hash as usize) & mask;
while index[slot] != 0 {
slot = (slot + 1) & mask;
}
index[slot] = id as u32 + 1;
}
self.index = index;
}
}
#[inline]
fn hash(key: &[u32]) -> u64 {
const K: u64 = 0x517c_c1b7_2722_0a95;
let mut h = key.len() as u64;
for &word in key {
h = (h.rotate_left(5) ^ u64::from(word)).wrapping_mul(K);
}
h ^ (h >> 32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slice_map_numbers_distinct_slices() {
let mut map = SliceMap::new();
assert_eq!(map.insert(&[1, 2, 3]), (0, true));
assert_eq!(map.insert(&[]), (1, true));
assert_eq!(map.insert(&[1, 2, 3]), (0, false));
assert_eq!(map.insert(&[]), (1, false));
assert_eq!(map.get(0), &[1, 2, 3]);
assert_eq!(map.get(1), &[] as &[u32]);
for i in 0..1000u32 {
let (id, new) = map.insert(&[i, i + 1]);
assert!(new);
assert_eq!(id, i + 2);
}
for i in 0..1000u32 {
assert_eq!(map.insert(&[i, i + 1]), (i + 2, false));
}
assert_eq!(map.len(), 1002);
}
}