347 lines
11 KiB
C++
347 lines
11 KiB
C++
#include "export.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <csignal>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include "Core/Base/global_include.h"
|
|
#include "magic_enum/../export.h"
|
|
#if defined(_WIN32)
|
|
#include "windows_include.h"
|
|
#else
|
|
#include <cerrno>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#endif
|
|
#if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC)
|
|
#include <crtdbg.h>
|
|
#include <cstdlib>
|
|
#endif
|
|
#ifdef _USE_GTEST
|
|
#include <gtest/gtest.h>
|
|
#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<std::mutex> 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<std::chrono::milliseconds>(
|
|
now.time_since_epoch());
|
|
return static_cast<std::uint64_t>(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<std::uint8_t>(ch - 'A')
|
|
: (ch >= 'a' && ch <= 'z')
|
|
? static_cast<std::uint8_t>(ch - 'a' + 26)
|
|
: (ch >= '0' && ch <= '9')
|
|
? static_cast<std::uint8_t>(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<std::uint8_t>((q0 << 2) | (q1 >> 4));
|
|
if (q2 == 64) {
|
|
break;
|
|
}
|
|
if (q2 > 64) {
|
|
return j;
|
|
}
|
|
plain[j++] = static_cast<std::uint8_t>((q1 << 4) | (q2 >> 2));
|
|
if (q3 == 64) {
|
|
break;
|
|
}
|
|
if (q3 > 64) {
|
|
return j;
|
|
}
|
|
plain[j++] = static_cast<std::uint8_t>((q2 << 6) | q3);
|
|
}
|
|
return j;
|
|
}
|
|
} // namespace
|
|
std::string base64_encode(std::string_view data) {
|
|
const auto text_len = static_cast<std::uint32_t>(data.size());
|
|
std::vector<std::uint8_t> encode(4 * ((text_len + 2) / 3));
|
|
const std::uint32_t encoded_len =
|
|
base64_encode_raw(reinterpret_cast<const std::uint8_t*>(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<std::uint32_t>(data.size());
|
|
if ((code_len & 0x03U) != 0U) {
|
|
return "";
|
|
}
|
|
std::vector<std::uint8_t> plain(code_len * 3 / 4);
|
|
const std::uint32_t decoded_len =
|
|
base64_decode_raw(reinterpret_cast<const std::uint8_t*>(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<int> normal_quit,
|
|
std::set<int> 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
|