use aok::{OK, Void};
use log::info;
use windex::{
CandidateAddresses, Error, HashBucketEntry, HashIndex, PREFETCH_WINDOW, prefetch_read_l1,
};
fn index_insert(index: &HashIndex, key: &[u8], address: u64) -> windex::Result<()> {
let hash = HashIndex::hash_key(key);
let tag = HashBucketEntry::tag_from_hash(hash);
index.insert_to_bucket(index.bucket_index_for_hash(hash), tag, address)
}
#[test]
fn test_candidate_addresses_stack_heap_lifecycle() -> Void {
info!("验证 CandidateAddresses 栈/堆生命周期、retain 回填与排序");
let mut list = CandidateAddresses::new();
assert_eq!(list.len(), 0);
assert!(list.is_empty());
assert_eq!(list.as_slice(), Some([].as_slice()));
assert_eq!(list.first(), None);
for i in 1..=8 {
list.push(i as u64 * 100);
}
assert_eq!(list.len(), 8);
assert_eq!(list.first(), Some(100));
assert_eq!(list[0], 100);
assert_eq!(list[7], 800);
assert!(list.contains(500));
assert!(!list.contains(999));
assert_eq!(
list.as_slice(),
Some(&[100, 200, 300, 400, 500, 600, 700, 800][..])
);
for i in 9..=12 {
list.push(i as u64 * 100);
}
assert_eq!(list.len(), 12);
assert_eq!(list.as_slice(), None, "突破 8 槽位必须进入堆扩展");
assert_eq!(list[11], 1200);
assert!(list.contains(1200));
list.retain(|x| (x / 100) % 2 != 0); assert_eq!(list.len(), 6);
assert_eq!(
list.as_slice(),
Some(&[100, 300, 500, 700, 900, 1100][..]),
"过滤后元素数 <= 8 必须回退回收至栈数组"
);
list.sort_descending();
assert_eq!(list.as_slice(), Some(&[1100, 900, 700, 500, 300, 100][..]));
let mut heap_list = CandidateAddresses::new();
let input = [3u64, 14, 1, 5, 9, 2, 6, 53, 58, 97, 93, 23, 84, 62, 64];
for &x in &input {
heap_list.push(x);
}
assert_eq!(heap_list.len(), 15);
assert_eq!(heap_list.as_slice(), None);
heap_list.sort_descending();
let mut expected = input.to_vec();
expected.sort_unstable_by(|a, b| b.cmp(a));
assert_eq!(heap_list.to_vec(), expected);
let collected: Vec<u64> = list.into_iter().collect();
assert_eq!(collected, [1100, 900, 700, 500, 300, 100]);
OK
}
#[test]
fn test_prefetch_batch_probes() -> Void {
info!("验证单批两级预取内核与逐项单查一致性");
let val = 42u64;
prefetch_read_l1(&val);
let arr = [1u8; 64];
prefetch_read_l1(arr.as_ptr());
let index = HashIndex::new(256)?;
let window = PREFETCH_WINDOW;
let mut keys = Vec::with_capacity(window);
for i in 0..window {
let key = format!("probe_key_{}", i).into_bytes();
index_insert(&index, &key, (i as u64) + 1000)?;
keys.push(key);
}
let mut record_addrs = Vec::with_capacity(window);
let probes = index.prefetch_batch_probes(
&keys,
|_| -> windex::Result<()> { Ok(()) },
|addr| record_addrs.push(addr),
)?;
for (i, probe) in probes[..window].iter().enumerate() {
assert_eq!(probe.hash, HashIndex::hash_key(&keys[i]), "哈希单次算定");
assert_eq!(
probe.first_addr,
index.find_tag_by_hash(probe.hash),
"首地址与 FindTag 同源"
);
assert_eq!(probe.first_addr, Some(1000 + i as u64), "链首即本键地址");
}
assert_eq!(
record_addrs.len(),
window,
"每个命中首地址各触发一次记录物理地址预取"
);
let probes =
index.prefetch_batch_probes(&keys[..3], |_| -> windex::Result<()> { Ok(()) }, |_| {})?;
for (i, probe) in probes.iter().enumerate().take(3) {
assert_eq!(probe.first_addr, Some(1000 + i as u64));
}
assert_eq!(probes[3].hash, 0);
assert_eq!(probes[3].first_addr, None);
let err = index.prefetch_batch_probes(
&keys[..2],
|_| Err(Error::InvalidBucketCount(0)),
|_| unreachable!("第一级即失败,不得进入第二级"),
);
assert!(err.is_err(), "迁移推进错误直接上抛");
OK
}