use std::ffi::c_void;
use std::fmt;
use std::fs::File;
use std::io;
use std::os::windows::io::AsRawHandle as _;
use std::ptr;
use std::ptr::NonNull;
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
use windows_sys::Win32::System::Memory::{
CreateFileMappingW, MEM_COMMIT, MEM_PRESERVE_PLACEHOLDER, MEM_RELEASE, MEM_REPLACE_PLACEHOLDER,
MEM_RESERVE, MEM_RESERVE_PLACEHOLDER, MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile3, PAGE_EXECUTE,
PAGE_GUARD, PAGE_NOACCESS, PAGE_READONLY, UnmapViewOfFile2, VirtualAlloc2, VirtualFree,
};
use windows_sys::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
use windows_sys::Win32::System::SystemServices::MEM_COALESCE_PLACEHOLDERS;
use windows_sys::Win32::System::Threading::GetCurrentProcess;
fn system_info() -> SYSTEM_INFO {
let mut info: SYSTEM_INFO = unsafe { core::mem::zeroed() };
unsafe { GetSystemInfo(&raw mut info) };
info
}
pub(crate) fn granularity() -> usize {
usize::try_from(system_info().dwAllocationGranularity).unwrap_or(64 * 1024)
}
pub(crate) fn page() -> usize {
usize::try_from(system_info().dwPageSize).unwrap_or(4096)
}
fn current_process() -> HANDLE {
unsafe { GetCurrentProcess() }
}
fn last_error() -> io::Error {
io::Error::last_os_error()
}
pub(crate) struct Backing {
section: HANDLE,
len: u64,
}
impl Backing {
pub(crate) fn new(file: &File) -> io::Result<Self> {
let len = file.metadata()?.len();
let handle: HANDLE = file.as_raw_handle().cast();
let section =
unsafe { CreateFileMappingW(handle, ptr::null(), PAGE_READONLY, 0, 0, ptr::null()) };
if section.is_null() {
return Err(last_error());
}
Ok(Self { section, len })
}
}
impl Drop for Backing {
fn drop(&mut self) {
unsafe {
CloseHandle(self.section);
}
}
}
impl fmt::Debug for Backing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Backing")
.field("section", &self.section)
.field("len", &self.len)
.finish()
}
}
pub(crate) struct Reservation {
base: NonNull<u8>,
span: usize,
mapped: usize,
}
impl Reservation {
pub(crate) fn new(span: usize) -> io::Result<Self> {
let base = unsafe {
VirtualAlloc2(
current_process(),
ptr::null(),
span,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS,
ptr::null_mut(),
0,
)
};
let base = NonNull::new(base.cast::<u8>()).ok_or_else(last_error)?;
Ok(Self {
base,
span,
mapped: 0,
})
}
pub(crate) fn base(&self) -> NonNull<u8> {
self.base
}
pub(crate) fn span(&self) -> usize {
self.span
}
pub(crate) fn map(&mut self, backing: &Backing, offset: u64, len: usize) -> io::Result<()> {
debug_assert!(
len <= self.span,
"the caller clamps a view to the reservation"
);
self.unmap()?;
if len < self.span {
let split = unsafe {
VirtualFree(
self.base.as_ptr().cast::<c_void>(),
len,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER,
)
};
if split == 0 {
return Err(last_error());
}
}
let remaining = backing.len.saturating_sub(offset);
let view_len = usize::try_from(remaining).unwrap_or(usize::MAX).min(len);
let view = unsafe {
MapViewOfFile3(
backing.section,
current_process(),
self.base.as_ptr().cast::<c_void>(),
offset,
view_len,
MEM_REPLACE_PLACEHOLDER,
PAGE_READONLY,
ptr::null_mut(),
0,
)
};
if view.Value.is_null() {
let failure = last_error();
if len < self.span {
self.coalesce();
}
return Err(failure);
}
self.mapped = len;
Ok(())
}
pub(crate) fn unmap(&mut self) -> io::Result<()> {
if self.mapped == 0 {
return Ok(());
}
let address = MEMORY_MAPPED_VIEW_ADDRESS {
Value: self.base.as_ptr().cast::<c_void>(),
};
let ok = unsafe { UnmapViewOfFile2(current_process(), address, MEM_PRESERVE_PLACEHOLDER) };
if ok == 0 {
return Err(last_error());
}
let was = self.mapped;
self.mapped = 0;
if was < self.span {
self.coalesce();
}
Ok(())
}
fn coalesce(&self) {
unsafe {
VirtualFree(
self.base.as_ptr().cast::<c_void>(),
self.span,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS,
);
}
}
}
impl Drop for Reservation {
fn drop(&mut self) {
let _ = self.unmap();
unsafe {
VirtualFree(self.base.as_ptr().cast::<c_void>(), 0, MEM_RELEASE);
}
}
}
impl fmt::Debug for Reservation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Reservation")
.field("base", &self.base)
.field("span", &self.span)
.field("mapped", &self.mapped)
.finish()
}
}
unsafe impl Send for Reservation {}
unsafe impl Sync for Reservation {}
#[cfg(feature = "probe")]
pub(crate) fn readable(ptr: *const u8, len: usize) -> bool {
if len == 0 {
return true;
}
let last = unsafe { ptr.add(len - 1) };
query_readable(ptr) && query_readable(last)
}
#[cfg(feature = "probe")]
pub(crate) fn handles() -> Option<u32> {
use windows_sys::Win32::System::Threading::GetProcessHandleCount;
let mut count = 0u32;
let ok = unsafe { GetProcessHandleCount(current_process(), &raw mut count) };
(ok != 0).then_some(count)
}
#[cfg(feature = "probe")]
fn query_readable(ptr: *const u8) -> bool {
use windows_sys::Win32::System::Memory::{MEMORY_BASIC_INFORMATION, VirtualQuery};
let mut info: MEMORY_BASIC_INFORMATION = unsafe { core::mem::zeroed() };
let written = unsafe {
VirtualQuery(
ptr.cast::<c_void>(),
&raw mut info,
size_of::<MEMORY_BASIC_INFORMATION>(),
)
};
if written == 0 {
return false;
}
if info.State != MEM_COMMIT {
return false;
}
let unreadable = PAGE_NOACCESS | PAGE_EXECUTE | PAGE_GUARD;
info.Protect != 0 && info.Protect & unreadable == 0
}