1 #ifndef _WIN32
2   #define _FILE_OFFSET_BITS 64
3 #endif // _WIN32
4 
5 #include <climits>
6 #include <cstring>
7 #include <system_error>
8 
9 #ifdef _WIN32
10   #include <Windows.h>
11 #else
12   #include <cerrno>
13 
14   #include <fcntl.h>
15   #include <unistd.h>
16   #include <sys/mman.h>
17   #include <sys/stat.h>
18   #include <sys/types.h>
19 #endif // _WIN32
20 
21 #include "mmap.h"
22 
23 using std::nullptr_t;
24 
25 namespace {
26 
27 #ifdef _WIN32
28 namespace win32 {
29 
30 class close_handle {
31 	struct handle {
32 		::HANDLE h = INVALID_HANDLE_VALUE;
33 
handle__anon72b18a280111::win32::close_handle::handle34 		handle(nullptr_t x = nullptr) noexcept {}
handle__anon72b18a280111::win32::close_handle::handle35 		handle(::HANDLE h) noexcept : h{ h } {}
36 
operator ::HANDLE__anon72b18a280111::win32::close_handle::handle37 		operator ::HANDLE() const noexcept { return h; }
38 
operator ==__anon72b18a280111::win32::close_handle::handle39 		bool operator==(nullptr_t) const noexcept { return h != 0 && h != INVALID_HANDLE_VALUE; }
operator !=__anon72b18a280111::win32::close_handle::handle40 		bool operator!=(nullptr_t) const noexcept { return !(*this == nullptr); }
41 	};
42 public:
43 	typedef handle pointer;
44 
operator ()(handle h)45 	void operator()(handle h) noexcept { ::CloseHandle(h); }
46 };
47 
48 typedef std::unique_ptr<void, win32::close_handle> handle_uptr;
49 
50 struct unmap_view_of_file {
operator ()__anon72b18a280111::win32::unmap_view_of_file51 	void operator()(void *ptr) noexcept { ::UnmapViewOfFile(ptr); }
52 };
53 
trap_error(const char * msg="")54 void trap_error(const char *msg = "")
55 {
56 	std::error_code code{ (int)::GetLastError(), std::system_category() };
57 	throw std::system_error{ code, msg };
58 }
59 
utf8_to_wchar(wchar_t unicode_path[MAX_PATH],const char * path)60 void utf8_to_wchar(wchar_t unicode_path[MAX_PATH], const char *path)
61 {
62 	if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, unicode_path, MAX_PATH * sizeof(wchar_t)) == 0)
63 		win32::trap_error("error converting path to UTF-16");
64 }
65 
create_new_file(const char * path,size_t size)66 void create_new_file(const char *path, size_t size)
67 {
68 	wchar_t unicode_path[MAX_PATH] = { 0 };
69 	handle_uptr file_handle_uptr;
70 	::HANDLE file_handle;
71 	::LARGE_INTEGER file_ptr;
72 
73 	win32::utf8_to_wchar(unicode_path, path);
74 
75 	if ((file_handle = ::CreateFileW(unicode_path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) == INVALID_HANDLE_VALUE)
76 		win32::trap_error("error opening file");
77 
78 	file_handle_uptr.reset(file_handle);
79 	file_ptr.QuadPart = size;
80 
81 	if (::SetFilePointerEx(file_handle, file_ptr, nullptr, FILE_BEGIN) == 0)
82 		win32::trap_error("error setting file pointer");
83 	if (::SetEndOfFile(file_handle) == 0)
84 		win32::trap_error("error setting end of file");
85 	if (::CloseHandle(file_handle_uptr.release()) == 0)
86 		win32::trap_error("error closing file handle");
87 }
88 
89 } // namespace win32
90 #else
91 namespace posix {
92 
93 class close_fd {
94 	struct descriptor {
95 		int fd = -1;
96 
97 		descriptor(nullptr_t x = nullptr) noexcept {}
98 		descriptor(int fd) noexcept : fd{ fd } {}
99 
100 		operator int() const noexcept { return fd; }
101 
102 		bool operator==(nullptr_t) const noexcept { return fd < 0; }
103 		bool operator!=(nullptr_t) const noexcept { return !(*this == nullptr); }
104 	};
105 public:
106 	typedef descriptor pointer;
107 
108 	void operator()(descriptor x) noexcept { ::close(x); }
109 };
110 
111 class munmap_file {
112 	struct map_pointer {
113 		void *ptr = MAP_FAILED;
114 
115 		map_pointer(nullptr_t x = nullptr) noexcept {}
116 		map_pointer(void *ptr) noexcept : ptr{ ptr } {}
117 
118 		operator void *() const noexcept { return ptr; }
119 
120 		bool operator==(nullptr_t) const noexcept { return ptr == MAP_FAILED; }
121 		bool operator!=(nullptr_t) const noexcept { return !(*this == nullptr); }
122 	};
123 public:
124 	typedef map_pointer pointer;
125 
126 	size_t size = 0;
127 
128 	void operator()(map_pointer ptr) noexcept { ::munmap(ptr, size); }
129 };
130 
131 void trap_error(const char *msg = "")
132 {
133 	std::error_code code{ errno, std::system_category() };
134 	throw std::system_error{ code, msg };
135 }
136 
137 off_t get_file_size(int fd)
138 {
139 	off_t pos = -1;
140 	off_t ret;
141 
142 	if ((pos = ::lseek(fd, 0, SEEK_CUR)) < 0)
143 		posix::trap_error("error getting file position");
144 	if ((ret = ::lseek(fd, 0, SEEK_END)) < 0)
145 		posix::trap_error("error seeking in file");
146 	if ((pos = ::lseek(fd, pos, SEEK_SET)) < 0)
147 		posix::trap_error("error setting file position");
148 
149 	return ret;
150 }
151 
152 void create_new_file(const char *path, size_t size)
153 {
154 	std::unique_ptr<void, close_fd> fd_uptr;
155 	int fd;
156 
157 	if ((fd = ::creat(path, 00666)) < 0)
158 		posix::trap_error("error creating file");
159 
160 	fd_uptr.reset(fd);
161 
162 	if (ftruncate(fd, size) < 0)
163 		posix::trap_error("error truncating file");
164 	if (close(fd) < 0)
165 		posix::trap_error("error closing file");
166 }
167 
168 } // namespace posix
169 #endif // _WIN32
170 
171 } // namespace
172 
173 
174 #ifdef _WIN32
175 class MemoryMappedFile::impl {
176 	std::unique_ptr<void, win32::unmap_view_of_file> m_map_view;
177 
178 	size_t m_size;
179 	bool m_writable;
180 
map_file(const char * path,DWORD desired_access1,DWORD share_mode,DWORD protect,DWORD desired_access2)181 	void map_file(const char *path, DWORD desired_access1, DWORD share_mode, DWORD protect, DWORD desired_access2)
182 	{
183 		win32::handle_uptr file_handle_uptr;
184 		win32::handle_uptr mapping_handle_uptr;
185 
186 		wchar_t unicode_path[MAX_PATH] = { 0 };
187 		::HANDLE file_handle;
188 		::HANDLE mapping_handle;
189 		void *map_view;
190 		::LARGE_INTEGER file_size;
191 
192 		win32::utf8_to_wchar(unicode_path, path);
193 
194 		if ((file_handle = ::CreateFileW(unicode_path, desired_access1, share_mode, nullptr, OPEN_EXISTING, 0, nullptr)) == INVALID_HANDLE_VALUE)
195 			win32::trap_error("error opening file");
196 
197 		file_handle_uptr.reset(file_handle);
198 
199 		if (GetFileSizeEx(file_handle, &file_size) == 0)
200 			win32::trap_error("error getting file size");
201 		if (file_size.QuadPart > SIZE_MAX)
202 			throw std::runtime_error{ "file too large to map" };
203 
204 		if ((mapping_handle = ::CreateFileMappingW(file_handle, nullptr, protect, file_size.HighPart, file_size.LowPart, nullptr)) == NULL)
205 			win32::trap_error("error creating file mapping");
206 
207 		mapping_handle_uptr.reset(mapping_handle);
208 
209 		if (!(map_view = ::MapViewOfFile(mapping_handle, desired_access2, 0, 0, 0)))
210 			win32::trap_error("error mapping view of file");
211 
212 		m_map_view.reset(map_view);
213 		m_size = static_cast<size_t>(file_size.QuadPart);
214 	}
215 public:
impl()216 	impl() noexcept : m_size{}, m_writable{} {}
217 
size() const218 	size_t size() const noexcept { return m_size; }
219 
read_ptr() const220 	const void *read_ptr() const noexcept { return m_map_view.get(); }
221 
write_ptr() const222 	void *write_ptr() const noexcept { return m_writable ? m_map_view.get() : nullptr; }
223 
map_read(const char * path)224 	void map_read(const char *path)
225 	{
226 		map_file(path, GENERIC_READ, FILE_SHARE_READ, PAGE_READONLY, FILE_MAP_READ);
227 		m_writable = false;
228 	}
229 
map_write(const char * path)230 	void map_write(const char *path)
231 	{
232 		map_file(path, GENERIC_READ | GENERIC_WRITE, 0, PAGE_READWRITE, FILE_MAP_WRITE);
233 		m_writable = true;
234 	}
235 
map_create(const char * path,size_t size)236 	void map_create(const char *path, size_t size)
237 	{
238 		win32::create_new_file(path, size);
239 		map_write(path);
240 	}
241 
flush()242 	void flush()
243 	{
244 		if (m_map_view && ::FlushViewOfFile(m_map_view.get(), 0) == 0)
245 			win32::trap_error("error flushing file");
246 	}
247 
close()248 	void close()
249 	{
250 		flush();
251 		m_map_view.reset();
252 	}
253 };
254 #else
255 class MemoryMappedFile::impl {
256 	std::unique_ptr<void, posix::munmap_file> m_ptr;
257 
258 	size_t m_size;
259 	bool m_writable;
260 
map_file(const char * path,int open_flags,int prot,int mmap_flags)261 	void map_file(const char *path, int open_flags, int prot, int mmap_flags)
262 	{
263 		std::unique_ptr<void, posix::close_fd> fd_uptr;
264 		int fd;
265 		off_t file_size;
266 		void *ptr;
267 
268 		if ((fd = ::open(path, open_flags)) < 0)
269 			posix::trap_error("error opening file");
270 
271 		fd_uptr.reset(fd);
272 
273 		if ((file_size = posix::get_file_size(fd)) > PTRDIFF_MAX)
274 			throw std::runtime_error{ "file too large to map" };
275 
276 		if ((ptr = ::mmap(nullptr, file_size, prot, mmap_flags, fd, 0)) == MAP_FAILED)
277 			posix::trap_error("error mapping file");
278 
279 		m_ptr.reset(ptr);
280 		m_ptr.get_deleter().size = static_cast<size_t>(file_size);
281 	}
282 public:
impl()283 	impl() noexcept : m_size{}, m_writable{} {}
284 
size() const285 	size_t size() const noexcept { return m_ptr.get_deleter().size; }
286 
read_ptr() const287 	const void *read_ptr() const noexcept { return m_ptr.get(); }
288 
write_ptr() const289 	void *write_ptr() const noexcept { return m_writable ? m_ptr.get() : nullptr; }
290 
map_read(const char * path)291 	void map_read(const char *path)
292 	{
293 		map_file(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
294 		m_writable = false;
295 	}
296 
map_write(const char * path)297 	void map_write(const char *path)
298 	{
299 		map_file(path, O_RDWR, PROT_READ | PROT_WRITE, MAP_SHARED);
300 		m_writable = true;
301 	}
302 
map_create(const char * path,size_t size)303 	void map_create(const char *path, size_t size)
304 	{
305 		posix::create_new_file(path, size);
306 		map_write(path);
307 	}
308 
flush()309 	void flush()
310 	{
311 		if (::msync(m_ptr.get(), m_size, MS_SYNC))
312 			posix::trap_error("error flushing file");
313 	}
314 
close()315 	void close()
316 	{
317 		flush();
318 		m_ptr.reset();
319 	}
320 };
321 #endif // _WIN32
322 
323 
324 const MemoryMappedFile::read_tag MemoryMappedFile::READ_TAG{};
325 const MemoryMappedFile::write_tag MemoryMappedFile::WRITE_TAG{};
326 const MemoryMappedFile::create_tag MemoryMappedFile::CREATE_TAG{};
327 
328 MemoryMappedFile::MemoryMappedFile() noexcept = default;
329 
330 MemoryMappedFile::MemoryMappedFile(MemoryMappedFile &&other) noexcept = default;
331 
MemoryMappedFile(const char * path,read_tag)332 MemoryMappedFile::MemoryMappedFile(const char *path, read_tag) : m_impl{ new impl{} }
333 {
334 	get_impl()->map_read(path);
335 }
336 
MemoryMappedFile(const char * path,write_tag)337 MemoryMappedFile::MemoryMappedFile(const char *path, write_tag) : m_impl{ new impl{} }
338 {
339 	get_impl()->map_write(path);
340 }
341 
MemoryMappedFile(const char * path,size_t size,create_tag)342 MemoryMappedFile::MemoryMappedFile(const char *path, size_t size, create_tag) : m_impl{ new impl{} }
343 {
344 	get_impl()->map_create(path, size);
345 }
346 
347 MemoryMappedFile::~MemoryMappedFile() = default;
348 
349 MemoryMappedFile &MemoryMappedFile::operator=(MemoryMappedFile &&other) noexcept = default;
350 
size() const351 size_t MemoryMappedFile::size() const noexcept { return get_impl()->size(); }
352 
read_ptr() const353 const void *MemoryMappedFile::read_ptr() const noexcept { return get_impl()->read_ptr(); }
354 
write_ptr()355 void *MemoryMappedFile::write_ptr() noexcept { return get_impl()->write_ptr(); }
356 
flush()357 void MemoryMappedFile::flush() { get_impl()->flush(); }
358 
close()359 void MemoryMappedFile::close() { get_impl()->close(); }
360