use gnitz_wire::{read_u32_le, read_u64_le};
use xorf::{BinaryFuse8, BinaryFuse8Ref, Descriptor, DmaSerializable, Filter, FilterRef};
pub(crate) fn build(mut keys: Vec<u64>) -> Option<BinaryFuse8> {
keys.sort_unstable();
keys.dedup();
match BinaryFuse8::try_from(keys.as_slice()) {
Ok(f) => Some(f),
Err(e) => {
gnitz_warn!("shard PK filter construction failed over {} keys: {e}", keys.len());
None
}
}
}
pub(crate) fn serialize(filter: &BinaryFuse8) -> Vec<u8> {
let mut buf = vec![0u8; Descriptor::DMA_LEN + filter.fingerprints.len()];
filter.dma_copy_descriptor_to(&mut buf[..Descriptor::DMA_LEN]);
buf[Descriptor::DMA_LEN..].copy_from_slice(filter.dma_fingerprints());
buf
}
fn validate(d: &Descriptor, fingerprints_len: usize) -> Option<()> {
let sl = d.segment_length as u64;
let scl = d.segment_count_length as u64;
(sl.is_power_of_two()
&& d.segment_length_mask as u64 == sl - 1
&& scl != 0
&& scl.is_multiple_of(sl)
&& fingerprints_len as u64 == scl + 2 * sl
&& fingerprints_len < u32::MAX as usize)
.then_some(())
}
pub(crate) fn is_valid(region: &[u8]) -> bool {
let Some(d) = region.get(..Descriptor::DMA_LEN) else {
return false;
};
validate(
&Descriptor {
seed: read_u64_le(d, 0),
segment_length: read_u32_le(d, 8),
segment_length_mask: read_u32_le(d, 12),
segment_count_length: read_u32_le(d, 16),
},
region.len() - Descriptor::DMA_LEN,
)
.is_some()
}
pub(crate) fn may_contain(region: &[u8], probe_key: u64) -> bool {
debug_assert!(is_valid(region));
let (descriptor, fingerprints) = region.split_at(Descriptor::DMA_LEN);
BinaryFuse8Ref::from_dma(descriptor, fingerprints).contains(&probe_key)
}
#[cfg(test)]
#[path = "tests/shard_filter.rs"]
mod tests;