chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
78 / 78
100.0%
Branches:
68 / 72
94.4%
Functions:
9 / 9
100.0%

util/disk_cache.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/util/disk_cache.h"
3
4 #include <algorithm>
5 #include <atomic>
6 #include <chrono>
7 #include <cstdint>
8 #include <fstream>
9 #include <functional>
10 #include <iterator>
11 #include <system_error>
12 #include <thread>
13 #include <vector>
14
15 #include <fmt/format.h>
16
17 #include "chrono_batch_render/util/environment.h"
18 #include "chrono_batch_render/util/log.h"
19 #include "chrono_batch_render/util/text.h"
20 #include "chrono_batch_render/util/version.h"
21
22 namespace chrono::batch_render {
23 namespace {
24
25 2651 std::string Preamble(std::string_view key) {
26 2651 return fmt::format("chrono_batch_render cache 1\n{}\n", key);
27 }
28
29 192 std::string UniqueSuffix() {
30 192 static std::atomic<uint64_t> counter{0};
31 192 const auto now = std::chrono::steady_clock::now().time_since_epoch().count();
32 192 return fmt::format(".{:x}.{:x}.{:x}.tmp",
33 576 std::hash<std::thread::id>{}(std::this_thread::get_id()), uint64_t(now),
34 192 counter++);
35 }
36
37 192 bool WriteTemporary(const std::filesystem::path& temporary, std::string_view key,
38 std::string_view bytes) {
39 192 std::ofstream file(temporary, std::ios::binary | std::ios::trunc);
40
2/2
✓ Branch 1 taken 192 times.
✓ Branch 4 taken 192 times.
384 file << Preamble(key) << bytes;
41
1/1
✓ Branch 1 taken 192 times.
192 return bool(file.flush());
42 192 }
43
44 }
45
46 5320 std::filesystem::path DiskCache::Path(std::string_view key) const {
47
2/2
✓ Branch 2 taken 5320 times.
✓ Branch 5 taken 5320 times.
15960 return m_directory / (Digest(key) + ".bin");
48 }
49
50 2671 std::optional<std::string> DiskCache::Load(std::string_view key) const {
51 5342 std::ifstream file(Path(key), std::ios::binary);
52
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 2459 times.
2671 if (!file) {
53 212 return std::nullopt;
54 }
55
56
2/2
✓ Branch 1 taken 2459 times.
✓ Branch 4 taken 2459 times.
4918 std::string bytes{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
57
1/1
✓ Branch 1 taken 2459 times.
2459 const std::string preamble = Preamble(key);
58
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2457 times.
2459 if (!bytes.starts_with(preamble)) {
59 2 return std::nullopt;
60 }
61 2457 std::error_code error;
62
1/1
✓ Branch 2 taken 2457 times.
2457 std::filesystem::last_write_time(Path(key), std::filesystem::file_time_type::clock::now(),
63 error);
64
1/1
✓ Branch 1 taken 2457 times.
4914 return bytes.substr(preamble.size());
65 2671 }
66
67 192 void DiskCache::Store(std::string_view key, std::string_view bytes) const {
68 192 const std::filesystem::path path = Path(key);
69
3/3
✓ Branch 1 taken 192 times.
✓ Branch 4 taken 192 times.
✓ Branch 7 taken 192 times.
576 const std::filesystem::path temporary = path.string() + UniqueSuffix();
70
1/1
✓ Branch 1 taken 192 times.
192 std::error_code error;
71
1/1
✓ Branch 1 taken 192 times.
192 std::filesystem::create_directories(m_directory, error);
72
3/3
✓ Branch 1 taken 192 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 191 times.
192 if (!WriteTemporary(temporary, key, bytes)) {
73
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 Log(LogLevel::Debug, "can't write {} to the disk cache", temporary.string());
74 1 std::filesystem::remove(temporary, error);
75 1 return;
76 }
77
78 191 std::filesystem::rename(temporary, path, error);
79
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 190 times.
191 if (error) {
80
3/3
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
2 Log(LogLevel::Debug, "can't add {} to the disk cache: {}", path.string(), error.message());
81 1 std::filesystem::remove(temporary, error);
82 }
83 384 }
84
85 30 std::filesystem::path UserCacheDirectory() {
86
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 29 times.
30 if (const std::string chosen = Environment("CHRONO_BATCH_RENDER_CACHE_DIR"); !chosen.empty()) {
87
1/1
✓ Branch 1 taken 1 time.
1 return chosen;
88 1 }
89 #if defined(_WIN32)
90 if (const std::string local = Environment("LOCALAPPDATA"); !local.empty()) {
91 return std::filesystem::path(local) / "chrono-batch-render" / "cache";
92 }
93 #elif defined(__APPLE__)
94 if (const std::string home = Environment("HOME"); !home.empty()) {
95 return std::filesystem::path(home) / "Library" / "Caches" / "chrono-batch-render";
96 }
97 #else
98
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 28 times.
29 if (const std::string cache = Environment("XDG_CACHE_HOME"); !cache.empty()) {
99
3/3
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
3 return std::filesystem::path(cache) / "chrono-batch-render";
100 1 }
101
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 1 time.
28 if (const std::string home = Environment("HOME"); !home.empty()) {
102
5/5
✓ Branch 1 taken 27 times.
✓ Branch 4 taken 27 times.
✓ Branch 7 taken 27 times.
✓ Branch 10 taken 27 times.
✓ Branch 13 taken 27 times.
135 return std::filesystem::path(home) / ".cache" / "chrono-batch-render";
103 27 }
104 #endif
105 1 std::error_code error;
106
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
3 return std::filesystem::temp_directory_path(error) / "chrono-batch-render-cache";
107 }
108
109 22 std::filesystem::path KernelCacheDirectory(const std::filesystem::path& root) {
110 22 const Version version = GetBuildInfo().version;
111
4/4
✓ Branch 2 taken 22 times.
✓ Branch 5 taken 22 times.
✓ Branch 8 taken 22 times.
✓ Branch 11 taken 22 times.
88 return root / "kernels" / fmt::format("{}.{}.{}", version.major, version.minor, version.patch);
112 }
113
114 13 void TrimKernelCache(const std::filesystem::path& root, uint64_t limit) {
115 13 namespace fs = std::filesystem;
116
1/1
✓ Branch 1 taken 13 times.
13 std::error_code error;
117
118
0/2
✗ Branch 22 not taken.
✗ Branch 23 not taken.
82894 struct Entry {
119 fs::path path;
120 fs::file_time_type used;
121 uint64_t bytes;
122 };
123
124 13 std::vector<Entry> entries;
125 13 uint64_t total = 0;
126 12731 for (const fs::directory_entry& entry :
127
9/9
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
✓ Branch 8 taken 12 times.
✓ Branch 9 taken 1 time.
✓ Branch 11 taken 12 times.
✓ Branch 12 taken 1 time.
✓ Branch 16 taken 12705 times.
✓ Branch 18 taken 12705 times.
✓ Branch 19 taken 13 times.
12781 fs::recursive_directory_iterator(root / "kernels", error)) {
128
2/2
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 12611 times.
12705 if (!entry.is_regular_file(error)) {
129 94 continue;
130 }
131 12611 const uint64_t bytes = entry.file_size(error);
132 12611 entries.push_back({entry.path(), entry.last_write_time(error), bytes});
133 12611 total += bytes;
134 13 }
135
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 time.
13 if (total <= limit) {
136 12 return;
137 }
138 1 std::ranges::sort(entries, {}, &Entry::used);
139
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for (const Entry& entry : entries) {
140
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if (total <= limit) {
141 break;
142 }
143
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (fs::remove(entry.path, error)) {
144 2 total -= entry.bytes;
145 }
146 }
147 12624 }
148
149 }
150