use std::fmt::{Debug, Formatter};
use std::mem::{size_of, size_of_val};
use std::ops::Deref;
use std::slice;
use crate::hyperloglog::HyperLogLog;
use crate::representation::{RepresentationTrait, REPRESENTATION_ARRAY};
pub(crate) const MAX_CAPACITY: usize = 128;
const LEN_OFFSET: usize = 56;
const PTR_MASK: usize = ((1 << LEN_OFFSET) - 1) & !3;
pub(crate) struct Array<'a, const P: usize, const W: usize> {
len: usize,
arr: &'a mut [u32],
}
impl<'a, const P: usize, const W: usize> Array<'a, P, W> {
#[inline]
pub(crate) fn insert(&mut self, h: u32) -> bool {
let cap = self.arr.len();
let found = if cap == 4 {
contains_fixed_vectorized::<4>(self.arr.try_into().unwrap(), h)
} else if cap == 8 {
contains_fixed_vectorized::<8>(self.arr.try_into().unwrap(), h)
} else {
let rlen = 16 * self.len.div_ceil(16);
contains_vectorized::<16>(unsafe { self.arr.get_unchecked(..rlen) }, h)
};
if found {
return true;
}
if self.len < cap {
self.arr[self.len] = h;
self.len += 1;
return true;
}
if cap < MAX_CAPACITY {
let new_arr = Self::from_vec(vec![0; cap * 2], self.len + 1);
new_arr.arr[..self.len].copy_from_slice(self.arr);
new_arr.arr[self.len] = h;
unsafe { self.drop() };
*self = new_arr;
return true;
};
false
}
#[inline]
pub(crate) fn from_vec(mut arr: Vec<u32>, len: usize) -> Array<'a, P, W> {
let cap = len.next_power_of_two().max(arr.len());
arr.resize(cap, 0);
let ptr = arr.as_mut_ptr();
std::mem::forget(arr);
let arr = unsafe { slice::from_raw_parts_mut(ptr, cap) };
Self { len, arr }
}
}
impl<const P: usize, const W: usize> RepresentationTrait for Array<'_, P, W> {
#[inline]
fn insert_encoded_hash(&mut self, h: u32) -> usize {
if self.insert(h) {
self.to_data()
} else {
let mut hll = HyperLogLog::<P, W>::new(self);
unsafe { self.drop() };
hll.insert_encoded_hash(h)
}
}
#[inline]
fn estimate(&self) -> usize {
self.len
}
#[inline]
fn size_of(&self) -> usize {
size_of::<usize>() + size_of_val(self.arr)
}
#[inline]
unsafe fn drop(&mut self) {
drop(Box::from_raw(self.arr));
}
#[inline]
fn to_data(&self) -> usize {
(self.len << LEN_OFFSET) | (PTR_MASK & self.arr.as_ptr() as usize) | REPRESENTATION_ARRAY
}
}
impl<const P: usize, const W: usize> Debug for Array<'_, P, W> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_string())
}
}
impl<const P: usize, const W: usize> PartialEq for Array<'_, P, W> {
fn eq(&self, other: &Self) -> bool {
self.deref() == other.deref()
}
}
impl<const P: usize, const W: usize> From<usize> for Array<'_, P, W> {
#[inline]
fn from(data: usize) -> Self {
let ptr = (data & PTR_MASK) as *mut u32;
let len = data >> LEN_OFFSET;
let cap = len.next_power_of_two();
let arr = unsafe { slice::from_raw_parts_mut(ptr, cap) };
Self { len, arr }
}
}
impl<const P: usize, const W: usize> Deref for Array<'_, P, W> {
type Target = [u32];
fn deref(&self) -> &Self::Target {
&self.arr[..self.len]
}
}
#[inline]
fn contains_vectorized<const N: usize>(a: &[u32], v: u32) -> bool {
debug_assert_eq!(a.len() % N, 0);
a.chunks_exact(N)
.any(|chunk| contains_fixed_vectorized::<N>(chunk.try_into().unwrap(), v))
}
#[inline]
fn contains_fixed_vectorized<const N: usize>(a: [u32; N], v: u32) -> bool {
let mut res = false;
for x in a {
res |= x == v
}
res
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn array_size() {
assert_eq!(std::mem::size_of::<Array<0, 0>>(), 24);
}
#[test]
fn test_from_vec_capacity_mismatch() {
let len = 100;
let vec: Vec<u32> = vec![0; len];
let arr: Array<14, 6> = Array::from_vec(vec, len);
let data = arr.to_data();
let mut restored: Array<14, 6> = Array::from(data);
for i in 0..28 {
assert!(restored.insert(i));
}
unsafe { restored.drop() };
}
}