use frame_alloc::{
CpuId, DepotAllocator, PageSize, PhysRange, PhysicalAllocator, Provenance, RegionInit,
SummaryBuddyAllocator,
};
use std::alloc::{Layout, alloc, dealloc};
use std::num::NonZeroUsize;
use std::ptr;
use std::ptr::NonNull;
pub struct IdentityProv;
unsafe impl Provenance for IdentityProv {
unsafe fn create(phys: usize) -> NonNull<u8> {
unsafe { NonNull::new_unchecked(ptr::with_exposed_provenance_mut(phys)) }
}
unsafe fn destroy<T>(p: NonNull<T>) -> usize {
p.addr().get()
}
}
pub struct Region {
ptr: *mut u8,
layout: Layout,
}
impl Region {
pub fn new(bytes: usize, align: usize) -> Self {
let layout = Layout::from_size_align(bytes, align).unwrap();
let ptr = unsafe { alloc(layout) };
assert!(!ptr.is_null(), "region allocation failed");
Self { ptr, layout }
}
pub fn addr(&self) -> usize {
self.ptr.expose_provenance()
}
}
impl Drop for Region {
fn drop(&mut self) {
unsafe { dealloc(self.ptr, self.layout) }
}
}
pub fn n(v: usize) -> NonZeroUsize {
NonZeroUsize::new(v).expect("non-zero count")
}
const BASE: PageSize = PageSize::from_log2(12);
const ORDERS: usize = 9;
const MAX_BLOCK: usize = BASE.bytes() << (ORDERS - 1);
const SPAN_FRAMES: usize = 256;
static PHYS: SummaryBuddyAllocator<ORDERS, IdentityProv> = SummaryBuddyAllocator::new(BASE);
fn main() {
let pool = Region::new(SPAN_FRAMES * BASE.bytes(), MAX_BLOCK);
let phys_base = pool.addr();
let frame = BASE.bytes();
let range = |lo: usize, hi: usize| PhysRange {
base: phys_base + lo * frame,
len: (hi - lo) * frame,
};
let usable = [range(0, 100), range(104, 200), range(205, 256)];
unsafe { PHYS.init(phys_base, SPAN_FRAMES * frame, &usable) };
println!("Initialised SummaryBuddy over a 1 MiB span with two reserved holes.");
print_stats("after init", &PHYS);
let single = PHYS
.allocate_physical(BASE, n(1))
.expect("single-frame alloc");
let block = PHYS
.allocate_physical(BASE, n(4))
.expect("4-frame contiguous alloc");
println!("\nallocate_physical(1 frame) -> {single:#x}");
println!("allocate_physical(4 frames) -> {block:#x}");
print_stats("with 5 frames out", &PHYS);
unsafe {
PHYS.deallocate_physical(BASE, n(1), single);
PHYS.deallocate_physical(BASE, n(4), block);
}
println!("\nfreed both — buddies merge back.");
print_stats("after free", &PHYS);
compose_with_depot();
}
fn compose_with_depot() {
struct OneCpu;
impl CpuId for OneCpu {
fn current_cpu() -> usize {
0
}
}
const SLOTS: usize = 8;
let mag: DepotAllocator<SummaryBuddyAllocator<ORDERS, IdentityProv>, OneCpu, SLOTS> =
DepotAllocator::new(BASE, SummaryBuddyAllocator::new(BASE));
let pool = Region::new(SPAN_FRAMES * BASE.bytes(), MAX_BLOCK);
unsafe { mag.init_region(pool.addr(), SPAN_FRAMES * BASE.bytes()) };
println!("\n── DepotAllocator<SummaryBuddyAllocator> ──");
let a = mag.allocate_physical(BASE, n(1)).expect("mag alloc");
unsafe { mag.deallocate_physical(BASE, n(1), a) };
let b = mag.allocate_physical(BASE, n(1)).expect("mag re-alloc");
println!("alloc {a:#x} -> free -> alloc {b:#x}");
assert_eq!(a, b, "the magazine should return the just-freed frame");
println!("re-alloc returned the cached frame (no backend round-trip).");
unsafe { mag.deallocate_physical(BASE, n(1), b) };
}
#[cfg(feature = "stats")]
fn print_stats(label: &str, a: &impl frame_alloc::AllocatorStats) {
let kib = |b: usize| b / 1024;
println!(
" [{label}] total {} KiB · free {} KiB · largest run {} KiB",
kib(a.total_bytes()),
kib(a.free_bytes()),
kib(a.largest_free_bytes()),
);
}
#[cfg(not(feature = "stats"))]
fn print_stats<A>(_label: &str, _a: &A) {}