use super::*;
#[test]
fn shrink_at_is_exactly_the_quarter_full_test() {
let quarter_full =
|count: usize, capacity: usize| 4 * count < capacity && capacity > MIN_CAPACITY;
for capacity in 0..=2_048usize {
for count in 0..=capacity {
assert_eq!(
count < shrink_at(capacity),
quarter_full(count, capacity),
"{count} in {capacity}"
);
}
}
for shift in 12..usize::BITS - 2 {
let capacity = 1usize << shift;
for count in [
0,
capacity / 4 - 1,
capacity / 4,
capacity / 4 + 1,
capacity,
] {
assert_eq!(
count < shrink_at(capacity),
quarter_full(count, capacity),
"{count} in {capacity}"
);
}
}
}
fn shrink_word(map: &HopscotchMap<u64, u64, Identity>) -> usize {
let guard = pin();
let table = unsafe { &*map.table.load(Ordering::Acquire, &guard).as_raw() };
table.shrink_at.load(Ordering::Relaxed)
}
#[test]
fn the_shrink_word_follows_the_table() {
let map = HopscotchMap::with_capacity_and_hasher(256, Identity);
assert_eq!(shrink_word(&map), 64, "a new table starts at its quarter");
for i in 0..20u64 {
map.insert(i * 256, i);
map.insert(i * 256 + 128, i);
}
map.remove(&0);
assert_eq!(map.capacity(), 256, "the shrink did not fit");
assert_eq!(shrink_word(&map), 19, "half of the 39 it failed at");
map.clear();
assert_eq!(shrink_word(&map), 64, "a clear sets the word back");
for i in 0..200u64 {
map.insert(i, i);
}
assert_eq!(map.capacity(), 512, "grown once");
assert_eq!(
shrink_word(&map),
128,
"a published table starts at its own quarter"
);
let least = HopscotchMap::with_capacity_and_hasher(MIN_CAPACITY, Identity);
assert_eq!(shrink_word(&least), 0);
}
#[test]
fn a_failed_shrink_does_not_recopy_the_table_on_every_remove() {
use alloc::sync::Arc;
use core::sync::atomic::AtomicUsize;
struct Counted(Arc<AtomicUsize>);
impl Clone for Counted {
fn clone(&self) -> Self {
self.0.fetch_add(1, Ordering::Relaxed);
Self(Arc::clone(&self.0))
}
}
let clones = Arc::new(AtomicUsize::new(0));
let map = HopscotchMap::with_capacity_and_hasher(256, Identity);
for i in 0..20u64 {
map.insert(i * 256, Counted(Arc::clone(&clones)));
map.insert(i * 256 + 128, Counted(Arc::clone(&clones)));
}
map.remove(&0);
assert_eq!(map.capacity(), 256);
assert!(clones.load(Ordering::Relaxed) > 1, "first shrink attempted");
clones.store(0, Ordering::Relaxed);
for i in 20..28u64 {
let key = i * 256;
map.insert(key, Counted(Arc::clone(&clones)));
map.remove(&key);
}
assert_eq!(
clones.load(Ordering::Relaxed),
8,
"only removed values cloned"
);
for i in 1..20u64 {
map.remove(&(i * 256));
}
map.remove(&128);
map.remove(&(256 + 128));
assert!(
map.capacity() < 256,
"a smaller population retries the shrink"
);
assert_eq!(map.len(), 18);
for i in 2..20u64 {
assert!(map.contains_key(&(i * 256 + 128)));
}
}
#[test]
fn a_clear_forgets_a_failed_shrink() {
let map = HopscotchMap::with_capacity_and_hasher(256, Identity);
for i in 0..20u64 {
map.insert(i * 256, i);
map.insert(i * 256 + 128, i);
}
map.remove(&0);
assert_eq!(
map.capacity(),
256,
"two homes that fold into one cannot shrink"
);
map.clear();
for i in 0..30u64 {
map.insert(i * 256 + 1, i);
}
map.remove(&1);
assert!(
map.capacity() < 256,
"an underfull population that fits shrinks after a clear"
);
assert_eq!(map.len(), 29);
for i in 1..30u64 {
assert!(map.contains_key(&(i * 256 + 1)));
}
}