use crate::util::{LARGE_THRESHOLD, MIN_ALIGN};
pub const NUM_SIZE_CLASSES: usize = 36;
pub static SIZE_CLASSES: [usize; NUM_SIZE_CLASSES] = {
let mut table = [0usize; NUM_SIZE_CLASSES];
let mut idx = 0;
let mut step = 16;
let mut base = 0;
let mut i = 0;
while i < 4 {
base += step;
table[idx] = base;
idx += 1;
i += 1;
}
base = 64;
while idx < NUM_SIZE_CLASSES {
step = base / 4;
let mut j = 0;
while j < 4 && idx < NUM_SIZE_CLASSES {
base += step;
table[idx] = base;
idx += 1;
j += 1;
}
}
table
};
const LOOKUP_TABLE_SIZE: usize = LARGE_THRESHOLD / MIN_ALIGN + 1;
static LOOKUP_TABLE: [u8; LOOKUP_TABLE_SIZE] = {
let mut table = [0u8; LOOKUP_TABLE_SIZE];
let mut i = 0;
while i < LOOKUP_TABLE_SIZE {
let size = (i + 1) * MIN_ALIGN; let mut lo = 0usize;
let mut hi = NUM_SIZE_CLASSES;
while lo < hi {
let mid = lo + (hi - lo) / 2;
if SIZE_CLASSES[mid] < size {
lo = mid + 1;
} else {
hi = mid;
}
}
table[i] = lo as u8;
i += 1;
}
table
};
#[inline(always)]
pub fn size_class_index(size: usize) -> Option<usize> {
if size > LARGE_THRESHOLD {
return None;
}
let clamped = if size >= MIN_ALIGN { size } else { MIN_ALIGN };
let bucket = (clamped - 1) >> 4; Some(LOOKUP_TABLE[bucket] as usize)
}
#[inline(always)]
pub fn slot_size(class_index: usize) -> usize {
SIZE_CLASSES[class_index]
}
pub static SLOT_MAGIC: [u64; NUM_SIZE_CLASSES] = {
let mut table = [0u64; NUM_SIZE_CLASSES];
let mut i = 0;
while i < NUM_SIZE_CLASSES {
let d = SIZE_CLASSES[i] as u128;
let two_64 = 1u128 << 64;
table[i] = two_64.div_ceil(d) as u64;
i += 1;
}
table
};
static SLOT_SHIFT: [u8; NUM_SIZE_CLASSES] = {
let mut table = [0xFFu8; NUM_SIZE_CLASSES];
let mut i = 0;
while i < NUM_SIZE_CLASSES {
let sz = SIZE_CLASSES[i];
if sz & (sz - 1) == 0 {
table[i] = sz.trailing_zeros() as u8;
}
i += 1;
}
table
};
#[inline(always)]
pub fn fast_div_slot(offset: usize, class_index: usize) -> usize {
let shift = SLOT_SHIFT[class_index];
if shift != 0xFF {
offset >> shift
} else {
let magic = SLOT_MAGIC[class_index];
((offset as u128 * magic as u128) >> 64) as usize
}
}
static SLOTS_PER_SLAB: [usize; NUM_SIZE_CLASSES] = {
let mut table = [0usize; NUM_SIZE_CLASSES];
let target = 65536usize; let mut i = 0;
while i < NUM_SIZE_CLASSES {
let sz = SIZE_CLASSES[i];
let count = target / sz;
table[i] = if count < 16 { 16 } else { count };
i += 1;
}
table
};
#[inline(always)]
pub fn slots_per_slab(class_index: usize) -> usize {
SLOTS_PER_SLAB[class_index]
}
#[allow(dead_code)]
pub fn slab_data_size(class_index: usize) -> usize {
slots_per_slab(class_index) * slot_size(class_index)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_classes_are_sorted() {
for i in 1..NUM_SIZE_CLASSES {
assert!(
SIZE_CLASSES[i] > SIZE_CLASSES[i - 1],
"class {} ({}) <= class {} ({})",
i,
SIZE_CLASSES[i],
i - 1,
SIZE_CLASSES[i - 1]
);
}
}
#[test]
fn first_class_is_min_align() {
assert_eq!(SIZE_CLASSES[0], MIN_ALIGN);
}
#[test]
fn last_class_is_large_threshold() {
assert_eq!(SIZE_CLASSES[NUM_SIZE_CLASSES - 1], LARGE_THRESHOLD);
}
#[test]
fn all_classes_aligned() {
for &sz in &SIZE_CLASSES {
assert_eq!(
sz % MIN_ALIGN,
0,
"class {} not aligned to {}",
sz,
MIN_ALIGN
);
}
}
#[test]
fn magic_division_correct() {
for (ci, &sz) in SIZE_CLASSES.iter().enumerate() {
let num_slots = slots_per_slab(ci);
for slot in 0..num_slots {
let offset = slot * sz;
let computed = fast_div_slot(offset, ci);
assert_eq!(
computed, slot,
"fast_div_slot({}, class={}) = {} expected {}",
offset, ci, computed, slot
);
}
}
}
#[test]
fn lookup_boundary_sizes() {
assert_eq!(size_class_index(0), Some(0));
assert_eq!(size_class_index(1), Some(0));
assert_eq!(size_class_index(16), Some(0));
assert_eq!(size_class_index(17), Some(1));
assert_eq!(size_class_index(16384), Some(NUM_SIZE_CLASSES - 1));
assert_eq!(size_class_index(16385), None);
}
}