use crate::VirtualMemoryError;
use std::ptr::NonNull;
#[cfg(windows)]
mod imp {
use super::{NonNull, VirtualMemoryError};
use windows_sys::Win32::System::Memory::{
MEM_COMMIT, MEM_PRESERVE_PLACEHOLDER, MEM_RELEASE, MEM_RESERVE, MEM_RESERVE_PLACEHOLDER,
PAGE_READWRITE, VirtualAlloc2, VirtualFree,
};
use windows_sys::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
pub fn granularity() -> usize {
let info = unsafe {
let mut info: SYSTEM_INFO = std::mem::zeroed();
GetSystemInfo(&mut info);
info
};
info.dwAllocationGranularity as usize
}
pub fn reserve(len: usize) -> Result<NonNull<u8>, VirtualMemoryError> {
let base = unsafe {
VirtualAlloc2(
std::ptr::null_mut(),
std::ptr::null(),
len,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS,
std::ptr::null_mut(),
0,
)
};
NonNull::new(base.cast::<u8>()).ok_or_else(|| last_error("VirtualAlloc2 (reserve)"))
}
pub unsafe fn map(
base: NonNull<u8>,
gap: (usize, usize),
offset: usize,
len: usize,
) -> Result<(), VirtualMemoryError> {
let target = unsafe { base.as_ptr().add(offset) };
let (gap_start, gap_len) = gap;
if !(offset == gap_start && len == gap_len) {
let split =
unsafe { VirtualFree(target.cast(), len, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER) };
if split == 0 {
return Err(last_error("VirtualFree (split placeholder)"));
}
}
let committed = unsafe {
VirtualAlloc2(
std::ptr::null_mut(),
target.cast(),
len,
MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER,
PAGE_READWRITE,
std::ptr::null_mut(),
0,
)
};
if committed.is_null() {
return Err(last_error("VirtualAlloc2 (replace placeholder)"));
}
Ok(())
}
pub unsafe fn unmap(
base: NonNull<u8>,
offset: usize,
len: usize,
) -> Result<(), VirtualMemoryError> {
let target = unsafe { base.as_ptr().add(offset) };
let freed =
unsafe { VirtualFree(target.cast(), len, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER) };
if freed == 0 {
return Err(last_error("VirtualFree (release block)"));
}
Ok(())
}
pub unsafe fn release(base: NonNull<u8>, _len: usize) {
unsafe {
VirtualFree(base.as_ptr().cast(), 0, MEM_RELEASE);
}
}
fn last_error(operation: &'static str) -> VirtualMemoryError {
let error = std::io::Error::last_os_error();
VirtualMemoryError::Os {
operation,
reason: error.to_string(),
code: error.raw_os_error().unwrap_or(0),
}
}
use windows_sys::Win32::System::Memory::{MEM_REPLACE_PLACEHOLDER, PAGE_NOACCESS};
}
#[cfg(unix)]
mod imp {
use super::{NonNull, VirtualMemoryError};
pub fn granularity() -> usize {
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if size > 0 { size as usize } else { 4096 }
}
pub fn reserve(len: usize) -> Result<NonNull<u8>, VirtualMemoryError> {
let base = unsafe {
libc::mmap(
std::ptr::null_mut(),
len,
libc::PROT_NONE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_NORESERVE,
-1,
0,
)
};
if base == libc::MAP_FAILED {
return Err(last_error("mmap (reserve)"));
}
NonNull::new(base.cast::<u8>()).ok_or_else(|| last_error("mmap (reserve)"))
}
pub unsafe fn map(
base: NonNull<u8>,
_gap: (usize, usize),
offset: usize,
len: usize,
) -> Result<(), VirtualMemoryError> {
let target = unsafe { base.as_ptr().add(offset) };
let mapped = unsafe {
libc::mmap(
target.cast(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
-1,
0,
)
};
if mapped == libc::MAP_FAILED {
return Err(last_error("mmap (commit block)"));
}
Ok(())
}
pub unsafe fn unmap(
base: NonNull<u8>,
offset: usize,
len: usize,
) -> Result<(), VirtualMemoryError> {
let target = unsafe { base.as_ptr().add(offset) };
let restored = unsafe {
libc::mmap(
target.cast(),
len,
libc::PROT_NONE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED | libc::MAP_NORESERVE,
-1,
0,
)
};
if restored == libc::MAP_FAILED {
return Err(last_error("mmap (release block)"));
}
Ok(())
}
pub unsafe fn release(base: NonNull<u8>, len: usize) {
unsafe {
libc::munmap(base.as_ptr().cast(), len);
}
}
fn last_error(operation: &'static str) -> VirtualMemoryError {
let error = std::io::Error::last_os_error();
VirtualMemoryError::Os {
operation,
reason: error.to_string(),
code: error.raw_os_error().unwrap_or(0),
}
}
}
pub(crate) use imp::{granularity, map, release, reserve, unmap};