#![warn(missing_docs)]
pub mod standard;
pub use standard::{FilterHealth, StandardBloomFilter};
pub mod counting;
pub use counting::{CounterSize, CountingBloomFilter};
pub mod scalable;
pub use scalable::{
CapacityExhaustedBehavior, GrowthStrategy, QueryStrategy, ScalableBloomFilter,
ScalableHealthMetrics,
};
#[cfg(feature = "trace")]
pub use scalable::{QueryTrace, QueryTraceBuilder};
#[cfg(feature = "concurrent")]
pub mod atomic_scalable;
#[cfg(feature = "concurrent")]
pub use atomic_scalable::AtomicScalableBloomFilter;
pub mod partitioned;
pub use partitioned::PartitionedBloomFilter;
#[cfg(feature = "concurrent")]
pub mod atomic_partitioned;
#[cfg(feature = "concurrent")]
pub use atomic_partitioned::AtomicPartitionedBloomFilter;
pub mod register_blocked;
pub use register_blocked::{RegisterBlockedBloomFilter, BLOCK_SIZE_BITS, BLOCK_SIZE_WORDS};
pub mod tree;
pub use tree::{
LocateIter, TreeBloomFilter, TreeBloomFilterBuilder, TreeCapacityStats, TreeConfig, TreeStats,
MAX_TOTAL_NODES, MAX_TREE_DEPTH,
};
pub mod classic_bits;
pub use classic_bits::ClassicBitsFilter;
pub mod classic_hash;
pub use classic_hash::ClassicHashFilter;
#[cfg(test)]
mod tests {
use super::*;
use crate::core::filter::BloomFilter;
#[test]
fn test_all_filters_accessible() {
let _standard: StandardBloomFilter<String> = StandardBloomFilter::new(100, 0.01).unwrap();
let _counting: CountingBloomFilter<String> = CountingBloomFilter::new(100, 0.01);
let _scalable: ScalableBloomFilter<String> = ScalableBloomFilter::new(100, 0.01).unwrap();
let _partitioned: PartitionedBloomFilter<String> =
PartitionedBloomFilter::new(100, 0.01).unwrap();
let _tree: TreeBloomFilter<String> = TreeBloomFilter::new(vec![2, 3], 100, 0.01).unwrap();
let _classic_bits: ClassicBitsFilter<String> = ClassicBitsFilter::with_fpr(100, 0.01);
let _classic_hash: ClassicHashFilter<String> = ClassicHashFilter::with_fpr(100, 0.01);
}
#[test]
fn test_counter_size_enum() {
assert_eq!(CounterSize::FourBit.max_value(), 15);
assert_eq!(CounterSize::EightBit.max_value(), 255);
assert_eq!(CounterSize::SixteenBit.max_value(), 65535);
assert_eq!(CounterSize::FourBit.bits(), 4);
assert_eq!(CounterSize::EightBit.bits(), 8);
assert_eq!(CounterSize::SixteenBit.bits(), 16);
}
#[test]
fn test_growth_strategy_enum() {
use scalable::GrowthStrategy;
match GrowthStrategy::Constant {
GrowthStrategy::Constant => {}
_ => panic!("Expected Constant"),
}
match GrowthStrategy::Geometric(2.0) {
GrowthStrategy::Geometric(scale) => {
assert_eq!(scale, 2.0);
}
_ => panic!("Expected Geometric"),
}
let default_strategy = GrowthStrategy::default();
match default_strategy {
GrowthStrategy::Geometric(scale) => {
assert_eq!(scale, 2.0);
}
_ => panic!("Expected default to be Geometric(2.0)"),
}
}
#[test]
fn test_scalable_new_types() {
let _silent = CapacityExhaustedBehavior::Silent;
let _error = CapacityExhaustedBehavior::Error;
#[cfg(debug_assertions)]
let _panic = CapacityExhaustedBehavior::Panic;
assert_eq!(
CapacityExhaustedBehavior::default(),
CapacityExhaustedBehavior::Silent
);
let _forward = QueryStrategy::Forward;
let _reverse = QueryStrategy::Reverse;
assert_eq!(QueryStrategy::default(), QueryStrategy::Reverse);
let mut filter: ScalableBloomFilter<i32> = ScalableBloomFilter::new(100, 0.01).unwrap();
for i in 0..50 {
filter.insert(&i);
}
let metrics = filter.health_metrics();
assert!(metrics.filter_count > 0);
assert_eq!(metrics.total_items, 50);
assert!(metrics.estimated_fpr > 0.0);
}
#[cfg(feature = "concurrent")]
#[test]
fn test_atomic_scalable_filter() {
use std::sync::Arc;
use std::thread;
let filter = Arc::new(AtomicScalableBloomFilter::new(100, 0.01).unwrap());
let mut handles = vec![];
for thread_id in 0..4 {
let f = Arc::clone(&filter);
let h = thread::spawn(move || {
for i in 0..25 {
f.insert(&(thread_id * 25 + i));
}
});
handles.push(h);
}
for h in handles {
h.join().unwrap();
}
assert_eq!(filter.len(), 100);
for i in 0..100 {
assert!(filter.contains(&i));
}
}
#[test]
fn test_filters_are_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<StandardBloomFilter<String>>();
assert_send_sync::<CountingBloomFilter<String>>();
assert_send_sync::<ScalableBloomFilter<String>>();
assert_send_sync::<PartitionedBloomFilter<String>>();
assert_send_sync::<TreeBloomFilter<String>>();
assert_send_sync::<ClassicBitsFilter<String>>();
assert_send_sync::<ClassicHashFilter<String>>();
}
#[test]
fn test_generic_type_flexibility() {
let _i32_filter: StandardBloomFilter<i32> = StandardBloomFilter::new(100, 0.01).unwrap();
let _u64_filter: StandardBloomFilter<u64> = StandardBloomFilter::new(100, 0.01).unwrap();
let _string_filter: StandardBloomFilter<String> =
StandardBloomFilter::new(100, 0.01).unwrap();
let _str_filter: StandardBloomFilter<&str> = StandardBloomFilter::new(100, 0.01).unwrap();
let _tuple_filter: StandardBloomFilter<(i32, String)> =
StandardBloomFilter::new(100, 0.01).unwrap();
let _vec_filter: StandardBloomFilter<Vec<u8>> =
StandardBloomFilter::new(100, 0.01).unwrap();
}
#[test]
fn test_basic_functionality_all_filters() {
let standard: StandardBloomFilter<i32> = StandardBloomFilter::new(100, 0.01).unwrap();
standard.insert(&42);
assert!(standard.contains(&42));
assert!(!standard.contains(&43));
let mut counting: CountingBloomFilter<i32> = CountingBloomFilter::new(100, 0.01);
counting.insert(&42);
assert!(counting.contains(&42));
let deleted = counting.delete(&42);
assert!(deleted, "Item should have been deleted");
assert!(!counting.contains(&42));
let mut scalable: ScalableBloomFilter<i32> = ScalableBloomFilter::new(10, 0.01).unwrap();
for i in 0..100 {
scalable.insert(&i);
}
assert!(scalable.contains(&50));
let mut partitioned: PartitionedBloomFilter<i32> =
PartitionedBloomFilter::new(100, 0.01).unwrap();
partitioned.insert(&42);
assert!(partitioned.contains(&42));
let mut tree: TreeBloomFilter<i32> = TreeBloomFilter::new(vec![2, 3], 100, 0.01).unwrap();
tree.insert_to_bin(&42, &[0, 1]).unwrap();
assert!(tree.contains_in_bin(&42, &[0, 1]).unwrap());
let mut classic_bits: ClassicBitsFilter<i32> = ClassicBitsFilter::new(1000, 7);
classic_bits.insert(&42);
assert!(classic_bits.contains(&42));
let mut classic_hash: ClassicHashFilter<i32> = ClassicHashFilter::new(1000, 3);
classic_hash.insert(&42);
assert!(classic_hash.contains(&42));
}
#[test]
fn test_documentation_patterns() {
let _filter: StandardBloomFilter<String> = StandardBloomFilter::new(1000, 0.01).unwrap();
let _filter = StandardBloomFilter::<String>::new(1000, 0.01).unwrap();
let filter = StandardBloomFilter::new(1000, 0.01).unwrap();
filter.insert(&"hello".to_string());
let _: bool = filter.contains(&"hello".to_string());
}
#[test]
fn test_clear_functionality() {
let mut standard: StandardBloomFilter<i32> = StandardBloomFilter::new(100, 0.01).unwrap();
standard.insert(&42);
assert!(standard.contains(&42));
standard.clear();
assert!(!standard.contains(&42));
let mut counting: CountingBloomFilter<i32> = CountingBloomFilter::new(100, 0.01);
counting.insert(&42);
assert!(counting.contains(&42));
counting.clear();
assert!(!counting.contains(&42));
}
#[test]
fn test_batch_operations() {
let filter: StandardBloomFilter<i32> = StandardBloomFilter::new(100, 0.01).unwrap();
let items = vec![1, 2, 3, 4, 5];
filter.insert_batch(&items);
for item in &items {
assert!(filter.contains(item));
}
let queries = vec![1, 2, 3, 6, 7, 8];
let results = filter.contains_batch(&queries);
assert_eq!(results[0..3], [true, true, true]);
assert_eq!(results[3..6], [false, false, false]);
}
#[test]
fn test_tree_bloom_filter_locate() {
let mut filter: TreeBloomFilter<String> =
TreeBloomFilter::new(vec![2, 2], 100, 0.01).unwrap();
filter.insert_to_bin(&"item1".to_string(), &[0, 1]).unwrap();
filter.insert_to_bin(&"item2".to_string(), &[1, 0]).unwrap();
let loc1 = filter.locate(&"item1".to_string());
assert_eq!(loc1.len(), 1);
assert_eq!(loc1[0], vec![0, 1]);
let loc2 = filter.locate(&"item2".to_string());
assert_eq!(loc2.len(), 1);
assert_eq!(loc2[0], vec![1, 0]);
}
#[test]
fn test_tree_bloom_filter_batch() {
let mut filter: TreeBloomFilter<String> = TreeBloomFilter::new(vec![2], 100, 0.01).unwrap();
let items = vec!["a".to_string(), "b".to_string(), "c".to_string()];
filter.insert_batch_to_bin(&items, &[0]).unwrap();
for item in &items {
assert!(filter.contains(item));
}
assert_eq!(filter.len(), 3);
}
#[test]
fn test_tree_bloom_filter_stats() {
let filter: TreeBloomFilter<String> = TreeBloomFilter::new(vec![2, 3], 100, 0.01).unwrap();
let stats = filter.stats();
assert_eq!(stats.depth, 2);
assert_eq!(stats.leaf_bins, 6); assert!(stats.total_nodes > 0);
assert!(stats.memory_usage > 0);
}
}