mod filter;
mod hash;
pub use filter::BloomFilter;
#[derive(Debug)]
pub struct BloomFilterBuilder {
expected_items: Option<usize>,
false_positive_rate: Option<f64>,
num_bits: Option<usize>,
num_hashes: Option<u32>,
}
impl Default for BloomFilterBuilder {
fn default() -> Self {
Self::new()
}
}
impl BloomFilterBuilder {
#[must_use]
pub fn new() -> Self {
Self {
expected_items: None,
false_positive_rate: None,
num_bits: None,
num_hashes: None,
}
}
#[must_use]
pub fn expected_items(mut self, n: usize) -> Self {
self.expected_items = Some(n);
self
}
#[must_use]
pub fn false_positive_rate(mut self, p: f64) -> Self {
self.false_positive_rate = Some(p);
self
}
#[must_use]
pub fn num_bits(mut self, bits: usize) -> Self {
self.num_bits = Some(bits);
self
}
#[must_use]
pub fn num_hashes(mut self, hashes: u32) -> Self {
self.num_hashes = Some(hashes);
self
}
#[must_use]
pub fn build(self) -> BloomFilter {
if let (Some(bits), Some(hashes)) = (self.num_bits, self.num_hashes) {
BloomFilter::with_params(bits, hashes)
} else {
let items = self.expected_items.unwrap_or(1000);
let fpr = self.false_positive_rate.unwrap_or(0.01);
BloomFilter::new(items, fpr)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bloom_filter_basic() {
let mut bf = BloomFilter::new(1000, 0.01);
bf.insert(b"hello");
bf.insert(b"world");
assert!(bf.may_contain(b"hello"));
assert!(bf.may_contain(b"world"));
assert!(!bf.may_contain(b"not_inserted"));
}
#[test]
fn test_bloom_filter_no_false_negatives() {
let mut bf = BloomFilter::new(10000, 0.01);
for i in 0..1000 {
let item = format!("item_{i}");
bf.insert(item.as_bytes());
}
for i in 0..1000 {
let item = format!("item_{i}");
assert!(
bf.may_contain(item.as_bytes()),
"False negative for item {i}",
);
}
}
#[test]
fn test_hash_distribution() {
use super::hash::{hash_fnv1a, hash_fnv1a_alt};
let h1 = hash_fnv1a(b"test");
let h2 = hash_fnv1a_alt(b"test");
assert_ne!(h1, h2);
let h1a = hash_fnv1a(b"input_a");
let h1b = hash_fnv1a(b"input_b");
assert_ne!(h1a, h1b);
}
#[test]
fn test_builder_pattern() {
let bf = BloomFilterBuilder::new()
.expected_items(500)
.false_positive_rate(0.001)
.build();
assert!(bf.num_bits() > 0);
assert_eq!(bf.num_hashes(), 10); }
#[test]
fn test_clear() {
let mut bf = BloomFilter::new(100, 0.01);
bf.insert(b"item");
assert!(bf.may_contain(b"item"));
bf.clear();
assert!(!bf.may_contain(b"item"));
}
}