-
Notifications
You must be signed in to change notification settings - Fork 35
/
file_mapped.h
169 lines (139 loc) · 5.03 KB
/
file_mapped.h
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* Part of WinLamb - Win32 API Lambda Library
* https://github.com/rodrigocfd/winlamb
* Copyright 2017-present Rodrigo Cesar de Freitas Dias
* This library is released under the MIT License
*/
#pragma once
#include "file.h"
namespace wl {
// Wrapper to a memory-mapped file.
class file_mapped final {
private:
file _file;
HANDLE _hMap = nullptr;
void* _pMem = nullptr;
public:
~file_mapped() {
this->close();
}
file_mapped() = default;
file_mapped(file_mapped&& other) noexcept { this->operator=(std::move(other)); }
file_mapped& operator=(file_mapped&& other) noexcept {
this->close();
std::swap(this->_file, other._file);
std::swap(this->_hMap, other._hMap);
std::swap(this->_pMem, other._pMem);
return *this;
}
file::access access_type() const noexcept { return this->_file.access_type(); }
size_t size() noexcept { return this->_file.size(); }
BYTE* p_mem() const noexcept { return reinterpret_cast<BYTE*>(this->_pMem); }
BYTE* p_past_mem() noexcept { return p_mem() + this->size(); }
file_mapped& close() noexcept {
if (this->_pMem) {
UnmapViewOfFile(this->_pMem);
this->_pMem = nullptr;
}
if (this->_hMap) {
CloseHandle(this->_hMap);
this->_hMap = nullptr;
}
this->_file.close();
return *this;
}
file_mapped& open(const std::wstring& filePath, file::access accessType) {
this->close();
// Open file.
this->_file.open_existing(filePath, accessType);
auto tooBad = [this](DWORD err, const char* msg) -> void {
this->close();
throw std::system_error(err, std::system_category(), msg);
};
// Mapping into memory.
this->_hMap = CreateFileMappingW(this->_file.hfile(), nullptr,
(accessType == file::access::READWRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
if (!this->_hMap) {
tooBad(GetLastError(), accessType == file::access::READWRITE ?
"CreateFileMapping failed to map file as read-write" :
"CreateFileMapping failed to map file as read-only");
}
// Get pointer to data block.
this->_pMem = MapViewOfFile(this->_hMap,
(accessType == file::access::READWRITE) ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, 0);
if (!this->_pMem) {
tooBad(GetLastError(), accessType == file::access::READWRITE ?
"CreateFileMapping failed to map file as read-write" :
"CreateFileMapping failed to map file as read-only");
}
return *this;
}
private:
void _check_file_mapped() const {
if (!this->_hMap || !this->_pMem || !this->_file.hfile()) {
throw std::logic_error("File has not been mapped.");
}
}
public:
// This method will truncate or expand the file, according to the new size.
file_mapped& set_new_size(size_t newSize) {
this->_check_file_mapped();
// Unmap file, but keep it open.
UnmapViewOfFile(this->_pMem);
CloseHandle(this->_hMap);
// Truncate/expand file, probably fail if file was opened as read-only.
this->_file.set_new_size(newSize);
auto tooBad = [this](DWORD err, const char* msg) -> void {
this->close();
throw std::system_error(err, std::system_category(), msg);
};
// Remap into memory.
this->_hMap = CreateFileMappingW(this->_file.hfile(), 0, PAGE_READWRITE, 0, 0, nullptr);
if (!this->_hMap) {
tooBad(GetLastError(), "CreateFileMapping failed to recreate mapping");
}
// Get new pointer to data block, old one just became invalid.
this->_pMem = MapViewOfFile(this->_hMap, FILE_MAP_WRITE, 0, 0, 0);
if (!this->_pMem) {
tooBad(GetLastError(), "MapViewOfFile failed to recreate mapping");
}
return *this;
}
// Reads file content, by default all at once.
file_mapped& read_to_buffer(std::vector<BYTE>& buf, size_t offset = 0, size_t numBytes = -1) {
this->_check_file_mapped();
if (offset >= this->size()) {
throw std::invalid_argument("Offset is beyond end of file.");
} else if (numBytes == -1 || offset + numBytes > this->size()) {
numBytes = this->size() - offset; // avoid reading beyond EOF
}
buf.resize(numBytes);
memcpy(&buf[0], this->p_mem() + offset, numBytes * sizeof(BYTE));
return *this;
}
// Retrieves file content, by default all at once.
std::vector<BYTE> read(size_t offset = 0, size_t numBytes = -1) {
std::vector<BYTE> buf;
this->read_to_buffer(buf, offset, numBytes);
return buf;
}
public:
class util final {
private:
util() = delete;
public:
static void read_to_buffer(const wchar_t* filePath, std::vector<BYTE>& buf) {
file_mapped fin;
fin.open(filePath, file::access::READONLY);
fin.read_to_buffer(buf);
}
static std::vector<BYTE> read(const wchar_t* filePath) {
std::vector<BYTE> buf;
read_to_buffer(filePath, buf);
return buf;
}
static void read_to_buffer(const std::wstring& filePath, std::vector<BYTE>& buf) { read_to_buffer(filePath.c_str(), buf); }
static std::vector<BYTE> read(const std::wstring& filePath) { return read(filePath.c_str()); }
};
};
}//namespace wl