CoolProp 8.0.0
An open-source fluid property and humid air property database
CPfilepaths.cpp
Go to the documentation of this file.
6
7#include <fstream>
8#include <algorithm>
9#include <atomic>
10#include <cerrno>
11#include <filesystem>
12#include <random>
13#include <sstream>
14#include <stdexcept>
15#include <system_error>
16
17// This will kill the horrible min and max macros
18#ifndef NOMINMAX
19# define NOMINMAX
20#endif
21
22namespace fs = std::filesystem;
23
24// Platform-specific includes needed by get_home_dir
25#if defined(__ISWINDOWS__)
26# define UNICODE
27# define _UNICODE
28# include "Windows.h"
29# include <windows.h>
30#else
31# include <sys/types.h>
32# include <sys/stat.h>
33# include <unistd.h>
34# if !defined(__powerpc__)
35# include <pwd.h>
36# endif
37#endif
38
39// ---- CalculateDirSize -------------------------------------------------------
40
41unsigned long long CalculateDirSize(const std::string& path) {
42 unsigned long long size = 0;
43 std::error_code ec;
44 for (const auto& entry : fs::recursive_directory_iterator(path, fs::directory_options::skip_permission_denied, ec)) {
45 if (!ec && entry.is_regular_file(ec) && !ec) {
46 size += entry.file_size(ec);
47 ec.clear();
48 }
49 }
50 return size;
51}
52
53// Legacy implementations kept for reference (powerpc / pre-NDK-r23 Android):
54//
55// #if defined(__ISWINDOWS__)
56// /// From http://stackoverflow.com/a/17827724/1360263
57// static bool IsBrowsePath(const std::wstring& path) {
58// return (path == L"." || path == L"..");
59// }
60// unsigned long long CalculateDirSize(const std::wstring& path, std::vector<std::wstring>* errVect) {
61// unsigned long long size = 0;
62// WIN32_FIND_DATAW data;
63// HANDLE sh = NULL;
64// sh = FindFirstFileW((path + L"\\*").c_str(), &data);
65// if (sh == INVALID_HANDLE_VALUE) {
66// if (errVect != NULL) errVect->push_back(path);
67// return size;
68// }
69// do {
70// if (!IsBrowsePath(data.cFileName)) {
71// if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
72// size += CalculateDirSize(path + L"\\" + data.cFileName, NULL);
73// else
74// size += data.nFileSizeHigh * (unsigned long long)(MAXDWORD) + data.nFileSizeLow;
75// }
76// } while (FindNextFileW(sh, &data));
77// FindClose(sh);
78// return size;
79// }
80// #else
81// // Android / powerpc fallback — stub returning 0
82// unsigned long long CalculateDirSize(const std::string& path) { return 0; }
83// #endif
84
85// ---- get_binary_file_contents -----------------------------------------------
86
87std::vector<char> get_binary_file_contents(const char* filename) {
88 std::ifstream in(filename, std::ios::in | std::ios::binary);
89 if (in) {
90 std::vector<char> contents;
91 in.seekg(0, std::ios::end);
92 contents.resize((unsigned int)in.tellg());
93 in.seekg(0, std::ios::beg);
94 in.read(&contents[0], contents.size());
95 in.close();
96 return (contents);
97 }
98 throw(errno);
99}
100
101// ---- write_bytes_atomic -----------------------------------------------------
102//
103// Write-temp + atomic-rename helper for cache files that may be written
104// concurrently by multiple processes/threads (notably the SVDSBTL
105// validation notebook's joblib loky workers — CoolProp-4no.2). See
106// declaration in CPfilepaths.h for full contract.
107
108namespace {
109
110// Process-unique 64-bit salt: drawn once per process from
111// std::random_device so two concurrent processes writing the same
112// target don't collide on temp-file paths. Combined with a process-
113// local atomic counter inside make_temp_sibling() to disambiguate
114// threads / repeated writes within the same process.
115const std::uint64_t kProcessSalt = []() {
116 std::random_device rd;
117 return (static_cast<std::uint64_t>(rd()) << 32) | static_cast<std::uint64_t>(rd());
118}();
119
120std::filesystem::path make_temp_sibling(const std::filesystem::path& target) {
121 static std::atomic<std::uint64_t> counter{0};
122 const auto seq = counter.fetch_add(1, std::memory_order_relaxed);
123 std::ostringstream oss;
124 oss << ".tmp." << std::hex << kProcessSalt << '.' << seq;
125 auto temp = target;
126 temp += oss.str();
127 return temp;
128}
129
130} // namespace
131
132void write_bytes_atomic(const std::filesystem::path& target, const void* bytes, std::size_t size, bool restrict_perms) {
133 const auto temp = make_temp_sibling(target);
134 {
135 std::ofstream out(temp, std::ios::binary | std::ios::trunc);
136 if (!out) {
137 throw std::runtime_error("write_bytes_atomic: cannot open temp " + temp.string());
138 }
139 if (size > 0) {
140 out.write(reinterpret_cast<const char*>(bytes), static_cast<std::streamsize>(size));
141 }
142 out.flush();
143 if (!out) {
144 std::error_code ec_rm;
145 std::filesystem::remove(temp, ec_rm);
146 throw std::runtime_error("write_bytes_atomic: write failed for temp " + temp.string());
147 }
148 }
149 if (restrict_perms) {
150 std::error_code perm_ec;
151 std::filesystem::permissions(temp, std::filesystem::perms::owner_read | std::filesystem::perms::owner_write,
152 std::filesystem::perm_options::replace, perm_ec);
153 // perm_ec ignored — owner-only is best-effort (no-op on Windows
154 // where the POSIX permission model doesn't apply).
155 }
156 std::error_code rename_ec;
157 std::filesystem::rename(temp, target, rename_ec);
158 if (rename_ec) {
159 std::error_code ec_rm;
160 std::filesystem::remove(temp, ec_rm);
161 throw std::runtime_error("write_bytes_atomic: rename to " + target.string() + " failed: " + rename_ec.message());
162 }
163}
164
165// ---- make_dirs --------------------------------------------------------------
166
167void make_dirs(const std::string& file_path) {
168 std::error_code ec;
169 fs::create_directories(fs::path(file_path), ec);
170 // Ignore errors (same behaviour as legacy code which silently skipped failures)
171}
172
173// Legacy implementation kept for reference (powerpc / pre-NDK-r23 Android):
174//
175// void make_dirs(std::string file_path) {
176// std::replace(file_path.begin(), file_path.end(), '\\', '/');
177// # if defined(__ISWINDOWS__)
178// const char sep = '\\';
179// # else
180// const char sep = '/';
181// # endif
182// std::vector<std::string> pathsplit = strsplit(file_path, '/');
183// std::string path = pathsplit[0];
184// for (std::size_t i = 0, sz = pathsplit.size(); i < sz; i++) {
185// if (!path_exists(path)) {
186// # if defined(__ISWINDOWS__)
187// int errcode = CreateDirectoryA((LPCSTR)path.c_str(), NULL);
188// if (errcode == 0) {
189// switch (GetLastError()) {
190// case ERROR_ALREADY_EXISTS: break;
191// case ERROR_PATH_NOT_FOUND:
192// throw CoolProp::ValueError(format("Unable to make the directory %s", path.c_str()));
193// default: break;
194// }
195// }
196// # elif !defined(__powerpc__)
197// mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
198// # endif
199// }
200// if (i < (sz - 1)) path += format("%c%s", sep, pathsplit[i + 1].c_str());
201// }
202// }
203
204// ---- get_separator ----------------------------------------------------------
205
206std::string get_separator() {
207 return {1, fs::path::preferred_separator};
208}
209
210// Legacy implementation kept for reference (powerpc / pre-NDK-r23 Android):
211//
212// std::string get_separator() {
213// #if defined(__ISLINUX__)
214// return std::string("/");
215// #elif defined(__ISAPPLE__)
216// return std::string("/");
217// #elif defined(__ISWINDOWS__)
218// return std::string("\\");
219// #else
220// throw CoolProp::NotImplementedError("This function is not defined for your platform.");
221// #endif
222// }
223
224// ---- get_home_dir -----------------------------------------------------------
225
226std::string get_home_dir() {
227// See http://stackoverflow.com/questions/2552416/how-can-i-find-the-users-home-dir-in-a-cross-platform-manner-using-c
228#if defined(__ISLINUX__) || defined(__ISAPPLE__)
229 char* home = nullptr;
230 home = getenv("HOME");
231# if defined(__ISAPPLE__)
232 if (home == NULL) {
233 struct passwd* pwd = getpwuid(getuid());
234 if (pwd) {
235 home = pwd->pw_dir;
236 }
237 }
238# endif
239 if (home == nullptr) {
240 throw CoolProp::NotImplementedError("Could not detect home directory.");
241 }
242 return {home};
243#elif defined(__ISWINDOWS__)
244# if defined(_MSC_VER)
245# pragma warning(push)
246# pragma warning(disable : 4996)
247# endif
248 char* pUSERPROFILE = getenv("USERPROFILE");
249 if (pUSERPROFILE != NULL) {
250 return std::string(pUSERPROFILE);
251 } else {
252 char* pHOMEDRIVE = getenv("HOMEDRIVE");
253 char* pHOMEPATH = getenv("HOMEPATH");
254 if (pHOMEDRIVE != NULL && pHOMEPATH != NULL) {
255 return std::string(pHOMEDRIVE) + std::string(pHOMEPATH);
256 } else {
257 return std::string("");
258 }
259 }
260# if defined(_MSC_VER)
261# pragma warning(pop)
262# endif
263#else
264 throw CoolProp::NotImplementedError("This function is not defined for your platform.");
265#endif
266};
267
268// ---- path_exists ------------------------------------------------------------
269
270bool path_exists(const std::string& path) {
271 std::error_code ec;
272 return fs::exists(fs::path(path), ec);
273}
274
275// Legacy implementation kept for reference (powerpc / pre-NDK-r23 Android):
276//
277// bool path_exists(const std::string& path) {
278// std::string path_cpy;
279// if (endswith(path, get_separator())) {
280// path_cpy = path.substr(0, path.size() - 1);
281// } else {
282// path_cpy = path;
283// }
284// # if defined(__ISWINDOWS__)
285// struct _stat buf;
286// return (_stat(path_cpy.c_str(), &buf) == 0);
287// # elif defined(__ISLINUX__) || defined(__ISAPPLE__)
288// struct stat st;
289// if (lstat(path_cpy.c_str(), &st) == 0)
290// return S_ISDIR(st.st_mode) || S_ISREG(st.st_mode);
291// return false;
292// # else
293// throw CoolProp::NotImplementedError("This function is not defined for your platform.");
294// # endif
295// }
296
297// ---- join_path --------------------------------------------------------------
298
299std::string join_path(const std::string& one, const std::string& two) {
300 return (fs::path(one) / two).string();
301}
302
303// Legacy implementation kept for reference (powerpc / pre-NDK-r23 Android):
304//
305// std::string join_path(const std::string& one, const std::string& two) {
306// std::string result;
307// std::string separator = get_separator();
308// if (!endswith(one, separator) && !one.empty()) {
309// result = one + separator;
310// } else {
311// result = one;
312// }
313// result.append(two);
314// return result;
315// }
316
317// ---- get_file_contents ------------------------------------------------------
318
319std::string get_file_contents(const char* filename) {
320 std::ifstream in(filename, std::ios::in | std::ios::binary);
321 if (in) {
322 std::string contents;
323 in.seekg(0, std::ios::end);
324 contents.resize((unsigned int)in.tellg());
325 in.seekg(0, std::ios::beg);
326 in.read(&contents[0], contents.size());
327 in.close();
328 return (contents);
329 }
330 throw(errno);
331}