util/disk_cache.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file disk_cache.cpp | ||
| 3 | /// @brief Cache folder lookup and cache key hashing. | ||
| 4 | #include "ernie/util/disk_cache.h" | ||
| 5 | |||
| 6 | #include <cstdio> | ||
| 7 | #include <cstdlib> | ||
| 8 | #include <fstream> | ||
| 9 | #include <random> | ||
| 10 | #include <system_error> | ||
| 11 | |||
| 12 | namespace ernie { | ||
| 13 | |||
| 14 | 4 | std::optional<std::filesystem::path> CacheDirectory(const char* variable, std::string_view folder) { | |
| 15 | 4 | namespace fs = std::filesystem; | |
| 16 | 4 | if (const char* own = std::getenv(variable)) { | |
| 17 | 2 | const std::string_view value(own); | |
| 18 | 2 | if (value.empty() || value == "0") { | |
| 19 | 1 | return std::nullopt; | |
| 20 | } | ||
| 21 | 3 | return fs::path(value); | |
| 22 | } | ||
| 23 | 2 | if (const char* xdg = std::getenv("XDG_CACHE_HOME"); xdg != nullptr && *xdg != '\0') { | |
| 24 | ✗ | return fs::path(xdg) / "ernie" / folder; | |
| 25 | } | ||
| 26 | #if defined(_WIN32) | ||
| 27 | if (const char* local = std::getenv("LOCALAPPDATA"); local != nullptr && *local != '\0') { | ||
| 28 | return fs::path(local) / "ernie" / folder; | ||
| 29 | } | ||
| 30 | #else | ||
| 31 | 2 | if (const char* home = std::getenv("HOME"); home != nullptr && *home != '\0') { | |
| 32 | 18 | return fs::path(home) / ".cache" / "ernie" / folder; | |
| 33 | } | ||
| 34 | #endif | ||
| 35 | ✗ | return std::nullopt; | |
| 36 | } | ||
| 37 | |||
| 38 | 42 | void CacheKey::Add(std::string_view text) { | |
| 39 | 42 | const std::uint64_t size = text.size(); | |
| 40 | 42 | Bytes(&size, sizeof(size)); | |
| 41 | 42 | Bytes(text.data(), text.size()); | |
| 42 | 42 | } | |
| 43 | |||
| 44 | 23 | std::string CacheKey::Hex() const { | |
| 45 | 23 | char text[33]; | |
| 46 | 23 | std::snprintf(text, sizeof(text), "%016llx%016llx", static_cast<unsigned long long>(hashes_[0]), | |
| 47 | 23 | static_cast<unsigned long long>(hashes_[1])); | |
| 48 | 23 | return text; | |
| 49 | } | ||
| 50 | |||
| 51 | 84 | void CacheKey::Bytes(const void* data, std::size_t size) { | |
| 52 | 84 | const auto* bytes = static_cast<const unsigned char*>(data); | |
| 53 | 252 | for (std::uint64_t& hash : hashes_) { | |
| 54 | 1444 | for (std::size_t i = 0; i < size; ++i) { | |
| 55 | 1276 | hash = (hash ^ bytes[i]) * 0x100000001b3ull; | |
| 56 | } | ||
| 57 | } | ||
| 58 | 84 | } | |
| 59 | |||
| 60 | 4 | std::optional<std::vector<float>> ReadCachedFloats(const std::filesystem::path& path, | |
| 61 | std::size_t count) { | ||
| 62 | 4 | std::ifstream in(path, std::ios::binary); | |
| 63 | 4 | std::vector<float> values(count); | |
| 64 | 4 | const auto bytes = static_cast<std::streamsize>(count * sizeof(float)); | |
| 65 | 6 | if (in.read(reinterpret_cast<char*>(values.data()), bytes) && | |
| 66 | 4 | in.peek() == std::ifstream::traits_type::eof()) { | |
| 67 | 1 | return values; | |
| 68 | } | ||
| 69 | 3 | return std::nullopt; | |
| 70 | 4 | } | |
| 71 | |||
| 72 | 1 | void WriteCachedFloats(const std::filesystem::path& path, std::span<const float> values) { | |
| 73 | 1 | namespace fs = std::filesystem; | |
| 74 | 1 | fs::path partial = path; | |
| 75 | 3 | partial += ".part" + std::to_string(std::random_device{}()); | |
| 76 | 1 | std::error_code error; | |
| 77 | 1 | fs::create_directories(path.parent_path(), error); | |
| 78 | 1 | { | |
| 79 | 1 | std::ofstream out(partial, std::ios::binary); | |
| 80 | 1 | out.write(reinterpret_cast<const char*>(values.data()), | |
| 81 | 1 | static_cast<std::streamsize>(values.size_bytes())); | |
| 82 | 1 | if (!out) { | |
| 83 | ✗ | error = std::make_error_code(std::errc::io_error); | |
| 84 | } | ||
| 85 | 1 | } | |
| 86 | 1 | if (!error) { | |
| 87 | 1 | fs::rename(partial, path, error); | |
| 88 | } | ||
| 89 | 1 | if (error) { | |
| 90 | ✗ | fs::remove(partial, error); | |
| 91 | } | ||
| 92 | 1 | } | |
| 93 | |||
| 94 | } // namespace ernie | ||
| 95 |