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;
(size.max(1) + mask) & !mask
}
pub fn alloc_aligned(
size: usize,
alignment: usize,
commit: bool,
allow_large: bool,
) -> Result<OsBlock, PrimError> {
assert!(
alignment.is_power_of_two(),
"alignment must be a power of two"
);
let alignment = alignment.max(page_size());
let size = page_align_up(size);
let size = if !prim::FREE_RETURNS_MEMORY && alignment >= crate::types::SEGMENT_SIZE {
size.next_multiple_of(crate::types::SEGMENT_SIZE)
} 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 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> {
unsafe { prim::commit(ptr, page_align_up(size)) }
}
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) }
}