#[cfg(any(target_os = "linux", target_os = "macos"))]
use core::ffi::c_void;
use core::ptr::NonNull;
#[cfg(any(target_os = "linux", target_os = "macos"))]
unsafe extern "C" {
fn mmap(
addr: *mut c_void,
length: usize,
prot: i32,
flags: i32,
fd: i32,
offset: i64,
) -> *mut c_void;
fn munmap(addr: *mut c_void, length: usize) -> i32;
fn madvise(addr: *mut c_void, length: usize, advice: i32) -> i32;
fn sysconf(name: i32) -> i64;
}
#[cfg(target_os = "linux")]
const SC_PAGESIZE: i32 = 30;
#[cfg(target_os = "macos")]
const SC_PAGESIZE: i32 = 29;
#[cfg(any(target_os = "linux", target_os = "macos"))]
const PROT_READ: i32 = 0x1;
#[cfg(any(target_os = "linux", target_os = "macos"))]
const PROT_WRITE: i32 = 0x2;
#[cfg(any(target_os = "linux", target_os = "macos"))]
const MAP_PRIVATE: i32 = 0x2;
#[cfg(target_os = "linux")]
const MAP_ANONYMOUS: i32 = 0x20;
#[cfg(target_os = "macos")]
const MAP_ANONYMOUS: i32 = 0x1000;
#[cfg(target_os = "linux")]
const MADV_DISCARD: i32 = 4;
#[cfg(target_os = "macos")]
const MADV_DISCARD: i32 = 5;
pub const PAGE: usize = 4096;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub fn page_size_matches() -> bool {
use core::sync::atomic::{AtomicU8, Ordering};
static ANSWER: AtomicU8 = AtomicU8::new(0); if let Some(known) = decode_memo(ANSWER.load(Ordering::Relaxed)) {
return known;
}
let got = unsafe { sysconf(SC_PAGESIZE) };
let ok = usable_page_size(got);
ANSWER.store(encode_memo(ok), Ordering::Relaxed);
ok
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
const fn decode_memo(stored: u8) -> Option<bool> {
match stored {
1 => Some(true),
2 => Some(false),
_ => None,
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
const fn encode_memo(answer: bool) -> u8 {
if answer { 1 } else { 2 }
}
#[must_use]
pub(crate) fn usable_page_size(measured: i64) -> bool {
measured > 0 && measured as u64 == PAGE as u64
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
pub fn page_size_matches() -> bool {
false
}
#[must_use]
pub const fn round_up(n: usize, align: usize) -> usize {
(n + align - 1) & !(align - 1)
}
pub fn map_aligned(len: usize, align: usize) -> Option<NonNull<u8>> {
if len == 0 || !align.is_power_of_two() || !len.is_multiple_of(align) {
return None;
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
None
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
if cfg!(miri) {
return None;
}
let total = len.checked_add(align)?;
let raw = unsafe {
mmap(
core::ptr::null_mut(),
total,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0,
)
};
if raw as isize == -1 {
return None;
}
NonNull::new(trim(raw as usize, total, len, align) as *mut u8)
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn trim(raw: usize, total: usize, len: usize, align: usize) -> usize {
let start = (raw + align - 1) & !(align - 1);
let prefix = start - raw;
let suffix = total - prefix - len;
if prefix > 0 {
unsafe { munmap(raw as *mut c_void, prefix) };
}
if suffix > 0 {
unsafe { munmap((start + len) as *mut c_void, suffix) };
}
start
}
pub unsafe fn unmap(ptr: NonNull<u8>, len: usize) {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
if cfg!(miri) {
return;
}
unsafe { munmap(ptr.as_ptr().cast(), len) };
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = (ptr, len);
}
}
pub unsafe fn discard(ptr: NonNull<u8>, len: usize) -> bool {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
if cfg!(miri) {
return false;
}
unsafe { madvise(ptr.as_ptr().cast(), len, MADV_DISCARD) == 0 }
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = (ptr, len);
false
}
}
#[must_use]
pub const fn available() -> bool {
cfg!(any(target_os = "linux", target_os = "macos")) && !cfg!(miri)
}
#[cfg(test)]
mod page_size_tests {
use super::{PAGE, usable_page_size};
#[test]
fn the_memo_round_trips_and_unknown_is_neither_answer() {
use super::{decode_memo, encode_memo};
assert_eq!(decode_memo(encode_memo(true)), Some(true));
assert_eq!(decode_memo(encode_memo(false)), Some(false));
assert_eq!(decode_memo(0), None, "0 is unasked, not an answer");
assert_ne!(encode_memo(true), 0, "an answer must not read as unasked");
assert_ne!(encode_memo(false), 0);
}
#[test]
fn only_an_exact_match_is_usable() {
assert!(usable_page_size(PAGE as i64));
assert!(!usable_page_size(16384), "a 16 KiB page is not our 4 KiB arithmetic");
assert!(!usable_page_size(1024), "a smaller page misaligns the same way");
assert!(!usable_page_size(-1), "a failed sysconf must not read as a match");
assert!(!usable_page_size(0));
}
}