#pragma once #include #include #include #include #if defined(PLATFORM_WINDOWS) #include #else #include #include #include #include #include #endif #if !defined(MAP_NORESERVE) //not supported on FreeBSD; flag removed in 11.0 #define MAP_NORESERVE 0 #endif namespace nall { struct file_map { struct mode { enum : uint { read, write, modify, append }; }; file_map(const file_map&) = delete; auto operator=(const file_map&) = delete; file_map() = default; file_map(file_map&& source) { operator=(move(source)); } file_map(const string& filename, uint mode) { open(filename, mode); } ~file_map() { close(); } explicit operator bool() const { return _open; } auto size() const -> uint64_t { return _size; } auto data() -> uint8_t* { return _data; } auto data() const -> const uint8_t* { return _data; } //auto operator=(file_map&& source) -> file_map&; //auto open(const string& filename, uint mode) -> bool; //auto close() -> void; private: bool _open = false; //zero-byte files return _data = nullptr, _size = 0 uint8_t* _data = nullptr; uint64_t _size = 0; #if defined(API_WINDOWS) HANDLE _file = INVALID_HANDLE_VALUE; HANDLE _map = INVALID_HANDLE_VALUE; public: auto operator=(file_map&& source) -> file_map& { _open = source._open; _data = source._data; _size = source._size; _file = source._file; _map = source._map; source._open = false; source._data = nullptr; source._size = 0; source._file = INVALID_HANDLE_VALUE; source._map = INVALID_HANDLE_VALUE; return *this; } auto open(const string& filename, uint mode_) -> bool { close(); if(file::exists(filename) && file::size(filename) == 0) return _open = true; int desiredAccess, creationDisposition, protection, mapAccess; switch(mode_) { default: return false; case mode::read: desiredAccess = GENERIC_READ; creationDisposition = OPEN_EXISTING; protection = PAGE_READONLY; mapAccess = FILE_MAP_READ; break; case mode::write: //write access requires read access desiredAccess = GENERIC_WRITE; creationDisposition = CREATE_ALWAYS; protection = PAGE_READWRITE; mapAccess = FILE_MAP_ALL_ACCESS; break; case mode::modify: desiredAccess = GENERIC_READ | GENERIC_WRITE; creationDisposition = OPEN_EXISTING; protection = PAGE_READWRITE; mapAccess = FILE_MAP_ALL_ACCESS; break; case mode::append: desiredAccess = GENERIC_READ | GENERIC_WRITE; creationDisposition = CREATE_NEW; protection = PAGE_READWRITE; mapAccess = FILE_MAP_ALL_ACCESS; break; } _file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr, creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr); if(_file == INVALID_HANDLE_VALUE) return false; _size = GetFileSize(_file, nullptr); _map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr); if(_map == INVALID_HANDLE_VALUE) { CloseHandle(_file); _file = INVALID_HANDLE_VALUE; return false; } _data = (uint8_t*)MapViewOfFile(_map, mapAccess, 0, 0, _size); return _open = true; } auto close() -> void { if(_data) { UnmapViewOfFile(_data); _data = nullptr; } if(_map != INVALID_HANDLE_VALUE) { CloseHandle(_map); _map = INVALID_HANDLE_VALUE; } if(_file != INVALID_HANDLE_VALUE) { CloseHandle(_file); _file = INVALID_HANDLE_VALUE; } _open = false; } #else int _fd = -1; public: auto operator=(file_map&& source) -> file_map& { _open = source._open; _data = source._data; _size = source._size; _fd = source._fd; source._open = false; source._data = nullptr; source._size = 0; source._fd = -1; return *this; } auto open(const string& filename, uint mode_) -> bool { close(); if(file::exists(filename) && file::size(filename) == 0) return _open = true; int openFlags = 0; int mmapFlags = 0; switch(mode_) { default: return false; case mode::read: openFlags = O_RDONLY; mmapFlags = PROT_READ; break; case mode::write: openFlags = O_RDWR | O_CREAT; //mmap() requires read access mmapFlags = PROT_WRITE; break; case mode::modify: openFlags = O_RDWR; mmapFlags = PROT_READ | PROT_WRITE; break; case mode::append: openFlags = O_RDWR | O_CREAT; mmapFlags = PROT_READ | PROT_WRITE; break; } _fd = ::open(filename, openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if(_fd < 0) return false; struct stat _stat; fstat(_fd, &_stat); _size = _stat.st_size; _data = (uint8_t*)mmap(nullptr, _size, mmapFlags, MAP_SHARED | MAP_NORESERVE, _fd, 0); if(_data == MAP_FAILED) { _data = nullptr; ::close(_fd); _fd = -1; return false; } return _open = true; } auto close() -> void { if(_data) { munmap(_data, _size); _data = nullptr; } if(_fd >= 0) { ::close(_fd); _fd = -1; } _open = false; } #endif }; }