use crate::{bfuse_contains_impl, bfuse_from_impl, Filter};
use alloc::{boxed::Box, vec::Vec};
use core::convert::TryFrom;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "bincode")]
use bincode::{Decode, Encode};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "bincode", derive(Encode, Decode))]
#[derive(Debug)]
pub struct BinaryFuse32 {
seed: u64,
segment_length: u32,
segment_length_mask: u32,
segment_count_length: u32,
pub fingerprints: Box<[u32]>,
}
impl Filter<u64> for BinaryFuse32 {
fn contains(&self, key: &u64) -> bool {
bfuse_contains_impl!(*key, self, fingerprint u32)
}
fn len(&self) -> usize {
self.fingerprints.len()
}
}
impl BinaryFuse32 {
pub fn try_from_iterator<T>(keys: T) -> Result<Self, &'static str>
where
T: ExactSizeIterator<Item = u64> + Clone,
{
bfuse_from_impl!(keys fingerprint u32, max iter 1_000)
}
}
impl TryFrom<&[u64]> for BinaryFuse32 {
type Error = &'static str;
fn try_from(keys: &[u64]) -> Result<Self, Self::Error> {
Self::try_from_iterator(keys.iter().copied())
}
}
impl TryFrom<&Vec<u64>> for BinaryFuse32 {
type Error = &'static str;
fn try_from(v: &Vec<u64>) -> Result<Self, Self::Error> {
Self::try_from_iterator(v.iter().copied())
}
}
impl TryFrom<Vec<u64>> for BinaryFuse32 {
type Error = &'static str;
fn try_from(v: Vec<u64>) -> Result<Self, Self::Error> {
Self::try_from_iterator(v.iter().copied())
}
}
#[cfg(test)]
mod test {
use crate::{BinaryFuse32, Filter};
use core::convert::TryFrom;
use alloc::vec::Vec;
use rand::Rng;
#[test]
fn test_initialization() {
const SAMPLE_SIZE: usize = 1_000_000;
let mut rng = rand::thread_rng();
let keys: Vec<u64> = (0..SAMPLE_SIZE).map(|_| rng.gen()).collect();
let filter = BinaryFuse32::try_from(&keys).unwrap();
for key in keys {
assert!(filter.contains(&key));
}
}
#[test]
fn test_bits_per_entry() {
const SAMPLE_SIZE: usize = 1_000_000;
let mut rng = rand::thread_rng();
let keys: Vec<u64> = (0..SAMPLE_SIZE).map(|_| rng.gen()).collect();
let filter = BinaryFuse32::try_from(&keys).unwrap();
let bpe = (filter.len() as f64) * 32.0 / (SAMPLE_SIZE as f64);
assert!(bpe < 36.2, "Bits per entry is {}", bpe);
}
#[test]
fn test_false_positives() {
const SAMPLE_SIZE: usize = 1_000_000;
let mut rng = rand::thread_rng();
let keys: Vec<u64> = (0..SAMPLE_SIZE).map(|_| rng.gen()).collect();
let filter = BinaryFuse32::try_from(&keys).unwrap();
let false_positives: usize = (0..SAMPLE_SIZE)
.map(|_| rng.gen())
.filter(|n| filter.contains(n))
.count();
let fp_rate: f64 = (false_positives * 100) as f64 / SAMPLE_SIZE as f64;
assert!(
fp_rate < 0.0000000000000001,
"False positive rate is {}",
fp_rate
);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(
expected = "Binary Fuse filters must be constructed from a collection containing all distinct keys."
)]
fn test_debug_assert_duplicates() {
let _ = BinaryFuse32::try_from(vec![1, 2, 1]);
}
}