use whasher::{GOLDEN_RATIO_64, mix13};
pub const CLUSTER_SLOT_COUNT: usize = 16384;
pub const SLOT_MASK: u16 = (CLUSTER_SLOT_COUNT - 1) as u16;
const MURMUR_PRIME_1: u64 = 0x85EB_CA6B;
const MURMUR_PRIME_2: u64 = 0xC2B2_AE35;
const _: () = assert!(
CLUSTER_SLOT_COUNT.is_power_of_two(),
"SLOT_MASK 单周期位与要求槽位总数为 2 的幂"
);
#[inline(always)]
const fn mix64(namespace: u64, db: u64) -> u64 {
let a = namespace.wrapping_mul(GOLDEN_RATIO_64);
let b = db.wrapping_mul(MURMUR_PRIME_1);
let c = namespace.wrapping_add(db).wrapping_mul(MURMUR_PRIME_2);
mix13(a ^ b ^ c)
}
#[inline(always)]
pub const fn slot_of(namespace: u64, db: u64) -> u16 {
(mix64(namespace, db) & SLOT_MASK as u64) as u16
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
#[test]
fn same_db_never_changes_slot() {
for db in 0..64u64 {
for ns in [0u64, 1, 7, 4096, u64::MAX] {
let s = slot_of(ns, db);
assert_eq!(s, slot_of(ns, db), "同 (ns, db) 必须恒同槽");
assert!((s as usize) < CLUSTER_SLOT_COUNT, "槽位越界: {s}");
}
}
}
#[test]
fn sibling_dbs_land_on_distinct_slots() {
let slots: Vec<u16> = (0..256u64).map(|db| slot_of(1, db)).collect();
let distinct: HashSet<u16> = slots.iter().copied().collect();
assert!(
distinct.len() >= 240,
"同 ns 连续 256 库应高概率离散,实得 {} 个不同槽位",
distinct.len()
);
}
#[test]
fn consecutive_dbs_distribute_uniformly_over_slots() {
let mut buckets = [0usize; 16];
for db in 0..4096u64 {
buckets[slot_of(0, db) as usize / (CLUSTER_SLOT_COUNT / 16)] += 1;
}
for (i, n) in buckets.iter().enumerate() {
assert!(
(160..=400).contains(n),
"槽位桶 {i} 计数 {n} 偏离均匀期望 256 过远: {buckets:?}"
);
}
let adjacency = (1..4096u64)
.filter(|&db| slot_of(0, db).wrapping_sub(slot_of(0, db - 1)).abs_diff(1) == 0)
.count();
assert!(
adjacency <= 8,
"相邻 db 槽位相邻(差 1)出现 {adjacency} 次,雪崩效应不足"
);
}
#[test]
fn namespaces_are_domain_separated() {
let slots: Vec<u16> = (0..256u64).map(|ns| slot_of(ns, 3)).collect();
let distinct: HashSet<u16> = slots.iter().copied().collect();
assert!(
distinct.len() >= 240,
"同 db 连续 256 ns 应高概率离散,实得 {} 个不同槽位",
distinct.len()
);
}
}