diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index c4c7385..ba049b7 100644 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -77,6 +77,7 @@ target_link_libraries(mpw MPW_LIB) target_link_libraries(mpw MPLITE_LIB) target_link_libraries(mpw NATIVE_LIB) target_link_libraries(mpw MACOS_LIB) +target_link_libraries(mpw CXX_LIB) set_target_properties(mpw PROPERTIES LINK_FLAGS "-ledit") diff --git a/cxx/CMakeLists.txt b/cxx/CMakeLists.txt new file mode 100644 index 0000000..6adab7e --- /dev/null +++ b/cxx/CMakeLists.txt @@ -0,0 +1,4 @@ + +set(CXX_SRC mapped_file.cpp) + +add_library(CXX_LIB ${CXX_SRC}) diff --git a/cxx/mapped_file.cpp b/cxx/mapped_file.cpp new file mode 100644 index 0000000..fee94a3 --- /dev/null +++ b/cxx/mapped_file.cpp @@ -0,0 +1,377 @@ +#include "mapped_file.h" +#include +#include +#include + +#include "unique_resource.h" + +namespace { + class defer { + public: + typedef std::function FX; + defer() = default; + + defer(FX fx) : _fx(fx) {} + defer(const defer &) = delete; + defer(defer &&) = default; + defer & operator=(const defer &) = delete; + defer & operator=(defer &&) = default; + + void cancel() { _fx = nullptr; } + ~defer() { if (_fx) _fx(); } + private: + FX _fx; + }; + + void throw_error(int error) { + throw std::system_error(error, std::system_category()); + } + + + void throw_error(int error, const std::string &what) { + throw std::system_error(error, std::system_category(), what); + } + + + +} + +#ifdef _WIN32 +#include + +namespace { + + void throw_error() { + throw_error(GetLastError()); + } + + void throw_error(const std::string &what) { + throw_error(GetLastError(), what); + } + +} + +void mapped_file_base::close() { + if (is_open()) { + + UnmapViewOfFile(_data); + CloseHandle(_map_handle); + CloseHandle(_file_handle); + reset(); + } +} + +void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) { + + + HANDLE fh; + HANDLE mh; + + // length of 0 in CreateFileMapping / MapViewOfFile + // means map the entire file. + + if (is_open()) { + throw std::runtime_error("mapped_file_base::open - file already open"); + } + + fh = CreateFile(p.c_str(), + 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) { + throw_error(); + } + + auto fh_close = make_unique_resource(fh, CloseHandle); + + + if (length == -1) { + LARGE_INTEGER file_size; + GetFileSizeEx(fh, &file_size); + length = 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_WRITE; + break; + } + + mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0); + if (mh == INVALID_HANDLE_VALUE) { + throw_error(); + } + + auto mh_close = make_unique_resource(mh, CloseHandle); + + + _data = MapViewOfFileEx(mh, + access, + (DWORD)(offset >> 32), + (DWORD)offset, + length, + nullptr); + if (!_data) { + throw_error(); + } + + + _file_handle = fh_close.release(); + _map_handle = mh_close.release(); + _size = length; + _flags = flags; +} + + +void mapped_file_base::create(const path_type& p, size_t length) { + + 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()) { + throw std::runtime_error("mapped_file_base::create - file already open"); + } + + + fh = CreateFile(p.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr + ); + if (fh == INVALID_HANDLE_VALUE) { + throw_error(); + } + + auto fh_close = make_unique_resource(fh, CloseHandle); + + + file_size.QuadPart = length; + if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN)); + if (!SetEndOfFile(fh)) throw_error(); + + mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0); + if (mh == INVALID_HANDLE_VALUE) { + throw_error(); + } + + auto mh_close = make_unique_resource(mh, CloseHandle); + + _data = MapViewOfFileEx(mh, + access, + (DWORD)(offset >> 32), + (DWORD)offset, + length, + nullptr); + + if (!_data) { + throw_error(); + } + + _file_handle = fh_close.release(); + _map_handle = mh_close.release(); + + _size = length; + _flags = readwrite; +} + + + +#else + +#include +#include +#include +#include +#include + +namespace { + + void throw_error() { + throw_error(errno); + } + + void throw_error(const std::string &what) { + throw_error(errno, what); + } + +} + +void mapped_file_base::close() { + if (is_open()) { + ::munmap(_data, _size); + ::close(_fd); + reset(); + } +} + + +void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) { + + int fd; + + int oflags = 0; + + if (is_open()) { + throw std::runtime_error("mapped_file_base::open - file already open"); + } + + switch (flags) { + case readonly: + oflags = O_RDONLY; + break; + default: + oflags = O_RDWR; + break; + } + + fd = ::open(p.c_str(), oflags); + if (fd < 0) { + throw_error(errno); + } + + //defer([fd](){::close(fd); }); + auto close_fd = make_unique_resource(fd, ::close); + + + + if (length == -1) { + struct stat st; + + if (::fstat(fd, &st) < 0) { + throw_error(errno); + } + 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; + throw_error(errno); + } + + _fd = close_fd.release(); + _size = length; + _flags = flags; +} + +void mapped_file_base::create(const path_type& p, size_t length) { + + int fd; + const size_t offset = 0; + + if (is_open()) { + throw std::runtime_error("mapped_file_base::create - file already open"); + } + + + + fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC); + if (fd < 0) { + throw_error(errno); + } + + //defer([fd](){::close(fd); }); + + auto close_fd = make_unique_resource(fd, ::close); + + + if (::ftruncate(fd, length) < 0) { + throw_error(errno); + } + + + _data = ::mmap(0, length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, offset); + + if (_data == MAP_FAILED) { + _data = nullptr; + throw_error(errno); + } + + _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; +} + + diff --git a/cxx/mapped_file.h b/cxx/mapped_file.h new file mode 100644 index 0000000..22da160 --- /dev/null +++ b/cxx/mapped_file.h @@ -0,0 +1,143 @@ +#ifndef __mapped_file_h__ +#define __mapped_file_h__ + +#ifdef HAVE_TSFS +#include +#else +#include +#endif + +#include + +class mapped_file_base { +public: + +#ifdef HAVE_TSFS + typedef std::filesystem::path path_type ; +#else + typedef std::string path_type ; +#endif + + enum mapmode { readonly, readwrite, priv }; + + void close(); + + bool is_open() const { + return _data != nullptr; + } + + size_t size() const { + return _size; + } + + operator bool() const { return is_open(); } + bool operator !() const { return !is_open(); } + + ~mapped_file_base() { close(); } + +protected: + + void swap(mapped_file_base &rhs); + + void open(const path_type& p, mapmode flags, size_t length, size_t offset); + void create(const path_type &p, size_t new_size); // always creates readwrite. + void reset(); + + + size_t _size = 0; + void *_data = nullptr; + mapmode _flags = readonly; + +#ifdef _WIN32 + void *_file_handle = nullptr; + void *_map_handle = nullptr; +#else + int _fd = -1; +#endif +}; + + + +class mapped_file : public mapped_file_base { + + typedef mapped_file_base base; + +public: + + typedef unsigned char value_type; + + typedef value_type *iterator; + typedef const value_type *const_iterator; + + typedef value_type &reference ; + typedef const value_type &const_reference; + + + mapped_file() = default; + mapped_file(const path_type& p, mapmode flags = readonly, size_t length = -1, size_t offset = 0) { + open(p, flags, length, offset); + } + + mapped_file(mapped_file &&); + mapped_file(const mapped_file &) = delete; + + mapped_file &operator=(mapped_file &&); + mapped_file &operator=(const mapped_file &) = delete; + + + void open(const path_type& p, mapmode flags, size_t length = -1, size_t offset = 0) { + base::open(p, flags, length, offset); + } + + + const value_type *data() const { + return (const value_type *)_data; + } + + value_type *data() { + return _flags == readonly ? (value_type *)nullptr : (value_type *)_data; + } + + const_iterator cbegin() const { + return data(); + } + + const_iterator cend() const { + return data() + size(); + } + + const_iterator begin() const { + return cbegin(); + } + + const_iterator end() const { + return cend(); + } + + + iterator begin() { + return _flags == readonly ? (iterator)nullptr : (iterator)_data; + } + + iterator end() { + return _flags == readonly ? (iterator)nullptr : (iterator)_data + size(); + } + + mapmode flags() const { + return _flags; + } + + void swap(mapped_file &rhs) { + base::swap(rhs); + } + +}; + +namespace std { + template + void swap(mapped_file &a, mapped_file &b) { + a.swap(b); + } +} + +#endif diff --git a/cxx/unique_resource.h b/cxx/unique_resource.h new file mode 100644 index 0000000..9ec9de0 --- /dev/null +++ b/cxx/unique_resource.h @@ -0,0 +1,95 @@ +#ifndef __unique_resource_h__ +#define __unique_resource_h__ + +#include + +template +class unique_resource { +public: + typedef T element_type; + typedef D deleter_type; + + unique_resource() = default; + unique_resource(const unique_resource &) = delete; + unique_resource(unique_resource &&rhs) { + swap(rhs); + } + + unique_resource(T t, D d): _pair(t,d), _active(true) + {} + + ~unique_resource() { + reset(); + } + + unique_resource &operator=(const unique_resource &) = delete; + unique_resource &operator=(unique_resource &&rhs) { + if (this != std::addressof(rhs)) { + reset(); + swap(rhs); + } + return *this; + } + + void swap(unique_resource & rhs) { + if (this != std::addressof(rhs)) { + std::swap(_active, rhs._active); + std::swap(_pair, rhs._pair); + } + } + + void reset(T t) { + reset(); + _active = true; + _pair.first = t; + } + + void reset(T t, D d) { + reset(); + _active = true; + _pair = std::make_pair(t, d); + } + + void reset() { + if (_active) { + (*_pair.second)(_pair.first); + _active = false; + } + } + + T release() { + _active = false; + return _pair.first;; + } + + T get() { + return _pair.first; + } + + operator bool() const { + return _active; + } + + D& get_deleter() { + return _pair.second; + } + + const D& get_deleter() const { + return _pair.second; + } + + +private: + std::pair _pair; + bool _active = false; +}; + +#define MAKE_UNIQUE_RESOURCE(T, D) \ + unique_resource(T, D) + +template +unique_resource make_unique_resource(T t, D d) { + return unique_resource(t, d); +} + +#endif