Wildmeshing Toolkit
Loading...
Searching...
No Matches
Cache.cpp
Go to the documentation of this file.
1#include "Cache.hpp"
2
3#include <chrono>
4#include <exception>
5#include <fstream>
6#include <iostream>
7#include <nlohmann/json.hpp>
8#include <sstream>
11#include <wmtk/utils/Logger.hpp>
12
13#include <filesystem>
14
15namespace fs = std::filesystem;
16
18{
19 return std::chrono::duration_cast<std::chrono::nanoseconds>(
20 std::chrono::system_clock::now().time_since_epoch())
21 .count();
22}
23
24std::string number_to_hex(int64_t l)
25{
26 return fmt::format("{0:x}", l);
27}
28
29namespace wmtk::io {
30
32 : m_cache_dir(std::move(o.m_cache_dir))
33 , m_file_paths(std::move(o.m_file_paths))
34 , m_multimeshes(std::move(o.m_multimeshes))
35 , m_delete_cache(o.m_delete_cache)
36{
37 // make sure that the other cache doesn't use delete semantics anymore
38 o.m_delete_cache = false;
39}
41{
42 m_cache_dir = std::move(o.m_cache_dir);
43 m_file_paths = std::move(o.m_file_paths);
44 m_multimeshes = std::move(o.m_multimeshes);
45 m_delete_cache = o.m_delete_cache;
46 // make sure that the other cache doesn't use delete semantics anymore
47 o.m_delete_cache = false;
48 return *this;
49}
50
51std::filesystem::path Cache::create_unique_directory(
52 const std::string& prefix,
53 const std::filesystem::path& location,
54 size_t max_tries)
55{
56 const fs::path tmp = location.empty() ? std::filesystem::temp_directory_path() : location;
57
58 const std::string timestamp = number_to_hex(nanoseconds_timestamp());
59
60 fs::path unique_dir;
61 for (size_t i = 0; i < max_tries; ++i) {
62 unique_dir = tmp / (prefix + "_" + timestamp + "_" + number_to_hex(i));
63
64 if (std::filesystem::create_directory(unique_dir)) {
65 return unique_dir;
66 }
67 }
68
69 throw std::runtime_error("Could not generate a unique directory.");
70}
71
72Cache::Cache(const std::string& prefix, const std::filesystem::path location, bool delete_cache)
73 : m_cache_dir(location)
74 , m_delete_cache(delete_cache)
75{
76 m_cache_dir = create_unique_directory(prefix, location);
77}
78
80{
81 if (m_delete_cache) {
82 const size_t max_tries = 1000;
83 for (size_t i = 0; fs::exists(m_cache_dir) && i < max_tries; ++i) {
84 fs::remove_all(m_cache_dir);
85 }
86
87 if (fs::exists(m_cache_dir)) {
88 const std::string path = fs::absolute(m_cache_dir).string();
89 wmtk::logger().warn("Could not remove cache folder {}", path);
90 }
91 }
92}
93
94const std::filesystem::path& Cache::create_unique_file(
95 const std::string& filename,
96 const std::string& extension,
97 size_t max_tries)
98{
99 const std::string timestamp = number_to_hex(nanoseconds_timestamp());
100
101 for (size_t i = 0; i < max_tries; ++i) {
102 const fs::path p =
103 m_cache_dir / (filename + "_" + timestamp + "_" + number_to_hex(i) + extension);
104
105 if (fs::exists(p)) {
106 continue;
107 }
108
109 // try to touch the file
110 std::ofstream ofs(p);
111 if (ofs.is_open()) {
112 m_file_paths[filename] = p;
113 ofs.close();
114 return m_file_paths[filename];
115 }
116 ofs.close();
117 }
118
119 throw std::runtime_error("Could not generate a unique file.");
120}
121
122const std::filesystem::path& Cache::get_file_path(const std::string& filename)
123{
124 const auto it = m_file_paths.find(filename);
125
126 if (it == m_file_paths.end()) {
127 // filename does not exist yet --> create it
128 return create_unique_file(filename, "");
129 } else {
130 return it->second;
131 }
132}
133
134std::filesystem::path Cache::get_file_path(const std::string& filename) const
135{
136 const auto it = m_file_paths.find(filename);
137
138 if (it == m_file_paths.end()) {
139 // filename does not exist yet --> create it
140 throw std::runtime_error("File with name '" + filename + "' does not exist in cache");
141 } else {
142 return it->second;
143 }
144}
145
146std::filesystem::path Cache::get_cache_path() const
147{
148 return m_cache_dir;
149}
150std::vector<int64_t> Cache::absolute_multi_mesh_id(const std::string& name) const
151{
152 auto mm_name = name.substr(0, name.find('.'));
153 return m_multimeshes.at(mm_name).get_id_from_path(name);
154}
155
156void Cache::load_multimesh(const std::string& name) const
157{
158 auto mm_name = name.substr(0, name.find('.'));
159 if (m_multimeshes.find(mm_name) == m_multimeshes.end()) {
160 m_multimeshes.emplace(
161 mm_name,
162 CachedMultiMesh(mm_name, std::map<std::string, std::vector<int64_t>>{}));
163 }
164 auto& cmm = m_multimeshes.at(mm_name);
165 if (!bool(cmm.get_root())) {
166 const fs::path p = get_file_path(mm_name);
167 cmm.load(p);
168 }
169}
170
171std::shared_ptr<Mesh> Cache::read_mesh(const std::string& name) const
172{
173 auto mm_name = name.substr(0, name.find('.'));
174 load_multimesh(mm_name);
175 return m_multimeshes.at(mm_name).get_from_path(name);
176}
178{
179 for (auto& pr : m_multimeshes) {
180 pr.second.flush();
181 }
182}
183
184std::vector<std::string> Cache::mesh_names()
185{
186 std::vector<std::string> names;
187 for (const auto& fp : m_file_paths) {
188 names.emplace_back(fp.first);
189 }
190
191 return names;
192}
193
195 const Mesh& m,
196 const std::string& name,
197 const std::map<std::string, std::vector<int64_t>>& multimesh_names)
198{
199 const auto it = m_file_paths.find(name);
200
201 fs::path p;
202
203 if (it == m_file_paths.end()) {
204 // file does not exist yet --> create it
205 p = create_unique_file(name, ".hdf5");
206 m_file_paths[name] = p;
207 } else {
208 p = it->second;
209 }
210
211 std::map<std::string, std::vector<int64_t>> mm_names = multimesh_names;
212 if (m_multimeshes.find(name) != m_multimeshes.end()) {
213 mm_names = m_multimeshes.at(name).get_multimesh_names();
214 mm_names.insert(multimesh_names.begin(), multimesh_names.end());
216 name,
217 mm_names,
218 const_cast<Mesh&>(m).get_multi_mesh_root().shared_from_this());
219 } else {
220 m_multimeshes.emplace(
221 name,
223 name,
224 multimesh_names,
225 const_cast<Mesh&>(m).get_multi_mesh_root().shared_from_this()));
226 }
227
228 HDF5Writer writer(p);
229 m.serialize(writer, &m);
230}
231
232bool Cache::export_cache(const std::filesystem::path& export_location)
233{
234 if (fs::exists(export_location)) {
235 return false;
236 }
237
238 fs::path cache_content_path;
239
240 // create a json with all cached names
241 {
242 nlohmann::json cache_content;
243 for (const auto& [first, second] : m_file_paths) {
244 cache_content[first] = fs::relative(second, m_cache_dir).string();
245 }
246
247 cache_content_path = create_unique_file(m_cache_content_name, ".json");
248 std::ofstream o(cache_content_path);
249 o << std::setw(4) << cache_content << std::endl;
250 o.close();
251 }
252
253 // copy folder to export location
254 fs::copy(m_cache_dir, export_location, fs::copy_options::recursive);
255
256 // delete json
257 fs::remove(cache_content_path);
259
260 return true;
261}
262
263bool Cache::import_cache(const std::filesystem::path& import_location)
264{
265 if (!fs::exists(import_location)) {
266 return false;
267 }
268 if (!m_file_paths.empty()) {
269 return false;
270 }
271
272 // remove current directory
273 fs::remove_all(m_cache_dir);
274 // copy import
275 fs::copy(import_location, m_cache_dir, fs::copy_options::recursive);
276
277 // find json
278 fs::path cache_content_path;
279 for (const auto& f : fs::directory_iterator(m_cache_dir)) {
280 const fs::path p = f.path();
281 if (p.stem().string().rfind(m_cache_content_name, 0) == 0) {
282 cache_content_path = p;
283 break;
284 }
285 }
286
287 if (cache_content_path.empty()) {
288 return false;
289 }
290
291 // read json
292 {
293 std::ifstream i(cache_content_path);
294 const nlohmann::json cache_content = nlohmann::json::parse(i);
295
296 std::map<std::string, std::string> map_paths =
297 cache_content.get<std::map<std::string, std::string>>();
298
299 // make file paths absolute
300 for (auto& [first, second] : map_paths) {
301 m_file_paths[first] = m_cache_dir / second;
302 }
303 }
304
305 // delete json
306 fs::remove(cache_content_path);
307
308 return true;
309}
310
311bool Cache::equals(const Cache& o)
312{
313 // check file names
314 if (m_file_paths.size() != o.m_file_paths.size() ||
315 !std::equal(
316 m_file_paths.begin(),
317 m_file_paths.end(),
318 m_file_paths.begin(),
319 [](const auto& a, const auto& b) { return a.first == b.first; })) {
320 wmtk::logger().info("File name list is unequal.");
321 return false;
322 }
323
324 // check files for equality
325 for (const auto& [file_name, path1] : m_file_paths) {
326 const auto& path2 = o.m_file_paths.at(file_name);
327
328 std::shared_ptr<Mesh> mesh_ptr_1 = wmtk::read_mesh(path1);
329 std::shared_ptr<Mesh> mesh_ptr_2 = wmtk::read_mesh(path2);
330
331 if (!(*mesh_ptr_1 == *mesh_ptr_2)) {
332 wmtk::logger().info("Mesh {} is unequal.", file_name);
333 return false;
334 }
335 }
336
337 return true;
338}
339
340} // namespace wmtk::io
int64_t nanoseconds_timestamp()
Definition Cache.cpp:17
std::string number_to_hex(int64_t l)
Definition Cache.cpp:24
void serialize(MeshWriter &writer, const Mesh *local_root=nullptr) const
Definition Mesh.cpp:93
void write_mesh(const Mesh &m, const std::string &name, const std::map< std::string, std::vector< int64_t > > &multimesh_names={})
Write a mesh to cache.
Definition Cache.cpp:194
void flush_multimeshes()
Unsets the mesh held by each cached mm - useful for debugging whether cache loading works.
Definition Cache.cpp:177
std::vector< std::string > mesh_names()
Get all names of the meshes stored in cache.
Definition Cache.cpp:184
const std::filesystem::path & get_file_path(const std::string &filename)
Get the path where the file with the given name is stored.
Definition Cache.cpp:122
std::filesystem::path get_cache_path() const
Get the path of the cache folder.
Definition Cache.cpp:146
const std::filesystem::path & create_unique_file(const std::string &filename, const std::string &extension, size_t max_tries=10000)
Create a file with the given name in the cache without overwriting any file with the same name.
Definition Cache.cpp:94
bool equals(const Cache &o)
Compare two caches for equality.
Definition Cache.cpp:311
std::map< std::string, std::filesystem::path > m_file_paths
Definition Cache.hpp:157
std::map< std::string, CachedMultiMesh > m_multimeshes
Definition Cache.hpp:158
bool m_delete_cache
Definition Cache.hpp:159
static std::filesystem::path create_unique_directory(const std::string &prefix, const std::filesystem::path &location="", size_t max_tries=10000)
Create a unique directory in the given location.
Definition Cache.cpp:51
Cache & operator=(Cache &&)
Definition Cache.cpp:40
void load_multimesh(const std::string &name) const
Definition Cache.cpp:156
bool import_cache(const std::filesystem::path &import_location)
Import a cache from the given location.
Definition Cache.cpp:263
static const std::string m_cache_content_name
Definition Cache.hpp:161
Cache(const std::string &prefix, const std::filesystem::path directory="", bool delete_cache=true)
This class creates and maintains a cache folder for storing temporary data.
Definition Cache.cpp:72
std::vector< int64_t > absolute_multi_mesh_id(const std::string &name) const
Definition Cache.cpp:150
bool export_cache(const std::filesystem::path &export_location)
Export the cache to the given location.
Definition Cache.cpp:232
std::shared_ptr< Mesh > read_mesh(const std::string &name) const
Load a mesh from cache.
Definition Cache.cpp:171
std::filesystem::path m_cache_dir
Definition Cache.hpp:156
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58
std::shared_ptr< Mesh > read_mesh(const std::filesystem::path &filename, const bool ignore_z_if_zero, const std::vector< std::string > &tetrahedron_attributes)