#include "mapped_file.h" #include "unique_resource.h" #include #include #include 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 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 HANDLE CreateFileX(const std::string &s, Args... args) { return CreateFileA(s.c_str(), std::forward(args)...); } template HANDLE CreateFileX(const std::wstring &s, Args... args) { return CreateFileW(s.c_str(), std::forward(args)...); } } void mapped_file_base::close() { if (is_open()) { UnmapViewOfFile(_data); CloseHandle(_map_handle); CloseHandle(_file_handle); reset(); } } template 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 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 #include #include #include #include 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; }