use std::hash::{Hash, Hasher};
use enum_dispatch::enum_dispatch;
use crate::array::{Array, MAX_CAPACITY};
use crate::hyperloglog::HyperLogLog;
use crate::representation::RepresentationError::*;
use crate::small::Small;
use crate::CardinalityEstimator;
pub(crate) const REPRESENTATION_MASK: usize = 0x0000_0000_0000_0003;
pub(crate) const REPRESENTATION_SMALL: usize = 0x0000_0000_0000_0000;
pub(crate) const REPRESENTATION_ARRAY: usize = 0x0000_0000_0000_0001;
pub(crate) const REPRESENTATION_HLL: usize = 0x0000_0000_0000_0003;
#[repr(u8)]
#[derive(Debug, PartialEq)]
#[enum_dispatch]
pub(crate) enum Representation<'a, const P: usize, const W: usize> {
Small(Small<P, W>),
Array(Array<'a, P, W>),
Hll(HyperLogLog<'a, P, W>),
}
#[enum_dispatch(Representation<P, W>)]
pub(crate) trait RepresentationTrait {
fn insert_encoded_hash(&mut self, h: u32) -> usize;
fn estimate(&self) -> usize;
fn size_of(&self) -> usize;
unsafe fn drop(&mut self);
fn to_data(&self) -> usize;
fn to_string(&self) -> String {
format!("estimate: {}, size: {}", self.estimate(), self.size_of())
}
}
#[derive(Debug)]
pub enum RepresentationError {
InvalidRepresentation,
SmallRepresentationInvalid,
ArrayRepresentationInvalid,
HllRepresentationInvalid,
}
impl<const P: usize, const W: usize> Representation<'_, P, W> {
#[inline]
pub(crate) fn from_data(data: usize) -> Self {
match data & REPRESENTATION_MASK {
REPRESENTATION_SMALL => Representation::Small(Small::from(data)),
REPRESENTATION_ARRAY => Representation::Array(Array::from(data)),
REPRESENTATION_HLL => Representation::Hll(HyperLogLog::<P, W>::from(data)),
_ => Representation::Small(Small::from(0)),
}
}
pub fn try_from<T, H>(
data: usize,
opt_vec: Option<Vec<u32>>,
) -> Result<CardinalityEstimator<T, H, P, W>, RepresentationError>
where
T: Hash + ?Sized,
H: Hasher + Default,
{
let mut estimator = CardinalityEstimator::<T, H, P, W>::new();
estimator.data = match data & REPRESENTATION_MASK {
REPRESENTATION_SMALL if opt_vec.is_some() => return Err(SmallRepresentationInvalid),
REPRESENTATION_SMALL => Small::<P, W>::from(data).to_data(),
REPRESENTATION_ARRAY => {
let vec = opt_vec.ok_or(ArrayRepresentationInvalid)?;
let len = vec.len();
if len <= 2 || len > MAX_CAPACITY {
return Err(ArrayRepresentationInvalid);
}
Array::<P, W>::from_vec(vec, len).to_data()
}
REPRESENTATION_HLL => {
let vec = opt_vec.ok_or(HllRepresentationInvalid)?;
if vec.len() != HyperLogLog::<P, W>::HLL_SLICE_LEN {
return Err(HllRepresentationInvalid);
}
HyperLogLog::<P, W>::from(vec).to_data()
}
_ => return Err(InvalidRepresentation),
};
Ok(estimator)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_size() {
assert_eq!(std::mem::size_of::<Representation<0, 0>>(), 32);
}
}