use std::{convert, fs, io, path, ptr, slice};
pub type Cursor<'a> = io::Cursor<&'a [u8]>;
#[derive(Debug)]
pub enum MappedFile<'a> {
Mmaped(&'a [u8]),
Slice(&'a [u8]),
}
impl<'a> MappedFile<'a> {
pub fn new<P>(path: P) -> io::Result<MappedFile<'a>>
where
P: convert::AsRef<path::Path>,
{
let file = fs::File::open(path)?;
memory_map_file(file)
}
pub fn cursor(&self) -> Cursor<'a> {
let slice = match self {
Self::Mmaped(mmaped) => mmaped,
Self::Slice(slice_) => slice_,
};
io::Cursor::new(slice)
}
}
impl<'a> From<&'a [u8]> for MappedFile<'a> {
fn from(value: &'a [u8]) -> Self {
Self::Slice(value)
}
}
impl<'a> Drop for MappedFile<'a> {
fn drop(&mut self) {
match self {
Self::Mmaped(mmap) => unmap_memory_mapped_file(mmap).expect("failed to unmap"),
Self::Slice(_) => {}
}
}
}
#[cfg(windows)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
mod windows {
use std::os::windows::prelude::AsRawHandle;
use std::os::windows::raw::HANDLE;
use super::*;
const PAGE_READONLY: DWORD = 2;
const FILE_MAP_READ: DWORD = 4;
type DWORD = u32;
type BOOL = u32;
type SIZE_T = usize;
type LPCSTR = *mut u8;
type LPVOID = *const u8;
extern "system" {
fn CreateFileMappingA(
h: HANDLE,
file_mapping_attrs: *const u8,
protect: DWORD,
max_size_high: DWORD,
max_size_low: DWORD,
name: LPCSTR,
) -> HANDLE;
fn MapViewOfFile(
file_mapping_object: HANDLE,
desired_access: DWORD,
file_offset_high: DWORD,
file_offset_low: DWORD,
number_of_bytes_to_map: SIZE_T,
) -> LPVOID;
fn CloseHandle(h: HANDLE) -> BOOL;
fn UnmapViewOfFile(base_address: LPVOID) -> BOOL;
}
pub fn memory_map_file<'a>(file: fs::File) -> Result<MappedFile<'a>, io::Error> {
let file_handle = file.as_raw_handle();
let mapping_handle = unsafe {
CreateFileMappingA(
file_handle,
ptr::null_mut(),
PAGE_READONLY,
0,
0,
ptr::null_mut(),
)
};
if mapping_handle.is_null() {
return Err(io::Error::last_os_error());
}
let size = file.metadata()?.len().try_into().unwrap();
let base = unsafe { MapViewOfFile(mapping_handle, FILE_MAP_READ, 0, 0, size) };
if base.is_null() {
unsafe {
CloseHandle(mapping_handle);
}
return Err(io::Error::last_os_error());
}
unsafe {
CloseHandle(mapping_handle);
}
if size > isize::MAX.try_into().unwrap() {
panic!("slice is too large");
}
Ok(MappedFile::Mmaped(unsafe {
slice::from_raw_parts(base, size)
}))
}
pub fn unmap_memory_mapped_file(data: &[u8]) -> Result<(), io::Error> {
match unsafe { UnmapViewOfFile(data.as_ptr()) } {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
}
#[cfg(windows)]
use windows::*;
#[cfg(unix)]
mod unix {
use std::os::fd::AsRawFd;
use super::*;
const PROT_READ: i32 = 1;
const MAP_SHARED: i32 = 1;
const MAP_FAILED: *const u8 = usize::MAX as _;
extern "system" {
fn mmap(
addr: *const u8,
length: usize,
prot: i32,
flags: i32,
fd: i32,
offset: i32,
) -> *const u8;
fn munmap(addr: *const u8, length: usize) -> i32;
}
pub fn memory_map_file<'a>(file: fs::File) -> Result<MappedFile<'a>, io::Error> {
let file_fd = file.as_raw_fd();
let size = file.metadata()?.len().try_into().unwrap();
let ret = unsafe { mmap(ptr::null_mut(), size, PROT_READ, MAP_SHARED, file_fd, 0) };
if ret == MAP_FAILED {
return Err(io::Error::last_os_error());
}
if size > isize::MAX.try_into().unwrap() {
panic!("slice is too large");
}
Ok(MappedFile::Mmaped(unsafe {
slice::from_raw_parts(ret, size)
}))
}
pub fn unmap_memory_mapped_file(data: &[u8]) -> Result<(), io::Error> {
match unsafe { munmap(data.as_ptr(), data.len()) } {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
}
#[cfg(unix)]
use unix::*;
#[cfg(not(any(windows, unix)))]
fn unimplemented() -> u32 {}