use rusty_alloc::alloc::{expand, free, malloc, realloc, stats, usable_size, zalloc};
#[test]
fn span_lifecycle_and_realloc() {
let s0 = stats();
let p = malloc(1024 * 1024); assert!(!p.is_null());
unsafe {
assert!(usable_size(p) >= 1024 * 1024);
p.write(7);
p.add(1024 * 1024 - 1).write(8);
}
let s1 = stats();
assert_eq!(s1.large_allocs - s0.large_allocs, 1, "large path not taken");
unsafe { free(p) };
let s2 = stats();
assert_eq!(
s2.pages_retired - s1.pages_retired,
1,
"large span not retired"
);
let q = malloc(900 * 1024);
assert!(!q.is_null());
let s3 = stats();
assert_eq!(
s3.segments, s2.segments,
"reclaimed span not reused — new segment allocated"
);
unsafe { free(q) };
let d = malloc(2 * 1024 * 1024);
unsafe {
core::ptr::write_bytes(d, 0xAB, 2 * 1024 * 1024);
free(d);
}
let z = zalloc(2 * 1024 * 1024);
assert!(!z.is_null());
unsafe {
for i in (0..2 * 1024 * 1024).step_by(4096) {
assert_eq!(
z.add(i).read(),
0,
"recycled span leaked dirty bytes at +{i}"
);
}
free(z);
}
let s4 = stats();
let blocks: Vec<*mut u8> = (0..2000).map(|_| malloc(2048)).collect();
let s5 = stats();
assert!(
s5.pages_fresh - s4.pages_fresh >= 2,
"burst should carve several pages"
);
for &b in &blocks {
unsafe { free(b) };
}
let s6 = stats();
assert!(
s6.pages_retired - s5.pages_retired >= (s5.pages_fresh - s4.pages_fresh) - 1,
"empty pages not retired (one may stay warm): fresh {} retired {}",
s5.pages_fresh - s4.pages_fresh,
s6.pages_retired - s5.pages_retired
);
let big = malloc(12 * 1024 * 1024);
assert!(!big.is_null());
let s7 = stats();
assert_eq!(
s7.segments, s6.segments,
"coalescing failed — 12 MiB span needed a new segment"
);
unsafe { free(big) };
let r = malloc(100);
unsafe {
core::ptr::write_bytes(r, 0x5A, 100);
let s_ip0 = stats().realloc_in_place;
let r2 = realloc(r, 112);
assert_eq!(r2, r, "grow-within-usable must stay in place");
assert_eq!(stats().realloc_in_place - s_ip0, 1);
let r3 = realloc(r2, 4096);
assert!(!r3.is_null());
for i in 0..100 {
assert_eq!(r3.add(i).read(), 0x5A, "realloc lost prefix at {i}");
}
let r4 = realloc(r3, 16);
assert!(!r4.is_null());
assert_eq!(r4.read(), 0x5A);
assert!(usable_size(r4) < 4096);
assert_eq!(expand(r4, 8), r4);
assert!(expand(r4, 1 << 20).is_null(), "expand must never move");
free(r4);
}
let m = malloc(64);
assert!(rusty_alloc::alloc::is_in_heap_region(m));
let stack_local = 0u8;
assert!(!rusty_alloc::alloc::is_in_heap_region(&stack_local));
unsafe { free(m) };
let end = stats();
assert_eq!(
end.allocs - s0.allocs,
end.frees - s0.frees,
"leak inside span lifecycle test"
);
}