use crate::types::{BIN_FULL, BIN_HUGE, INTPTR_SIZE, MEDIUM_OBJ_SIZE_MAX, wsize_from_size};
pub const BIN_COUNT: usize = BIN_FULL + 1;
pub const PAGES_DIRECT: usize = crate::types::SMALL_WSIZE_MAX + 1;
#[inline]
pub fn bin(size: usize) -> usize {
let wsize = wsize_from_size(size);
if wsize <= 1 {
1
} else if wsize <= 8 {
(wsize + 1) & !1
} else if size > MEDIUM_OBJ_SIZE_MAX {
BIN_HUGE
} else {
let w = wsize - 1;
let b = (usize::BITS - 1 - w.leading_zeros()) as usize; ((b << 2) + ((w >> (b - 2)) & 0x03)) - 3
}
}
#[inline]
pub const fn bin_size(bin: usize) -> usize {
if bin <= 8 {
bin * INTPTR_SIZE
} else {
let t = bin + 3;
let b = t >> 2;
let m = t & 3;
((5 + m) << (b - 2)) * INTPTR_SIZE
}
}
#[inline]
pub fn good_size(size: usize) -> usize {
if size <= MEDIUM_OBJ_SIZE_MAX {
bin_size(bin(size))
} else {
crate::os::page_align_up(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bin_size_inverts_bin() {
for size in 1..=MEDIUM_OBJ_SIZE_MAX {
let b = bin(size);
let bs = bin_size(b);
assert!(bs >= size, "bin_size({b}) = {bs} < size {size}");
assert_eq!(bin(bs), b, "bin_size({b}) = {bs} maps to bin {}", bin(bs));
assert_eq!(good_size(good_size(size)), good_size(size));
}
}
#[test]
fn known_size_classes() {
for (size, good) in [
(1, 8),
(8, 8),
(9, 16),
(17, 32), (24, 32),
(33, 48), (56, 64), (64, 64),
(65, 80),
(72, 80),
(80, 80),
(100, 112),
(128, 128),
(129, 160),
(256, 256),
(257, 320),
(1024, 1024),
(1025, 1280),
(4097, 5120),
(65536, 65536), (65537, 69632), (131072, 131072),
] {
assert_eq!(good_size(size), good, "good_size({size})");
}
}
#[test]
fn fragmentation_bound() {
for size in 65..=MEDIUM_OBJ_SIZE_MAX {
let g = good_size(size);
assert!(g - size <= size / 4 + 16, "waste {}-{size} too large", g);
}
}
}