use crate::bucket_array::hash::{
Count, Insert, Key, KeyLookup, KeyStorage, Shape, ValueBucketArray, ValueLookup,
};
use crate::bucket_array::mem::BucketArrayMemory;
use std::fmt::Debug;
use std::ops::{BitAnd, BitOr, Shl, Shr};
#[inline(always)]
pub(crate) fn search<const TEMP_N: usize, const TEMP_CAP: usize, A, F, C, K, KS>(
array: &A,
scratchpad: &mut BucketArrayMemory<TEMP_N, TEMP_CAP, C>,
num_bits: usize,
mut predicate: F,
) where
A: Shape<K> + KeyLookup<KS, K>,
F: FnMut(K, CollisionLocation),
C: Count,
K: Key,
KS: KeyStorage<K>,
{
for first_bucket in 0..=(A::NUM_BUCKETS / 2) {
let second_bucket = first_bucket.wrapping_neg() % A::NUM_BUCKETS;
let mut paired_item_hash =
ValueBucketArray::<'_, { TEMP_N }, { TEMP_CAP }, u8, K, C>::new(scratchpad);
for first_item in array.item_range(first_bucket) {
let first_hash_remainder = array.item_stored_key(first_bucket, first_item);
let _ = paired_item_hash.insert(
first_hash_remainder.into_key(),
C::from_item_index(first_item),
);
}
for second_item in array.item_range(second_bucket) {
let second_hash = array.item_full_key(second_bucket, second_item);
let hash_complement = second_hash.wrapping_neg();
let (_, hash_complement_remainder) = array.split_wide_key(hash_complement);
let (bucket_in_paired_hash, _) =
paired_item_hash.split_wide_key(hash_complement_remainder);
for first_item in paired_item_hash
.item_range(bucket_in_paired_hash)
.map(|item| paired_item_hash.item_value(bucket_in_paired_hash, item))
{
let first_item: usize = first_item.into();
let first_hash = array.item_full_key(first_bucket, first_item);
let sum = first_hash.wrapping_add(&second_hash);
if sum.low_bits_are_zero(num_bits) {
predicate(
sum >> num_bits,
CollisionLocation {
first_bucket,
first_item,
second_item,
},
);
}
}
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct CollisionLocation {
pub(crate) first_bucket: usize,
pub(crate) first_item: usize,
pub(crate) second_item: usize,
}
impl CollisionLocation {
#[inline(always)]
pub(crate) fn pair<A: ValueLookup<T> + Shape<K>, K: Key, T: Copy>(&self, array: &A) -> [T; 2] {
[
array.item_value(self.first_bucket, self.first_item),
array.item_value(
self.first_bucket.wrapping_neg() % A::NUM_BUCKETS,
self.second_item,
),
]
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct PackedCollision<
T: Copy
+ TryFrom<usize>
+ TryInto<usize>
+ Shl<usize, Output = T>
+ Shr<usize, Output = T>
+ BitAnd<T, Output = T>
+ BitOr<T, Output = T>,
const BUCKET_BITS: usize,
const ITEM_BITS: usize,
>(T);
impl<
T: Copy
+ TryFrom<usize>
+ TryInto<usize>
+ Shl<usize, Output = T>
+ Shr<usize, Output = T>
+ BitAnd<T, Output = T>
+ BitOr<T, Output = T>,
const BUCKET_BITS: usize,
const ITEM_BITS: usize,
> PackedCollision<T, BUCKET_BITS, ITEM_BITS>
{
#[inline(always)]
pub(crate) fn new(inner: T) -> Self {
Self(inner)
}
#[inline(always)]
pub(crate) fn into_inner(self) -> T {
self.0
}
#[inline(always)]
fn from_usize(i: usize) -> T {
i.try_into()
.map_err(|_| ())
.expect("masked collision field always fits into bitfield type")
}
#[inline(always)]
fn to_usize(i: T) -> usize {
i.try_into()
.map_err(|_| ())
.expect("masked collision field always fits in usize")
}
#[inline(always)]
pub(crate) fn pack(loc: &CollisionLocation) -> Self {
assert!(loc.first_bucket < (1 << BUCKET_BITS));
assert!(loc.first_item < (1 << ITEM_BITS));
assert!(loc.second_item < (1 << ITEM_BITS));
let first_bucket: T = Self::from_usize(loc.first_bucket) << (ITEM_BITS * 2);
let first_item: T = Self::from_usize(loc.first_item) << ITEM_BITS;
let second_item: T = Self::from_usize(loc.second_item);
Self(first_bucket | first_item | second_item)
}
#[inline(always)]
pub(crate) fn unpack(&self) -> CollisionLocation {
let bucket_mask = Self::from_usize((1_usize << BUCKET_BITS) - 1);
let item_mask = Self::from_usize((1_usize << ITEM_BITS) - 1);
CollisionLocation {
first_bucket: Self::to_usize((self.0 >> (ITEM_BITS * 2)) & bucket_mask),
first_item: Self::to_usize((self.0 >> ITEM_BITS) & item_mask),
second_item: Self::to_usize(self.0 & item_mask),
}
}
}