use crate::{xor_contains_impl, xor_from_impl, Filter};
use alloc::{boxed::Box, vec::Vec};
#[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))]
pub struct Xor8 {
pub seed: u64,
pub block_length: usize,
pub fingerprints: Box<[u8]>,
}
impl Filter<u64> for Xor8 {
fn contains(&self, key: &u64) -> bool {
xor_contains_impl!(*key, self, fingerprint u8)
}
fn len(&self) -> usize {
self.fingerprints.len()
}
}
impl Xor8 {
pub fn from_iterator<T>(keys: T) -> Self
where
T: ExactSizeIterator<Item = u64> + Clone,
{
xor_from_impl!(keys fingerprint u8)
}
}
impl From<&[u64]> for Xor8 {
fn from(keys: &[u64]) -> Self {
Self::from_iterator(keys.iter().copied())
}
}
impl From<&Vec<u64>> for Xor8 {
fn from(v: &Vec<u64>) -> Self {
Self::from_iterator(v.iter().copied())
}
}
impl From<Vec<u64>> for Xor8 {
fn from(v: Vec<u64>) -> Self {
Self::from_iterator(v.iter().copied())
}
}
#[cfg(test)]
mod test {
use crate::{Filter, Xor8};
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 = Xor8::from(&keys);
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 = Xor8::from(&keys);
let bpe = (filter.len() as f64) * 8.0 / (SAMPLE_SIZE as f64);
assert!(bpe < 10., "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 = Xor8::from(&keys);
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.406, "False positive rate is {}", fp_rate);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(
expected = "Xor filters must be constructed from a collection containing all distinct keys."
)]
fn test_debug_assert_duplicates() {
let _ = Xor8::from(vec![1, 2, 1]);
}
}