use super::Cfg;
use crate::{Config, FullError, GenMap, InsertError, Key, KeyLayout, KeyPiece, Packed, Split};
use core::mem::size_of;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::vec::Vec;
struct Compact;
impl Config for Compact {
type Idx = u32;
type Gen = u8;
type Layout = Packed<u32, 8>;
type Storage<S> = Vec<S>;
}
struct Tiny;
impl Config for Tiny {
type Idx = u16;
type Gen = u8;
type Layout = Packed<u16, 4>;
type Storage<S> = Vec<S>;
}
struct TinyWrap;
impl Config for TinyWrap {
type Idx = u16;
type Gen = u8;
type Layout = Packed<u16, 4>;
type Storage<S> = Vec<S>;
const WRAP_ON_OVERFLOW: bool = true;
}
struct Byte;
impl Config for Byte {
type Idx = u8;
type Gen = u8;
type Layout = Packed<u8, 4>;
type Storage<S> = Vec<S>;
}
struct Huge;
impl Config for Huge {
type Idx = u128;
type Gen = u128;
type Layout = Packed<u128, 64>;
type Storage<S> = Vec<S>;
}
struct Native;
impl Config for Native {
type Idx = usize;
type Gen = u8;
type Layout = Packed<usize, 8>;
type Storage<S> = Vec<S>;
}
fn key<C: Config>(idx: C::Idx, generation: C::Gen) -> Key<C> {
unsafe { Key::from_raw_parts(idx, generation.into_non_zero().unwrap()) }
}
#[test]
fn packed_keys_are_the_size_of_their_field() {
assert_eq!(size_of::<Key<Compact>>(), 4);
assert_eq!(size_of::<Key<Tiny>>(), 2);
assert_eq!(size_of::<Key<Byte>>(), 1);
assert_eq!(size_of::<Key<Huge>>(), 16);
assert_eq!(size_of::<Key<Native>>(), size_of::<usize>());
}
#[test]
fn option_of_a_packed_key_costs_nothing_extra() {
assert_eq!(size_of::<Option<Key<Compact>>>(), 4);
assert_eq!(size_of::<Option<Key<Tiny>>>(), 2);
assert_eq!(size_of::<Option<Key<Byte>>>(), 1);
assert_eq!(size_of::<Option<Key<Huge>>>(), 16);
}
#[test]
fn limits_follow_the_bit_counts() {
assert_eq!(
<Packed<u32, 8> as KeyLayout<u32, u8>>::max_idx(),
(1 << 24) - 1
);
assert_eq!(
<Packed<u32, 8> as KeyLayout<u32, u8>>::max_generation(),
u8::MAX
);
assert_eq!(<Packed<u16, 4> as KeyLayout<u16, u8>>::max_idx(), 4095);
assert_eq!(<Packed<u16, 4> as KeyLayout<u16, u8>>::max_generation(), 15);
assert_eq!(<Packed<u8, 4> as KeyLayout<u8, u8>>::max_idx(), 15);
assert_eq!(
<Packed<u128, 64> as KeyLayout<u128, u128>>::max_idx(),
u64::MAX as u128
);
assert_eq!(
<Packed<u128, 64> as KeyLayout<u128, u128>>::max_generation(),
u64::MAX as u128
);
assert_eq!(
<Packed<usize, 8> as KeyLayout<usize, u8>>::max_idx(),
(1usize << (usize::BITS - 8)) - 1
);
}
#[test]
fn split_limits_are_the_integer_limits() {
assert_eq!(<Split as KeyLayout<u8, u16>>::max_idx(), u8::MAX);
assert_eq!(<Split as KeyLayout<u8, u16>>::max_generation(), u16::MAX);
assert_eq!(<Split as KeyLayout<u128, usize>>::max_idx(), u128::MAX);
assert_eq!(
<Split as KeyLayout<u128, usize>>::max_generation(),
usize::MAX
);
}
#[test]
fn every_layout_has_an_odd_largest_generation() {
assert_eq!(
<Packed<u32, 8> as KeyLayout<u32, u8>>::max_generation() % 2,
1
);
assert_eq!(
<Packed<u16, 4> as KeyLayout<u16, u8>>::max_generation() % 2,
1
);
assert_eq!(<Packed<u16, 1> as KeyLayout<u16, u8>>::max_generation(), 1);
assert_eq!(<Split as KeyLayout<u8, u8>>::max_generation() % 2, 1);
}
#[test]
fn parts_round_trip_through_a_packed_key() {
let max_idx = <Packed<u32, 8> as KeyLayout<u32, u8>>::max_idx();
for (idx, generation) in [
(0, 1),
(1, 1),
(7, 255),
(max_idx, 1),
(max_idx, 255),
(12345, 99),
] {
let k = key::<Compact>(idx, generation);
assert_eq!(k.idx(), idx);
assert_eq!(k.generation(), generation);
assert_eq!(k.non_zero_generation().get(), generation);
}
let k = key::<Huge>(u64::MAX as u128, u64::MAX as u128);
assert_eq!(k.idx(), u64::MAX as u128);
assert_eq!(k.generation(), u64::MAX as u128);
let k = key::<Byte>(15, 15);
assert_eq!((k.idx(), k.generation()), (15, 15));
}
#[test]
fn the_parts_do_not_bleed_into_each_other() {
let a = key::<Compact>(1, 1);
let b = key::<Compact>(0, 255);
let c = key::<Compact>(1, 255);
assert_eq!(a.idx(), 1);
assert_eq!(b.idx(), 0);
assert_eq!(b.generation(), 255);
assert_eq!(c.idx(), 1);
assert_eq!(c.generation(), 255);
assert_ne!(a, b);
assert_ne!(a, c);
assert_ne!(b, c);
}
#[test]
fn packed_keys_work_with_the_map() {
let mut map = GenMap::<&str, Compact>::new_with_config();
let a = map.insert("a");
let b = map.insert("b");
assert_eq!((a.idx(), a.generation()), (0, 1));
assert_eq!((b.idx(), b.generation()), (1, 1));
assert_eq!(map[a], "a");
assert_eq!(map.get(b), Some(&"b"));
assert_eq!(map.remove(a), Some("a"));
assert!(map.get(a).is_none());
assert!(!map.contains_key(a));
let c = map.insert("c");
assert_eq!((c.idx(), c.generation()), (0, 3));
assert_ne!(c, a);
assert!(map.get(a).is_none());
assert_eq!(map[c], "c");
assert_eq!(map.len(), 2);
let rebuilt = key::<Compact>(c.idx(), c.generation());
assert_eq!(rebuilt, c);
assert_eq!(map.get(rebuilt), Some(&"c"));
}
#[test]
fn packed_keys_are_ordered_by_index_then_generation() {
let a = key::<Compact>(1, 3);
let b = key::<Compact>(1, 5);
let c = key::<Compact>(2, 1);
assert!(a < b);
assert!(b < c);
let mut sorted = [c, b, a];
sorted.sort();
assert_eq!(sorted, [a, b, c]);
}
#[test]
fn packed_keys_hash_and_compare_by_their_parts() {
fn hash_of<C: Config>(key: Key<C>) -> u64 {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
hasher.finish()
}
let a = key::<Compact>(4, 7);
let same = key::<Compact>(4, 7);
let other = key::<Compact>(4, 9);
assert_eq!(a, same);
assert_eq!(hash_of(a), hash_of(same));
assert_ne!(a, other);
}
#[test]
fn debug_prints_both_parts_of_a_packed_key() {
let k = key::<Compact>(4, 7);
let text = std::format!("{k:?}");
assert!(text.contains("idx: 4"));
assert!(text.contains("generation: 7"));
}
fn use_up_one_slot<C: Config<Idx = u16, Gen = u8>>(map: &mut GenMap<u32, C>) -> Vec<Key<C>> {
let mut keys = Vec::new();
for i in 0..8u32 {
let key = map.insert(i);
assert_eq!(key.idx(), 0);
assert_eq!(key.generation(), (2 * i + 1) as u8);
keys.push(key);
if i < 7 {
assert_eq!(map.remove(key), Some(i));
}
}
keys
}
#[test]
fn a_slot_retires_when_the_generation_field_is_full() {
let mut map = GenMap::<u32, Tiny>::new_with_config();
let keys = use_up_one_slot(&mut map);
let last = keys[7];
assert_eq!(last.generation(), 15);
assert_eq!(map.remove(last), Some(7));
assert!(map.get(last).is_none());
assert_eq!(map.len(), 0);
assert_eq!(map.slots_len(), 1);
let fresh = map.insert(100);
assert_eq!((fresh.idx(), fresh.generation()), (1, 1));
assert_eq!(map.slots_len(), 2);
for key in keys {
assert!(map.get(key).is_none());
}
}
#[test]
fn a_slot_wraps_when_the_generation_field_is_full_and_the_config_says_so() {
let mut map = GenMap::<u32, TinyWrap>::new_with_config();
let keys = use_up_one_slot(&mut map);
assert_eq!(map.remove(keys[7]), Some(7));
let again = map.insert(100);
assert_eq!((again.idx(), again.generation()), (0, 1));
assert_eq!(map.slots_len(), 1);
assert_eq!(again, keys[0]);
assert_eq!(map.get(keys[0]), Some(&100));
assert!(map.get(keys[7]).is_none());
}
#[test]
fn detach_refuses_a_slot_at_the_generation_limit() {
let mut map = GenMap::<u32, Tiny>::new_with_config();
let keys = use_up_one_slot(&mut map);
let last = keys[7];
assert!(map.detach(last).is_none());
assert_eq!(map.get(last), Some(&7));
assert_eq!(map.len(), 1);
assert_eq!(map.remove(last), Some(7));
let fresh = map.insert(8);
assert_eq!(fresh.generation(), 1);
assert_eq!(map.detach(fresh), Some(8));
map.reattach(fresh, 9);
assert_eq!(map[fresh], 9);
}
#[test]
fn the_index_field_limits_the_slot_count() {
let mut map = GenMap::<u8, Byte>::new_with_config();
let keys: Vec<_> = (0..16u8).map(|i| map.insert(i)).collect();
for (i, key) in keys.iter().enumerate() {
assert_eq!(key.idx() as usize, i);
assert_eq!(map[*key], i as u8);
}
assert_eq!(map.slots_len(), 16);
assert!(matches!(
map.try_insert(16),
Err(InsertError::IndexExhausted(16))
));
assert!(matches!(map.vacant_entry(), Err(FullError::IndexExhausted)));
assert_eq!(map.len(), 16);
assert_eq!(map.slots_len(), 16);
assert_eq!(map.remove(keys[5]), Some(5));
let again = map.insert(50);
assert_eq!((again.idx(), again.generation()), (5, 3));
assert_eq!(map.len(), 16);
}
#[test]
#[should_panic(expected = "can not address more than 16 slots")]
fn insert_panics_when_the_index_field_is_full() {
let mut map = GenMap::<u8, Byte>::new_with_config();
for i in 0..=16u8 {
map.insert(i);
}
}
#[test]
fn parts_wider_than_their_fields_work() {
let mut huge = GenMap::<i32, Huge>::new_with_config();
let k = huge.insert(1);
assert_eq!((k.idx(), k.generation()), (0, 1));
assert_eq!(huge.remove(k), Some(1));
let k = huge.insert(2);
assert_eq!((k.idx(), k.generation()), (0, 3));
assert_eq!(huge[k], 2);
let mut native = GenMap::<i32, Native>::new_with_config();
let k = native.insert(3);
assert_eq!((k.idx(), k.generation()), (0, 1));
assert_eq!(native[k], 3);
}
#[test]
fn a_packed_key_for_a_missing_slot_matches_nothing() {
let mut map = GenMap::<i32, Compact>::new_with_config();
let k = map.insert(1);
let missing = key::<Compact>(1000, 1);
let stale = key::<Compact>(k.idx(), 3);
assert!(map.get(missing).is_none());
assert!(map.get(stale).is_none());
assert!(map.remove(missing).is_none());
assert!(map.remove(stale).is_none());
assert_eq!(map[k], 1);
}
#[test]
fn split_and_packed_hand_out_the_same_parts() {
let mut split = GenMap::<u32, Cfg<u32, u32>>::new_with_config();
let mut packed = GenMap::<u32, Compact>::new_with_config();
let mut split_keys = Vec::new();
let mut packed_keys = Vec::new();
for i in 0..20 {
split_keys.push(split.insert(i));
packed_keys.push(packed.insert(i));
if i % 3 == 0 {
let at = (i as usize) / 2;
assert_eq!(split.remove(split_keys[at]), packed.remove(packed_keys[at]));
}
}
for (s, p) in split_keys.iter().zip(&packed_keys) {
assert_eq!(s.idx(), p.idx());
assert_eq!(s.generation(), u32::from(p.generation()));
assert_eq!(split.get(*s), packed.get(*p));
}
}