use rusty_alloc::alloc::{free, malloc, usable_size};
use rusty_alloc::types::SEGMENT_SIZE;
const MIB: usize = 1024 * 1024;
fn segment_base(p: *mut u8) -> usize {
p as usize & !(SEGMENT_SIZE - 1)
}
fn assert_share_one_segment(sizes: &[usize]) {
let blocks: Vec<*mut u8> = sizes
.iter()
.map(|&n| {
let p = malloc(n);
assert!(!p.is_null(), "malloc({n}) failed");
unsafe {
assert!(usable_size(p) >= n, "usable_size below request for {n}");
*p = 0xAB;
*p.add(n - 1) = 0xCD;
}
p
})
.collect();
let bases: Vec<usize> = blocks.iter().map(|&p| segment_base(p)).collect();
for (i, &b) in bases.iter().enumerate() {
assert_eq!(
b, bases[0],
"block {i} ({} bytes) landed in a different segment — span routing \
regressed to dedicated reservations (the segment tax)",
sizes[i]
);
}
for (&p, &n) in blocks.iter().zip(sizes) {
unsafe {
assert_eq!(*p, 0xAB);
assert_eq!(*p.add(n - 1), 0xCD);
free(p);
}
}
}
#[test]
fn twenty_mib_span_shares_its_segment() {
assert_share_one_segment(&[20 * MIB, 8 * MIB, 62 * 64 * 1024]);
}
#[test]
fn detector_tensor_span_shares_its_segment() {
assert_share_one_segment(&[402 * 64 * 1024, 6 * MIB]);
}
#[test]
fn sixteen_mib_tail_is_usable() {
assert_share_one_segment(&[16 * MIB, 15 * MIB]);
}
#[test]
fn maximum_span_fills_one_segment_exactly() {
let n = rusty_alloc::types::LARGE_OBJ_SIZE_MAX;
let p = malloc(n);
assert!(!p.is_null(), "malloc(LARGE_OBJ_SIZE_MAX) failed");
unsafe {
assert!(usable_size(p) >= n);
*p = 0x5A;
*p.add(n - 1) = 0xA5;
assert_eq!(
p as usize - segment_base(p),
64 * 1024,
"maximum span did not start at the first usable slice"
);
assert_eq!(*p, 0x5A);
assert_eq!(*p.add(n - 1), 0xA5);
free(p);
}
}
#[test]
fn one_past_the_boundary_is_huge_and_correct() {
let n = rusty_alloc::types::LARGE_OBJ_SIZE_MAX + 1;
let p = malloc(n);
assert!(!p.is_null(), "malloc(LARGE_OBJ_SIZE_MAX + 1) failed");
unsafe {
assert!(usable_size(p) >= n);
*p = 0x11;
*p.add(n - 1) = 0x22;
assert_eq!(*p, 0x11);
assert_eq!(*p.add(n - 1), 0x22);
free(p);
}
}