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
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::aliases::WinResult;
use crate::co;
use crate::handles::{HFILE, HFILEMAP, HFILEMAPVIEW};

/// Access type for [`MappedFile::open`](crate::MappedFile::open).
pub enum MappedFileAccess {
	Read,
	ReadWrite,
}

/// Manages a memory-mapped file, which can be read/written through slices.
pub struct MappedFile {
	access: MappedFileAccess,
	hfile: HFILE,
	hmap: HFILEMAP,
	hview: HFILEMAPVIEW,
	size: usize,
}

impl Drop for MappedFile {
	fn drop(&mut self) {
		if !self.hview.is_null() { let _ = self.hview.UnmapViewOfFile(); }
		if !self.hmap.is_null() { let _ = self.hmap.CloseHandle(); }
		if !self.hfile.is_null() { let _ = self.hfile.CloseHandle(); }
	}
}

impl MappedFile {
	/// Opens a file and maps it in memory according to the given permissions.
	pub fn open(
		file_path: &str, access: MappedFileAccess) -> WinResult<MappedFile>
	{
		let (hfile, _) = HFILE::CreateFile(
			file_path,
			match access {
				MappedFileAccess::Read => co::GENERIC::READ,
				MappedFileAccess::ReadWrite => co::GENERIC::READ | co::GENERIC::WRITE,
			},
			match access {
				MappedFileAccess::Read => co::FILE_SHARE::READ,
				MappedFileAccess::ReadWrite => co::FILE_SHARE::NoValue,
			},
			None,
			match access {
				MappedFileAccess::Read => co::DISPOSITION::OPEN_EXISTING,
				MappedFileAccess::ReadWrite => co::DISPOSITION::OPEN_ALWAYS
			},
			co::FILE_ATTRIBUTE::NORMAL,
			None,
		)?;

		let mut new_self = Self {
			access,
			hfile,
			hmap: HFILEMAP::NULL,
			hview: HFILEMAPVIEW::NULL,
			size: 0,
		};
		new_self.map_in_memory()?;
		Ok(new_self)
	}

	fn map_in_memory(&mut self) -> WinResult<()> {
		self.hmap = self.hfile.CreateFileMapping(
			None,
			match self.access {
				MappedFileAccess::Read => co::PAGE::READONLY,
				MappedFileAccess::ReadWrite => co::PAGE::READWRITE,
			},
			None,
			None,
		)?;

		self.hview = self.hmap.MapViewOfFile(
			match self.access {
				MappedFileAccess::Read => co::FILE_MAP::READ,
				MappedFileAccess::ReadWrite => co::FILE_MAP::READ | co::FILE_MAP::WRITE,
			},
			0,
			None,
		)?;

		self.size = self.hfile.GetFileSizeEx()?; // cache
		Ok(())
	}

	/// Returns the size of the file. This value is cached.
	pub fn size(&self) -> usize {
		self.size
	}

	/// Returns a slice to the mapped memory.
	pub fn as_slice(&self) -> &[u8] {
		self.hview.as_slice(self.size)
	}

	/// Returns a mutable slice to the mapped memory.
	pub fn as_mut_slice(&mut self) -> &mut [u8] {
		self.hview.as_mut_slice(self.size)
	}

	/// Resizes the file, which will be remapped in memory. All slices must be
	/// recreated.
	pub fn resize(&mut self, num_bytes: usize) -> WinResult<()> {
		self.hview.UnmapViewOfFile()?;
		self.hmap.CloseHandle()?;

		self.hfile.SetFilePointerEx(num_bytes as _, co::FILE_STARTING_POINT::BEGIN)?;
		self.hfile.SetEndOfFile()?;
		self.hfile.SetFilePointerEx(0, co::FILE_STARTING_POINT::BEGIN)?;

		self.map_in_memory()?;
		Ok(())
	}
}