use crate::CuckooFilter;
const DEFAULT_INITIAL_BUCKETS_HINT: usize = 1024;
const DEFAULT_GROW_THRESHOLD: f64 = 0.95;
const GROWTH_FACTOR: usize = 2;
const BUCKET_SIZE: usize = 4;
pub struct DynamicCuckooFilter {
layers: Vec<CuckooFilter>,
layer_capacities: Vec<usize>,
grow_threshold: f64,
}
impl DynamicCuckooFilter {
pub fn new(initial_capacity: usize) -> Self {
Self::with_threshold(initial_capacity, DEFAULT_GROW_THRESHOLD)
}
pub fn with_threshold(initial_capacity: usize, grow_threshold: f64) -> Self {
let cap = initial_capacity.max(DEFAULT_INITIAL_BUCKETS_HINT / BUCKET_SIZE);
let t = if grow_threshold.is_finite() && grow_threshold > 0.0 && grow_threshold < 1.0 {
grow_threshold
} else {
DEFAULT_GROW_THRESHOLD
};
Self {
layers: vec![CuckooFilter::with_capacity(cap)],
layer_capacities: vec![cap],
grow_threshold: t,
}
}
pub fn layer_count(&self) -> usize {
self.layers.len()
}
pub fn len(&self) -> usize {
self.layers.iter().map(|l| l.len()).sum()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn insert(&mut self, key: &str) -> bool {
if self.should_grow() {
self.grow();
}
let active = self.layers.len() - 1;
if self.layers[active].insert(key) {
return true;
}
self.grow();
let active = self.layers.len() - 1;
self.layers[active].insert(key)
}
pub fn contains(&self, key: &str) -> bool {
self.layers.iter().any(|l| l.contains(key))
}
pub fn delete(&mut self, key: &str) -> bool {
for layer in self.layers.iter_mut().rev() {
if layer.delete(key) {
return true;
}
}
false
}
pub fn load_factor(&self) -> f64 {
let active = self.layers.len() - 1;
let cap = self.layer_capacities[active];
if cap == 0 {
0.0
} else {
self.layers[active].len() as f64 / cap as f64
}
}
fn should_grow(&self) -> bool {
let active = self.layers.len() - 1;
let cap = self.layer_capacities[active];
if cap == 0 {
return false;
}
self.layers[active].len() as f64 / cap as f64 >= self.grow_threshold
}
fn grow(&mut self) {
let last = self.layer_capacities.len() - 1;
let new_cap = self.layer_capacities[last] * GROWTH_FACTOR;
self.layers.push(CuckooFilter::with_capacity(new_cap));
self.layer_capacities.push(new_cap);
}
}
#[cfg(test)]
#[path = "dynamic_tests.rs"]
mod tests;