use crate::basicqht::*;
use crate::filter::Filter;
pub use rand::rngs::StdRng;
pub use rand::{FromEntropy, Rng};
pub use std::collections::hash_map::DefaultHasher;
pub use std::hash::{Hash, Hasher};
pub use rust_dense_bitset::DenseBitSetExtended;
const FINGERPRINT_SIZE_LIMIT: usize = 8;
pub struct QQuotientHashTable {
n_cells: usize,
n_buckets: usize,
fingerprint_size: usize,
pow_fingerprint_size: u64,
qht: DenseBitSetExtended,
rng: StdRng,
}
impl QQuotientHashTable {
pub fn new(memory_size: usize, n_buckets: usize, fingerprint_size: usize) -> Self {
if fingerprint_size > FINGERPRINT_SIZE_LIMIT {
panic!("[QHTc Filter] Incorrect parameters, fingerprint_size cannot exceed 8.");
} else if fingerprint_size == 0 {
panic!("[QHTc Filter] Incorrect parameters, fingerprint_size cannot be zero.");
}
if n_buckets == 0 {
panic!("[QHTc Filter] Incorrect parameters, n_buckets cannot be zero.");
}
let rng = StdRng::from_entropy();
let pow_fingerprint_size = 2u64.pow(fingerprint_size as u32);
let n_cells = memory_size / (n_buckets * fingerprint_size);
if n_cells == 0 {
panic!("[QHT Filter] Incorrect parameters, memory size should be at least n_buckets * fingerprint_size");
}
let qht = DenseBitSetExtended::with_capacity(n_cells * n_buckets * fingerprint_size);
Self {
n_cells,
n_buckets,
fingerprint_size,
pow_fingerprint_size,
qht,
rng,
}
}
fn get_random_bucket(&mut self) -> usize {
self.rng.gen_range(0, self.n_buckets)
}
fn insert_empty(&mut self, address: usize, fingerprint: Fingerprint) -> bool {
for idx in 0..self.n_buckets {
if self.get_fingerprint_from_bucket(address, idx) == 0 {
self.insert_fingerprint_in_bucket(address, idx, fingerprint);
return true;
}
}
false
}
}
impl_basicqht!(QQuotientHashTable);
impl Filter for QQuotientHashTable {
fn lookup(&self, e: impl Hash) -> bool {
let fingerprint = self.get_fingerprint(&e);
let address = (get_hash(&e, 1, 0) as usize) % self.n_cells;
self.in_cell(address, fingerprint)
}
fn insert(&mut self, e: impl Hash) -> bool {
let fingerprint = self.get_fingerprint(&e);
let address = (get_hash(&e, 1, 0) as usize) % self.n_cells;
let detected = self.in_cell(address, fingerprint);
if !self.insert_empty(address, fingerprint) {
let bucket = self.get_random_bucket();
self.insert_fingerprint_in_bucket(address, bucket, fingerprint);
}
detected
}
}