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)
}
}
#[inline(always)]
pub fn is_aligned_to(x: usize, align: usize) -> bool {
let a = align.max(1);
a.is_power_of_two() && (x & (a - 1)) == 0
}
const RECIP32: [u64; 4] = [recip32(1), recip32(3), recip32(5), recip32(7)];
const fn recip32(d: u64) -> u64 {
(1u64 << 32).div_ceil(d)
}
#[inline(always)]
pub(crate) fn div_by_block_size(n: usize, bsize: usize) -> usize {
let k = bsize.trailing_zeros();
let odd = bsize >> k;
let m = (n >> k) as u64;
debug_assert!(
matches!(odd, 1 | 3 | 5 | 7),
"bin_size {bsize} has odd part {odd}, outside the {{1,3,5,7}} this relies on"
);
debug_assert!(
m < (1 << 30),
"div_by_block_size: {n} >> {k} exceeds the bound the 32-bit reciprocals are exact over"
);
((m * RECIP32[(odd >> 1) & 3]) >> 32) as usize
}
#[inline(always)]
pub(crate) fn exact_div_by_block_size(n: usize, bsize: usize) -> usize {
const INV3: usize = crate::page::odd_mod_inverse(3);
const INV5: usize = crate::page::odd_mod_inverse(5);
const INV7: usize = crate::page::odd_mod_inverse(7);
let k = bsize.trailing_zeros();
let odd = bsize >> k;
debug_assert!(
bsize != 0 && n.is_multiple_of(bsize),
"exact_div_by_block_size: {n} is not a multiple of {bsize}"
);
let n = n >> k;
match odd {
1 => n,
3 => n.wrapping_mul(INV3),
5 => n.wrapping_mul(INV5),
7 => n.wrapping_mul(INV7),
_ => unreachable!("block_size {bsize} has odd part {odd}, not in {{1,3,5,7}}"),
}
}
#[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), ] {
if size > crate::types::MEDIUM_OBJ_SIZE_MAX {
continue;
}
assert_eq!(good_size(size), good, "good_size({size})");
}
}
#[test]
fn good_size_above_binned_range_is_page_rounded() {
let ps = crate::os::page_size();
assert!(
ps.is_power_of_two() && ps >= 4096,
"implausible page size {ps}"
);
for size in [
MEDIUM_OBJ_SIZE_MAX + 1,
MEDIUM_OBJ_SIZE_MAX + ps - 1,
2 * MEDIUM_OBJ_SIZE_MAX,
1024 * 1024 + 1,
] {
let g = good_size(size);
assert_eq!(g, crate::os::page_align_up(size), "good_size({size})");
assert!(g >= size, "good_size({size}) = {g} < size");
assert_eq!(g % ps, 0, "good_size({size}) = {g} is not page-aligned");
assert!(g - size < ps, "good_size({size}) = {g} over-rounded");
assert_eq!(good_size(g), g, "good_size not idempotent at {g}");
}
}
#[test]
fn fragmentation_bound() {
for size in 65..=MEDIUM_OBJ_SIZE_MAX {
let g = good_size(size);
assert!(g - size <= size / 4 + 16, "waste {g}-{size} too large");
}
}
}
#[cfg(test)]
mod div_helper_tests {
use super::*;
#[test]
fn every_bin_size_has_odd_part_in_1_3_5_7() {
for bin in 1..BIN_COUNT {
let bs = bin_size(bin);
if bs == 0 {
continue;
}
let odd = bs >> bs.trailing_zeros();
assert!(
matches!(odd, 1 | 3 | 5 | 7),
"bin {bin} size {bs} has odd part {odd}"
);
}
}
#[test]
fn div_by_block_size_equals_real_division() {
for bin in 1..BIN_COUNT {
let bs = bin_size(bin);
if bs == 0 {
continue;
}
for n in [0usize, 1, bs - 1, bs, bs + 1, 3 * bs, 3 * bs + 7, 1 << 20] {
assert_eq!(
div_by_block_size(n, bs),
n / bs,
"div_by_block_size({n}, {bs})"
);
}
}
}
}
#[cfg(test)]
mod exact_div_tests {
use super::{bin_size, div_by_block_size, exact_div_by_block_size};
#[test]
fn exact_form_matches_real_division_for_every_bin() {
for bin in 1..=40usize {
let bs = bin_size(bin);
if bs == 0 {
continue;
}
for k in [0usize, 1, 2, 3, 17, 255, 4095] {
let n = k * bs;
assert_eq!(
exact_div_by_block_size(n, bs),
n / bs,
"bin {bin} bsize {bs} multiple {k}"
);
}
}
}
#[test]
fn general_form_agrees_on_multiples_and_survives_non_multiples() {
for bin in 1..=40usize {
let bs = bin_size(bin);
if bs == 0 {
continue;
}
for k in [1usize, 9, 100] {
let n = k * bs;
assert_eq!(div_by_block_size(n, bs), exact_div_by_block_size(n, bs));
}
for d in [1usize, bs / 2, bs - 1] {
if d == 0 || d >= bs {
continue;
}
let n = 3 * bs + d;
assert_eq!(div_by_block_size(n, bs), 3, "bin {bin} offset {d}");
}
}
}
}