diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index d4f2b7c01c..a57fadafe4 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -18,6 +18,7 @@ #ifdef _WIN32 #include #include +#include #else #include #include @@ -40,13 +41,13 @@ namespace { #ifdef _WIN32 /** - * Converts the file access mode and file type enums to a file access mode wide string. - * - * @param mode File access mode - * @param type File type - * - * @returns A pointer to a wide string representing the file access mode. - */ +* Converts the file access mode and file type enums to a file access mode wide string. +* +* @param mode File access mode +* @param type File type +* +* @returns A pointer to a wide string representing the file access mode. +*/ [[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) { switch (type) { case FileType::BinaryFile: @@ -83,12 +84,12 @@ namespace { } /** - * Converts the file-share access flag enum to a Windows defined file-share access flag. - * - * @param flag File-share access flag - * - * @returns Windows defined file-share access flag. - */ +* Converts the file-share access flag enum to a Windows defined file-share access flag. +* +* @param flag File-share access flag +* +* @returns Windows defined file-share access flag. +*/ [[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) { switch (flag) { case FileShareFlag::ShareNone: @@ -106,13 +107,13 @@ namespace { #else /** - * Converts the file access mode and file type enums to a file access mode string. - * - * @param mode File access mode - * @param type File type - * - * @returns A pointer to a string representing the file access mode. - */ +* Converts the file access mode and file type enums to a file access mode string. +* +* @param mode File access mode +* @param type File type +* +* @returns A pointer to a string representing the file access mode. +*/ [[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) { switch (type) { case FileType::BinaryFile: @@ -151,12 +152,12 @@ namespace { #endif /** - * Converts the seek origin enum to a seek origin integer. - * - * @param origin Seek origin - * - * @returns Seek origin integer. - */ +* Converts the seek origin enum to a seek origin integer. +* +* @param origin Seek origin +* +* @returns Seek origin integer. +*/ [[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) { switch (origin) { case SeekOrigin::SetOrigin: @@ -182,7 +183,7 @@ std::string ReadStringFromFile(const std::filesystem::path& path, FileType type) } size_t WriteStringToFile(const std::filesystem::path& path, FileType type, - std::string_view string) { + std::string_view string) { if (Exists(path) && !IsFile(path)) { return 0; } @@ -193,7 +194,7 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type, } size_t AppendStringToFile(const std::filesystem::path& path, FileType type, - std::string_view string) { + std::string_view string) { if (Exists(path) && !IsFile(path)) { return 0; } @@ -248,40 +249,13 @@ FileType IOFile::GetType() const { return file_type; } -void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) { - Close(); - file_path = path; - file_access_mode = mode; - file_type = type; - errno = 0; -#ifdef _WIN32 - if (flag != FileShareFlag::ShareNone) { - file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag)); - } else { - _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); - } -#elif ANDROID - if (Android::IsContentUri(path)) { - ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!"); - auto const fd = Android::OpenContentUri(path, Android::OpenMode::Read); - if (fd != -1) { - file = fdopen(fd, "r"); - if (errno != 0 && file == nullptr) - LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(), strerror(errno)); - } else { - LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str()); - } - } else { - file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); - } -#elif defined(__HAIKU__) || defined(__managarm__) || defined(__OPENORBIS__) || defined(__APPLE__) - file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); -#elif defined(__unix__) - if (type == FileType::BinaryFile && mode == FileAccessMode::Read) { +#ifdef __unix__ +int PlatformMapReadOnly(IOFile& io, const char* path) { + io.mmap_fd = open(path., O_RDONLY); + if (io.mmap_fd > 0) { struct stat st; - mmap_fd = open(path.c_str(), O_RDONLY); - fstat(mmap_fd, &st); - mmap_size = st.st_size; + fstat(io.mmap_fd, &st); + io.mmap_size = st.st_size; int map_flags = MAP_PRIVATE; #ifdef MAP_PREFAULT_READ @@ -315,18 +289,97 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File u64 big_file_threshold = 512_MiB; map_flags |= u64(st.st_size) >= big_file_threshold ? MAP_ALIGNED_SUPER : 0; #endif - mmap_base = (u8*)mmap(nullptr, mmap_size, PROT_READ, map_flags, mmap_fd, 0); - if (mmap_base == MAP_FAILED) { - close(mmap_fd); - mmap_fd = -1; + io.mmap_base = (u8*)mmap(nullptr, io.mmap_size, PROT_READ, map_flags, io.mmap_fd, 0); + if (io.mmap_base == MAP_FAILED) { + close(io.mmap_fd); + io.mmap_fd = -1; } else { - posix_madvise(mmap_base, mmap_size, POSIX_MADV_WILLNEED); + posix_madvise(mmap_base, io.mmap_size, POSIX_MADV_WILLNEED); } } - // mmap(2) failed or simply we can't use it - if (mmap_fd == -1) { + return io.mmap_fd; +} +void PlatformUnmap(IOFile& io) { + if (io.mmap_fd != -1) { + munmap(io.mmap_base, io.mmap_size); + close(io.mmap_fd); + io.mmap_fd = -1; + } +} +#else +int PlatformMapReadOnly(IOFile& io, const char* path) { + io.file_handle = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr); + if (HANDLE(io.file_handle) != INVALID_HANDLE_VALUE) { + io.mapping_handle = CreateFileMappingW(file_handle, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (io.mapping_handle) { + io.mmap_base = (char const*)MapViewOfFile(HANDLE(io.mapping_handle), FILE_MAP_READ, 0, 0, 0); + if (io.mmap_base) { + _LARGE_INTEGER pvalue; + GetFileSizeEx(file_handle, &pvalue); + io.mmap_size = uint32_t(pvalue.QuadPart); + } else { + CloseHandle(io.mapping_handle); + CloseHandle(io.file_handle); + return -1; + } + } else { + CloseHandle(io.file_handle); + return -1; + } + } + return 0; +} +void PlatformUnmap(IOFile& io) { + if(io.mapping_handle) { + if(io.mmap_base) + UnmapViewOfFile(HANDLE(io.mmap_base)); + CloseHandle(HANDLE(io.mapping_handle)); + } + if(io.file_handle != INVALID_HANDLE_VALUE) + CloseHandle(HANDLE(io.file_handle)); +} +#endif + +void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) { + Close(); + file_path = path; + file_access_mode = mode; + file_type = type; + errno = 0; +#ifdef _WIN32 + if (flag != FileShareFlag::ShareNone) { + file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag)); + } else { + _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); + } +#elif ANDROID + if (Android::IsContentUri(path)) { + ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!"); + if (PlatformMapReadOnly(*this, path.c_str()) == -1) { + LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str()); + int const fd = Android::OpenContentUri(path, Android::OpenMode::Read); + if (fd != -1) { + file = fdopen(fd, "r"); + if (errno != 0 && file == nullptr) + LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(), strerror(errno)); + } else { + LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str()); + } + } + } else { file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); } +#elif defined(__HAIKU__) || defined(__managarm__) || defined(__OPENORBIS__) || defined(__APPLE__) + file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); +#elif defined(__unix__) + if (type == FileType::BinaryFile && mode == FileAccessMode::Read) { + if (PlatformMapReadOnly(*this, path.c_str()) == -1) { + LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str()); + } + } + if (mmap_fd == -1) { + file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); // mmap(2) failed or simply we can't use it + } #else // Some other fancy OS (ahem fucking Darwin/Mac OSX) file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); @@ -339,13 +392,7 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File } void IOFile::Close() { -#ifdef __unix__ - if (mmap_fd != -1) { - munmap(mmap_base, mmap_size); - close(mmap_fd); - mmap_fd = -1; - } -#endif + PlatformUnmap(*this); if (file) { errno = 0; const auto close_result = std::fclose(file) == 0; @@ -359,11 +406,7 @@ void IOFile::Close() { } bool IOFile::IsOpen() const { -#ifdef __unix__ - return file != nullptr || mmap_fd != -1; -#else - return file != nullptr; -#endif + return file != nullptr || IsMappedFile(); } std::string IOFile::ReadString(size_t length) const { @@ -380,9 +423,7 @@ size_t IOFile::WriteString(std::span string) const { } bool IOFile::Flush() const { -#ifdef __unix__ - ASSERT(mmap_fd == -1); -#endif + ASSERT(!IsMappedFile()); if (file) { errno = 0; auto const flush_result = std::fflush(file) == 0; @@ -397,9 +438,7 @@ bool IOFile::Flush() const { } bool IOFile::Commit() const { -#ifdef __unix__ - ASSERT(mmap_fd == -1); -#endif + ASSERT(!IsMappedFile()); if (file) { errno = 0; #ifdef _WIN32 @@ -418,9 +457,7 @@ bool IOFile::Commit() const { } bool IOFile::SetSize(u64 size) const { -#ifdef __unix__ - ASSERT(mmap_fd == -1); -#endif + ASSERT(!IsMappedFile()); if (file) { errno = 0; #ifdef _WIN32 @@ -439,10 +476,8 @@ bool IOFile::SetSize(u64 size) const { } u64 IOFile::GetSize() const { -#ifdef __unix__ - if (mmap_fd != -1) + if (IsMappedFile()) return mmap_size; -#endif if (file) { // Flush any unwritten buffered data into the file prior to retrieving the file mmap_size. std::fflush(file); @@ -476,8 +511,7 @@ u64 IOFile::GetSize() const { } bool IOFile::Seek(s64 offset, SeekOrigin origin) const { -#ifdef __unix__ - if (mmap_fd != -1) { + if (IsMappedFile()) { // fuck you to whoever made this method const switch (origin) { case SeekOrigin::SetOrigin: @@ -492,7 +526,6 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const { } return true; } -#endif if (file) { errno = 0; const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0; @@ -507,12 +540,10 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const { } s64 IOFile::Tell() const { -#ifdef __unix__ - if (mmap_fd != -1) { + if (IsMappedFile()) { errno = 0; return s64(mmap_offset); } -#endif if (file) { errno = 0; return ftello(file); diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 04d1ceabab..937c7d6054 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -25,12 +25,12 @@ enum class SeekOrigin { }; /** - * Opens a file stream at path with the specified open mode. - * - * @param file_stream Reference to file stream - * @param path Filesystem path - * @param open_mode File stream open mode - */ +* Opens a file stream at path with the specified open mode. +* +* @param file_stream Reference to file stream +* @param path Filesystem path +* @param open_mode File stream open mode +*/ template void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path, std::ios_base::openmode open_mode) { @@ -49,14 +49,14 @@ void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::op #endif /** - * Reads an entire file at path and returns a string of the contents read from the file. - * If the filesystem object at path is not a regular file, this function returns an empty string. - * - * @param path Filesystem path - * @param type File type - * - * @returns A string of the contents read from the file. - */ +* Reads an entire file at path and returns a string of the contents read from the file. +* If the filesystem object at path is not a regular file, this function returns an empty string. +* +* @param path Filesystem path +* @param type File type +* +* @returns A string of the contents read from the file. +*/ [[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type); #ifdef _WIN32 @@ -71,18 +71,18 @@ template #endif /** - * Writes a string to a file at path and returns the number of characters successfully written. - * If a file already exists at path, its contents will be erased. - * If a file does not exist at path, it creates and opens a new empty file for writing. - * If the filesystem object at path exists and is not a regular file, this function returns 0. - * - * @param path Filesystem path - * @param type File type - * - * @returns Number of characters successfully written. - */ +* Writes a string to a file at path and returns the number of characters successfully written. +* If a file already exists at path, its contents will be erased. +* If a file does not exist at path, it creates and opens a new empty file for writing. +* If the filesystem object at path exists and is not a regular file, this function returns 0. +* +* @param path Filesystem path +* @param type File type +* +* @returns Number of characters successfully written. +*/ [[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type, - std::string_view string); + std::string_view string); #ifdef _WIN32 template @@ -96,15 +96,15 @@ template #endif /** - * Appends a string to a file at path and returns the number of characters successfully written. - * If a file does not exist at path, it creates and opens a new empty file for appending. - * If the filesystem object at path exists and is not a regular file, this function returns 0. - * - * @param path Filesystem path - * @param type File type - * - * @returns Number of characters successfully written. - */ +* Appends a string to a file at path and returns the number of characters successfully written. +* If a file does not exist at path, it creates and opens a new empty file for appending. +* If the filesystem object at path exists and is not a regular file, this function returns 0. +* +* @param path Filesystem path +* @param type File type +* +* @returns Number of characters successfully written. +*/ [[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type, std::string_view string); @@ -132,14 +132,14 @@ public: FileShareFlag flag = FileShareFlag::ShareReadOnly); /** - * An IOFile is a lightweight wrapper on C Library file operations. - * Automatically closes an open file on the destruction of an IOFile object. - * - * @param path Filesystem path - * @param mode File access mode - * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file - * @param flag (Windows only) File-share access flag, default is ShareReadOnly - */ + * An IOFile is a lightweight wrapper on C Library file operations. + * Automatically closes an open file on the destruction of an IOFile object. + * + * @param path Filesystem path + * @param mode File access mode + * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file + * @param flag (Windows only) File-share access flag, default is ShareReadOnly + */ explicit IOFile(const std::filesystem::path& path, FileAccessMode mode, FileType type = FileType::BinaryFile, FileShareFlag flag = FileShareFlag::ShareReadOnly); @@ -153,65 +153,65 @@ public: IOFile& operator=(IOFile&& other) noexcept; /** - * Gets the path of the file. - * - * @returns The path of the file. - */ + * Gets the path of the file. + * + * @returns The path of the file. + */ [[nodiscard]] std::filesystem::path GetPath() const; /** - * Gets the access mode of the file. - * - * @returns The access mode of the file. - */ + * Gets the access mode of the file. + * + * @returns The access mode of the file. + */ [[nodiscard]] FileAccessMode GetAccessMode() const; /** - * Gets the type of the file. - * - * @returns The type of the file. - */ + * Gets the type of the file. + * + * @returns The type of the file. + */ [[nodiscard]] FileType GetType() const; /** - * Opens a file at path with the specified file access mode. - * This function behaves differently depending on the FileAccessMode. - * These behaviors are documented in each enum value of FileAccessMode. - * - * @param path Filesystem path - * @param mode File access mode - * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file - * @param flag (Windows only) File-share access flag, default is ShareReadOnly - */ + * Opens a file at path with the specified file access mode. + * This function behaves differently depending on the FileAccessMode. + * These behaviors are documented in each enum value of FileAccessMode. + * + * @param path Filesystem path + * @param mode File access mode + * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file + * @param flag (Windows only) File-share access flag, default is ShareReadOnly + */ void Open(const std::filesystem::path& path, FileAccessMode mode, - FileType type = FileType::BinaryFile, - FileShareFlag flag = FileShareFlag::ShareReadOnly); + FileType type = FileType::BinaryFile, + FileShareFlag flag = FileShareFlag::ShareReadOnly); /// Closes the file if it is opened. void Close(); /** - * Checks whether the file is open. - * Use this to check whether the calls to Open() or Close() succeeded. - * - * @returns True if the file is open, false otherwise. - */ + * Checks whether the file is open. + * Use this to check whether the calls to Open() or Close() succeeded. + * + * @returns True if the file is open, false otherwise. + */ [[nodiscard]] bool IsOpen() const; /** - * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. - * If T is not a contiguous container as defined by the concept IsContiguousContainer, this - * calls ReadObject and T must be a trivially copyable object. - * - * See ReadSpan for more details if T is a contiguous container. - * See ReadObject for more details if T is a trivially copyable object. - * - * @tparam T Contiguous container or trivially copyable object - * - * @param data Container of T::value_type data or reference to object - * - * @returns Count of T::value_type data or objects successfully read. - */ + * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. + * If T is not a contiguous container as defined by the concept IsContiguousContainer, this + * calls ReadObject and T must be a trivially copyable object. + * + * See ReadSpan for more details if T is a contiguous container. + * See ReadObject for more details if T is a trivially copyable object. + * + * @tparam T Contiguous container or trivially copyable object + * + * @param data Container of T::value_type data or reference to object + * + * @returns Count of T::value_type data or objects successfully read. + */ template [[nodiscard]] size_t Read(T& data) const { if constexpr (IsContiguousContainer) { @@ -224,19 +224,19 @@ public: } /** - * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. - * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this - * calls WriteObject and T must be a trivially copyable object. - * - * See WriteSpan for more details if T is a contiguous container. - * See WriteObject for more details if T is a trivially copyable object. - * - * @tparam T Contiguous container or trivially copyable object - * - * @param data Container of T::value_type data or const reference to object - * - * @returns Count of T::value_type data or objects successfully written. - */ + * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. + * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this + * calls WriteObject and T must be a trivially copyable object. + * + * See WriteSpan for more details if T is a contiguous container. + * See WriteObject for more details if T is a trivially copyable object. + * + * @tparam T Contiguous container or trivially copyable object + * + * @param data Container of T::value_type data or const reference to object + * + * @returns Count of T::value_type data or objects successfully written. + */ template [[nodiscard]] size_t Write(const T& data) const { if constexpr (IsContiguousContainer) { @@ -250,195 +250,187 @@ public: } /** - * Reads a span of T data from a file sequentially. - * This function reads from the current position of the file pointer and - * advances it by the (count of T * sizeof(T)) bytes successfully read. - * - * Failures occur when: - * - The file is not open - * - The opened file lacks read permissions - * - Attempting to read beyond the end-of-file - * - * @tparam T Data type - * - * @param data Span of T data - * - * @returns Count of T data successfully read. - */ + * Reads a span of T data from a file sequentially. + * This function reads from the current position of the file pointer and + * advances it by the (count of T * sizeof(T)) bytes successfully read. + * + * Failures occur when: + * - The file is not open + * - The opened file lacks read permissions + * - Attempting to read beyond the end-of-file + * + * @tparam T Data type + * + * @param data Span of T data + * + * @returns Count of T data successfully read. + */ template [[nodiscard]] size_t ReadSpan(std::span data) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); -#ifdef __unix__ - if (mmap_fd != -1) { + if (IsMappedFile()) { std::memcpy(data.data(), mmap_base + mmap_offset, sizeof(T) * data.size()); return data.size(); } -#endif return IsOpen() ? std::fread(data.data(), sizeof(T), data.size(), file) : 0; } /** - * Writes a span of T data to a file sequentially. - * This function writes from the current position of the file pointer and - * advances it by the (count of T * sizeof(T)) bytes successfully written. - * - * Failures occur when: - * - The file is not open - * - The opened file lacks write permissions - * - * @tparam T Data type - * - * @param data Span of T data - * - * @returns Count of T data successfully written. - */ + * Writes a span of T data to a file sequentially. + * This function writes from the current position of the file pointer and + * advances it by the (count of T * sizeof(T)) bytes successfully written. + * + * Failures occur when: + * - The file is not open + * - The opened file lacks write permissions + * + * @tparam T Data type + * + * @param data Span of T data + * + * @returns Count of T data successfully written. + */ template [[nodiscard]] size_t WriteSpan(std::span data) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); -#ifdef __unix__ - if (mmap_fd != -1) { + if (IsMappedFile()) { std::memcpy(mmap_base + mmap_offset, data.data(), sizeof(T) * data.size()); return data.size(); } -#endif return IsOpen() ? std::fwrite(data.data(), sizeof(T), data.size(), file) : 0; } /** - * Reads a T object from a file sequentially. - * This function reads from the current position of the file pointer and - * advances it by the sizeof(T) bytes successfully read. - * - * Failures occur when: - * - The file is not open - * - The opened file lacks read permissions - * - Attempting to read beyond the end-of-file - * - * @tparam T Data type - * - * @param object Reference to object - * - * @returns True if the object is successfully read from the file, false otherwise. - */ + * Reads a T object from a file sequentially. + * This function reads from the current position of the file pointer and + * advances it by the sizeof(T) bytes successfully read. + * + * Failures occur when: + * - The file is not open + * - The opened file lacks read permissions + * - Attempting to read beyond the end-of-file + * + * @tparam T Data type + * + * @param object Reference to object + * + * @returns True if the object is successfully read from the file, false otherwise. + */ template [[nodiscard]] bool ReadObject(T& object) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); static_assert(!std::is_pointer_v, "T must not be a pointer to an object."); -#ifdef __unix__ - if (mmap_fd != -1) { + if (IsMappedFile()) { std::memcpy(&object, mmap_base + mmap_offset, sizeof(T)); return sizeof(T); } -#endif return IsOpen() ? std::fread(&object, sizeof(T), 1, file) == 1 : false; } /** - * Writes a T object to a file sequentially. - * This function writes from the current position of the file pointer and - * advances it by the sizeof(T) bytes successfully written. - * - * Failures occur when: - * - The file is not open - * - The opened file lacks write permissions - * - * @tparam T Data type - * - * @param object Const reference to object - * - * @returns True if the object is successfully written to the file, false otherwise. - */ + * Writes a T object to a file sequentially. + * This function writes from the current position of the file pointer and + * advances it by the sizeof(T) bytes successfully written. + * + * Failures occur when: + * - The file is not open + * - The opened file lacks write permissions + * + * @tparam T Data type + * + * @param object Const reference to object + * + * @returns True if the object is successfully written to the file, false otherwise. + */ template [[nodiscard]] bool WriteObject(const T& object) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); static_assert(!std::is_pointer_v, "T must not be a pointer to an object."); -#ifdef __unix__ - if (mmap_fd != -1) { + if (IsMappedFile()) { std::memcpy(mmap_base + mmap_offset, &object, sizeof(T)); return sizeof(T); } -#endif return IsOpen() ? std::fwrite(&object, sizeof(T), 1, file) == 1 : false; } /** - * Specialized function to read a string of a given length from a file sequentially. - * This function writes from the current position of the file pointer and - * advances it by the number of characters successfully read. - * The size of the returned string may not match length if not all bytes are successfully read. - * - * @param length Length of the string - * - * @returns A string read from the file. - */ + * Specialized function to read a string of a given length from a file sequentially. + * This function writes from the current position of the file pointer and + * advances it by the number of characters successfully read. + * The size of the returned string may not match length if not all bytes are successfully read. + * + * @param length Length of the string + * + * @returns A string read from the file. + */ [[nodiscard]] std::string ReadString(size_t length) const; /** - * Specialized function to write a string to a file sequentially. - * This function writes from the current position of the file pointer and - * advances it by the number of characters successfully written. - * - * @param string Span of const char backed std::string or std::string_view - * - * @returns Number of characters successfully written. - */ + * Specialized function to write a string to a file sequentially. + * This function writes from the current position of the file pointer and + * advances it by the number of characters successfully written. + * + * @param string Span of const char backed std::string or std::string_view + * + * @returns Number of characters successfully written. + */ [[nodiscard]] size_t WriteString(std::span string) const; /** - * Attempts to flush any unwritten buffered data into the file. - * - * @returns True if the flush was successful, false otherwise. - */ + * Attempts to flush any unwritten buffered data into the file. + * + * @returns True if the flush was successful, false otherwise. + */ bool Flush() const; /** - * Attempts to commit the file into the disk. - * Note that this is an expensive operation as this forces the operating system to write - * the contents of the file associated with the file descriptor into the disk. - * - * @returns True if the commit was successful, false otherwise. - */ + * Attempts to commit the file into the disk. + * Note that this is an expensive operation as this forces the operating system to write + * the contents of the file associated with the file descriptor into the disk. + * + * @returns True if the commit was successful, false otherwise. + */ bool Commit() const; /** - * Resizes the file to a given size. - * If the file is resized to a smaller size, the remainder of the file is discarded. - * If the file is resized to a larger size, the new area appears as if zero-filled. - * - * Failures occur when: - * - The file is not open - * - * @param size File size in bytes - * - * @returns True if the file resize succeeded, false otherwise. - */ + * Resizes the file to a given size. + * If the file is resized to a smaller size, the remainder of the file is discarded. + * If the file is resized to a larger size, the new area appears as if zero-filled. + * + * Failures occur when: + * - The file is not open + * + * @param size File size in bytes + * + * @returns True if the file resize succeeded, false otherwise. + */ [[nodiscard]] bool SetSize(u64 size) const; /** - * Gets the size of the file. - * - * Failures occur when: - * - The file is not open - * - * @returns The file size in bytes of the file. Returns 0 on failure. - */ + * Gets the size of the file. + * + * Failures occur when: + * - The file is not open + * + * @returns The file size in bytes of the file. Returns 0 on failure. + */ [[nodiscard]] u64 GetSize() const; /** - * Moves the current position of the file pointer with the specified offset and seek origin. - * - * @param offset Offset from seek origin - * @param origin Seek origin - * - * @returns True if the file pointer has moved to the specified offset, false otherwise. - */ + * Moves the current position of the file pointer with the specified offset and seek origin. + * + * @param offset Offset from seek origin + * @param origin Seek origin + * + * @returns True if the file pointer has moved to the specified offset, false otherwise. + */ [[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const; /** - * Gets the current position of the file pointer. - * - * @returns The current position of the file pointer. - */ + * Gets the current position of the file pointer. + * + * @returns The current position of the file pointer. + */ [[nodiscard]] s64 Tell() const; private: @@ -446,13 +438,21 @@ private: FileAccessMode file_access_mode{}; FileType file_type{}; std::FILE* file = nullptr; -#ifdef __unix__ + + // Any decent system should have mmap() for files + // Systems with artifical mmap() limitations should simply change the logic within file.cpp + // and reduce the threshold for which the mmap() is set to +#ifdef _WIN32 + void *mapping_handle = nullptr; + void *file_handle = nullptr; + bool IsMappedFile() { return file_handle != nullptr; } +#else // POSIX int mmap_fd = -1; + bool IsMappedFile() { return mmap_fd != -1; } +#endif u8* mmap_base = nullptr; size_t mmap_size = 0; - // fuck you - mutable off_t mmap_offset = 0; -#endif + mutable off_t mmap_offset = 0; // fuck you }; } // namespace Common::FS