#![cfg(not(miri))]
#![cfg(feature = "alloc-core")]
use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
use sefer_alloc::AllocCore;
struct Counting;
std::thread_local! {
static ALLOC_COUNT: Cell<usize> = const { Cell::new(0) };
static DEALLOC_COUNT: Cell<usize> = const { Cell::new(0) };
}
fn alloc_count() -> usize {
ALLOC_COUNT.with(|c| c.get())
}
fn dealloc_count() -> usize {
DEALLOC_COUNT.with(|c| c.get())
}
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let _ = ALLOC_COUNT.try_with(|c| c.set(c.get() + 1));
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let _ = DEALLOC_COUNT.try_with(|c| c.set(c.get() + 1));
unsafe { System.dealloc(ptr, layout) };
}
}
#[global_allocator]
static GLOBAL: Counting = Counting;
const LIVE_CAP: usize = 256;
#[test]
fn m5_alloc_path_does_not_touch_global_allocator() {
let layouts: [Layout; 5] = [
Layout::from_size_align(16, 8).unwrap(),
Layout::from_size_align(64, 8).unwrap(),
Layout::from_size_align(256, 16).unwrap(),
Layout::from_size_align(4096, 4096).unwrap(),
Layout::from_size_align(1024 * 1024, 4096).unwrap(), ];
let mut a = AllocCore::new().expect("primordial bootstrap");
let mut live_ptrs: [*mut u8; LIVE_CAP] = [core::ptr::null_mut(); LIVE_CAP];
let mut live_layouts: [Layout; LIVE_CAP] = [Layout::from_size_align(1, 1).unwrap(); LIVE_CAP];
let mut live_n: usize = 0;
let alloc_before = alloc_count();
let dealloc_before = dealloc_count();
for cycle in 0..20 {
for (li, layout) in layouts.iter().enumerate() {
let ptr = if cycle % 3 == 0 {
a.alloc_zeroed(*layout)
} else {
a.alloc(*layout)
};
assert!(!ptr.is_null(), "alloc failed for layout {li}");
if live_n < LIVE_CAP {
live_ptrs[live_n] = ptr;
live_layouts[live_n] = *layout;
live_n += 1;
} else {
a.dealloc(ptr, *layout);
}
}
let mut i = 1;
while i < live_n {
a.dealloc(live_ptrs[i], live_layouts[i]);
live_n -= 1;
live_ptrs[i] = live_ptrs[live_n];
live_layouts[i] = live_layouts[live_n];
i += 1;
}
}
for (realloc_count, i) in (0..live_n).enumerate() {
if realloc_count >= 4 {
break;
}
let layout = live_layouts[i];
let grown = a.realloc(live_ptrs[i], layout, layout.size() * 2);
assert!(!grown.is_null());
let gl = Layout::from_size_align(layout.size() * 2, layout.align()).unwrap();
let shrunk = a.realloc(grown, gl, layout.size() / 2 + 1);
assert!(!shrunk.is_null());
live_ptrs[i] = shrunk;
live_layouts[i] = Layout::from_size_align(layout.size() / 2 + 1, layout.align()).unwrap();
}
let alloc_delta = alloc_count() - alloc_before;
let dealloc_delta = dealloc_count() - dealloc_before;
assert_eq!(
alloc_delta, 0,
"AllocCore allocated {alloc_delta} times through the global allocator (M5 violation)"
);
assert_eq!(
dealloc_delta, 0,
"AllocCore deallocated {dealloc_delta} times through the global allocator (M5 violation)"
);
drop(a);
}