use core::sync::atomic::{AtomicUsize, Ordering};
use crate::prim::{self, PrimError};
#[derive(Debug, Clone, Copy)]
pub struct OsBlock {
pub ptr: *mut u8,
pub size: usize,
pub is_large: bool,
pub is_zero: bool,
}
static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
static ALLOC_GRANULARITY: AtomicUsize = AtomicUsize::new(0);
static LARGE_PAGE_SIZE: AtomicUsize = AtomicUsize::new(usize::MAX);
fn config_init() {
let cfg = prim::mem_init();
PAGE_SIZE.store(cfg.page_size, Ordering::Relaxed);
ALLOC_GRANULARITY.store(cfg.alloc_granularity, Ordering::Relaxed);
LARGE_PAGE_SIZE.store(cfg.large_page_size, Ordering::Relaxed);
}
pub fn page_size() -> usize {
let v = PAGE_SIZE.load(Ordering::Relaxed);
if v != 0 {
return v;
}
config_init();
PAGE_SIZE.load(Ordering::Relaxed)
}
pub fn alloc_granularity() -> usize {
let v = ALLOC_GRANULARITY.load(Ordering::Relaxed);
if v != 0 {
return v;
}
config_init();
ALLOC_GRANULARITY.load(Ordering::Relaxed)
}
pub fn large_page_size() -> usize {
let v = LARGE_PAGE_SIZE.load(Ordering::Relaxed);
if v != usize::MAX {
return v;
}
config_init();
LARGE_PAGE_SIZE.load(Ordering::Relaxed)
}
pub fn page_align_up(size: usize) -> usize {
let ps = page_size();
let mask = (1usize << (ps.trailing_zeros() & 63)) - 1;
let base = size.max(1);
match base.checked_add(mask) {
Some(s) => s & !mask,
None => base,
}
}
pub fn alloc_aligned(
size: usize,
alignment: usize,
commit: bool,
allow_large: bool,
) -> Result<OsBlock, PrimError> {
if !alignment.is_power_of_two() {
return Err(22);
}
let alignment = alignment.max(page_size());
let size = page_align_up(size);
if size.checked_add(alignment).is_none() {
return Err(12);
}
let size = if !prim::FREE_RETURNS_MEMORY && alignment >= crate::types::SEGMENT_SIZE {
size.checked_next_multiple_of(crate::types::SEGMENT_SIZE)
.ok_or(12u32)?
} else {
size
};
let a = unsafe { prim::alloc(size, alignment, commit, allow_large)? };
let from_origin = if crate::REGION_STRIDES {
(a.ptr as usize).wrapping_sub(crate::prim::fixed::stride_base())
} else {
a.ptr as usize
};
debug_assert!(
crate::bins::is_aligned_to(from_origin, alignment),
"prim returned misaligned block"
);
Ok(OsBlock {
ptr: a.ptr,
size,
is_large: a.is_large,
is_zero: a.is_zero,
})
}
pub fn range_is_reserved(ptr: *const u8, size: usize) -> bool {
prim::range_is_reserved(ptr, size)
}
pub unsafe fn free(block: OsBlock) -> Result<(), PrimError> {
if !prim::FREE_RETURNS_MEMORY && crate::arena::adopt_os_block(block.ptr, block.size).is_some() {
return Ok(());
}
#[cfg(all(target_arch = "wasm32", not(miri)))]
if crate::slice_pool::free_range(block.ptr.expose_provenance(), block.size) {
return Ok(());
}
unsafe { prim::free(block.ptr, block.size) }
}
pub unsafe fn commit(ptr: *mut u8, size: usize) -> Result<bool, PrimError> {
#[cfg(all(test, feature = "std"))]
if test_hooks::take_commit_failure() {
return Err(test_hooks::INJECTED);
}
unsafe { prim::commit(ptr, page_align_up(size)) }
}
#[cfg(all(test, feature = "std"))]
pub(crate) mod test_hooks {
use core::cell::Cell;
pub const INJECTED: super::PrimError = 0xFA11;
std::thread_local! {
static FAIL_COMMITS: Cell<usize> = const { Cell::new(0) };
}
pub fn fail_next_commits(n: usize) {
FAIL_COMMITS.with(|c| c.set(n));
}
pub(super) fn take_commit_failure() -> bool {
FAIL_COMMITS.with(|c| {
let n = c.get();
if n > 0 {
c.set(n - 1);
}
n > 0
})
}
}
pub unsafe fn decommit(ptr: *mut u8, size: usize) -> Result<bool, PrimError> {
unsafe { prim::decommit(ptr, page_align_up(size)) }
}
pub unsafe fn purge(ptr: *mut u8, size: usize, purge_decommits: bool) -> Result<bool, PrimError> {
if purge_decommits {
unsafe { decommit(ptr, size) }
} else {
unsafe { prim::reset(ptr, page_align_up(size))? };
Ok(false)
}
}
pub unsafe fn protect(ptr: *mut u8, size: usize, on: bool) -> Result<(), PrimError> {
unsafe { prim::protect(ptr, page_align_up(size), on) }
}