1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
extern crate libc;

use std::{io, mem, ptr, slice};
use std::fs::File;
use std::path::Path;
use std::os::unix::io::AsRawFd;

/// Memory mapped file.
pub struct FileMap {
	ptr: *mut libc::c_void,
	size: usize,
}
impl FileMap {
	/// Maps the whole file into memory.
	pub fn open<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<FileMap> {
		Self::_open(path.as_ref())
	}
	fn _open(path: &Path) -> io::Result<FileMap> {
		// Open the file and get its fd
		let file = File::open(path)?;
		let fd = file.as_raw_fd();

		// Find its file size aligned to page boundary
		let size = unsafe {
			let mut stat = mem::uninitialized();
			if libc::fstat(fd, &mut stat) < 0 {
				return Err(io::Error::last_os_error());
			}
			// Round up to nearest multiple of page_size
			let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
			(stat.st_size as usize + page_size - 1) & !(page_size - 1)
		};

		// Mmap the file
		unsafe {
			let ptr = libc::mmap(
				ptr::null_mut(),
				size as libc::size_t,
				libc::PROT_READ,
				libc::MAP_PRIVATE,
				fd,
				0
			);
			if ptr == libc::MAP_FAILED {
				Err(io::Error::last_os_error())
			}
			else {
				Ok(FileMap { ptr, size })
			}
		}
	}
}
impl Drop for FileMap {
	fn drop(&mut self) {
		unsafe {
			let _result = libc::munmap(self.ptr, self.size as libc::size_t);
			debug_assert_eq!(_result, 0, "unable to munmap: {}", io::Error::last_os_error());
		}
	}
}
impl AsRef<[u8]> for FileMap {
	fn as_ref(&self) -> &[u8] {
		unsafe {
			slice::from_raw_parts(self.ptr as *const _, self.size)
		}
	}
}