/
Mr.Stalin
/
FOnline-Engine
Обзор
Документация
Войти
/
Mr.Stalin
/
FOnline-Engine
Код
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Source/Essentials/DiskFileSystem.cpp
455 строк
13 KB
cvet
Non const locals (#190)
24 июл 2026, 10:46
Не верифицирован
24 июл 2026, 10:46
4883d25
Код
Авторство
О чём код?
// __________ ___ ______ _ // / ____/ __ \____ / (_)___ ___ / ____/___ ____ _(_)___ ___ // / /_ / / / / __ \/ / / __ \/ _ \ / __/ / __ \/ __ `/ / __ \/ _ \ // / __/ / /_/ / / / / / / / / / __/ / /___/ / / / /_/ / / / / / __/ // /_/ \____/_/ /_/_/_/_/ /_/\___/ /_____/_/ /_/\__, /_/_/ /_/\___/ // /____/ // FOnline Engine // https://fonline.ru // https://github.com/cvet/fonline // // MIT License // // Copyright (c) 2006 - 2026, Anton Tsvetinskiy aka cvet <cvet@tut.by> // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include "DiskFileSystem.h" #include "SafeArithmetics.h" FO_BEGIN_NAMESPACE auto fs_make_path(string_view path) -> std::u8string { FO_NO_STACK_TRACE_ENTRY(); return {path.begin(), path.end()}; } auto fs_path_to_string(const std::filesystem::path& path) -> string { FO_NO_STACK_TRACE_ENTRY(); auto u8_str = path.u8string(); return strex(string(u8_str.begin(), u8_str.end())).normalize_path_slashes(); } auto fs_resolve_path(string_view path) -> string { FO_STACK_TRACE_ENTRY(); std::error_code ec; auto resolved = std::filesystem::absolute(std::filesystem::path {fs_make_path(path)}, ec); return !ec ? fs_path_to_string(resolved) : strex(path).normalize_path_slashes(); } auto fs_exists(string_view path) noexcept -> bool { FO_STACK_TRACE_ENTRY(); std::error_code ec; return std::filesystem::exists(std::filesystem::path {fs_make_path(path)}, ec) && !ec; } auto fs_is_dir(string_view path) noexcept -> bool { FO_STACK_TRACE_ENTRY(); std::error_code ec; return std::filesystem::is_directory(std::filesystem::path {fs_make_path(path)}, ec) && !ec; } auto fs_is_absolute_path(string_view path) noexcept -> bool { FO_NO_STACK_TRACE_ENTRY(); return !path.empty() && std::filesystem::path {fs_make_path(path)}.is_absolute(); } auto fs_is_relative_path(string_view path) noexcept -> bool { FO_NO_STACK_TRACE_ENTRY(); return path.empty() || std::filesystem::path {fs_make_path(path)}.is_relative(); } auto fs_make_writable_path(string_view user_writable_path, string_view relative) -> string { FO_STACK_TRACE_ENTRY(); // Portable layout, or an already-absolute path: leave it as-is (written next to the exe / as given). if (user_writable_path.empty() || fs_is_absolute_path(relative)) { return string(relative); } // Installed layout: layer the relative writable path under the writable root. return strex(user_writable_path).combine_path(relative).str(); } auto fs_create_directories(string_view dir) noexcept -> bool { FO_STACK_TRACE_ENTRY(); if (dir.empty()) { return true; } std::error_code ec; auto fs_dir = std::filesystem::path {fs_make_path(dir)}; std::filesystem::create_directories(fs_dir, ec); return std::filesystem::exists(fs_dir, ec) && !ec && std::filesystem::is_directory(fs_dir, ec) && !ec; } auto fs_last_write_time(string_view path) noexcept -> uint64_t { FO_STACK_TRACE_ENTRY(); std::error_code ec; auto wt = std::filesystem::last_write_time(std::filesystem::path {fs_make_path(path)}, ec); return !ec ? wt.time_since_epoch().count() : 0; } auto fs_file_size(string_view path) noexcept -> optional<uint64_t> { FO_STACK_TRACE_ENTRY(); std::error_code ec; uintmax_t size = std::filesystem::file_size(std::filesystem::path {fs_make_path(path)}, ec); return !ec ? optional<uint64_t> {size} : std::nullopt; } auto fs_read_file(string_view path) -> optional<string> { FO_STACK_TRACE_ENTRY(); std::error_code ec; auto fs_path = std::filesystem::path {fs_make_path(path)}; uintmax_t file_size = std::filesystem::file_size(fs_path, ec); if (ec) { return std::nullopt; } FO_VERIFY_AND_THROW(std::cmp_less_equal(file_size, std::numeric_limits<size_t>::max()), "Disk file is too large to fit into memory buffer"); std::ifstream file {fs_path, std::ios::binary}; if (!file) { return std::nullopt; } string content; content.resize(static_cast<size_t>(file_size)); if (!content.empty()) { file.read(content.data(), static_cast<std::streamsize>(content.size())); if (!file || file.gcount() != static_cast<std::streamsize>(content.size())) { return std::nullopt; } } return content; } auto fs_compare_file_content(string_view path, const_span<uint8_t> content) -> bool { FO_STACK_TRACE_ENTRY(); auto existing_content = fs_read_file(path); if (!existing_content || existing_content->size() != content.size()) { return false; } if (content.empty()) { return true; } return MemCompare((*existing_content).data(), content.data(), content.size()); } auto fs_write_file(string_view path, string_view content) -> bool { FO_STACK_TRACE_ENTRY(); string dir = strex(path).extract_dir().str(); if (!dir.empty() && !fs_create_directories(dir)) { return false; } std::ofstream file {std::filesystem::path {fs_make_path(path)}, std::ios::binary | std::ios::trunc}; if (!file) { return false; } if (!content.empty()) { file.write(content.data(), static_cast<std::streamsize>(content.size())); } file.flush(); return !!file; } auto fs_write_file(string_view path, const_span<uint8_t> content) -> bool { FO_STACK_TRACE_ENTRY(); string dir = strex(path).extract_dir().str(); if (!dir.empty() && !fs_create_directories(dir)) { return false; } std::ofstream file {std::filesystem::path {fs_make_path(path)}, std::ios::binary | std::ios::trunc}; if (!file) { return false; } if (!content.empty()) { file.write(make_ptr(content.data()).reinterpret_as<char>().get(), static_cast<std::streamsize>(content.size())); } file.flush(); return !!file; } auto fs_remove_file(string_view path) noexcept -> bool { FO_STACK_TRACE_ENTRY(); std::error_code ec; auto fs_path = std::filesystem::path {fs_make_path(path)}; std::filesystem::remove(fs_path, ec); return !std::filesystem::exists(fs_path, ec) && !ec; } auto fs_remove_dir_tree(string_view dir) noexcept -> bool { FO_STACK_TRACE_ENTRY(); std::error_code ec; auto fs_dir = std::filesystem::path {fs_make_path(dir)}; std::filesystem::remove_all(fs_dir, ec); return !std::filesystem::exists(fs_dir, ec) && !ec; } auto fs_touch_file(string_view path) noexcept -> bool { FO_STACK_TRACE_ENTRY(); auto fs_path = std::filesystem::path {fs_make_path(path)}; std::error_code ec; bool exists = std::filesystem::exists(fs_path, ec); if (ec) { return false; } if (exists) { std::filesystem::last_write_time(fs_path, std::filesystem::file_time_type::clock::now(), ec); return !ec; } std::ofstream new_file {fs_path}; return !!new_file; } auto fs_rename(string_view from_path, string_view to_path) noexcept -> bool { FO_STACK_TRACE_ENTRY(); std::error_code ec; std::filesystem::rename(std::filesystem::path {fs_make_path(from_path)}, std::filesystem::path {fs_make_path(to_path)}, ec); return !ec; } auto fs_open_ifstream(string_view path, std::ios::openmode mode) -> std::ifstream { FO_STACK_TRACE_ENTRY(); return std::ifstream {std::filesystem::path {fs_make_path(path)}, mode}; } auto fs_hash_file(string_view path) -> optional<uint64_t> { FO_STACK_TRACE_ENTRY(); // FNV-1a 64. constexpr uint64_t offset = UINT64_C(0xcbf29ce484222325); constexpr uint64_t prime = UINT64_C(0x100000001b3); auto step = [](uint64_t hash, ptr<const uint8_t> bytes, size_t count) noexcept { for (size_t i = 0; i < count; ++i) { hash = (hash ^ bytes[i]) * prime; } return hash; }; auto stream = fs_open_ifstream(path); if (!stream) { return std::nullopt; } array<char, 0x10000> buf {}; uint64_t hash = offset; while (stream) { auto read_buf = make_nptr(buf.data()); stream.read(read_buf.get(), numeric_cast<std::streamsize>(buf.size())); auto read_size = numeric_cast<size_t>(stream.gcount()); if (read_size != 0) { auto hash_bytes = read_buf.reinterpret_as<const uint8_t>(); hash = step(hash, hash_bytes, read_size); } if (stream.bad()) { return std::nullopt; } } return hash; } auto fs_hash_data(const_span<uint8_t> data) noexcept -> uint64_t { FO_STACK_TRACE_ENTRY(); // FNV-1a 64. constexpr uint64_t offset = UINT64_C(0xcbf29ce484222325); constexpr uint64_t prime = UINT64_C(0x100000001b3); auto step = [](uint64_t hash, ptr<const uint8_t> bytes, size_t count) noexcept { for (size_t i = 0; i < count; ++i) { hash = (hash ^ bytes[i]) * prime; } return hash; }; if (data.empty()) { return offset; } return step(offset, data.data(), data.size()); } static void RecursiveDirLook(string_view base_dir, string_view cur_dir, bool recursive, const FsFileVisitor& visitor) { FO_STACK_TRACE_ENTRY(); auto full_dir = std::filesystem::path {fs_make_path(strex(base_dir).combine_path(cur_dir))}; auto dir_iterator = std::filesystem::directory_iterator(full_dir, std::filesystem::directory_options::follow_directory_symlink); for (const auto& dir_entry : dir_iterator) { auto u8_str = dir_entry.path().filename().u8string(); string path = string(u8_str.begin(), u8_str.end()); if (!path.empty() && path.front() != '.' && path.front() != '~') { if (dir_entry.is_directory()) { if (path.front() != '_' && recursive) { RecursiveDirLook(base_dir, strex(cur_dir).combine_path(path), recursive, visitor); } } else { uintmax_t file_size = dir_entry.file_size(); FO_VERIFY_AND_THROW(std::cmp_less_equal(file_size, std::numeric_limits<size_t>::max()), "Disk file is too large to fit into memory buffer"); visitor(strex(cur_dir).combine_path(path), static_cast<size_t>(file_size), dir_entry.last_write_time().time_since_epoch().count()); } } } } void fs_iterate_dir(string_view dir, bool recursive, const FsFileVisitor& visitor) { FO_STACK_TRACE_ENTRY(); RecursiveDirLook(dir, "", recursive, visitor); } auto stream_read_exact(std::istream& stream, span<uint8_t> buf) -> bool { FO_STACK_TRACE_ENTRY(); if (buf.empty()) { return true; } std::streamsize stream_len = numeric_cast<std::streamsize>(buf.size()); auto target_chars = make_ptr(buf.data()).reinterpret_as<char>(); stream.read(target_chars.get(), stream_len); return !!stream && stream.gcount() == stream_len; } auto stream_get_size(std::istream& stream) -> size_t { FO_STACK_TRACE_ENTRY(); auto cur_pos = stream.tellg(); if (cur_pos < 0) { return 0; } stream.clear(); stream.seekg(0, std::ios_base::end); if (!stream) { return 0; } auto end_pos = stream.tellg(); if (end_pos < 0) { return 0; } stream.clear(); stream.seekg(cur_pos, std::ios_base::beg); if (!stream) { return 0; } return static_cast<size_t>(end_pos); } auto stream_get_read_pos(std::istream& stream) -> size_t { FO_STACK_TRACE_ENTRY(); auto pos = stream.tellg(); return pos >= 0 ? static_cast<size_t>(pos) : 0; } auto stream_set_read_pos(std::istream& stream, int32_t offset, std::ios_base::seekdir origin) -> bool { FO_STACK_TRACE_ENTRY(); stream.clear(); stream.seekg(offset, origin); return !!stream; } FO_END_NAMESPACE