pagealloc 1.0.0

Low-level, cross-platform page allocation library
Documentation
use crate::{Error, OsError, Protection, Result};
use core::ffi::c_void;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use windows_sys::Win32;

pub type RawOsError = i32;

pub unsafe fn alloc(location: *mut u8, size: usize, protection: Protection) -> Result<NonNull<u8>> {
    unsafe {
        let r = Win32::System::Memory::VirtualAlloc(
            location as *mut c_void,
            size,
            Win32::System::Memory::MEM_RESERVE | Win32::System::Memory::MEM_COMMIT,
            protection_flag(protection),
        );
        NonNull::new(r as *mut u8).ok_or_else(error)
    }
}

pub unsafe fn dealloc(start: NonNull<u8>, _: usize) -> Result<()> {
    unsafe {
        if Win32::System::Memory::VirtualFree(
            start.as_ptr() as *mut c_void,
            0,
            Win32::System::Memory::MEM_RELEASE,
        ) != 0
        {
            Ok(())
        } else {
            Err(error())
        }
    }
}

pub unsafe fn clear(start: NonNull<u8>, size: usize, protection: Protection) -> Result<()> {
    unsafe {
        if Win32::System::Memory::VirtualFree(
            start.as_ptr() as *mut c_void,
            0,
            Win32::System::Memory::MEM_DECOMMIT,
        ) != 0
            && !Win32::System::Memory::VirtualAlloc(
                start.as_ptr() as *mut c_void,
                size,
                Win32::System::Memory::MEM_COMMIT,
                protection_flag(protection),
            )
            .is_null()
        {
            Ok(())
        } else {
            Err(error())
        }
    }
}

pub unsafe fn protect(start: NonNull<u8>, size: usize, protection: Protection) -> Result<()> {
    unsafe {
        if Win32::System::Memory::VirtualProtect(
            start.as_ptr() as *mut c_void,
            size,
            protection_flag(protection),
            &mut 0,
        ) != 0
        {
            Ok(())
        } else {
            Err(error())
        }
    }
}

pub unsafe fn advise_free(start: NonNull<u8>, size: usize) -> Result<()> {
    unsafe {
        if !Win32::System::Memory::VirtualAlloc(
            start.as_ptr() as *mut c_void,
            size,
            Win32::System::Memory::MEM_RESET,
            Win32::System::Memory::PAGE_NOACCESS,
        )
        .is_null()
        {
            Ok(())
        } else {
            Err(error())
        }
    }
}

pub fn page_size() -> usize {
    let mut info = MaybeUninit::uninit();
    let info = unsafe {
        Win32::System::SystemInformation::GetSystemInfo(info.as_mut_ptr());
        info.assume_init()
    };

    info.dwPageSize as usize
}

pub fn alloc_granularity() -> usize {
    let mut info = MaybeUninit::uninit();
    let info = unsafe {
        Win32::System::SystemInformation::GetSystemInfo(info.as_mut_ptr());
        info.assume_init()
    };

    info.dwAllocationGranularity as usize
}

fn protection_flag(protection: Protection) -> Win32::System::Memory::PAGE_PROTECTION_FLAGS {
    match protection {
        Protection::None => Win32::System::Memory::PAGE_NOACCESS,
        Protection::Read => Win32::System::Memory::PAGE_READONLY,
        Protection::ReadWrite => Win32::System::Memory::PAGE_READWRITE,
        Protection::Exec => Win32::System::Memory::PAGE_EXECUTE,
        Protection::ExecRead => Win32::System::Memory::PAGE_EXECUTE_READ,
        Protection::ExecReadWrite => Win32::System::Memory::PAGE_EXECUTE_READWRITE,
    }
}

#[cold]
fn error() -> Error {
    unsafe { Error::Os(OsError(Win32::Foundation::GetLastError() as i32)) }
}