mirror of
https://github.com/ksherlock/merlin-utils.git
synced 2024-12-26 21:30:47 +00:00
390 lines
7.5 KiB
C++
390 lines
7.5 KiB
C++
|
#include "mapped_file.h"
|
||
|
#include "unique_resource.h"
|
||
|
#include <memory>
|
||
|
#include <functional>
|
||
|
#include <system_error>
|
||
|
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
void set_or_throw_error(std::error_code *ec, int error, const std::string &what) {
|
||
|
if (ec) *ec = std::error_code(error, std::system_category());
|
||
|
else throw std::system_error(error, std::system_category(), what);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#include <windows.h>
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
/*
|
||
|
* allocating a new string could reset GetLastError() to 0.
|
||
|
*/
|
||
|
void set_or_throw_error(std::error_code *ec, const char *what) {
|
||
|
auto e = GetLastError();
|
||
|
set_or_throw_error(ec, e, what);
|
||
|
}
|
||
|
|
||
|
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||
|
auto e = GetLastError();
|
||
|
set_or_throw_error(ec, e, what);
|
||
|
}
|
||
|
|
||
|
|
||
|
template<class ...Args>
|
||
|
HANDLE CreateFileX(const std::string &s, Args... args) {
|
||
|
return CreateFileA(s.c_str(), std::forward<Args>(args)...);
|
||
|
}
|
||
|
|
||
|
template<class ...Args>
|
||
|
HANDLE CreateFileX(const std::wstring &s, Args... args) {
|
||
|
return CreateFileW(s.c_str(), std::forward<Args>(args)...);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::close() {
|
||
|
if (is_open()) {
|
||
|
|
||
|
UnmapViewOfFile(_data);
|
||
|
CloseHandle(_map_handle);
|
||
|
CloseHandle(_file_handle);
|
||
|
reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
void mapped_file_base::open_common(const T& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||
|
|
||
|
if (ec) ec->clear();
|
||
|
|
||
|
HANDLE fh;
|
||
|
HANDLE mh;
|
||
|
|
||
|
// length of 0 in CreateFileMapping / MapViewOfFile
|
||
|
// means map the entire file.
|
||
|
|
||
|
if (is_open()) close();
|
||
|
|
||
|
fh = CreateFileX(p,
|
||
|
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_READ,
|
||
|
nullptr,
|
||
|
OPEN_EXISTING,
|
||
|
flags == readonly ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL,
|
||
|
nullptr
|
||
|
);
|
||
|
if (fh == INVALID_HANDLE_VALUE)
|
||
|
return set_or_throw_error(ec, "CreateFile");
|
||
|
|
||
|
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||
|
|
||
|
|
||
|
if (length == -1) {
|
||
|
LARGE_INTEGER file_size;
|
||
|
GetFileSizeEx(fh, &file_size);
|
||
|
length = (size_t)file_size.QuadPart;
|
||
|
}
|
||
|
|
||
|
if (length == 0) return;
|
||
|
|
||
|
DWORD protect = 0;
|
||
|
DWORD access = 0;
|
||
|
switch (flags) {
|
||
|
case readonly:
|
||
|
protect = PAGE_READONLY;
|
||
|
access = FILE_MAP_READ;
|
||
|
break;
|
||
|
case readwrite:
|
||
|
protect = PAGE_READWRITE;
|
||
|
access = FILE_MAP_WRITE;
|
||
|
break;
|
||
|
case priv:
|
||
|
protect = PAGE_WRITECOPY;
|
||
|
access = FILE_MAP_COPY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||
|
if (mh == INVALID_HANDLE_VALUE)
|
||
|
return set_or_throw_error(ec, "CreateFileMapping");
|
||
|
|
||
|
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||
|
|
||
|
|
||
|
ULARGE_INTEGER ll;
|
||
|
ll.QuadPart = offset;
|
||
|
|
||
|
_data = MapViewOfFileEx(mh,
|
||
|
access,
|
||
|
ll.HighPart,
|
||
|
ll.LowPart,
|
||
|
length,
|
||
|
nullptr);
|
||
|
if (!_data)
|
||
|
return set_or_throw_error(ec, "MapViewOfFileEx");
|
||
|
|
||
|
|
||
|
_file_handle = fh_close.release();
|
||
|
_map_handle = mh_close.release();
|
||
|
_size = length;
|
||
|
_flags = flags;
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::open(const std::string &p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||
|
open_common(p, flags, length, offset, ec);
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::open(const std::wstring &p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||
|
open_common(p, flags, length, offset, ec);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
template<class T>
|
||
|
void mapped_file_base::create_common(const T& p, size_t length, std::error_code *ec) {
|
||
|
|
||
|
if (ec) ec->clear();
|
||
|
|
||
|
const size_t offset = 0;
|
||
|
|
||
|
HANDLE fh;
|
||
|
HANDLE mh;
|
||
|
LARGE_INTEGER file_size;
|
||
|
|
||
|
const DWORD protect = PAGE_READWRITE;
|
||
|
const DWORD access = FILE_MAP_WRITE;
|
||
|
|
||
|
|
||
|
if (is_open()) close();
|
||
|
|
||
|
fh = CreateFileX(p,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_READ,
|
||
|
nullptr,
|
||
|
CREATE_ALWAYS,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
nullptr
|
||
|
);
|
||
|
if (fh == INVALID_HANDLE_VALUE)
|
||
|
return set_or_throw_error(ec, "CreateFile");
|
||
|
|
||
|
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||
|
|
||
|
if (length == 0) return;
|
||
|
|
||
|
file_size.QuadPart = length;
|
||
|
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN))
|
||
|
return set_or_throw_error(ec, "SetFilePointerEx");
|
||
|
|
||
|
if (!SetEndOfFile(fh))
|
||
|
return set_or_throw_error(ec, "SetEndOfFile");
|
||
|
|
||
|
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||
|
if (mh == INVALID_HANDLE_VALUE)
|
||
|
return set_or_throw_error(ec, "CreateFileMapping");
|
||
|
|
||
|
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||
|
|
||
|
ULARGE_INTEGER ll;
|
||
|
ll.QuadPart = offset;
|
||
|
|
||
|
_data = MapViewOfFileEx(mh,
|
||
|
access,
|
||
|
ll.HighPart,
|
||
|
ll.LowPart,
|
||
|
length,
|
||
|
nullptr);
|
||
|
|
||
|
if (!_data)
|
||
|
return set_or_throw_error(ec, "MapViewOfFileEx");
|
||
|
|
||
|
_file_handle = fh_close.release();
|
||
|
_map_handle = mh_close.release();
|
||
|
|
||
|
_size = length;
|
||
|
_flags = readwrite;
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::create(const std::string &p, size_t length, std::error_code *ec) {
|
||
|
create_common(p, length, ec);
|
||
|
}
|
||
|
void mapped_file_base::create(const std::wstring &p, size_t length, std::error_code *ec) {
|
||
|
create_common(p, length, ec);
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <cerrno>
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
|
||
|
void set_or_throw_error(std::error_code *ec, const char *what) {
|
||
|
set_or_throw_error(ec, errno, what);
|
||
|
}
|
||
|
|
||
|
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||
|
set_or_throw_error(ec, errno, what);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::close() {
|
||
|
if (is_open()) {
|
||
|
::munmap(_data, _size);
|
||
|
::close(_fd);
|
||
|
reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void mapped_file_base::open(const std::string& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||
|
|
||
|
if (ec) ec->clear();
|
||
|
|
||
|
int fd;
|
||
|
|
||
|
int oflags = 0;
|
||
|
|
||
|
if (is_open()) close();
|
||
|
|
||
|
switch (flags) {
|
||
|
case readonly:
|
||
|
oflags = O_RDONLY;
|
||
|
break;
|
||
|
default:
|
||
|
oflags = O_RDWR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
fd = ::open(p.c_str(), oflags);
|
||
|
if (fd < 0) {
|
||
|
return set_or_throw_error(ec, "open");
|
||
|
}
|
||
|
|
||
|
auto close_fd = make_unique_resource(fd, ::close);
|
||
|
|
||
|
|
||
|
if (length == -1) {
|
||
|
struct stat st;
|
||
|
|
||
|
if (::fstat(fd, &st) < 0) {
|
||
|
set_or_throw_error(ec, "stat");
|
||
|
return;
|
||
|
}
|
||
|
length = st.st_size;
|
||
|
}
|
||
|
|
||
|
if (length == 0) return;
|
||
|
|
||
|
_data = ::mmap(0, length,
|
||
|
flags == readonly ? PROT_READ : PROT_READ | PROT_WRITE,
|
||
|
flags == priv ? MAP_PRIVATE : MAP_SHARED,
|
||
|
fd, offset);
|
||
|
|
||
|
if (_data == MAP_FAILED) {
|
||
|
_data = nullptr;
|
||
|
return set_or_throw_error(ec, "mmap");
|
||
|
}
|
||
|
|
||
|
_fd = close_fd.release();
|
||
|
_size = length;
|
||
|
_flags = flags;
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::create(const std::string& p, size_t length, std::error_code *ec) {
|
||
|
|
||
|
if (ec) ec->clear();
|
||
|
|
||
|
int fd;
|
||
|
const size_t offset = 0;
|
||
|
|
||
|
if (is_open()) close();
|
||
|
|
||
|
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||
|
if (fd < 0) {
|
||
|
return set_or_throw_error(ec, "open");
|
||
|
}
|
||
|
|
||
|
|
||
|
auto close_fd = make_unique_resource(fd, ::close);
|
||
|
|
||
|
|
||
|
if (length == 0) return;
|
||
|
|
||
|
if (::ftruncate(fd, length) < 0) {
|
||
|
return set_or_throw_error(ec, "ftruncate");
|
||
|
}
|
||
|
|
||
|
|
||
|
_data = ::mmap(0, length,
|
||
|
PROT_READ | PROT_WRITE,
|
||
|
MAP_SHARED,
|
||
|
fd, offset);
|
||
|
|
||
|
if (_data == MAP_FAILED) {
|
||
|
_data = nullptr;
|
||
|
return set_or_throw_error(ec, "mmap");
|
||
|
}
|
||
|
|
||
|
_fd = close_fd.release();
|
||
|
_size = length;
|
||
|
_flags = readwrite;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
void mapped_file_base::reset() {
|
||
|
_data = nullptr;
|
||
|
_size = 0;
|
||
|
_flags = readonly;
|
||
|
#ifdef _WIN32
|
||
|
_file_handle = nullptr;
|
||
|
_map_handle = nullptr;
|
||
|
#else
|
||
|
_fd = -1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void mapped_file_base::swap(mapped_file_base &rhs)
|
||
|
{
|
||
|
if (std::addressof(rhs) != this) {
|
||
|
std::swap(_data, rhs._data);
|
||
|
std::swap(_size, rhs._size);
|
||
|
std::swap(_flags, rhs._flags);
|
||
|
#ifdef _WIN32
|
||
|
std::swap(_file_handle, rhs._file_handle);
|
||
|
std::swap(_map_handle, rhs._map_handle);
|
||
|
#else
|
||
|
std::swap(_fd, rhs._fd);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mapped_file::mapped_file(mapped_file &&rhs) : mapped_file() {
|
||
|
swap(rhs);
|
||
|
//rhs.reset();
|
||
|
}
|
||
|
|
||
|
mapped_file& mapped_file::operator=(mapped_file &&rhs) {
|
||
|
if (std::addressof(rhs) == this) return *this;
|
||
|
|
||
|
swap(rhs);
|
||
|
rhs.close();
|
||
|
//rhs.reset();
|
||
|
return *this;
|
||
|
}
|
||
|
|