use std::fs::File;
use std::io;
use std::os::windows::io::AsRawHandle;
use std::ptr;
use winapi::um::memoryapi::{
CreateFileMappingW, MapViewOfFileEx, FlushViewOfFile, UnmapViewOfFile,
FILE_MAP_READ, FILE_MAP_WRITE, FILE_MAP_EXECUTE, FILE_MAP_COPY,
};
use winapi::um::winnt::{
PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE,
SEC_COMMIT, SEC_RESERVE, SEC_LARGE_PAGES, MEM_COMMIT, MEM_RESERVE,
};
use winapi::um::handleapi::CloseHandle;
use winapi::um::sysinfoapi::GetSystemInfo;
use winapi::um::winbase::VirtualAlloc;
use winapi::shared::minwindef::{DWORD, LPVOID};
use winapi::shared::basetsd::SIZE_T;
use crate::error::{Error, Result};
use crate::mmap::MmapRaw;
use crate::advanced::{HugePageSize, NumaPolicy};
use crate::platform::Advice;
use crate::utils::alignment;
pub unsafe fn map_file(
file: &File,
offset: u64,
len: usize,
readable: bool,
writable: bool,
executable: bool,
huge_pages: Option<HugePageSize>,
numa_policy: Option<NumaPolicy>,
stack: bool,
copy_on_write: bool,
populate: bool,
alignment: Option<usize>,
) -> Result<MmapRaw> {
let page_protection = if executable {
if writable {
PAGE_EXECUTE_READWRITE
} else {
PAGE_EXECUTE_READ
}
} else if writable {
PAGE_READWRITE
} else {
PAGE_READONLY
};
let mut desired_access = 0;
if readable {
desired_access |= FILE_MAP_READ;
}
if writable {
if copy_on_write {
desired_access |= FILE_MAP_COPY;
} else {
desired_access |= FILE_MAP_WRITE;
}
}
if executable {
desired_access |= FILE_MAP_EXECUTE;
}
let mut additional_flags = SEC_COMMIT;
if huge_pages.is_some() {
additional_flags |= SEC_LARGE_PAGES;
}
let system_info = get_system_info();
let page_size = system_info.dwPageSize as u64;
let aligned_offset = offset & !(page_size - 1);
let offset_delta = offset - aligned_offset;
let aligned_len = len + offset_delta as usize;
let maximum_size_high = ((aligned_offset + aligned_len as u64) >> 32) as DWORD;
let maximum_size_low = ((aligned_offset + aligned_len as u64) & 0xFFFFFFFF) as DWORD;
let file_mapping = CreateFileMappingW(
file.as_raw_handle(),
ptr::null_mut(),
page_protection | additional_flags,
maximum_size_high,
maximum_size_low,
ptr::null(),
);
if file_mapping.is_null() {
return Err(Error::Io(io::Error::last_os_error()));
}
let offset_high = (aligned_offset >> 32) as DWORD;
let offset_low = (aligned_offset & 0xFFFFFFFF) as DWORD;
let mut aligned_addr: LPVOID = ptr::null_mut();
if let Some(align) = alignment {
if align > page_size as usize {
return Err(Error::InvalidArgument("Custom alignment beyond page size is not supported on Windows".into()));
}
}
let addr = MapViewOfFileEx(
file_mapping,
desired_access,
offset_high,
offset_low,
aligned_len as SIZE_T,
aligned_addr,
);
CloseHandle(file_mapping);
if addr.is_null() {
return Err(Error::Io(io::Error::last_os_error()));
}
if populate {
for i in (0..aligned_len).step_by(page_size as usize) {
ptr::read_volatile((addr as usize + i) as *const u8);
}
}
let ptr = (addr as usize + offset_delta as usize) as *mut u8;
Ok(MmapRaw { ptr, len })
}
pub unsafe fn map_anon(
len: usize,
readable: bool,
writable: bool,
executable: bool,
huge_pages: Option<HugePageSize>,
numa_policy: Option<NumaPolicy>,
stack: bool,
populate: bool,
alignment: Option<usize>,
) -> Result<MmapRaw> {
let page_protection = if executable {
if writable {
PAGE_EXECUTE_READWRITE
} else {
PAGE_EXECUTE_READ
}
} else if writable {
PAGE_READWRITE
} else {
PAGE_READONLY
};
let mut allocation_type = MEM_RESERVE | MEM_COMMIT;
if huge_pages.is_some() {
allocation_type |= SEC_LARGE_PAGES as DWORD;
}
let system_info = get_system_info();
let page_size = system_info.dwPageSize as usize;
let mut aligned_len = len;
let mut aligned_addr: LPVOID = ptr::null_mut();
if let Some(align) = alignment {
if align > page_size {
let extra = align - 1;
aligned_len = len + extra;
return Err(Error::InvalidArgument("Custom alignment beyond page size is not supported on Windows".into()));
}
}
let addr = VirtualAlloc(
aligned_addr,
aligned_len as SIZE_T,
allocation_type,
page_protection,
);
if addr.is_null() {
return Err(Error::Io(io::Error::last_os_error()));
}
if populate {
for i in (0..aligned_len).step_by(page_size) {
ptr::read_volatile((addr as usize + i) as *const u8);
}
}
Ok(MmapRaw { ptr: addr as *mut u8, len: aligned_len })
}
pub unsafe fn flush(addr: *mut u8, len: usize, _async_flush: bool) -> Result<()> {
let result = FlushViewOfFile(addr as LPVOID, len as SIZE_T);
if result != 0 {
Ok(())
} else {
Err(Error::Io(io::Error::last_os_error()))
}
}
pub unsafe fn unmap(addr: *mut u8, _len: usize) -> Result<()> {
let result = UnmapViewOfFile(addr as LPVOID);
if result != 0 {
Ok(())
} else {
Err(Error::Io(io::Error::last_os_error()))
}
}
pub unsafe fn advise(_addr: *mut u8, _len: usize, _advice: Advice) -> Result<()> {
Ok(())
}
#[inline]
fn get_system_info() -> winapi::um::sysinfoapi::SYSTEM_INFO {
let mut system_info: winapi::um::sysinfoapi::SYSTEM_INFO = unsafe { std::mem::zeroed() };
unsafe { GetSystemInfo(&mut system_info) };
system_info
}