2018-10-06 20:38:44 -07:00
|
|
|
#pragma once
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2015-07-17 21:35:01 -07:00
|
|
|
#ifndef _WIN32
|
2017-12-28 23:56:31 -08:00
|
|
|
#include <cstdlib>
|
2015-05-27 02:09:05 -07:00
|
|
|
#include <dirent.h>
|
2015-06-10 16:34:14 -07:00
|
|
|
#include <fcntl.h>
|
2022-01-31 16:06:54 -08:00
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/stat.h>
|
2016-01-24 18:16:40 -08:00
|
|
|
#include <sys/statvfs.h>
|
2022-01-31 16:06:54 -08:00
|
|
|
#include <unistd.h>
|
2019-02-26 21:13:19 -08:00
|
|
|
#if __linux__ || __APPLE__
|
|
|
|
extern "C" int rep_closefrom(int lower);
|
|
|
|
#define closefrom rep_closefrom
|
|
|
|
#endif
|
2015-07-22 12:14:50 -07:00
|
|
|
#else
|
2015-08-30 20:36:24 -07:00
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
|
|
#endif
|
2015-09-26 21:35:36 -07:00
|
|
|
#ifndef NOMINMAX
|
|
|
|
#define NOMINMAX
|
|
|
|
#endif
|
2015-07-22 12:14:50 -07:00
|
|
|
#include <Windows.h>
|
2017-12-28 23:56:31 -08:00
|
|
|
#include <cwchar>
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <cwctype>
|
2017-02-24 23:58:36 -08:00
|
|
|
#include <Shlwapi.h>
|
2015-08-30 20:36:24 -07:00
|
|
|
#include "winsupport.hpp"
|
2021-06-30 11:20:45 -07:00
|
|
|
#include <nowide/stackstring.hpp>
|
2015-05-27 02:09:05 -07:00
|
|
|
#endif
|
|
|
|
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <algorithm>
|
2017-12-28 23:56:31 -08:00
|
|
|
#include <cinttypes>
|
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdio>
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <ctime>
|
2015-05-19 22:22:32 -07:00
|
|
|
#include <functional>
|
2015-09-29 23:23:07 -07:00
|
|
|
#include <list>
|
2015-10-14 16:06:47 -07:00
|
|
|
#include <map>
|
2021-07-11 17:58:16 -07:00
|
|
|
#include <optional>
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <regex>
|
|
|
|
#include <string>
|
2021-10-25 16:17:51 -07:00
|
|
|
#include <optional>
|
2019-08-14 20:20:25 -07:00
|
|
|
|
2021-07-11 17:58:16 -07:00
|
|
|
#include "FourCC.hpp"
|
2017-12-28 23:56:31 -08:00
|
|
|
#include "athena/Global.hpp"
|
2021-07-11 17:58:16 -07:00
|
|
|
#include "logvisor/logvisor.hpp"
|
2022-02-17 16:38:31 -08:00
|
|
|
#include "xxhash.h"
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2021-07-11 17:58:16 -07:00
|
|
|
|
2018-06-01 17:02:20 -07:00
|
|
|
#if defined(__has_feature)
|
|
|
|
#if __has_feature(thread_sanitizer)
|
|
|
|
#define HECL_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifndef HECL_NO_SANITIZE_THREAD
|
|
|
|
#define HECL_NO_SANITIZE_THREAD
|
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
namespace hecl {
|
|
|
|
namespace Database {
|
2015-09-29 23:23:07 -07:00
|
|
|
class Project;
|
2015-10-11 21:38:49 -07:00
|
|
|
struct DataSpecEntry;
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace Database
|
2015-09-29 23:23:07 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
namespace blender {
|
2019-10-01 00:23:35 -07:00
|
|
|
enum class BlendType { None, Mesh, ColMesh, Armature, Actor, Area,
|
|
|
|
World, MapArea, MapUniverse, Frame, PathMesh };
|
2017-12-28 23:56:31 -08:00
|
|
|
|
2019-09-04 14:22:05 -07:00
|
|
|
class ANIMOutStream;
|
2017-12-28 23:56:31 -08:00
|
|
|
class Connection;
|
|
|
|
class DataStream;
|
|
|
|
class PyOutStream;
|
2019-09-04 14:22:05 -07:00
|
|
|
class Token;
|
|
|
|
|
|
|
|
struct Action;
|
|
|
|
struct Actor;
|
|
|
|
struct Armature;
|
|
|
|
struct Bone;
|
2017-12-28 23:56:31 -08:00
|
|
|
struct ColMesh;
|
|
|
|
struct Light;
|
|
|
|
struct MapArea;
|
|
|
|
struct MapUniverse;
|
2019-09-04 14:22:05 -07:00
|
|
|
struct Material;
|
2017-12-28 23:56:31 -08:00
|
|
|
struct Matrix3f;
|
2018-04-07 13:52:35 -07:00
|
|
|
struct Matrix4f;
|
2019-09-04 14:22:05 -07:00
|
|
|
struct Mesh;
|
|
|
|
struct PathMesh;
|
2017-12-28 23:56:31 -08:00
|
|
|
struct PoolSkinIndex;
|
2019-09-04 14:22:05 -07:00
|
|
|
struct World;
|
2017-12-28 23:56:31 -08:00
|
|
|
|
|
|
|
extern class Token SharedBlenderToken;
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace blender
|
2017-12-28 23:56:31 -08:00
|
|
|
|
2015-08-06 12:10:12 -07:00
|
|
|
extern unsigned VerbosityLevel;
|
2017-12-15 18:13:20 -08:00
|
|
|
extern bool GuiMode;
|
2016-03-04 15:02:44 -08:00
|
|
|
extern logvisor::Module LogModule;
|
2015-07-05 18:35:08 -07:00
|
|
|
|
2017-11-12 22:13:53 -08:00
|
|
|
std::string Char16ToUTF8(std::u16string_view src);
|
|
|
|
std::u16string UTF8ToChar16(std::string_view src);
|
2015-06-09 15:19:59 -07:00
|
|
|
|
2015-12-30 14:03:37 -08:00
|
|
|
/* humanize_number port from FreeBSD's libutil */
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class HNFlags { None = 0, Decimal = 0x01, NoSpace = 0x02, B = 0x04, Divisor1000 = 0x08, IECPrefixes = 0x10 };
|
2015-12-30 14:03:37 -08:00
|
|
|
ENABLE_BITWISE_ENUM(HNFlags)
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class HNScale { None = 0, AutoScale = 0x20 };
|
2015-12-30 14:03:37 -08:00
|
|
|
ENABLE_BITWISE_ENUM(HNScale)
|
|
|
|
|
|
|
|
std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags);
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
static inline void ToLower(std::string& str) {
|
|
|
|
std::transform(str.begin(), str.end(), str.begin(),
|
|
|
|
[](char c) { return std::tolower(static_cast<unsigned char>(c)); });
|
|
|
|
}
|
|
|
|
static inline void ToUpper(std::string& str) {
|
|
|
|
std::transform(str.begin(), str.end(), str.begin(),
|
|
|
|
[](char c) { return std::toupper(static_cast<unsigned char>(c)); });
|
|
|
|
}
|
2019-10-01 00:23:35 -07:00
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
#if _WIN32
|
|
|
|
using Sstat = struct ::_stat64;
|
2017-06-10 11:40:27 -07:00
|
|
|
#else
|
2021-06-30 13:27:53 -07:00
|
|
|
using Sstat = struct stat;
|
2017-06-10 11:40:27 -07:00
|
|
|
#endif
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
constexpr size_t StrLen(const char* str) { return std::char_traits<char>::length(str); }
|
|
|
|
|
2015-08-04 18:54:35 -07:00
|
|
|
void SanitizePath(std::string& path);
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline void Unlink(const char* file) {
|
2015-08-05 15:59:59 -07:00
|
|
|
#if _WIN32
|
2021-06-30 11:20:45 -07:00
|
|
|
const nowide::wstackstring wfile(file);
|
|
|
|
_wunlink(wfile.get());
|
2015-08-05 15:59:59 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
unlink(file);
|
2015-08-05 15:59:59 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline void MakeDir(const char* dir) {
|
2015-05-27 02:09:05 -07:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
HRESULT err;
|
2021-06-30 11:20:45 -07:00
|
|
|
const nowide::wstackstring wdir(dir);
|
|
|
|
if (!CreateDirectoryW(wdir.get(), NULL))
|
2018-12-07 21:18:42 -08:00
|
|
|
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("MakeDir({})"), dir);
|
2015-05-27 02:09:05 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (mkdir(dir, 0755))
|
|
|
|
if (errno != EEXIST)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("MakeDir({}): {}"), dir, strerror(errno));
|
2015-07-28 16:54:54 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
int RecursiveMakeDir(const char* dir);
|
2017-02-03 19:45:39 -08:00
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline std::optional<std::string> GetEnv(const char* name) {
|
2017-12-05 19:22:31 -08:00
|
|
|
#if WINDOWS_STORE
|
2018-12-07 21:18:42 -08:00
|
|
|
return nullptr;
|
2017-12-05 19:22:31 -08:00
|
|
|
#else
|
2021-06-30 11:20:45 -07:00
|
|
|
#if _WIN32
|
|
|
|
size_t sz = 0;
|
|
|
|
const nowide::wshort_stackstring wname(name);
|
|
|
|
_wgetenv_s(&sz, nullptr, 0, wname.get());
|
|
|
|
if (sz == 0) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
auto wbuf = std::make_unique<wchar_t[]>(sz);
|
|
|
|
_wgetenv_s(&sz, wbuf.get(), sz, wname.get());
|
2021-11-21 13:39:57 -08:00
|
|
|
return nowide::narrow(wbuf.get(), sz - 1); // null-terminated
|
2016-03-28 14:38:31 -07:00
|
|
|
#else
|
2021-11-21 13:39:57 -08:00
|
|
|
const auto* env = getenv(name);
|
|
|
|
if (env != nullptr) {
|
|
|
|
return env;
|
|
|
|
}
|
|
|
|
return {};
|
2016-03-28 14:38:31 -07:00
|
|
|
#endif
|
2017-12-05 19:22:31 -08:00
|
|
|
#endif
|
2016-03-28 14:38:31 -07:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline char* Getcwd(char* buf, int maxlen) {
|
|
|
|
#if _WIN32
|
|
|
|
auto wbuf = std::make_unique<wchar_t[]>(maxlen);
|
|
|
|
wchar_t* result = _wgetcwd(wbuf.get(), maxlen);
|
|
|
|
if (result == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return nowide::narrow(buf, maxlen, wbuf.get());
|
2015-06-09 19:40:03 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return getcwd(buf, maxlen);
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string GetcwdStr();
|
2015-12-15 13:55:50 -08:00
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline bool IsAbsolute(std::string_view path) {
|
2015-12-17 20:55:46 -08:00
|
|
|
#if _WIN32
|
2021-06-30 11:20:45 -07:00
|
|
|
if (path.size() && (path[0] == '\\' || path[0] == '/'))
|
2018-12-07 21:18:42 -08:00
|
|
|
return true;
|
2021-06-30 11:20:45 -07:00
|
|
|
if (path.size() >= 2 && iswalpha(path[0]) && path[1] == ':')
|
2018-12-07 21:18:42 -08:00
|
|
|
return true;
|
2015-12-15 13:55:50 -08:00
|
|
|
#else
|
2021-06-30 11:20:45 -07:00
|
|
|
if (path.size() && path[0] == '/')
|
2018-12-07 21:18:42 -08:00
|
|
|
return true;
|
2015-12-15 13:55:50 -08:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
return false;
|
2015-12-15 13:55:50 -08:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string GetTmpDir();
|
2017-02-24 00:27:07 -08:00
|
|
|
|
2017-12-05 19:22:31 -08:00
|
|
|
#if !WINDOWS_STORE
|
2021-06-30 11:20:45 -07:00
|
|
|
int RunProcess(const char* path, const char* const args[]);
|
2017-12-05 19:22:31 -08:00
|
|
|
#endif
|
2017-02-24 00:27:07 -08:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class FileLockType { None = 0, Read, Write };
|
2021-06-30 11:20:45 -07:00
|
|
|
inline FILE* Fopen(const char* path, const char* mode, FileLockType lock = FileLockType::None) {
|
|
|
|
#if _WIN32
|
|
|
|
const nowide::wstackstring wpath(path);
|
|
|
|
const nowide::wshort_stackstring wmode(mode);
|
|
|
|
FILE* fp = _wfopen(wpath.get(), wmode.get());
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!fp)
|
|
|
|
return nullptr;
|
2015-06-09 15:19:59 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
FILE* fp = fopen(path, mode);
|
|
|
|
if (!fp)
|
|
|
|
return nullptr;
|
2015-08-31 17:26:35 -07:00
|
|
|
#endif
|
2015-06-09 15:19:59 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
if (lock != FileLockType::None) {
|
2015-06-10 16:34:14 -07:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
OVERLAPPED ov = {};
|
|
|
|
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
|
|
|
|
&ov);
|
2015-06-10 16:34:14 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Error, FMT_STRING("flock {}: {}"), path, strerror(errno));
|
2015-06-10 16:34:14 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-06-10 16:34:14 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
return fp;
|
2015-06-09 15:19:59 -07:00
|
|
|
}
|
|
|
|
|
2019-08-21 16:28:10 -07:00
|
|
|
struct UniqueFileDeleter {
|
|
|
|
void operator()(FILE* file) const noexcept { std::fclose(file); }
|
|
|
|
};
|
|
|
|
using UniqueFilePtr = std::unique_ptr<FILE, UniqueFileDeleter>;
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline UniqueFilePtr FopenUnique(const char* path, const char* mode,
|
2019-08-21 16:28:10 -07:00
|
|
|
FileLockType lock = FileLockType::None) {
|
|
|
|
return UniqueFilePtr{Fopen(path, mode, lock)};
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int FSeek(FILE* fp, int64_t offset, int whence) {
|
2016-02-03 16:55:13 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _fseeki64(fp, offset, whence);
|
2016-02-03 16:55:13 -08:00
|
|
|
#elif __APPLE__ || __FreeBSD__
|
2018-12-07 21:18:42 -08:00
|
|
|
return fseeko(fp, offset, whence);
|
2016-02-03 16:55:13 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return fseeko64(fp, offset, whence);
|
2016-02-03 16:55:13 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int64_t FTell(FILE* fp) {
|
2016-02-03 16:55:13 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _ftelli64(fp);
|
2016-02-03 16:55:13 -08:00
|
|
|
#elif __APPLE__ || __FreeBSD__
|
2018-12-07 21:18:42 -08:00
|
|
|
return ftello(fp);
|
2016-02-03 16:55:13 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return ftello64(fp);
|
2016-02-03 16:55:13 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline int Rename(const char* oldpath, const char* newpath) {
|
|
|
|
#if _WIN32
|
|
|
|
const nowide::wstackstring woldpath(oldpath);
|
|
|
|
const nowide::wstackstring wnewpath(newpath);
|
|
|
|
return MoveFileExW(woldpath.get(), wnewpath.get(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
|
2016-01-04 16:00:34 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return rename(oldpath, newpath);
|
2016-01-04 16:00:34 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline int Stat(const char* path, Sstat* statOut) {
|
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
size_t pos;
|
2021-06-30 11:20:45 -07:00
|
|
|
const nowide::wstackstring wpath(path);
|
|
|
|
const wchar_t* wpathP = wpath.get();
|
|
|
|
for (pos = 0; pos < 3 && wpathP[pos] != L'\0'; ++pos) {}
|
|
|
|
if (pos == 2 && wpathP[1] == L':') {
|
|
|
|
wchar_t fixPath[4] = {wpathP[0], L':', L'/', L'\0'};
|
|
|
|
return _wstat64(fixPath, statOut);
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2021-06-30 11:20:45 -07:00
|
|
|
return _wstat64(wpath.get(), statOut);
|
2015-06-09 19:40:03 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return stat(path, statOut);
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline int StrCmp(const char* str1, const char* str2) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!str1 || !str2)
|
|
|
|
return str1 != str2;
|
|
|
|
return strcmp(str1, str2);
|
2015-09-30 17:40:06 -07:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline int StrNCmp(const char* str1, const char* str2, size_t count) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!str1 || !str2)
|
|
|
|
return str1 != str2;
|
2019-08-14 22:11:39 -07:00
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
return std::char_traits<char>::compare(str1, str2, count);
|
2017-10-27 03:10:08 -07:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline int StrCaseCmp(const char* str1, const char* str2) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!str1 || !str2)
|
|
|
|
return str1 != str2;
|
2021-06-30 11:20:45 -07:00
|
|
|
#if _WIN32
|
|
|
|
return _stricmp(str1, str2);
|
2016-01-22 19:38:51 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return strcasecmp(str1, str2);
|
2016-01-22 19:38:51 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline unsigned long StrToUl(const char* str, char** endPtr, int base) {
|
2018-12-07 21:18:42 -08:00
|
|
|
return strtoul(str, endPtr, base);
|
2016-10-01 16:18:52 -07:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
inline bool PathRelative(const char* path) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!path || !path[0])
|
|
|
|
return false;
|
2017-12-05 19:22:31 -08:00
|
|
|
#if _WIN32 && !WINDOWS_STORE
|
2021-06-30 11:20:45 -07:00
|
|
|
const nowide::wstackstring wpath(path);
|
|
|
|
return PathIsRelativeW(wpath.get());
|
2017-02-24 23:58:36 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return path[0] != '/';
|
2017-02-24 23:58:36 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int ConsoleWidth(bool* ok = nullptr) {
|
2018-12-07 21:18:42 -08:00
|
|
|
int retval = 80;
|
2015-07-20 16:27:22 -07:00
|
|
|
#if _WIN32
|
2017-12-05 19:22:31 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:18:42 -08:00
|
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
|
|
|
|
retval = info.dwSize.X;
|
|
|
|
if (ok)
|
|
|
|
*ok = true;
|
2017-12-05 19:22:31 -08:00
|
|
|
#endif
|
2015-07-20 16:27:22 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (ok)
|
|
|
|
*ok = false;
|
|
|
|
struct winsize w;
|
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) {
|
|
|
|
retval = w.ws_col;
|
2018-03-28 01:06:34 -07:00
|
|
|
if (ok)
|
2018-12-07 21:18:42 -08:00
|
|
|
*ok = true;
|
|
|
|
}
|
2015-07-20 16:27:22 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
if (retval < 10)
|
|
|
|
return 10;
|
|
|
|
return retval;
|
2015-07-20 16:27:22 -07:00
|
|
|
}
|
|
|
|
|
2018-03-23 14:40:12 -07:00
|
|
|
class MultiProgressPrinter;
|
2015-05-27 02:09:05 -07:00
|
|
|
class ProjectRootPath;
|
2015-05-19 22:22:32 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Hash representation used for all storable and comparable objects
|
|
|
|
*
|
|
|
|
* Hashes are used within HECL to avoid redundant storage of objects;
|
|
|
|
* providing a rapid mechanism to compare for equality.
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class Hash {
|
2015-11-15 20:30:06 -08:00
|
|
|
protected:
|
2018-12-07 21:18:42 -08:00
|
|
|
uint64_t hash = 0;
|
|
|
|
|
2015-05-19 22:22:32 -07:00
|
|
|
public:
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr Hash() noexcept = default;
|
|
|
|
constexpr Hash(const Hash&) noexcept = default;
|
2019-08-14 23:50:16 -07:00
|
|
|
constexpr Hash(Hash&&) noexcept = default;
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr Hash(uint64_t hashin) noexcept : hash(hashin) {}
|
2019-08-23 23:55:06 -07:00
|
|
|
explicit Hash(const void* buf, size_t len) noexcept : hash(XXH64(buf, len, 0)) {}
|
|
|
|
explicit Hash(std::string_view str) noexcept : hash(XXH64(str.data(), str.size(), 0)) {}
|
2019-08-23 23:52:24 -07:00
|
|
|
|
|
|
|
constexpr uint32_t val32() const noexcept { return uint32_t(hash) ^ uint32_t(hash >> 32); }
|
|
|
|
constexpr uint64_t val64() const noexcept { return uint64_t(hash); }
|
|
|
|
constexpr size_t valSizeT() const noexcept { return size_t(hash); }
|
2018-12-07 21:18:42 -08:00
|
|
|
template <typename T>
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr T valT() const noexcept;
|
2019-08-14 23:50:16 -07:00
|
|
|
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr Hash& operator=(const Hash& other) noexcept = default;
|
2019-08-14 23:50:16 -07:00
|
|
|
constexpr Hash& operator=(Hash&& other) noexcept = default;
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr bool operator==(const Hash& other) const noexcept { return hash == other.hash; }
|
|
|
|
constexpr bool operator!=(const Hash& other) const noexcept { return !operator==(other); }
|
|
|
|
constexpr bool operator<(const Hash& other) const noexcept { return hash < other.hash; }
|
|
|
|
constexpr bool operator>(const Hash& other) const noexcept { return hash > other.hash; }
|
|
|
|
constexpr bool operator<=(const Hash& other) const noexcept { return hash <= other.hash; }
|
|
|
|
constexpr bool operator>=(const Hash& other) const noexcept { return hash >= other.hash; }
|
|
|
|
constexpr explicit operator bool() const noexcept { return hash != 0; }
|
2015-06-10 16:34:14 -07:00
|
|
|
};
|
2018-12-07 21:18:42 -08:00
|
|
|
template <>
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr uint32_t Hash::valT<uint32_t>() const noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
return val32();
|
|
|
|
}
|
|
|
|
template <>
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr uint64_t Hash::valT<uint64_t>() const noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
return val64();
|
|
|
|
}
|
2015-06-10 16:34:14 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Timestamp representation used for comparing modtimes of cooked resources
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class Time final {
|
|
|
|
time_t ts;
|
|
|
|
|
2015-06-10 16:34:14 -07:00
|
|
|
public:
|
2020-04-05 06:34:22 -07:00
|
|
|
Time() : ts(std::time(nullptr)) {}
|
|
|
|
constexpr Time(time_t ti) noexcept : ts{ti} {}
|
|
|
|
constexpr Time(const Time& other) noexcept : ts{other.ts} {}
|
|
|
|
[[nodiscard]] constexpr time_t getTs() const { return ts; }
|
|
|
|
constexpr Time& operator=(const Time& other) noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
ts = other.ts;
|
|
|
|
return *this;
|
|
|
|
}
|
2020-04-05 06:34:22 -07:00
|
|
|
[[nodiscard]] constexpr bool operator==(const Time& other) const noexcept { return ts == other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator!=(const Time& other) const noexcept { return ts != other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator<(const Time& other) const noexcept { return ts < other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator>(const Time& other) const noexcept { return ts > other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator<=(const Time& other) const noexcept { return ts <= other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator>=(const Time& other) const noexcept { return ts >= other.ts; }
|
2015-05-19 22:22:32 -07:00
|
|
|
};
|
|
|
|
|
2015-12-30 14:03:37 -08:00
|
|
|
/**
|
|
|
|
* @brief Case-insensitive comparator for std::map sorting
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
struct CaseInsensitiveCompare {
|
2021-06-30 11:20:45 -07:00
|
|
|
// Allow heterogeneous lookup with maps that use this comparator.
|
hecl/hecl: Allow CaseInsensitiveCompare to be used with heterogenous lookup
It's quite common to see maps or sets that make use of a string->object
association, usually as <std::string, T>. However, this can result in
inefficient code when performing lookups or indexing, as something like:
std::map<std::string, T> some_map;
...
auto iter = some_map.find("Name");
...
will result in a std::string instance being constructed around "Name",
which is less than ideal.
With a heterogenous comparator, however (such as std::less<>), like:
std::map<std::string, T, std::less<>>
we allow the container to do automatic type deduction and comparisons.
This allows types comparable to the key type to be used in find calls,
etc, without actually needing to construct the key type around the other
type.
The main way to enable containers to perform such behavior is by
defining a type named is_transparent within the comparator type.
2020-02-24 01:31:52 -08:00
|
|
|
using is_transparent = void;
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
bool operator()(std::string_view lhs, std::string_view rhs) const {
|
2019-08-14 20:20:25 -07:00
|
|
|
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char lhs, char rhs) {
|
|
|
|
return std::tolower(static_cast<unsigned char>(lhs)) < std::tolower(static_cast<unsigned char>(rhs));
|
|
|
|
});
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-12-30 14:03:37 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Directory traversal tool for accessing sorted directory entries
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class DirectoryEnumerator {
|
2015-12-30 14:03:37 -08:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
|
|
|
|
struct Entry {
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string m_path;
|
|
|
|
std::string m_name;
|
2018-12-07 21:18:42 -08:00
|
|
|
size_t m_fileSz;
|
|
|
|
bool m_isDir;
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
Entry(std::string path, std::string name, size_t sz, bool isDir)
|
|
|
|
: m_path(std::move(path)), m_name(std::move(name)), m_fileSz(sz), m_isDir(isDir) {}
|
2018-12-07 21:18:42 -08:00
|
|
|
};
|
2015-12-30 14:03:37 -08:00
|
|
|
|
|
|
|
private:
|
2018-12-07 21:18:42 -08:00
|
|
|
std::vector<Entry> m_entries;
|
2015-12-30 14:03:37 -08:00
|
|
|
|
|
|
|
public:
|
2021-06-30 11:20:45 -07:00
|
|
|
DirectoryEnumerator(std::string_view path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
|
2018-12-07 21:18:42 -08:00
|
|
|
bool reverse = false, bool noHidden = false);
|
2015-12-30 14:03:37 -08:00
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
explicit operator bool() const { return !m_entries.empty(); }
|
|
|
|
[[nodiscard]] size_t size() const { return m_entries.size(); }
|
|
|
|
[[nodiscard]] std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
|
|
|
|
[[nodiscard]] std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
|
2015-12-30 14:03:37 -08:00
|
|
|
};
|
|
|
|
|
2015-09-29 14:50:07 -07:00
|
|
|
/**
|
2016-09-24 18:57:43 -07:00
|
|
|
* @brief Special ProjectRootPath class for opening Database::Project instances
|
2015-09-29 14:50:07 -07:00
|
|
|
*
|
|
|
|
* Constructing a ProjectPath requires supplying a ProjectRootPath to consistently
|
|
|
|
* resolve canonicalized relative paths.
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class ProjectRootPath {
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string m_projRoot;
|
2018-12-07 21:18:42 -08:00
|
|
|
Hash m_hash = 0;
|
2015-09-29 23:23:07 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Empty constructor
|
|
|
|
*
|
|
|
|
* Used to preallocate ProjectPath for later population using assign()
|
|
|
|
*/
|
|
|
|
ProjectRootPath() = default;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Tests for non-empty project root path
|
|
|
|
*/
|
2019-08-21 14:31:20 -07:00
|
|
|
explicit operator bool() const { return m_projRoot.size() != 0; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct a representation of a project root path
|
|
|
|
* @param path valid filesystem-path (relative or absolute) to project root
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
ProjectRootPath(std::string_view path) : m_projRoot(path) {
|
2018-12-07 21:18:42 -08:00
|
|
|
SanitizePath(m_projRoot);
|
|
|
|
m_hash = Hash(m_projRoot);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Access fully-canonicalized absolute path
|
|
|
|
* @return Absolute path reference
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getAbsolutePath() const { return m_projRoot; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Make absolute path project relative
|
|
|
|
* @param absPath Absolute path
|
2021-06-30 11:20:45 -07:00
|
|
|
* @return std::string of path relative to project root
|
2018-12-07 21:18:42 -08:00
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string getProjectRelativeFromAbsolute(std::string_view absPath) const {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (absPath.size() > m_projRoot.size()) {
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string absPathForward(absPath);
|
|
|
|
for (char& ch : absPathForward)
|
|
|
|
if (ch == '\\')
|
|
|
|
ch = '/';
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!absPathForward.compare(0, m_projRoot.size(), m_projRoot)) {
|
|
|
|
auto beginIt = absPathForward.cbegin() + m_projRoot.size();
|
2021-06-30 11:20:45 -07:00
|
|
|
while (*beginIt == '/')
|
2018-12-07 21:18:42 -08:00
|
|
|
++beginIt;
|
2021-06-30 11:20:45 -07:00
|
|
|
return std::string(beginIt, absPathForward.cend());
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2016-01-04 16:00:34 -08:00
|
|
|
}
|
2021-06-30 11:20:45 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("unable to resolve '{}' as project relative '{}'"), absPath,
|
2019-08-14 23:27:36 -07:00
|
|
|
m_projRoot);
|
2021-06-30 11:20:45 -07:00
|
|
|
return std::string();
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create directory at path
|
|
|
|
*
|
|
|
|
* Fatal log report is issued if directory is not able to be created or doesn't already exist.
|
|
|
|
* If directory already exists, no action taken.
|
|
|
|
*/
|
|
|
|
void makeDir() const { MakeDir(m_projRoot.c_str()); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief HECL-specific xxhash
|
|
|
|
* @return unique hash value
|
|
|
|
*/
|
2019-08-23 23:52:24 -07:00
|
|
|
Hash hash() const noexcept { return m_hash; }
|
|
|
|
bool operator==(const ProjectRootPath& other) const noexcept { return m_hash == other.m_hash; }
|
|
|
|
bool operator!=(const ProjectRootPath& other) const noexcept { return !operator==(other); }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain c-string of final path component
|
|
|
|
* @return Final component c-string (may be empty)
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getLastComponent() const {
|
|
|
|
size_t pos = m_projRoot.rfind('/');
|
|
|
|
if (pos == std::string::npos)
|
2018-12-07 21:18:42 -08:00
|
|
|
return {};
|
|
|
|
return {m_projRoot.c_str() + pos + 1, size_t(m_projRoot.size() - pos - 1)};
|
|
|
|
}
|
2015-09-29 14:50:07 -07:00
|
|
|
};
|
|
|
|
|
2015-05-27 02:09:05 -07:00
|
|
|
/**
|
|
|
|
* @brief Canonicalized project path representation using POSIX conventions
|
|
|
|
*
|
|
|
|
* HECL uses POSIX-style paths (with '/' separator) and directory tokens
|
|
|
|
* ('.','..') to resolve files within a project. The database internally
|
|
|
|
* uses this representation to track working files.
|
|
|
|
*
|
|
|
|
* This class provides a convenient way to resolve paths relative to the
|
|
|
|
* project root. Part of this representation involves resolving symbolic
|
|
|
|
* links to regular file/directory paths and determining its type.
|
|
|
|
*
|
|
|
|
* NOTE THAT PROJECT PATHS ARE TREATED AS CASE SENSITIVE!!
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class ProjectPath {
|
|
|
|
Database::Project* m_proj = nullptr;
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string m_absPath;
|
|
|
|
std::string m_relPath;
|
|
|
|
std::string m_auxInfo;
|
2018-12-07 21:18:42 -08:00
|
|
|
Hash m_hash = 0;
|
|
|
|
void ComputeHash() {
|
|
|
|
if (m_auxInfo.size())
|
|
|
|
m_hash = Hash(m_relPath + '|' + m_auxInfo);
|
|
|
|
else
|
|
|
|
m_hash = Hash(m_relPath);
|
|
|
|
}
|
|
|
|
|
2015-05-27 02:09:05 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @brief Empty constructor
|
|
|
|
*
|
|
|
|
* Used to preallocate ProjectPath for later population using assign()
|
|
|
|
*/
|
|
|
|
ProjectPath() = default;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Tests for non-empty project path
|
|
|
|
*/
|
2019-08-21 14:31:20 -07:00
|
|
|
explicit operator bool() const { return m_absPath.size() != 0; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Clears path
|
|
|
|
*/
|
|
|
|
void clear() {
|
|
|
|
m_proj = nullptr;
|
|
|
|
m_absPath.clear();
|
|
|
|
m_relPath.clear();
|
|
|
|
m_hash = 0;
|
|
|
|
}
|
2015-11-09 11:44:47 -08:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @brief Construct a project subpath representation within a project's root path
|
|
|
|
* @param project previously constructed Project to use root path of
|
|
|
|
* @param path valid filesystem-path (relative or absolute) to subpath
|
|
|
|
*/
|
|
|
|
ProjectPath(Database::Project& project, std::string_view path) { assign(project, path); }
|
|
|
|
void assign(Database::Project& project, std::string_view path);
|
2015-09-29 14:50:07 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @brief Construct a project subpath representation within another subpath
|
|
|
|
* @param parentPath previously constructed ProjectPath which ultimately connects to a ProjectRootPath
|
|
|
|
* @param path valid filesystem-path (relative or absolute) to subpath
|
|
|
|
*/
|
|
|
|
ProjectPath(const ProjectPath& parentPath, std::string_view path) { assign(parentPath, path); }
|
|
|
|
void assign(const ProjectPath& parentPath, std::string_view path);
|
2015-07-22 12:14:50 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @brief Determine if ProjectPath represents project root directory
|
|
|
|
* @return true if project root directory
|
|
|
|
*/
|
|
|
|
bool isRoot() const { return m_relPath.empty(); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return new ProjectPath with extension added
|
|
|
|
* @param ext file extension to add (nullptr may be passed to remove the extension)
|
|
|
|
* @param replace remove existing extension (if any) before appending new extension
|
|
|
|
* @return new path with extension
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
ProjectPath getWithExtension(const char* ext, bool replace = false) const;
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Access fully-canonicalized absolute path
|
|
|
|
* @return Absolute path reference
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getAbsolutePath() const { return m_absPath; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Access fully-canonicalized project-relative path
|
|
|
|
* @return Relative pointer to within absolute-path or "." for project root-directory (use isRoot to detect)
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getRelativePath() const {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (m_relPath.size())
|
|
|
|
return m_relPath;
|
2021-06-30 11:20:45 -07:00
|
|
|
static const std::string dot = ".";
|
2018-12-07 21:18:42 -08:00
|
|
|
return dot;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain cooked equivalent of this ProjectPath
|
|
|
|
* @param spec DataSpec to get path against
|
|
|
|
* @return Cooked representation path
|
|
|
|
*/
|
|
|
|
ProjectPath getCookedPath(const Database::DataSpecEntry& spec) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain path of parent entity (a directory for file paths)
|
|
|
|
* @return Parent Path
|
|
|
|
*
|
|
|
|
* This will not resolve outside the project root (error in that case)
|
|
|
|
*/
|
|
|
|
ProjectPath getParentPath() const {
|
2021-06-30 11:20:45 -07:00
|
|
|
if (m_relPath == ".")
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("attempted to resolve parent of root project path"));
|
2021-06-30 11:20:45 -07:00
|
|
|
size_t pos = m_relPath.rfind('/');
|
|
|
|
if (pos == std::string::npos)
|
|
|
|
return ProjectPath(*m_proj, "");
|
|
|
|
return ProjectPath(*m_proj, std::string(m_relPath.begin(), m_relPath.begin() + pos));
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain c-string of final path component (stored within relative path)
|
|
|
|
* @return Final component c-string (may be empty)
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getLastComponent() const {
|
|
|
|
size_t pos = m_relPath.rfind('/');
|
|
|
|
if (pos == std::string::npos)
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_relPath;
|
|
|
|
return {m_relPath.c_str() + pos + 1, m_relPath.size() - pos - 1};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain c-string of extension of final path component (stored within relative path)
|
|
|
|
* @return Final component extension c-string (may be empty)
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getLastComponentExt() const {
|
|
|
|
std::string_view lastCompOrig = getLastComponent().data();
|
|
|
|
const char* end = lastCompOrig.data() + lastCompOrig.size();
|
|
|
|
const char* lastComp = end;
|
2018-12-07 21:18:42 -08:00
|
|
|
while (lastComp != lastCompOrig.data()) {
|
2021-06-30 11:20:45 -07:00
|
|
|
if (*lastComp == '.')
|
2018-12-07 21:18:42 -08:00
|
|
|
return {lastComp + 1, size_t(end - lastComp - 1)};
|
|
|
|
--lastComp;
|
2015-10-14 16:06:47 -07:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Build vector of project-relative directory/file components
|
|
|
|
* @return Vector of path components
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
std::vector<std::string> getPathComponents() const {
|
|
|
|
std::vector<std::string> ret;
|
2018-12-07 21:18:42 -08:00
|
|
|
if (m_relPath.empty())
|
|
|
|
return ret;
|
|
|
|
auto it = m_relPath.cbegin();
|
|
|
|
if (*it == '/') {
|
|
|
|
ret.push_back("/");
|
|
|
|
++it;
|
2016-01-06 13:03:41 -08:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string comp;
|
2021-06-30 11:20:45 -07:00
|
|
|
for (; it != m_relPath.cend(); ++it) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (*it == '/') {
|
|
|
|
if (comp.empty())
|
|
|
|
continue;
|
|
|
|
ret.push_back(std::move(comp));
|
|
|
|
comp.clear();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
comp += *it;
|
|
|
|
}
|
|
|
|
if (comp.size())
|
|
|
|
ret.push_back(std::move(comp));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
std::string_view getAuxInfo() const { return m_auxInfo; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct a path with the aux info overwritten with specified string
|
|
|
|
* @param auxStr string to replace existing auxInfo with
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
ProjectPath ensureAuxInfo(std::string_view auxStr) const {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (auxStr.empty())
|
|
|
|
return ProjectPath(getProject(), getRelativePath());
|
|
|
|
else
|
2021-06-30 11:20:45 -07:00
|
|
|
return ProjectPath(getProject(), std::string(getRelativePath()) + '|' + auxStr.data());
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2016-08-30 18:13:00 -07:00
|
|
|
|
2019-10-01 00:23:35 -07:00
|
|
|
template<typename StringT>
|
|
|
|
class EncodableString {
|
|
|
|
friend class ProjectPath;
|
|
|
|
using EncStringView = std::basic_string_view<typename StringT::value_type>;
|
|
|
|
StringT m_ownedString;
|
|
|
|
EncStringView m_stringView;
|
|
|
|
EncodableString(StringT s) : m_ownedString(std::move(s)), m_stringView(m_ownedString) {}
|
|
|
|
EncodableString(EncStringView sv) : m_stringView(sv) {}
|
|
|
|
EncodableString(const EncodableString&) = delete;
|
|
|
|
EncodableString& operator=(const EncodableString&) = delete;
|
|
|
|
EncodableString(EncodableString&&) = delete;
|
|
|
|
EncodableString& operator=(EncodableString&&) = delete;
|
|
|
|
public:
|
|
|
|
operator EncStringView() const { return m_stringView; }
|
|
|
|
};
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
EncodableString<std::string> getEncodableString() const {
|
2019-10-01 00:23:35 -07:00
|
|
|
if (!getAuxInfo().empty())
|
2021-06-30 11:20:45 -07:00
|
|
|
return {std::string(getRelativePath()) + '|' + getAuxInfo().data()};
|
2019-10-01 00:23:35 -07:00
|
|
|
else
|
|
|
|
return {getRelativePath()};
|
|
|
|
}
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @brief Type of path
|
|
|
|
*/
|
|
|
|
enum class Type {
|
|
|
|
None, /**< If path doesn't reference a valid filesystem entity, this is returned */
|
|
|
|
File, /**< Singular file path (confirmed with filesystem) */
|
|
|
|
Directory, /**< Singular directory path (confirmed with filesystem) */
|
|
|
|
Glob, /**< Glob-path (whenever one or more '*' occurs in syntax) */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get type of path based on syntax and filesystem queries
|
|
|
|
* @return Type of path
|
|
|
|
*/
|
|
|
|
Type getPathType() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if nothing exists at path
|
|
|
|
* @return True if nothing exists at path
|
|
|
|
*/
|
|
|
|
bool isNone() const { return getPathType() == Type::None; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if regular file exists at path
|
|
|
|
* @return True if regular file exists at path
|
|
|
|
*/
|
|
|
|
bool isFile() const { return getPathType() == Type::File; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if directory exists at path
|
|
|
|
* @return True if directory exists at path
|
|
|
|
*/
|
|
|
|
bool isDirectory() const { return getPathType() == Type::Directory; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Certain singular resource targets are cooked based on this test
|
|
|
|
* @return True if file or glob
|
|
|
|
*/
|
|
|
|
bool isFileOrGlob() const {
|
|
|
|
Type type = getPathType();
|
|
|
|
return (type == Type::File || type == Type::Glob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get time of last modification with special behaviors for directories and glob-paths
|
|
|
|
* @return Time object representing entity's time of last modification
|
|
|
|
*
|
|
|
|
* Regular files simply return their modtime as queried from the OS
|
|
|
|
* Directories return the latest modtime of all first-level regular files
|
|
|
|
* Glob-paths return the latest modtime of all matched regular files
|
|
|
|
*/
|
|
|
|
Time getModtime() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Insert directory children into list
|
|
|
|
* @param outPaths list to append children to
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
void getDirChildren(std::map<std::string, ProjectPath>& outPaths) const;
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct DirectoryEnumerator set to project path
|
|
|
|
*/
|
|
|
|
hecl::DirectoryEnumerator enumerateDir() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Insert glob matches into existing vector
|
|
|
|
* @param outPaths Vector to add matches to (will not erase existing contents)
|
|
|
|
*/
|
|
|
|
void getGlobResults(std::vector<ProjectPath>& outPaths) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Count how many directory levels deep in project path is
|
|
|
|
* @return Level Count
|
|
|
|
*/
|
|
|
|
size_t levelCount() const {
|
|
|
|
size_t count = 0;
|
2021-06-30 11:20:45 -07:00
|
|
|
for (char ch : m_relPath)
|
|
|
|
if (ch == '/' || ch == '\\')
|
2018-12-07 21:18:42 -08:00
|
|
|
++count;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create directory at path
|
|
|
|
*
|
|
|
|
* Fatal log report is issued if directory is not able to be created or doesn't already exist.
|
|
|
|
* If directory already exists, no action taken.
|
|
|
|
*/
|
|
|
|
void makeDir() const { MakeDir(m_absPath.c_str()); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create directory chain leading up to path
|
|
|
|
* @param includeLastComp if set, the ProjectPath is assumed to be a
|
|
|
|
* directory, creating the final component
|
|
|
|
*/
|
|
|
|
void makeDirChain(bool includeLastComp) const {
|
2021-06-30 11:20:45 -07:00
|
|
|
std::vector<std::string> comps = getPathComponents();
|
2018-12-07 21:18:42 -08:00
|
|
|
auto end = comps.cend();
|
|
|
|
if (end != comps.cbegin() && !includeLastComp)
|
|
|
|
--end;
|
2021-06-30 11:20:45 -07:00
|
|
|
ProjectPath compPath(*m_proj, ".");
|
2018-12-07 21:18:42 -08:00
|
|
|
for (auto it = comps.cbegin(); it != end; ++it) {
|
|
|
|
compPath = ProjectPath(compPath, *it);
|
|
|
|
compPath.makeDir();
|
2015-08-04 14:37:12 -07:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Fetch project that contains path
|
|
|
|
* @return Project
|
|
|
|
*/
|
|
|
|
Database::Project& getProject() const {
|
|
|
|
if (!m_proj)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("ProjectPath::getProject() called on unqualified path"));
|
2018-12-07 21:18:42 -08:00
|
|
|
return *m_proj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief HECL-specific xxhash
|
|
|
|
* @return unique hash value
|
|
|
|
*/
|
2019-08-23 23:52:24 -07:00
|
|
|
Hash hash() const noexcept { return m_hash; }
|
|
|
|
bool operator==(const ProjectPath& other) const noexcept { return m_hash == other.m_hash; }
|
|
|
|
bool operator!=(const ProjectPath& other) const noexcept { return !operator==(other); }
|
2019-10-01 00:23:35 -07:00
|
|
|
|
|
|
|
uint32_t parsedHash32() const;
|
2015-05-27 02:09:05 -07:00
|
|
|
};
|
|
|
|
|
2016-08-30 18:13:00 -07:00
|
|
|
/**
|
2016-08-31 16:07:40 -07:00
|
|
|
* @brief Handy functions not directly provided via STL strings
|
2016-08-30 18:13:00 -07:00
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class StringUtils {
|
2016-08-30 18:13:00 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
static bool BeginsWith(std::string_view str, std::string_view test) {
|
|
|
|
if (test.size() > str.size())
|
|
|
|
return false;
|
2019-08-14 20:47:56 -07:00
|
|
|
return str.compare(0, test.size(), test) == 0;
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool EndsWith(std::string_view str, std::string_view test) {
|
|
|
|
if (test.size() > str.size())
|
|
|
|
return false;
|
2019-08-14 20:47:56 -07:00
|
|
|
return str.compare(str.size() - test.size(), std::string_view::npos, test) == 0;
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:20:45 -07:00
|
|
|
static std::string TrimWhitespace(std::string_view str) {
|
2018-12-07 21:18:42 -08:00
|
|
|
auto bit = str.begin();
|
2021-06-30 11:20:45 -07:00
|
|
|
while (bit != str.cend() && std::isspace(static_cast<unsigned char>(*bit)))
|
2018-12-07 21:18:42 -08:00
|
|
|
++bit;
|
|
|
|
auto eit = str.end();
|
2021-06-30 11:20:45 -07:00
|
|
|
while (eit != str.cbegin() && std::isspace(static_cast<unsigned char>(*(eit - 1))))
|
2018-12-07 21:18:42 -08:00
|
|
|
--eit;
|
|
|
|
return {bit, eit};
|
|
|
|
}
|
2016-08-30 18:13:00 -07:00
|
|
|
};
|
|
|
|
|
2016-08-11 19:33:03 -07:00
|
|
|
/**
|
|
|
|
* @brief Mutex-style centralized resource-path tracking
|
|
|
|
*
|
|
|
|
* Provides a means to safely parallelize resource processing; detecting when another
|
|
|
|
* thread is working on the same resource.
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class ResourceLock {
|
|
|
|
static bool SetThreadRes(const ProjectPath& path);
|
|
|
|
static void ClearThreadRes();
|
|
|
|
bool good;
|
|
|
|
|
2016-08-11 19:33:03 -07:00
|
|
|
public:
|
2019-08-14 23:10:49 -07:00
|
|
|
explicit operator bool() const { return good; }
|
2018-12-07 21:18:42 -08:00
|
|
|
static bool InProgress(const ProjectPath& path);
|
2019-08-14 23:10:49 -07:00
|
|
|
explicit ResourceLock(const ProjectPath& path) : good{SetThreadRes(path)} {}
|
2018-12-07 21:18:42 -08:00
|
|
|
~ResourceLock() {
|
|
|
|
if (good)
|
|
|
|
ClearThreadRes();
|
|
|
|
}
|
|
|
|
ResourceLock(const ResourceLock&) = delete;
|
|
|
|
ResourceLock& operator=(const ResourceLock&) = delete;
|
|
|
|
ResourceLock(ResourceLock&&) = delete;
|
|
|
|
ResourceLock& operator=(ResourceLock&&) = delete;
|
2016-08-11 19:33:03 -07:00
|
|
|
};
|
|
|
|
|
2015-06-10 21:55:06 -07:00
|
|
|
/**
|
|
|
|
* @brief Search from within provided directory for the project root
|
|
|
|
* @param path absolute or relative file path to search from
|
2015-09-29 23:23:07 -07:00
|
|
|
* @return Newly-constructed root path (bool-evaluating to false if not found)
|
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
ProjectRootPath SearchForProject(std::string_view path);
|
2015-09-29 23:23:07 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Search from within provided directory for the project root
|
|
|
|
* @param path absolute or relative file path to search from
|
|
|
|
* @param subpathOut remainder of provided path assigned to this ProjectPath
|
|
|
|
* @return Newly-constructed root path (bool-evaluating to false if not found)
|
2015-06-10 21:55:06 -07:00
|
|
|
*/
|
2021-06-30 11:20:45 -07:00
|
|
|
ProjectRootPath SearchForProject(std::string_view path, std::string& subpathOut);
|
2015-05-27 02:09:05 -07:00
|
|
|
|
2015-09-30 17:40:06 -07:00
|
|
|
/**
|
|
|
|
* @brief Test if given path is a PNG (based on file header)
|
|
|
|
* @param path Path to test
|
|
|
|
* @return true if PNG
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
bool IsPathPNG(const hecl::ProjectPath& path);
|
2015-09-30 17:40:06 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if given path is a blend (based on file header)
|
|
|
|
* @param path Path to test
|
|
|
|
* @return true if blend
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
bool IsPathBlend(const hecl::ProjectPath& path);
|
2015-09-30 17:40:06 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if given path is a yaml (based on file extension)
|
|
|
|
* @param path Path to test
|
|
|
|
* @return true if yaml
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
bool IsPathYAML(const hecl::ProjectPath& path);
|
2015-09-30 17:40:06 -07:00
|
|
|
|
2015-09-12 20:40:22 -07:00
|
|
|
#undef bswap16
|
|
|
|
#undef bswap32
|
|
|
|
#undef bswap64
|
2015-05-27 02:09:05 -07:00
|
|
|
|
|
|
|
/* Type-sensitive byte swappers */
|
2015-06-09 15:19:59 -07:00
|
|
|
template <typename T>
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr T bswap16(T val) noexcept {
|
2015-05-22 01:21:44 -07:00
|
|
|
#if __GNUC__
|
2018-12-07 21:18:42 -08:00
|
|
|
return __builtin_bswap16(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#elif _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _byteswap_ushort(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return (val = (val << 8) | ((val >> 8) & 0xFF));
|
2015-05-22 01:21:44 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-09 15:19:59 -07:00
|
|
|
template <typename T>
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr T bswap32(T val) noexcept {
|
2015-05-22 01:21:44 -07:00
|
|
|
#if __GNUC__
|
2018-12-07 21:18:42 -08:00
|
|
|
return __builtin_bswap32(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#elif _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _byteswap_ulong(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
|
|
|
|
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
|
|
|
|
return val;
|
2015-05-22 01:21:44 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-09 15:19:59 -07:00
|
|
|
template <typename T>
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr T bswap64(T val) noexcept {
|
2015-05-22 01:21:44 -07:00
|
|
|
#if __GNUC__
|
2018-12-07 21:18:42 -08:00
|
|
|
return __builtin_bswap64(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#elif _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _byteswap_uint64(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
|
|
|
|
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
|
|
|
|
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
|
|
|
|
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
|
2015-05-22 01:21:44 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-05-27 02:09:05 -07:00
|
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SBig(int16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr uint16_t SBig(uint16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr int32_t SBig(int32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr uint32_t SBig(uint32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr int64_t SBig(int64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr uint64_t SBig(uint64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr float SBig(float val) noexcept {
|
2019-08-14 23:27:36 -07:00
|
|
|
union {
|
|
|
|
float f;
|
|
|
|
atInt32 i;
|
|
|
|
} uval1 = {val};
|
|
|
|
union {
|
|
|
|
atInt32 i;
|
|
|
|
float f;
|
|
|
|
} uval2 = {bswap32(uval1.i)};
|
2019-06-11 19:01:19 -07:00
|
|
|
return uval2.f;
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr double SBig(double val) noexcept {
|
2019-08-14 23:27:36 -07:00
|
|
|
union {
|
|
|
|
double f;
|
|
|
|
atInt64 i;
|
|
|
|
} uval1 = {val};
|
|
|
|
union {
|
|
|
|
atInt64 i;
|
|
|
|
double f;
|
|
|
|
} uval2 = {bswap64(uval1.i)};
|
2019-06-11 19:01:19 -07:00
|
|
|
return uval2.f;
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SBIG
|
2018-12-07 21:18:42 -08:00
|
|
|
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-06-11 21:02:23 -07:00
|
|
|
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SLittle(int16_t val) noexcept { return val; }
|
|
|
|
constexpr uint16_t SLittle(uint16_t val) noexcept { return val; }
|
|
|
|
constexpr int32_t SLittle(int32_t val) noexcept { return val; }
|
|
|
|
constexpr uint32_t SLittle(uint32_t val) noexcept { return val; }
|
|
|
|
constexpr int64_t SLittle(int64_t val) noexcept { return val; }
|
|
|
|
constexpr uint64_t SLittle(uint64_t val) noexcept { return val; }
|
|
|
|
constexpr float SLittle(float val) noexcept { return val; }
|
|
|
|
constexpr double SLittle(double val) noexcept { return val; }
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SLITTLE
|
2015-07-19 13:52:22 -07:00
|
|
|
#define SLITTLE(q) (q)
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-05-27 02:09:05 -07:00
|
|
|
#else
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SLittle(int16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr uint16_t SLittle(uint16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr int32_t SLittle(int32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr uint32_t SLittle(uint32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr int64_t SLittle(int64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr uint64_t SLittle(uint64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr float SLittle(float val) noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
int32_t ival = bswap32(*((int32_t*)(&val)));
|
|
|
|
return *((float*)(&ival));
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr double SLittle(double val) noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
int64_t ival = bswap64(*((int64_t*)(&val)));
|
|
|
|
return *((double*)(&ival));
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SLITTLE
|
2018-12-07 21:18:42 -08:00
|
|
|
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-06-11 21:02:23 -07:00
|
|
|
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SBig(int16_t val) noexcept { return val; }
|
|
|
|
constexpr uint16_t SBig(uint16_t val) noexcept { return val; }
|
|
|
|
constexpr int32_t SBig(int32_t val) noexcept { return val; }
|
|
|
|
constexpr uint32_t SBig(uint32_t val) noexcept { return val; }
|
|
|
|
constexpr int64_t SBig(int64_t val) noexcept { return val; }
|
|
|
|
constexpr uint64_t SBig(uint64_t val) noexcept { return val; }
|
|
|
|
constexpr float SBig(float val) noexcept { return val; }
|
|
|
|
constexpr double SBig(double val) noexcept { return val; }
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SBIG
|
2015-07-19 13:52:22 -07:00
|
|
|
#define SBIG(q) (q)
|
2015-05-27 02:09:05 -07:00
|
|
|
#endif
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-05-27 02:09:05 -07:00
|
|
|
|
2019-05-07 20:47:34 -07:00
|
|
|
template <typename SizeT>
|
2019-08-23 23:45:40 -07:00
|
|
|
constexpr void hash_combine_impl(SizeT& seed, SizeT value) noexcept {
|
2019-08-14 23:27:36 -07:00
|
|
|
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
2019-05-07 20:47:34 -07:00
|
|
|
}
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace hecl
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2019-10-01 00:23:35 -07:00
|
|
|
#define CHAINED_SIGNAL_HANDLER(name, signal) \
|
|
|
|
class ChainedSignalHandler_##name { \
|
|
|
|
typedef void(*sighandler_t)(int); \
|
|
|
|
typedef void(*sigaction_t)(int, siginfo_t*, void*); \
|
|
|
|
static std::atomic_bool m_isSetup; \
|
|
|
|
static sighandler_t m_nextsh; \
|
|
|
|
static sigaction_t m_nextsa; \
|
|
|
|
static void my_sig_action(int sig, siginfo_t* si, void* ctx); \
|
|
|
|
static void sig_action(int sig, siginfo_t* si, void* ctx) { \
|
|
|
|
my_sig_action(sig, si, ctx); \
|
|
|
|
if (m_nextsa) \
|
|
|
|
m_nextsa(sig, si, ctx); \
|
|
|
|
else if (m_nextsh) \
|
|
|
|
m_nextsh(sig); \
|
|
|
|
} \
|
|
|
|
public: \
|
|
|
|
static void setup() { \
|
|
|
|
if (ChainedSignalHandler_##name::m_isSetup.exchange(true) == true) \
|
|
|
|
return; \
|
|
|
|
{ \
|
|
|
|
struct sigaction sold; \
|
|
|
|
if (sigaction(signal, nullptr, &sold) == 0) { \
|
|
|
|
if (sold.sa_flags & SA_SIGINFO) \
|
|
|
|
m_nextsa = sold.sa_sigaction; \
|
|
|
|
else \
|
|
|
|
m_nextsh = sold.sa_handler; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
{ \
|
|
|
|
struct sigaction snew = {}; \
|
|
|
|
snew.sa_sigaction = sig_action; \
|
|
|
|
snew.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_SIGINFO; \
|
|
|
|
sigaction(signal, &snew, nullptr); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
}; \
|
|
|
|
std::atomic_bool ChainedSignalHandler_##name::m_isSetup = {false}; \
|
|
|
|
ChainedSignalHandler_##name::sighandler_t ChainedSignalHandler_##name::m_nextsh = {}; \
|
|
|
|
ChainedSignalHandler_##name::sigaction_t ChainedSignalHandler_##name::m_nextsa = {}; \
|
|
|
|
inline void ChainedSignalHandler_##name::my_sig_action
|
|
|
|
|
|
|
|
#define SETUP_CHAINED_SIGNAL_HANDLER(name) \
|
|
|
|
ChainedSignalHandler_##name::setup()
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
namespace std {
|
|
|
|
template <>
|
|
|
|
struct hash<hecl::ProjectPath> {
|
|
|
|
size_t operator()(const hecl::ProjectPath& val) const noexcept { return val.hash().valSizeT(); }
|
2015-06-10 16:34:14 -07:00
|
|
|
};
|
2018-12-07 21:18:42 -08:00
|
|
|
template <>
|
|
|
|
struct hash<hecl::Hash> {
|
|
|
|
size_t operator()(const hecl::Hash& val) const noexcept { return val.valSizeT(); }
|
2015-11-12 18:12:09 -08:00
|
|
|
};
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace std
|