#ifndef UTILS_HPP #define UTILS_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.hpp" #include "error.hpp" #include "utils.hpp" #if IS_WINDOWS #include #else #include #endif namespace cpptrace { namespace detail { inline std::vector split(const std::string& str, const std::string& delims) { std::vector vec; size_t old_pos = 0; size_t pos = 0; while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) { vec.emplace_back(str.substr(old_pos, pos - old_pos)); old_pos = pos + 1; } vec.emplace_back(str.substr(old_pos)); return vec; } template inline std::string join(const C& container, const std::string& delim) { auto iter = std::begin(container); auto end = std::end(container); std::string str; if(std::distance(iter, end) > 0) { str += *iter; while(++iter != end) { str += delim; str += *iter; } } return str; } constexpr const char* const whitespace = " \t\n\r\f\v"; inline std::string trim(const std::string& str) { if(str.empty()) { return ""; } const size_t left = str.find_first_not_of(whitespace); const size_t right = str.find_last_not_of(whitespace) + 1; return str.substr(left, right - left); } inline std::string to_hex(uintptr_t addr) { std::stringstream sstream; sstream< struct byte_swapper; template struct byte_swapper { T operator()(T val) { return val; } }; template struct byte_swapper { T operator()(T val) { return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8)); } }; template struct byte_swapper { T operator()(T val) { return ((((val) & 0xff000000) >> 24) | (((val) & 0x00ff0000) >> 8) | (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24)); } }; template struct byte_swapper { T operator()(T val) { 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)); } }; template::value, int>::type = 0> T byteswap(T value) { return byte_swapper{}(value); } inline void enable_virtual_terminal_processing_if_needed() { // enable colors / ansi processing if necessary #if IS_WINDOWS // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4; #endif HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; if(hOut == INVALID_HANDLE_VALUE) return; if(!GetConsoleMode(hOut, &dwMode)) return; if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return; #endif } // NOLINTNEXTLINE(misc-no-recursion) inline constexpr unsigned n_digits(unsigned value) { return value < 10 ? 1 : 1 + n_digits(value / 10); } static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result"); static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result"); static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result"); static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result"); static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result"); // TODO: Re-evaluate use of off_t template::value, int>::type = 0> T load_bytes(FILE* obj_file, off_t offset) { T object; CPPTRACE_VERIFY(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error"); CPPTRACE_VERIFY(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error"); return object; } struct nullopt_t {}; static constexpr nullopt_t nullopt; template< typename T, typename std::enable_if::type, void>::value, int>::type = 0 > class optional { bool holds_value = false; union { T uvalue; }; public: // clang-tidy false positive // NOLINTNEXTLINE(modernize-use-equals-default) optional() noexcept {} optional(nullopt_t) noexcept {} ~optional() { reset(); } optional(const optional& other) : holds_value(other.holds_value) { if(holds_value) { new (static_cast(std::addressof(uvalue))) T(other.uvalue); } } optional(optional&& other) noexcept(std::is_nothrow_move_constructible::value) : holds_value(other.holds_value) { if(holds_value) { new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); } } optional& operator=(const optional& other) { optional copy(other); swap(*this, copy); return *this; } optional& operator=(optional&& other) noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) { reset(); if(other.holds_value) { new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); holds_value = true; } return *this; } template< typename U = T, typename std::enable_if::type, optional>::value, int>::type = 0 > // clang-tidy false positive // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) optional(U&& value) : holds_value(true) { new (static_cast(std::addressof(uvalue))) T(std::forward(value)); } template< typename U = T, typename std::enable_if::type, optional>::value, int>::type = 0 > optional& operator=(U&& value) { if(holds_value) { uvalue = std::forward(value); } else { new (static_cast(std::addressof(uvalue))) T(std::forward(value)); holds_value = true; } return *this; } optional& operator=(nullopt_t) noexcept { reset(); return *this; } void swap(optional& other) { if(holds_value && other.holds_value) { std::swap(uvalue, other.uvalue); } else if(holds_value && !other.holds_value) { new (&other.uvalue) T(std::move(uvalue)); uvalue.~T(); } else if(!holds_value && other.holds_value) { new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); other.uvalue.~T(); } std::swap(holds_value, other.holds_value); } bool has_value() const { return holds_value; } operator bool() const { return holds_value; } void reset() { if(holds_value) { uvalue.~T(); } holds_value = false; } T& unwrap() & { if(!holds_value) { throw std::runtime_error{"Optional does not contain a value"}; } return uvalue; } const T& unwrap() const & { if(!holds_value) { throw std::runtime_error{"Optional does not contain a value"}; } return uvalue; } T&& unwrap() && { if(!holds_value) { throw std::runtime_error{"Optional does not contain a value"}; } return std::move(uvalue); } const T&& unwrap() const && { if(!holds_value) { throw std::runtime_error{"Optional does not contain a value"}; } return std::move(uvalue); } template T value_or(U&& default_value) const & { return holds_value ? uvalue : static_cast(std::forward(default_value)); } template T value_or(U&& default_value) && { return holds_value ? std::move(uvalue) : static_cast(std::forward(default_value)); } }; // shamelessly stolen from stackoverflow inline bool directory_exists(const std::string& path) { #if IS_WINDOWS DWORD dwAttrib = GetFileAttributesA(path.c_str()); return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); #else struct stat sb; return stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode); #endif } inline std::string basename(const std::string& path) { // Assumes no trailing /'s auto pos = path.rfind('/'); if(pos == std::string::npos) { return path; } else { return path.substr(pos + 1); } } } } #endif