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