#![deny(warnings)]
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use windows::MmapInner;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
use unix::MmapInner;
use std::cell::UnsafeCell;
use std::fs::{self, File};
use std::io::{Error, ErrorKind, Result};
use std::path::Path;
use std::rc::Rc;
use std::slice;
use std::sync::Arc;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Protection {
Read,
ReadWrite,
ReadCopy,
}
impl Protection {
fn as_open_options(self) -> fs::OpenOptions {
let mut options = fs::OpenOptions::new();
options.read(true)
.write(self.write());
options
}
pub fn write(self) -> bool {
match self {
Protection::ReadWrite | Protection::ReadCopy => true,
_ => false,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct MmapOptions {
pub stack: bool,
}
pub struct Mmap {
inner: MmapInner
}
impl Mmap {
pub fn open(file: &File, prot: Protection) -> Result<Mmap> {
let len = try!(file.metadata()).len() as usize;
MmapInner::open(file, prot, 0, len).map(|inner| Mmap { inner: inner })
}
pub fn open_path<P>(path: P, prot: Protection) -> Result<Mmap>
where P: AsRef<Path> {
let file = try!(prot.as_open_options().open(path));
let len = try!(file.metadata()).len() as usize;
MmapInner::open(&file, prot, 0, len).map(|inner| Mmap { inner: inner })
}
pub fn open_with_offset(file: &File,
prot: Protection,
offset: usize,
len: usize) -> Result<Mmap> {
MmapInner::open(file, prot, offset, len).map(|inner| Mmap { inner: inner })
}
pub fn anonymous(len: usize, prot: Protection) -> Result<Mmap> {
Mmap::anonymous_with_options(len, prot, Default::default())
}
pub fn anonymous_with_options(len: usize,
prot: Protection,
options: MmapOptions) -> Result<Mmap> {
MmapInner::anonymous(len, prot, options).map(|inner| Mmap { inner: inner })
}
pub fn flush(&mut self) -> Result<()> {
let len = self.len();
self.inner.flush(0, len)
}
pub fn flush_async(&mut self) -> Result<()> {
let len = self.len();
self.inner.flush_async(0, len)
}
pub fn flush_range(&mut self, offset: usize, len: usize) -> Result<()> {
self.inner.flush(offset, len)
}
pub fn flush_async_range(&mut self, offset: usize, len: usize) -> Result<()> {
self.inner.flush_async(offset, len)
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn ptr(&self) -> *const u8 {
self.inner.ptr()
}
pub fn mut_ptr(&mut self) -> *mut u8 {
self.inner.mut_ptr()
}
pub unsafe fn as_slice(&self) -> &[u8] {
slice::from_raw_parts(self.ptr(), self.len())
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
slice::from_raw_parts_mut(self.mut_ptr(), self.len())
}
pub fn into_view(self) -> MmapView {
let len = self.len();
MmapView { inner: Rc::new(UnsafeCell::new(self)),
offset: 0,
len: len }
}
pub fn into_view_sync(self) -> MmapViewSync {
let len = self.len();
MmapViewSync { inner: Arc::new(UnsafeCell::new(self)),
offset: 0,
len: len }
}
}
pub struct MmapView {
inner: Rc<UnsafeCell<Mmap>>,
offset: usize,
len: usize,
}
impl MmapView {
pub fn split_at(self, offset: usize) -> Result<(MmapView, MmapView)> {
if self.len < offset {
return Err(Error::new(ErrorKind::InvalidInput,
"mmap view split offset must be less than the view length"));
}
let MmapView { inner, offset: self_offset, len: self_len } = self;
Ok((MmapView { inner: inner.clone(),
offset: self_offset,
len: offset },
MmapView { inner: inner,
offset: self_offset + offset,
len: self_len - offset }))
}
pub fn restrict(&mut self, offset: usize, len: usize) -> Result<()> {
if offset + len > self.len {
return Err(Error::new(ErrorKind::InvalidInput,
"mmap view may only be restricted to a subrange \
of the current view"));
}
self.offset = self.offset + offset;
self.len = len;
Ok(())
}
fn inner(&self) -> &Mmap {
unsafe {
&*self.inner.get()
}
}
fn inner_mut(&self) -> &mut Mmap {
unsafe {
&mut *self.inner.get()
}
}
pub fn flush(&mut self) -> Result<()> {
self.inner_mut().flush_range(self.offset, self.len)
}
pub fn flush_async(&mut self) -> Result<()> {
self.inner_mut().flush_async_range(self.offset, self.len)
}
pub fn len(&self) -> usize {
self.len
}
pub fn ptr(&self) -> *const u8 {
unsafe { self.inner().ptr().offset(self.offset as isize) }
}
pub fn mut_ptr(&mut self) -> *mut u8 {
unsafe { self.inner_mut().mut_ptr().offset(self.offset as isize) }
}
pub unsafe fn as_slice(&self) -> &[u8] {
&self.inner().as_slice()[self.offset..self.offset + self.len]
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.inner_mut().as_mut_slice()[self.offset..self.offset + self.len]
}
}
pub struct MmapViewSync {
inner: Arc<UnsafeCell<Mmap>>,
offset: usize,
len: usize,
}
impl MmapViewSync {
pub fn split_at(self, offset: usize) -> Result<(MmapViewSync, MmapViewSync)> {
if self.len < offset {
return Err(Error::new(ErrorKind::InvalidInput,
"mmap view split offset must be less than the view length"));
}
let MmapViewSync { inner, offset: self_offset, len: self_len } = self;
Ok((MmapViewSync { inner: inner.clone(),
offset: self_offset,
len: offset },
MmapViewSync { inner: inner,
offset: self_offset + offset,
len: self_len - offset }))
}
pub fn restrict(&mut self, offset: usize, len: usize) -> Result<()> {
if offset + len > self.len {
return Err(Error::new(ErrorKind::InvalidInput,
"mmap view may only be restricted to a subrange \
of the current view"));
}
self.offset = self.offset + offset;
self.len = len;
Ok(())
}
fn inner(&self) -> &Mmap {
unsafe {
&*self.inner.get()
}
}
fn inner_mut(&self) -> &mut Mmap {
unsafe {
&mut *self.inner.get()
}
}
pub fn flush(&mut self) -> Result<()> {
self.inner_mut().flush()
}
pub fn flush_async(&mut self) -> Result<()> {
self.inner_mut().flush_async()
}
pub fn len(&self) -> usize {
self.len
}
pub fn ptr(&self) -> *const u8 {
unsafe { self.inner().ptr().offset(self.offset as isize) }
}
pub fn mut_ptr(&mut self) -> *mut u8 {
unsafe { self.inner_mut().mut_ptr().offset(self.offset as isize) }
}
pub unsafe fn as_slice(&self) -> &[u8] {
&self.inner().as_slice()[self.offset..self.offset + self.len]
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.inner_mut().as_mut_slice()[self.offset..self.offset + self.len]
}
}
unsafe impl Sync for MmapViewSync {}
unsafe impl Send for MmapViewSync {}
#[cfg(test)]
mod test {
extern crate tempdir;
use std::{fs, iter};
use std::io::{Read, Write};
use std::thread;
use std::sync::Arc;
use super::*;
#[test]
fn map_file() {
let expected_len = 128;
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
fs::OpenOptions::new()
.write(true)
.create(true)
.open(&path).unwrap()
.set_len(expected_len as u64).unwrap();
let mut mmap = Mmap::open_path(path, Protection::ReadWrite).unwrap();
let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = iter::repeat(0).take(len).collect::<Vec<_>>();
let incr = (0..len).map(|n| n as u8).collect::<Vec<_>>();
assert_eq!(&zeros[..], unsafe { mmap.as_slice() });
unsafe { mmap.as_mut_slice() }.write_all(&incr[..]).unwrap();
assert_eq!(&incr[..], unsafe { mmap.as_slice() });
}
#[test]
fn map_empty_file() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
fs::OpenOptions::new()
.write(true)
.create(true)
.open(&path).unwrap();
assert!(Mmap::open_path(path, Protection::ReadWrite).is_err());
}
#[test]
fn map_anon() {
let expected_len = 128;
let mut mmap = Mmap::anonymous(expected_len, Protection::ReadWrite).unwrap();
let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = iter::repeat(0).take(len).collect::<Vec<_>>();
let incr = (0..len).map(|n| n as u8).collect::<Vec<_>>();
assert_eq!(&zeros[..], unsafe { mmap.as_slice() });
unsafe { mmap.as_mut_slice() }.write_all(&incr[..]).unwrap();
assert_eq!(&incr[..], unsafe { mmap.as_slice() });
}
#[test]
fn file_write() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path).unwrap();
file.set_len(128).unwrap();
let write = b"abc123";
let mut read = [0u8; 6];
let mut mmap = Mmap::open_path(&path, Protection::ReadWrite).unwrap();
unsafe { mmap.as_mut_slice() }.write_all(write).unwrap();
mmap.flush().unwrap();
file.read(&mut read).unwrap();
assert_eq!(write, &read);
}
#[test]
fn flush_range() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path).unwrap();
file.set_len(128).unwrap();
let write = b"abc123";
let mut mmap = Mmap::open_with_offset(&file, Protection::ReadWrite, 2, write.len()).unwrap();
unsafe { mmap.as_mut_slice() }.write_all(write).unwrap();
mmap.flush_range(0, write.len()).unwrap();
}
#[test]
fn map_copy() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path).unwrap();
file.set_len(128).unwrap();
let nulls = b"\0\0\0\0\0\0";
let write = b"abc123";
let mut read = [0u8; 6];
let mut mmap = Mmap::open_path(&path, Protection::ReadCopy).unwrap();
unsafe { mmap.as_mut_slice() }.write(write).unwrap();
mmap.flush().unwrap();
unsafe { mmap.as_slice() }.read(&mut read).unwrap();
assert_eq!(write, &read);
file.read(&mut read).unwrap();
assert_eq!(nulls, &read);
let mmap2 = Mmap::open_path(&path, Protection::Read).unwrap();
unsafe { mmap2.as_slice() }.read(&mut read).unwrap();
assert_eq!(nulls, &read);
}
#[test]
fn map_offset() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
file.set_len(500000 as u64).unwrap();
let offset = 5099;
let len = 50050;
let mut mmap = Mmap::open_with_offset(&file,
Protection::ReadWrite,
offset,
len).unwrap();
assert_eq!(len, mmap.len());
let zeros = iter::repeat(0).take(len).collect::<Vec<_>>();
let incr = (0..len).map(|n| n as u8).collect::<Vec<_>>();
assert_eq!(&zeros[..], unsafe { mmap.as_slice() });
unsafe { mmap.as_mut_slice() }.write_all(&incr[..]).unwrap();
assert_eq!(&incr[..], unsafe { mmap.as_slice() });
}
#[test]
fn index() {
let mut mmap = Mmap::anonymous(128, Protection::ReadWrite).unwrap();
unsafe { mmap.as_mut_slice()[0] = 42 };
assert_eq!(42, unsafe { mmap.as_slice()[0] });
}
#[test]
fn sync_send() {
let mmap = Arc::new(Mmap::anonymous(128, Protection::ReadWrite).unwrap());
thread::spawn(move || {
unsafe {
mmap.as_slice();
}
});
}
#[test]
fn view() {
let len = 128;
let split = 32;
let mut view = Mmap::anonymous(len, Protection::ReadWrite).unwrap().into_view();
let incr = (0..len).map(|n| n as u8).collect::<Vec<_>>();
unsafe { view.as_mut_slice() }.write_all(&incr[..]).unwrap();
let (mut view1, view2) = view.split_at(32).unwrap();
assert_eq!(view1.len(), split);
assert_eq!(view2.len(), len - split);
assert_eq!(&incr[0..split], unsafe { view1.as_slice() });
assert_eq!(&incr[split..], unsafe { view2.as_slice() });
view1.restrict(10, 10).unwrap();
assert_eq!(&incr[10..20], unsafe { view1.as_slice() })
}
#[test]
fn view_sync() {
let len = 128;
let split = 32;
let mut view = Mmap::anonymous(len, Protection::ReadWrite).unwrap().into_view_sync();
let incr = (0..len).map(|n| n as u8).collect::<Vec<_>>();
unsafe { view.as_mut_slice() }.write_all(&incr[..]).unwrap();
let (mut view1, view2) = view.split_at(32).unwrap();
assert_eq!(view1.len(), split);
assert_eq!(view2.len(), len - split);
assert_eq!(&incr[0..split], unsafe { view1.as_slice() });
assert_eq!(&incr[split..], unsafe { view2.as_slice() });
view1.restrict(10, 10).unwrap();
assert_eq!(&incr[10..20], unsafe { view1.as_slice() })
}
#[test]
fn view_write() {
let len = 131072; let split = 66560;
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path).unwrap();
file.set_len(len).unwrap();
let incr = (0..len).map(|n| n as u8).collect::<Vec<_>>();
let incr1 = incr[0..split].to_owned();
let incr2 = incr[split..].to_owned();
let (mut view1, mut view2) = Mmap::open_path(&path, Protection::ReadWrite)
.unwrap()
.into_view_sync()
.split_at(split)
.unwrap();
let join1 = thread::spawn(move || {
unsafe { view1.as_mut_slice() }.write(&incr1).unwrap();
view1.flush().unwrap();
});
let join2 = thread::spawn(move || {
unsafe { view2.as_mut_slice() }.write(&incr2).unwrap();
view2.flush().unwrap();
});
join1.join().unwrap();
join2.join().unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
assert_eq!(incr, &buf[..]);
}
#[test]
fn view_sync_send() {
let view = Arc::new(Mmap::anonymous(128, Protection::ReadWrite).unwrap().into_view_sync());
thread::spawn(move || {
unsafe {
view.as_slice();
}
});
}
}