use libc;
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::io::{self, Result, Error, ErrorKind};
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::winnt::{HANDLE, PAGE_READWRITE};
use winapi::um::memoryapi::{CreateFileMappingW, OpenFileMappingW, FILE_MAP_ALL_ACCESS, MapViewOfFile, UnmapViewOfFile};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::minwinbase::SECURITY_ATTRIBUTES;
use winapi::shared::minwindef::DWORD;
use winapi::ctypes::c_void;
pub type MemoryId = HANDLE;
pub const INVALID_MEMORY_ID: MemoryId = INVALID_HANDLE_VALUE;
pub const NULL: HANDLE = 0 as HANDLE;
fn last_error() -> io::Error {
io::Error::from_raw_os_error(unsafe { GetLastError() as i32 })
}
#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i8 i16 i32 i64 isize }
pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
if t.is_minus_one() {
Err(last_error())
} else {
Ok(t)
}
}
#[derive(Debug)]
pub struct Memory {
id: HANDLE,
first: Option<*mut libc::c_void>,
size: usize,
}
impl Memory {
pub fn new(mut name: String, size: usize, _path_name: Option<String>) -> Result<Memory> {
if name.len() == 0 {
name = String::from("/tmp");
}
unsafe {
let mut name: Vec<u16> = OsStr::new(&name).encode_wide().chain(once(0)).collect();
let mut handle = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, name.as_mut_ptr());
if handle == NULL {
handle = CreateFileMappingW(INVALID_HANDLE_VALUE, 0 as *mut SECURITY_ATTRIBUTES, PAGE_READWRITE, (size >> 16 >> 16) as DWORD, (size & 0xffffffff) as DWORD, name.as_mut_ptr());
if handle == NULL {
return Err(last_error());
}
}
return Ok(Memory {
id: handle,
first: None,
size: size,
})
}
}
pub fn first_memory(&mut self) -> Result<Option<*mut libc::c_void>> {
self.check_vaild()?;
if self.first.is_some() {
return Ok(self.first);
}
unsafe {
match MapViewOfFile(self.id, FILE_MAP_ALL_ACCESS, 0, 0, 0) {
addr if addr.is_null() => Ok(None),
addr => {
self.first = Some(addr as *mut libc::c_void);
Ok(self.first)
}
}
}
}
pub fn offset_memory(&mut self, offset: usize) -> Result<Option<*mut libc::c_void>> {
if offset >= self.size {
return Err(Error::new(ErrorKind::InvalidData, "offset bigger than size"));
}
if let Some(first) = self.first_memory()? {
unsafe {
return Ok(Some(first.offset(offset as isize)));
}
}
return Ok(None)
}
pub fn deattch(&mut self) -> Result<()> {
self.check_vaild()?;
if self.first.is_none() {
return Ok(());
}
unsafe {
if self.first.is_some() {
cvt(UnmapViewOfFile(self.first.unwrap() as *const c_void))?;
self.first = None;
}
cvt(CloseHandle(self.id))?;
self.id = INVALID_HANDLE_VALUE;
}
return Ok(());
}
pub fn destory(&mut self) -> Result<()> {
self.check_vaild()?;
self.deattch()?;
Ok(())
}
pub fn is_vaild(&self) -> bool {
return self.id != INVALID_MEMORY_ID
}
pub fn check_vaild(&self) -> Result<bool> {
if !self.is_vaild() {
return Err(Error::new(ErrorKind::InvalidData, "no vaild"));
}
return Ok(true);
}
}
impl Drop for Memory {
fn drop(&mut self) {
let _ = self.deattch();
}
}