use std::fmt::Debug;
use crate::Code;
use crate::Symbol;
use crate::builder::fsst_hash;
pub const HASH_TABLE_SIZE: usize = 1 << 11;
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct TableEntry {
pub(crate) symbol: Symbol,
pub(crate) code: Code,
pub(crate) ignored_bits: u16,
}
assert_sizeof!(TableEntry => 16);
impl TableEntry {
pub(crate) fn is_unused(&self) -> bool {
self.code == Code::UNUSED
}
}
#[derive(Clone, Debug)]
pub(crate) struct LossyPHT {
slots: Vec<TableEntry>,
}
impl LossyPHT {
pub(crate) fn new() -> Self {
let slots = vec![
TableEntry {
symbol: Symbol::ZERO,
code: Code::UNUSED,
ignored_bits: 64,
};
HASH_TABLE_SIZE
];
Self { slots }
}
pub(crate) fn insert(&mut self, symbol: Symbol, len: usize, code: u8) -> bool {
let prefix_3bytes = symbol.to_u64() & 0xFF_FF_FF;
let slot = fsst_hash(prefix_3bytes) as usize & (HASH_TABLE_SIZE - 1);
let entry = &mut self.slots[slot];
if !entry.is_unused() {
false
} else {
entry.symbol = symbol;
entry.code = Code::new_symbol_building(code, len);
entry.ignored_bits = (64 - 8 * symbol.len()) as u16;
true
}
}
pub(crate) fn renumber(&mut self, new_codes: &[u8]) {
for slot in self.slots.iter_mut() {
if slot.code != Code::UNUSED {
let old_code = slot.code.code();
let new_code = new_codes[old_code as usize];
let len = slot.code.len();
slot.code = Code::new_symbol(new_code, len as usize);
}
}
}
pub(crate) fn remove(&mut self, symbol: Symbol) {
let prefix_3bytes = symbol.to_u64() & 0xFF_FF_FF;
let slot = fsst_hash(prefix_3bytes) as usize & (HASH_TABLE_SIZE - 1);
self.slots[slot].code = Code::UNUSED;
}
#[inline]
pub(crate) fn lookup(&self, word: u64) -> &TableEntry {
let prefix_3bytes = word & 0xFF_FF_FF;
let slot = fsst_hash(prefix_3bytes) as usize & (HASH_TABLE_SIZE - 1);
unsafe { self.slots.get_unchecked(slot) }
}
}
impl Default for LossyPHT {
fn default() -> Self {
Self::new()
}
}