use std::sync::atomic::Ordering;
use crate::{
Error, Result,
chain::{ChainStep, ChainWalker},
entry::HashBucketEntry,
table::HashIndex,
};
pub const CHUNK_BITS: usize = 14;
pub const CHUNK_SIZE: usize = 1 << CHUNK_BITS;
pub const SPLIT_UNSTARTED: i64 = 0;
pub const SPLIT_IN_PROGRESS: i64 = 1;
pub const SPLIT_COMPLETED: i64 = 2;
#[inline(always)]
pub const fn chunk_count(old_index_size: usize) -> usize {
let count = old_index_size / CHUNK_SIZE;
if count == 0 { 1 } else { count }
}
#[inline(always)]
pub const fn chunk_offset_for_hash(hash: u64, old_mask: usize) -> usize {
((hash as usize) & old_mask) >> CHUNK_BITS
}
pub fn split_single_bucket<F, T>(
old_index: &HashIndex,
new_index: &HashIndex,
bucket_idx: usize,
mut record_locator: F,
mut trace_back: T,
) -> Result<()>
where
F: FnMut(u64) -> Option<(u64, u64)>,
T: FnMut(u64, usize) -> Option<u64>,
{
let old_size = old_index.size;
let high_bit_pos = old_size.trailing_zeros();
let left_bucket_idx = bucket_idx & old_index.mask;
let right_bucket_idx = left_bucket_idx + old_size;
let mut walker = ChainWalker::new(old_index.get_bucket(left_bucket_idx));
loop {
for slot in walker.curr.entries.iter().take(crate::DATA_ENTRIES) {
let raw = slot.load(Ordering::Acquire);
if raw == 0 {
continue;
}
let entry = HashBucketEntry::from_raw(raw);
if !entry.is_valid() {
continue;
}
let addr = entry.address();
if addr == 0 {
continue;
}
let tag = entry.tag();
if let Some((hash, prev_addr)) = record_locator(addr) {
let bit = ((hash >> high_bit_pos) & 1) as usize;
let (primary_idx, other_idx, target_bit) = if bit == 0 {
(left_bucket_idx, right_bucket_idx, 1)
} else {
(right_bucket_idx, left_bucket_idx, 0)
};
new_index.insert_to_bucket(primary_idx, tag, addr)?;
if prev_addr != 0
&& let Some(other_addr) = trace_back(prev_addr, target_bit)
{
new_index.insert_to_bucket(other_idx, tag, other_addr)?;
}
} else {
new_index.insert_to_bucket(left_bucket_idx, tag, addr)?;
new_index.insert_to_bucket(right_bucket_idx, tag, addr)?;
}
}
match walker.advance(&old_index.overflow_pool) {
ChainStep::Next => {}
ChainStep::End => break,
ChainStep::Cycle => return Err(Error::OverflowCycleDetected),
}
}
Ok(())
}
pub fn split_chunk<F, T>(
old_index: &HashIndex,
new_index: &HashIndex,
chunk_idx: usize,
num_chunks: usize,
mut record_locator: F,
mut trace_back: T,
) -> Result<()>
where
F: FnMut(u64) -> Option<(u64, u64)>,
T: FnMut(u64, usize) -> Option<u64>,
{
let chunk_size = old_index.size / num_chunks;
let start_bucket = chunk_size * (chunk_idx & (num_chunks - 1));
let end_bucket = start_bucket + chunk_size;
for b in start_bucket..end_bucket {
split_single_bucket(
old_index,
new_index,
b,
&mut record_locator,
&mut trace_back,
)?;
}
Ok(())
}