use super::mem;
use winapi::shared::basetsd::SIZE_T;
use winapi::shared::minwindef::{BOOL, DWORD, LPCVOID, LPVOID};
use winapi::shared::ntdef::LPCWSTR;
use winapi::um::memoryapi::{
CreateFileMappingW, MapViewOfFileEx, UnmapViewOfFile, VirtualAlloc,
VirtualFree, FILE_MAP_ALL_ACCESS,
};
use winapi::um::winnt::{
MEM_RELEASE, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE, SEC_COMMIT,
};
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::minwinbase::LPSECURITY_ATTRIBUTES;
use winapi::um::sysinfoapi::{GetSystemInfo, LPSYSTEM_INFO, SYSTEM_INFO};
pub use winapi::shared::ntdef::HANDLE;
use super::AllocError;
pub fn allocation_granularity() -> usize {
unsafe {
let mut system_info = mem::MaybeUninit::<SYSTEM_INFO>::uninit();
GetSystemInfo(system_info.as_mut_ptr() as LPSYSTEM_INFO);
let system_info = system_info.assume_init();
let allocation_granularity =
system_info.dwAllocationGranularity as usize;
let page_size = system_info.dwPageSize as usize;
page_size.max(allocation_granularity)
}
}
pub fn allocate_mirrored(size: usize) -> Result<*mut u8, AllocError> {
const MAX_NO_ALLOC_ITERS: usize = 10;
unsafe {
let half_size = size / 2;
assert!(size != 0);
assert!(half_size % allocation_granularity() == 0);
let file_mapping = create_file_mapping(half_size)?;
let mut no_iters = 0;
let virt_ptr = loop {
if no_iters > MAX_NO_ALLOC_ITERS {
close_file_mapping(file_mapping)
.expect("freeing physical memory failed");
return Err(AllocError::Other);
}
let virt_ptr = reserve_virtual_memory(size)?;
if map_view_of_file(file_mapping, half_size, virt_ptr).is_err() {
no_iters += 1;
continue;
}
if map_view_of_file(
file_mapping,
half_size,
virt_ptr.offset(half_size as isize),
)
.is_err()
{
no_iters += 1;
if unmap_view_of_file(virt_ptr).is_err() {
close_file_mapping(file_mapping)
.expect("freeing physical memory failed");
panic!("unmapping first half of memory failed")
}
continue;
}
break virt_ptr;
};
close_file_mapping(file_mapping).expect("closing file handle failed");
Ok(virt_ptr)
}
}
pub unsafe fn deallocate_mirrored(ptr: *mut u8, size: usize) {
assert!(!ptr.is_null());
assert!(size != 0);
assert!(size % allocation_granularity() == 0);
let half_size = size / 2;
unmap_view_of_file(ptr).expect("unmapping first buffer half failed");
let second_half_ptr = ptr.offset(half_size as isize);
unmap_view_of_file(second_half_ptr)
.expect("unmapping second buffer half failed");
}
fn create_file_mapping(size: usize) -> Result<HANDLE, AllocError> {
unsafe {
assert!(size != 0);
assert!(size % allocation_granularity() == 0);
let dw_maximum_size_low: DWORD = size as DWORD;
let dw_maximum_size_high: DWORD =
match (mem::size_of::<DWORD>(), mem::size_of::<usize>()) {
(4, 4) | (8, 8) => 0,
(4, 8) => (size >> 32) as DWORD,
_ => unimplemented!(),
};
let h: HANDLE = CreateFileMappingW(
INVALID_HANDLE_VALUE as HANDLE,
0 as LPSECURITY_ATTRIBUTES,
PAGE_READWRITE | SEC_COMMIT as DWORD,
dw_maximum_size_high,
dw_maximum_size_low,
0 as LPCWSTR,
);
if h.is_null() {
let s = tiny_str!("create_file_mapping (with size: {})", size);
print_error(s.as_str());
return Err(AllocError::Oom);
}
Ok(h)
}
}
unsafe fn close_file_mapping(file_mapping: HANDLE) -> Result<(), ()> {
assert!(!file_mapping.is_null());
let r: BOOL = CloseHandle(file_mapping);
if r == 0 {
print_error("close_file_mapping");
return Err(());
}
Ok(())
}
fn reserve_virtual_memory(size: usize) -> Result<(*mut u8), AllocError> {
unsafe {
assert!(size != 0);
assert!(size % allocation_granularity() == 0);
let r: LPVOID = VirtualAlloc(
0 as LPVOID,
size as SIZE_T,
MEM_RESERVE,
PAGE_NOACCESS,
);
if r.is_null() {
print_error("reserve_virtual_memory(alloc failed)");
return Err(AllocError::Oom);
}
let fr = VirtualFree(
r,
0 as SIZE_T,
MEM_RELEASE as DWORD,
);
if fr == 0 {
print_error("reserve_virtual_memory(free failed)");
return Err(AllocError::Other);
}
Ok(r as *mut u8)
}
}
unsafe fn map_view_of_file(
file_mapping: HANDLE, size: usize, address: *mut u8,
) -> Result<(), ()> {
assert!(!file_mapping.is_null());
assert!(!address.is_null());
assert!(size != 0);
assert!(size % allocation_granularity() == 0);
let r: LPVOID = MapViewOfFileEx(
file_mapping,
FILE_MAP_ALL_ACCESS,
0 as DWORD,
0 as DWORD,
size as SIZE_T,
address as LPVOID,
);
if r.is_null() {
print_error("map_view_of_file");
return Err(());
}
debug_assert!(r == address as LPVOID);
Ok(())
}
unsafe fn unmap_view_of_file(address: *mut u8) -> Result<(), ()> {
assert!(!address.is_null());
let r = UnmapViewOfFile( address as LPCVOID);
if r == 0 {
print_error("unmap_view_of_file");
return Err(());
}
Ok(())
}
#[cfg(all(debug_assertions, feature = "use_std"))]
fn print_error(location: &str) {
eprintln!(
"Error at {}: {}",
location,
::std::io::Error::last_os_error()
);
}
#[cfg(not(all(debug_assertions, feature = "use_std")))]
fn print_error(_location: &str) {}