1+ #include " Cache.hpp"
2+
3+ #include < fmt/format.h>
4+ #include < chrono>
5+ #include < exception>
6+ #include < fstream>
7+ #include < iostream>
8+ #include < sstream>
9+ #include < wmtk/io/HDF5Writer.hpp>
10+ #include < wmtk/io/MeshReader.hpp>
11+ #include < wmtk/utils/Logger.hpp>
12+
13+ #include < nlohmann/json.hpp>
14+
15+ #include < filesystem>
16+
17+ namespace fs = std::filesystem;
18+
19+ long long nanoseconds_timestamp ()
20+ {
21+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
22+ std::chrono::system_clock::now ().time_since_epoch ())
23+ .count ();
24+ }
25+
26+ std::string number_to_hex (long long l)
27+ {
28+ return fmt::format (" {0:x}" , l);
29+ }
30+
31+ namespace wmtk ::io {
32+
33+ std::filesystem::path Cache::create_unique_directory (
34+ const std::string& prefix,
35+ const std::filesystem::path& location,
36+ size_t max_tries)
37+ {
38+ const fs::path tmp = location.empty () ? std::filesystem::temp_directory_path () : location;
39+
40+ const std::string timestamp = number_to_hex (nanoseconds_timestamp ());
41+
42+ fs::path unique_dir;
43+ for (size_t i = 0 ; i < max_tries; ++i) {
44+ unique_dir = tmp / (prefix + " _" + timestamp + " _" + number_to_hex (i));
45+
46+ if (std::filesystem::create_directory (unique_dir)) {
47+ return unique_dir;
48+ }
49+ }
50+
51+ throw std::runtime_error (" Could not generate a unique directory." );
52+ }
53+
54+ Cache::Cache (const std::string& prefix, const std::filesystem::path location)
55+ : m_cache_dir(location)
56+ {
57+ m_cache_dir = create_unique_directory (prefix, location);
58+ }
59+
60+ Cache::~Cache ()
61+ {
62+ const size_t max_tries = 1000 ;
63+ for (size_t i = 0 ; fs::exists (m_cache_dir) && i < max_tries; ++i) {
64+ fs::remove_all (m_cache_dir);
65+ }
66+
67+ if (fs::exists (m_cache_dir)) {
68+ wmtk::logger ().warn (" Could not remove cache folder {}" , fs::absolute (m_cache_dir));
69+ }
70+ }
71+
72+ const std::filesystem::path& Cache::create_unique_file (
73+ const std::string& filename,
74+ const std::string& extension,
75+ size_t max_tries)
76+ {
77+ const std::string timestamp = number_to_hex (nanoseconds_timestamp ());
78+
79+ for (size_t i = 0 ; i < max_tries; ++i) {
80+ const fs::path p =
81+ m_cache_dir / (filename + " _" + timestamp + " _" + number_to_hex (i) + extension);
82+
83+ if (fs::exists (p)) {
84+ continue ;
85+ }
86+
87+ // try to touch the file
88+ std::ofstream ofs (p);
89+ if (ofs.is_open ()) {
90+ m_file_paths[filename] = p;
91+ ofs.close ();
92+ return m_file_paths[filename];
93+ }
94+ ofs.close ();
95+ }
96+
97+ throw std::runtime_error (" Could not generate a unique file." );
98+ }
99+
100+ const std::filesystem::path& Cache::get_file_path (const std::string& filename)
101+ {
102+ const auto it = m_file_paths.find (filename);
103+
104+ if (it == m_file_paths.end ()) {
105+ // filename does not exist yet --> create it
106+ return create_unique_file (filename, " " );
107+ } else {
108+ return it->second ;
109+ }
110+ }
111+
112+ std::filesystem::path Cache::get_file_path (const std::string& filename) const
113+ {
114+ const auto it = m_file_paths.find (filename);
115+
116+ if (it == m_file_paths.end ()) {
117+ // filename does not exist yet --> create it
118+ throw std::runtime_error (" File with name '" + filename + " ' does not exist in cache" );
119+ } else {
120+ return it->second ;
121+ }
122+ }
123+
124+ std::filesystem::path Cache::get_cache_path () const
125+ {
126+ return m_cache_dir;
127+ }
128+
129+ std::shared_ptr<Mesh> Cache::read_mesh (const std::string& name) const
130+ {
131+ const fs::path p = get_file_path (name);
132+ return wmtk::read_mesh (p);
133+ }
134+
135+ void Cache::write_mesh (Mesh& m, const std::string& name)
136+ {
137+ const auto it = m_file_paths.find (name);
138+
139+ fs::path p;
140+
141+ if (it == m_file_paths.end ()) {
142+ // file does not exist yet --> create it
143+ p = create_unique_file (name, " .hdf5" );
144+ m_file_paths[name] = p;
145+ } else {
146+ p = it->second ;
147+ }
148+
149+ HDF5Writer writer (p);
150+ m.serialize (writer);
151+ }
152+
153+ bool Cache::export_cache (const std::filesystem::path& export_location)
154+ {
155+ if (fs::exists (export_location)) {
156+ return false ;
157+ }
158+
159+ fs::path cache_content_path;
160+
161+ // create a json with all cached names
162+ {
163+ nlohmann::json cache_content;
164+ for (const auto & [first, second] : m_file_paths) {
165+ cache_content[first] = fs::relative (second, m_cache_dir).string ();
166+ }
167+
168+ cache_content_path = create_unique_file (m_cache_content_name, " .json" );
169+ std::ofstream o (cache_content_path);
170+ o << std::setw (4 ) << cache_content << std::endl;
171+ o.close ();
172+ }
173+
174+ // copy folder to export location
175+ fs::copy (m_cache_dir, export_location, fs::copy_options::recursive);
176+
177+ // delete json
178+ fs::remove (cache_content_path);
179+
180+ return true ;
181+ }
182+
183+ bool Cache::import_cache (const std::filesystem::path& import_location)
184+ {
185+ if (!fs::exists (import_location)) {
186+ return false ;
187+ }
188+ if (!m_file_paths.empty ()) {
189+ return false ;
190+ }
191+
192+ // remove current directory
193+ fs::remove_all (m_cache_dir);
194+ // copy import
195+ fs::copy (import_location, m_cache_dir, fs::copy_options::recursive);
196+
197+ // find json
198+ fs::path cache_content_path;
199+ for (const auto & f : fs::directory_iterator (m_cache_dir)) {
200+ const fs::path p = f.path ();
201+ if (p.stem ().string ().rfind (m_cache_content_name, 0 ) == 0 ) {
202+ cache_content_path = p;
203+ break ;
204+ }
205+ }
206+
207+ if (cache_content_path.empty ()) {
208+ return false ;
209+ }
210+
211+ // read json
212+ {
213+ std::ifstream i (cache_content_path);
214+ const nlohmann::json cache_content = nlohmann::json::parse (i);
215+
216+ std::map<std::string, std::string> map_paths =
217+ cache_content.get <std::map<std::string, std::string>>();
218+
219+ // make file paths absolute
220+ for (auto & [first, second] : map_paths) {
221+ m_file_paths[first] = m_cache_dir / second;
222+ }
223+ }
224+
225+ // delete json
226+ fs::remove (cache_content_path);
227+
228+ return true ;
229+ }
230+
231+ } // namespace wmtk::io
0 commit comments