use polars_utils::IdxSize;
use polars_utils::select::select_unpredictable;
use polars_utils::vec::PushUnchecked;
use crate::EvictIdx;
const H2_MULT: u64 = 0xf1357aea2e62a9c5;
#[derive(Clone)]
struct Slot {
tag: u32,
last_access_tag: u32,
key_index: IdxSize,
}
pub struct FixedIndexTable<K> {
slots: Vec<Slot>,
keys: Vec<K>,
num_filled_slots: usize, shift: u8,
prng: u64,
}
impl<K> FixedIndexTable<K> {
pub fn new(num_slots: IdxSize) -> Self {
assert!(num_slots.is_power_of_two());
assert!(num_slots > 1);
let empty_slot = Slot {
tag: u32::MAX,
last_access_tag: u32::MAX,
key_index: IdxSize::MAX,
};
Self {
slots: vec![empty_slot; num_slots as usize],
shift: 64 - num_slots.trailing_zeros() as u8,
num_filled_slots: 0,
keys: Vec::with_capacity(1 + num_slots as usize),
prng: 0,
}
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn push_unmapped_key(&mut self, key: K) -> IdxSize {
let idx = self.keys.len();
self.keys.push(key);
idx as IdxSize
}
pub fn insert_key<Q, E, I, V>(
&mut self,
hash: u64,
key: Q,
force_insert: bool,
mut eq: E,
mut insert: I,
mut evict_insert: V,
) -> Option<EvictIdx>
where
E: FnMut(&Q, &K) -> bool,
I: FnMut(Q) -> K,
V: FnMut(Q, &mut K),
{
let tag = hash as u32;
let h1 = (hash >> self.shift) as usize;
let h2 = (hash.wrapping_mul(H2_MULT) >> self.shift) as usize;
unsafe {
let s1 = self.slots.get_unchecked(h1);
let s2 = self.slots.get_unchecked(h2);
let s1_delta = s1.tag ^ tag;
let s2_delta = s2.tag ^ tag;
if s1_delta & s2_delta == 0 {
let ha = select_unpredictable(s1_delta == 0, h1, h2);
let sa = self.slots.get_unchecked_mut(ha);
if let Some(sak) = self.keys.get(sa.key_index as usize) {
if eq(&key, sak) {
sa.last_access_tag = tag;
return Some(EvictIdx::new(sa.key_index, false));
}
}
if s1_delta == s2_delta {
let hb = h1 ^ h2 ^ ha;
let sb = self.slots.get_unchecked_mut(hb);
if let Some(sbk) = self.keys.get(sb.key_index as usize) {
if eq(&key, sbk) {
sb.last_access_tag = tag;
return Some(EvictIdx::new(sb.key_index, false));
}
}
}
}
let num_keys = self.keys.len() as IdxSize;
if self.num_filled_slots < self.slots.len() {
let s1 = self.slots.get_unchecked_mut(h1);
if s1.key_index >= num_keys {
s1.tag = tag;
s1.last_access_tag = tag;
s1.key_index = num_keys;
self.keys.push_unchecked(insert(key));
self.num_filled_slots += 1;
return Some(EvictIdx::new(s1.key_index, false));
}
let s2 = self.slots.get_unchecked_mut(h2);
if s2.key_index >= num_keys {
s2.tag = tag;
s2.last_access_tag = tag;
s2.key_index = num_keys;
self.keys.push_unchecked(insert(key));
self.num_filled_slots += 1;
return Some(EvictIdx::new(s2.key_index, false));
}
}
let hr = select_unpredictable(self.prng >> 63 != 0, h1, h2);
self.prng = self.prng.wrapping_add(hash);
let slot = self.slots.get_unchecked_mut(hr);
if (slot.last_access_tag == tag) | force_insert {
slot.tag = tag;
let evict_key = self.keys.get_unchecked_mut(slot.key_index as usize);
evict_insert(key, evict_key);
Some(EvictIdx::new(slot.key_index, true))
} else {
slot.last_access_tag = tag;
None
}
}
}
pub fn keys(&self) -> &[K] {
&self.keys
}
}