use proptest::prelude::*;
use size_classes::{build_table, size2class_len, Params, SizeClasses};
mod common;
use common::walk_class_for;
fn scan_class_for(table: &[usize], size: usize, align: usize) -> Option<usize> {
let need = size.max(align);
table
.iter()
.position(|&b| b >= need && b.is_multiple_of(align))
}
const A_MB: usize = 16;
const A_EX: &[usize] = &[256, 512, 1024, 2048, 4096];
const A_N: usize = 32 + A_EX.len();
const A_P: Params = Params::new(A_MB, (5, 4), 32, A_EX, 1 << 20);
const A_T: [usize; A_N] = build_table::<A_N>(A_P);
const A_MAX: usize = A_T[A_N - 1];
const A_L: usize = size2class_len(A_MAX, A_MB);
static A_SC: SizeClasses<A_N, A_L> = SizeClasses::build(A_P);
const B_MB: usize = 8;
const B_EX: &[usize] = &[];
const B_N: usize = 24;
const B_P: Params = Params::new(B_MB, (3, 2), 24, B_EX, 1 << 20);
const B_T: [usize; B_N] = build_table::<B_N>(B_P);
const B_MAX: usize = B_T[B_N - 1];
const B_L: usize = size2class_len(B_MAX, B_MB);
static B_SC: SizeClasses<B_N, B_L> = SizeClasses::build(B_P);
const C_MB: usize = 64;
const C_EX: &[usize] = &[8192, 16384, 65536];
const C_N: usize = 30 + C_EX.len();
const C_P: Params = Params::new(C_MB, (9, 8), 30, C_EX, 1 << 20);
const C_T: [usize; C_N] = build_table::<C_N>(C_P);
const C_MAX: usize = C_T[C_N - 1];
const C_L: usize = size2class_len(C_MAX, C_MB);
static C_SC: SizeClasses<C_N, C_L> = SizeClasses::build(C_P);
fn pow2_up_to(max: usize) -> impl Strategy<Value = usize> {
let hi = (usize::BITS - 1 - max.leading_zeros()) as usize;
(0..=hi).prop_map(|e| 1usize << e)
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(64))]
#[test]
fn scheme_a_jump_eq_walk_and_fidelity(
size in 1usize..=(2 * A_MAX),
align in pow2_up_to(A_MAX),
) {
let got = A_SC.class_for(size, align);
prop_assert_eq!(got, walk_class_for(&A_T, A_SC.size2class(), A_MB, size, align), "jump != walk (A)");
prop_assert_eq!(got, scan_class_for(&A_T, size, align), "jump != scan (A)");
if let Some(idx) = got {
prop_assert!(A_T[idx] >= size.max(align));
prop_assert!(A_T[idx].is_multiple_of(align));
}
}
#[test]
fn scheme_b_jump_eq_walk_and_fidelity(
size in 1usize..=(2 * B_MAX),
align in pow2_up_to(B_MAX),
) {
let got = B_SC.class_for(size, align);
prop_assert_eq!(got, walk_class_for(&B_T, B_SC.size2class(), B_MB, size, align), "jump != walk (B)");
prop_assert_eq!(got, scan_class_for(&B_T, size, align), "jump != scan (B)");
if let Some(idx) = got {
prop_assert!(B_T[idx] >= size.max(align));
prop_assert!(B_T[idx].is_multiple_of(align));
}
}
#[test]
fn scheme_c_jump_eq_walk_and_fidelity(
size in 1usize..=(2 * C_MAX),
align in pow2_up_to(C_MAX),
) {
let got = C_SC.class_for(size, align);
prop_assert_eq!(got, walk_class_for(&C_T, C_SC.size2class(), C_MB, size, align), "jump != walk (C)");
prop_assert_eq!(got, scan_class_for(&C_T, size, align), "jump != scan (C)");
if let Some(idx) = got {
prop_assert!(C_T[idx] >= size.max(align));
prop_assert!(C_T[idx].is_multiple_of(align));
}
}
}
#[test]
fn every_scheme_table_is_strictly_increasing_and_min_block_aligned() {
fn check(table: &[usize], min_block: usize) {
assert_eq!(table[0], min_block, "first class must be min_block");
for w in table.windows(2) {
assert!(w[0] < w[1], "not strictly increasing: {w:?}");
}
for &b in table {
assert!(
b.is_multiple_of(min_block),
"class {b} not a multiple of min_block {min_block}"
);
}
}
check(&A_T, A_MB);
check(&B_T, B_MB);
check(&C_T, C_MB);
}