mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-30 06:43:20 +03:00
fd3b22a836
The current code for indexing into bucket arrays is quite tricky and unsafe, partly because it has to keep manually assuring the compiler that a bucket index is always less than 21. By encapsulating that knowledge in a 21-value enum, we can make the code clearer and safer, without giving up performance. Having a dedicated `BucketIndex` type could also help with further cleanups of `VecCache` indexing.
143 lines
4.0 KiB
Rust
143 lines
4.0 KiB
Rust
use super::*;
|
|
|
|
#[test]
|
|
#[should_panic(expected = "bucket index out of range")]
|
|
fn bucket_index_n_buckets() {
|
|
BucketIndex::from_raw(BUCKETS);
|
|
}
|
|
|
|
#[test]
|
|
fn bucket_index_round_trip() {
|
|
for i in 0..BUCKETS {
|
|
assert_eq!(BucketIndex::from_raw(i).to_usize(), i);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn bucket_index_iter_all_len() {
|
|
let len = BucketIndex::iter_all().len();
|
|
assert_eq!(len, BUCKETS);
|
|
|
|
let len = BucketIndex::iter_all().collect::<Vec<_>>().len();
|
|
assert_eq!(len, BUCKETS);
|
|
|
|
let len = BucketIndex::enumerate_buckets(&[(); BUCKETS]).len();
|
|
assert_eq!(len, BUCKETS);
|
|
}
|
|
|
|
#[test]
|
|
fn bucket_index_capacity() {
|
|
// Check that the combined capacity of all buckets is 2^32 slots.
|
|
// That's 1 larger than `u32::MAX`, so store the total as a `u64`.
|
|
let mut total = 0u64;
|
|
for i in BucketIndex::iter_all() {
|
|
total += u64::try_from(i.capacity()).unwrap();
|
|
}
|
|
assert_eq!(total, 1 << 32);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(miri))]
|
|
fn vec_cache_empty_exhaustive() {
|
|
let cache: VecCache<u32, u32, u32> = VecCache::default();
|
|
for key in 0..=u32::MAX {
|
|
assert!(cache.lookup(&key).is_none());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn vec_cache_insert_and_check() {
|
|
let cache: VecCache<u32, u32, u32> = VecCache::default();
|
|
cache.complete(0, 1, 2);
|
|
assert_eq!(cache.lookup(&0), Some((1, 2)));
|
|
}
|
|
|
|
#[test]
|
|
fn sparse_inserts() {
|
|
let cache: VecCache<u32, u8, u32> = VecCache::default();
|
|
let end = if cfg!(target_pointer_width = "64") && cfg!(target_os = "linux") {
|
|
// For paged memory, 64-bit systems we should be able to sparsely allocate all of the pages
|
|
// needed for these inserts cheaply (without needing to actually have gigabytes of resident
|
|
// memory).
|
|
31
|
|
} else {
|
|
// Otherwise, still run the test but scaled back:
|
|
//
|
|
// Each slot is 5 bytes, so 2^25 entries (on non-virtual memory systems, like e.g. Windows) will
|
|
// mean 160 megabytes of allocated memory. Going beyond that is probably not reasonable for
|
|
// tests.
|
|
25
|
|
};
|
|
for shift in 0..end {
|
|
let key = 1u32 << shift;
|
|
cache.complete(key, shift, key);
|
|
assert_eq!(cache.lookup(&key), Some((shift, key)));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn concurrent_stress_check() {
|
|
let cache: VecCache<u32, u32, u32> = VecCache::default();
|
|
std::thread::scope(|s| {
|
|
for idx in 0..100 {
|
|
let cache = &cache;
|
|
s.spawn(move || {
|
|
cache.complete(idx, idx, idx);
|
|
});
|
|
}
|
|
});
|
|
|
|
for idx in 0..100 {
|
|
assert_eq!(cache.lookup(&idx), Some((idx, idx)));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn slot_entries_table() {
|
|
assert_eq!(
|
|
ENTRIES_BY_BUCKET,
|
|
[
|
|
4096, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152,
|
|
4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912,
|
|
1073741824, 2147483648
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn bucket_entries_matches() {
|
|
for i in BucketIndex::iter_all() {
|
|
assert_eq!(i.capacity(), ENTRIES_BY_BUCKET[i]);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(miri))]
|
|
fn slot_index_exhaustive() {
|
|
let mut buckets = [0u32; 21];
|
|
for idx in 0..=u32::MAX {
|
|
buckets[SlotIndex::from_index(idx).bucket_idx] += 1;
|
|
}
|
|
let slot_idx = SlotIndex::from_index(0);
|
|
assert_eq!(slot_idx.index_in_bucket, 0);
|
|
assert_eq!(slot_idx.bucket_idx, BucketIndex::Bucket00);
|
|
let mut prev = slot_idx;
|
|
for idx in 1..=u32::MAX {
|
|
let slot_idx = SlotIndex::from_index(idx);
|
|
|
|
// SAFETY: Ensure indices don't go out of bounds of buckets.
|
|
assert!(slot_idx.index_in_bucket < slot_idx.bucket_idx.capacity());
|
|
|
|
if prev.bucket_idx == slot_idx.bucket_idx {
|
|
assert_eq!(prev.index_in_bucket + 1, slot_idx.index_in_bucket);
|
|
} else {
|
|
assert_eq!(slot_idx.index_in_bucket, 0);
|
|
}
|
|
|
|
assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.bucket_idx.capacity() as u32);
|
|
assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.bucket_idx.capacity(), "{idx}",);
|
|
|
|
prev = slot_idx;
|
|
}
|
|
}
|