#include "export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Core/Base/global_include.h" #include "magic_enum/../export.h" #if defined(_WIN32) #include "windows_include.h" #else #include #include #include #endif #if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC) #include #include #endif #ifdef _USE_GTEST #include #endif namespace Psc { namespace { bool local_time_from_time_t(std::time_t value, std::tm& out) noexcept { #if defined(_WIN32) // MinGW / Clang / MSVC 对 localtime_s 支持不完全一致,这里统一用带锁的 // std::localtime。 static std::mutex mtx; std::lock_guard lock(mtx); std::tm* tmp = std::localtime(&value); if (!tmp) { return false; } out = *tmp; return true; #elif defined(__unix__) || defined(__APPLE__) return ::localtime_r(&value, &out) != nullptr; #else std::tm* tmp = std::localtime(&value); if (!tmp) { return false; } out = *tmp; return true; #endif } } // namespace std::uint64_t get_current_millisecond_timestamp() { const auto now = std::chrono::system_clock::now(); const auto duration = std::chrono::duration_cast( now.time_since_epoch()); return static_cast(duration.count()); } int redict_main_with_gtest(int argc, char* argv[], Main_Function_Type main_func) { #if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif set_console_utf8(); if (argc == 1) { #ifdef _USE_GTEST ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); #else return main_func(argc, argv); #endif } const int new_argc = argc - 1; char** new_argv = &argv[1]; int ret = 0; #ifdef _USE_GTEST const std::string type = argv[1]; if (type != "main") { ::testing::InitGoogleTest(&argc, argv); ret = RUN_ALL_TESTS(); } else { ret = main_func(new_argc, new_argv); } #else ret = main_func(new_argc, new_argv); #endif #if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC) if (_CrtDumpMemoryLeaks()) { std::cout << "[CRT] Detected memory leaks.\n"; } else { std::cout << "[CRT] No memory leaks detected.\n"; } #endif std::cout << "ecap ret: " << ret << std::endl; return ret; } std::string get_exe_dir() { const std::string exe_path = get_exe_path(); if (exe_path.empty()) { return ""; } return std::filesystem::path(exe_path).parent_path().string(); } std::string get_abs_path(std::string_view path) { if (path.empty()) { return ""; } if (path.rfind("@", 0) == 0) { return get_exe_dir() + std::string(path.substr(1)); } if (path.rfind("~", 0) == 0) { return get_home_dir() + std::string(path.substr(1)); } return std::string(path); } std::string utc_2_local_time(std::time_t us_timestamp, const char* format) { std::tm local_tm{}; if (!local_time_from_time_t(us_timestamp, local_tm)) { return "invalid_time"; } std::ostringstream oss; oss << std::put_time(&local_tm, format ? format : "%H:%M:%S"); return oss.str(); } namespace { constexpr std::uint8_t alphabet_map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; constexpr std::uint8_t invalid_base64 = 255; constexpr std::uint8_t decode_char(unsigned char ch) noexcept { return (ch >= 'A' && ch <= 'Z') ? static_cast(ch - 'A') : (ch >= 'a' && ch <= 'z') ? static_cast(ch - 'a' + 26) : (ch >= '0' && ch <= '9') ? static_cast(ch - '0' + 52) : (ch == '+') ? 62 : (ch == '/') ? 63 : (ch == '=') ? 64 : invalid_base64; } std::uint32_t base64_encode_raw(const std::uint8_t* text, std::uint32_t text_len, std::uint8_t* encode) { std::uint32_t i = 0; std::uint32_t j = 0; for (; i + 3 <= text_len; i += 3) { encode[j++] = alphabet_map[text[i] >> 2]; encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)]; encode[j++] = alphabet_map[((text[i + 1] << 2) & 0x3c) | (text[i + 2] >> 6)]; encode[j++] = alphabet_map[text[i + 2] & 0x3f]; } if (i < text_len) { const std::uint32_t tail = text_len - i; if (tail == 1) { encode[j++] = alphabet_map[text[i] >> 2]; encode[j++] = alphabet_map[(text[i] << 4) & 0x30]; encode[j++] = '='; encode[j++] = '='; } else { encode[j++] = alphabet_map[text[i] >> 2]; encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)]; encode[j++] = alphabet_map[(text[i + 1] << 2) & 0x3c]; encode[j++] = '='; } } return j; } std::uint32_t base64_decode_raw(const std::uint8_t* code, std::uint32_t code_len, std::uint8_t* plain) { if (!code || !plain || (code_len & 0x03U) != 0U) { return 0; } std::uint32_t j = 0; for (std::uint32_t i = 0; i < code_len; i += 4) { const std::uint8_t q0 = decode_char(code[i]); const std::uint8_t q1 = decode_char(code[i + 1]); const std::uint8_t q2 = decode_char(code[i + 2]); const std::uint8_t q3 = decode_char(code[i + 3]); if (q0 >= 64 || q1 >= 64) { return j; } plain[j++] = static_cast((q0 << 2) | (q1 >> 4)); if (q2 == 64) { break; } if (q2 > 64) { return j; } plain[j++] = static_cast((q1 << 4) | (q2 >> 2)); if (q3 == 64) { break; } if (q3 > 64) { return j; } plain[j++] = static_cast((q2 << 6) | q3); } return j; } } // namespace std::string base64_encode(std::string_view data) { const auto text_len = static_cast(data.size()); std::vector encode(4 * ((text_len + 2) / 3)); const std::uint32_t encoded_len = base64_encode_raw(reinterpret_cast(data.data()), text_len, encode.data()); return std::string(encode.begin(), encode.begin() + encoded_len); } std::string base64_decode(std::string_view data) { const auto code_len = static_cast(data.size()); if ((code_len & 0x03U) != 0U) { return ""; } std::vector plain(code_len * 3 / 4); const std::uint32_t decoded_len = base64_decode_raw(reinterpret_cast(data.data()), code_len, plain.data()); return std::string(plain.begin(), plain.begin() + decoded_len); } std::string signal_to_string(int signal) { switch (signal) { case SIGINT: return "SIGINT (Ctrl+C)"; case SIGILL: return "SIGILL (Illegal Instruction)"; case SIGFPE: return "SIGFPE (Floating Point Exception)"; case SIGSEGV: return "SIGSEGV (Segmentation Fault)"; case SIGTERM: return "SIGTERM (Termination Request)"; case SIGABRT: return "SIGABRT (Abort Signal)"; #if defined(_WIN32) && defined(SIGBREAK) case SIGBREAK: return "SIGBREAK (Breakpoint)"; #endif #if defined(_MSC_VER) && defined(SIGABRT_COMPAT) case SIGABRT_COMPAT: return "SIGABRT_COMPAT (Abort Compatibility Signal)"; #endif default: return "Unknown signal:" + std::to_string(signal); } } std::string get_current_time_as_string() { const auto now = std::chrono::system_clock::now(); const std::time_t now_time = std::chrono::system_clock::to_time_t(now); std::tm local_tm{}; if (!local_time_from_time_t(now_time, local_tm)) { return "invalid_time"; } std::ostringstream ss; ss << std::put_time(&local_tm, "%Y-%m-%d_%H-%M-%S"); return ss.str(); } namespace fs = std::filesystem; void create_file_if_not_exists(std::string_view file_name) { std::error_code ec; const fs::path file_path(file_name); if (fs::exists(file_path, ec)) { return; } const fs::path parent = file_path.parent_path(); if (!parent.empty()) { fs::create_directories(parent, ec); if (ec) { std::cerr << "Failed to create parent directory: " << parent.string() << ", error: " << ec.message() << std::endl; return; } } std::ofstream ofs(file_path, std::ios::out | std::ios::app); if (!ofs) { std::cerr << "Failed to create the file: " << file_name << std::endl; return; } const auto perms = fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all; fs::permissions(file_path, perms, ec); } void create_dir_if_not_exists(std::string_view dir_name) { std::error_code ec; const fs::path dir_path(dir_name); if (!fs::exists(dir_path, ec)) { fs::create_directories(dir_path, ec); if (ec) { std::cerr << "Failed to create directory: " << dir_name << ", error: " << ec.message() << std::endl; return; } } const auto perms = fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all; fs::permissions(dir_path, perms, ec); } void write_stack_trace_to_file(std::string_view stack_trace, std::string_view filename) { create_file_if_not_exists(filename); std::ofstream out_file(filename.data(), std::ios::out | std::ios::app); if (out_file.is_open()) { out_file << stack_trace << std::endl; } else { std::cerr << "Unable to open file " << filename << " for writing." << std::endl; } } void signal_handler(int signal_code) { std::cout << "signal_handler [" << signal_to_string(signal_code) << "]" << std::endl; stop_program = signal_code; } void signal_handler_ignore(int) { } void init_signal_config(std::set normal_quit, std::set ignore_signal) { if (is_big_endian) { std::cout << "大端序 This system is Big-endian." << std::endl; } else { std::cout << "小端序 This system is Little-endian." << std::endl; } for (auto signum : normal_quit) { std::signal(signum, signal_handler); } for (auto signum : ignore_signal) { std::signal(signum, signal_handler_ignore); } } } // namespace Psc