#![warn(missing_docs)]
#![warn(clippy::all)]
#![allow(clippy::module_name_repetitions)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod builder;
pub mod core;
pub mod error;
pub mod filters;
pub mod hash;
pub mod sync;
pub mod util;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub mod serde_support;
#[cfg(feature = "metrics")]
#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
pub mod metrics;
pub use crate::error::{BloomCraftError, Result};
pub use crate::core::{
BloomFilter,
ConcurrentBloomFilter,
DeletableBloomFilter,
MergeableBloomFilter,
MutableBloomFilter,
ScalableBloomFilter as ScalableBloomFilterTrait,
SharedBloomFilter,
};
pub use crate::filters::{
CapacityExhaustedBehavior,
ClassicBitsFilter,
ClassicHashFilter,
CounterSize,
CountingBloomFilter,
FilterHealth,
GrowthStrategy,
PartitionedBloomFilter,
QueryStrategy,
RegisterBlockedBloomFilter,
ScalableBloomFilter,
StandardBloomFilter,
TreeBloomFilter,
};
#[cfg(feature = "trace")]
pub use crate::filters::{QueryTrace, QueryTraceBuilder};
#[cfg(feature = "concurrent")]
pub use crate::filters::{AtomicPartitionedBloomFilter, AtomicScalableBloomFilter};
pub use crate::hash::{
BloomHasher,
DoubleHashing,
EnhancedDoubleHashing,
HashStrategy,
HashStrategyKind,
IndexingStrategy,
StdHasher,
TripleHashing,
};
#[cfg(feature = "wyhash")]
pub use crate::hash::{WyHasher, WyHasherBuilder};
#[cfg(feature = "xxhash")]
pub use crate::hash::{XxHasher, XxHasherBuilder};
#[cfg(feature = "simd")]
pub use crate::hash::SimdHasher;
pub use crate::builder::{
CountingBloomFilterBuilder,
ScalableBloomFilterBuilder,
StandardBloomFilterBuilder,
};
pub use crate::sync::{ShardedBloomFilter, StripedBloomFilter};
#[cfg(feature = "serde")]
pub use crate::serde_support as serde;
#[cfg(feature = "metrics")]
pub use crate::metrics::{
FalsePositiveTracker,
FilterMetrics,
LatencyHistogram,
LatencyStats,
MetricsCollector,
MetricsSnapshot,
};
pub mod prelude {
pub use crate::builder::{
CountingBloomFilterBuilder,
ScalableBloomFilterBuilder,
StandardBloomFilterBuilder,
};
pub use crate::core::{
BloomFilter,
ConcurrentBloomFilter,
DeletableBloomFilter,
MergeableBloomFilter,
MutableBloomFilter,
SharedBloomFilter,
};
pub use crate::error::{BloomCraftError, Result};
pub use crate::filters::{
ClassicBitsFilter,
ClassicHashFilter,
CountingBloomFilter,
PartitionedBloomFilter,
RegisterBlockedBloomFilter,
ScalableBloomFilter,
StandardBloomFilter,
TreeBloomFilter,
};
pub use crate::hash::{
BloomHasher,
DoubleHashing,
EnhancedDoubleHashing,
HashStrategy,
HashStrategyKind,
IndexingStrategy,
StdHasher,
TripleHashing,
};
pub use crate::sync::{ShardedBloomFilter, StripedBloomFilter};
#[cfg(feature = "concurrent")]
pub use crate::filters::{AtomicPartitionedBloomFilter, AtomicScalableBloomFilter};
#[cfg(feature = "trace")]
pub use crate::filters::{QueryTrace, QueryTraceBuilder};
#[cfg(feature = "serde")]
pub use crate::serde_support::prelude::*;
#[cfg(feature = "metrics")]
pub use crate::metrics::prelude::*;
}
#[cfg(test)]
mod tests {
use super::prelude::*;
#[test]
fn test_prelude_imports() {
let filter = StandardBloomFilter::<String>::new(100, 0.01).unwrap();
filter.insert(&"test".to_string());
assert!(filter.contains(&"test".to_string()));
}
#[test]
fn test_trait_usage() {
fn test_filter<F: BloomFilter<String>>(filter: &mut F) {
filter.insert(&"item".to_string());
assert!(filter.contains(&"item".to_string()));
}
let mut filter = StandardBloomFilter::<String>::new(100, 0.01).unwrap();
test_filter(&mut filter);
}
#[test]
fn test_builder() {
let filter = StandardBloomFilter::<String>::new(1000, 0.01).unwrap();
assert!(filter.is_empty());
}
#[test]
fn test_concurrent_filter_shared_trait() {
use std::sync::Arc;
let filter = Arc::new(ShardedBloomFilter::<String>::new(1000, 0.01));
filter.insert(&"concurrent".to_string());
assert!(filter.contains(&"concurrent".to_string()));
let f2 = Arc::clone(&filter);
let handle = std::thread::spawn(move || {
f2.insert(&"thread_item".to_string());
});
handle.join().unwrap();
assert!(filter.contains(&"thread_item".to_string()));
}
#[test]
fn test_striped_filter_shared_trait() {
use std::sync::Arc;
let filter = Arc::new(StripedBloomFilter::<String>::new(1000, 0.01).unwrap());
filter.insert(&"striped".to_string());
assert!(filter.contains(&"striped".to_string()));
}
#[test]
fn test_shared_filter_clear() {
let filter = ShardedBloomFilter::<String>::new(1000, 0.01);
filter.insert(&"item1".to_string());
filter.insert(&"item2".to_string());
assert!(!filter.is_empty());
filter.clear();
assert!(filter.is_empty());
assert!(!filter.contains(&"item1".to_string()));
}
#[cfg(feature = "serde")]
#[test]
fn test_serialization() {
let filter = StandardBloomFilter::<String>::new(100, 0.01).unwrap();
filter.insert(&"serialize_me".to_string());
let bytes = bincode::serialize(&filter).unwrap();
let restored: StandardBloomFilter<String> = bincode::deserialize(&bytes).unwrap();
assert!(restored.contains(&"serialize_me".to_string()));
}
#[test]
fn test_concurrent_bloom_filter_trait() {
use std::sync::Arc;
let filter = Arc::new(StandardBloomFilter::<String>::new(1000, 0.01).unwrap());
filter.insert_concurrent(&"atomic_item".to_string());
assert!(filter.contains(&"atomic_item".to_string()));
let f2 = Arc::clone(&filter);
let handle = std::thread::spawn(move || {
f2.insert_concurrent(&"thread_atomic".to_string());
});
handle.join().unwrap();
assert!(filter.contains(&"thread_atomic".to_string()));
}
#[test]
fn test_tree_bloom_filter_basic() {
let mut filter = TreeBloomFilter::<String>::new(vec![2, 3], 100, 0.01).unwrap();
filter.insert_to_bin(&"item1".to_string(), &[0, 1]).unwrap();
assert!(filter.contains(&"item1".to_string()));
let locations = filter.locate(&"item1".to_string());
assert_eq!(locations.len(), 1);
assert_eq!(locations[0], vec![0, 1]);
}
#[test]
fn test_tree_bloom_filter_hierarchy() {
let mut router = TreeBloomFilter::<String>::new(vec![3, 10], 1000, 0.01).unwrap();
router.insert_to_bin(&"session:alice".to_string(), &[1, 5]).unwrap();
assert!(router.contains_in_bin(&"session:alice".to_string(), &[1, 5]).unwrap());
assert!(!router.contains_in_bin(&"session:alice".to_string(), &[0, 3]).unwrap());
assert!(router.contains(&"session:alice".to_string()));
}
#[test]
fn test_tree_vs_standard_difference() {
let mut tree = TreeBloomFilter::<i32>::new(vec![2, 2], 100, 0.01).unwrap();
let standard = StandardBloomFilter::<i32>::new(100, 0.01).unwrap();
tree.insert_to_bin(&42, &[0, 1]).unwrap();
assert!(tree.contains_in_bin(&42, &[0, 1]).unwrap());
assert!(!tree.contains_in_bin(&42, &[1, 0]).unwrap());
standard.insert(&42);
assert!(standard.contains(&42));
}
}