#[cfg(all(feature = "alloc-core", feature = "alloc-decommit"))]
#[cfg_attr(miri, ignore)]
#[test]
fn slot_recycle_lifts_cap() {
use core::alloc::Layout;
use sefer_alloc::alloc_core::AllocCore;
let mut ac = AllocCore::new().expect("primordial");
let layout = Layout::from_size_align(256, 8).unwrap();
const N: usize = 40_000; const ROUNDS: usize = 100;
let decommit_before = AllocCore::dbg_decommit_count();
for round in 0..ROUNDS {
let mut ptrs = Vec::with_capacity(N);
for i in 0..N {
let p = ac.alloc(layout);
assert!(
!p.is_null(),
"alloc returned null at round={round} i={i} — \
slot recycle failed (cap exhausted)"
);
ptrs.push(p);
}
for (i, &p) in ptrs.iter().enumerate() {
unsafe {
let b = (i & 0xFF) as u8;
p.write(b);
assert_eq!(p.read(), b, "write/readback failed at round={round} i={i}");
}
}
for &p in &ptrs {
ac.dealloc(p, layout);
}
}
let decommit_after = AllocCore::dbg_decommit_count();
assert!(
decommit_after > decommit_before,
"no decommit fired during {ROUNDS} rounds of churn — \
recycle path was never exercised (decommit hook miswired). \
Ensure N is large enough to spill into >= 2 fresh Small segments per round \
so that the first fresh segment is non-current when emptied."
);
}
#[cfg(all(feature = "alloc-core", not(feature = "alloc-decommit")))]
#[cfg_attr(miri, ignore)]
#[test]
fn without_decommit_cap_is_hard() {
use core::alloc::Layout;
use sefer_alloc::{alloc_core::AllocCore, SegmentLayout};
let mut ac = AllocCore::new().expect("primordial");
let large_size = SegmentLayout::SMALL_MAX + SegmentLayout::PAGE;
let layout = Layout::from_size_align(large_size, SegmentLayout::PAGE).unwrap();
const ATTEMPT: usize = 1025;
let mut ptrs = Vec::with_capacity(ATTEMPT);
let mut null_count = 0usize;
for _ in 0..ATTEMPT {
let p = ac.alloc(layout);
if p.is_null() {
null_count += 1;
break;
}
ptrs.push(p);
}
assert!(
null_count > 0,
"expected at least one null from large alloc after MAX_SEGMENTS — \
table must be full without decommit"
);
for (&p, _) in ptrs.iter().zip(std::iter::repeat(layout)) {
ac.dealloc(p, layout);
}
}
#[cfg(all(feature = "alloc-core", feature = "alloc-decommit"))]
#[test]
fn recycled_slot_is_reused() {
use core::alloc::Layout;
use std::collections::HashSet;
use sefer_alloc::alloc_core::AllocCore;
let mut ac = AllocCore::new().expect("primordial");
let layout = Layout::from_size_align(2048, 8).unwrap();
const N: usize = 6000;
let decommit_before = AllocCore::dbg_decommit_count();
let mut ptrs = Vec::with_capacity(N);
for i in 0..N {
let p = ac.alloc(layout);
assert!(!p.is_null(), "phase-1 alloc null at i={i}");
ptrs.push(p);
}
let set1: HashSet<usize> = ptrs.iter().map(|&p| p as usize).collect();
assert_eq!(set1.len(), N, "phase-1 alloc handed out duplicates");
for &p in &ptrs {
ac.dealloc(p, layout);
}
let decommit_after = AllocCore::dbg_decommit_count();
assert!(
decommit_after > decommit_before,
"no decommit fired — segment stayed current throughout (working set \
too small or decommit hook miswired); \
decommit_before={decommit_before}, after={decommit_after}"
);
let mut ptrs2 = Vec::with_capacity(N);
for i in 0..N {
let p = ac.alloc(layout);
assert!(!p.is_null(), "phase-3 alloc null at i={i} after recycle");
unsafe {
let b = (i & 0xFF) as u8;
p.write(b);
assert_eq!(p.read(), b, "phase-3 write/readback failed at i={i}");
}
ptrs2.push(p);
}
let set2: HashSet<usize> = ptrs2.iter().map(|&p| p as usize).collect();
assert_eq!(
set2.len(),
N,
"phase-3 alloc handed out duplicates after recycle"
);
for &p in &ptrs2 {
ac.dealloc(p, layout);
}
}