Older/ToolKit/Util/util.cpp

666 lines
19 KiB
C++
Raw Normal View History

2024-09-28 23:55:00 +08:00
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <random>
#include "util.h"
#include "local_time.h"
#include "File.h"
#include "onceToken.h"
#include "logger.h"
#include "uv_errno.h"
#include "Network/sockutil.h"
#if defined(_WIN32)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
extern "C" const IMAGE_DOS_HEADER __ImageBase;
#endif // defined(_WIN32)
#if defined(__MACH__) || defined(__APPLE__)
#include <limits.h>
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
int uv_exepath(char *buffer, int *size) {
/* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
char abspath[PATH_MAX * 2 + 1];
char exepath[PATH_MAX + 1];
uint32_t exepath_size;
size_t abspath_size;
if (buffer == nullptr || size == nullptr || *size == 0)
return -EINVAL;
exepath_size = sizeof(exepath);
if (_NSGetExecutablePath(exepath, &exepath_size))
return -EIO;
if (realpath(exepath, abspath) != abspath)
return -errno;
abspath_size = strlen(abspath);
if (abspath_size == 0)
return -EIO;
*size -= 1;
if ((size_t) *size > abspath_size)
*size = abspath_size;
memcpy(buffer, abspath, *size);
buffer[*size] = '\0';
return 0;
}
#endif //defined(__MACH__) || defined(__APPLE__)
using namespace std;
namespace toolkit {
static constexpr char CCH[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
string makeRandStr(int sz, bool printable) {
string ret;
ret.resize(sz);
std::mt19937 rng(std::random_device{}());
for (int i = 0; i < sz; ++i) {
if (printable) {
uint32_t x = rng() % (sizeof(CCH) - 1);
ret[i] = CCH[x];
} else {
ret[i] = rng() % 0xFF;
}
}
return ret;
}
bool is_safe(uint8_t b) {
return b >= ' ' && b < 128;
}
string hexdump(const void *buf, size_t len) {
string ret("\r\n");
char tmp[8];
const uint8_t *data = (const uint8_t *) buf;
for (size_t i = 0; i < len; i += 16) {
for (int j = 0; j < 16; ++j) {
if (i + j < len) {
int sz = snprintf(tmp, sizeof(tmp), "%.2x ", data[i + j]);
ret.append(tmp, sz);
} else {
int sz = snprintf(tmp, sizeof(tmp), " ");
ret.append(tmp, sz);
}
}
for (int j = 0; j < 16; ++j) {
if (i + j < len) {
ret += (is_safe(data[i + j]) ? data[i + j] : '.');
} else {
ret += (' ');
}
}
ret += ('\n');
}
return ret;
}
string hexmem(const void *buf, size_t len) {
string ret;
char tmp[8];
const uint8_t *data = (const uint8_t *) buf;
for (size_t i = 0; i < len; ++i) {
int sz = sprintf(tmp, "%.2x ", data[i]);
ret.append(tmp, sz);
}
return ret;
}
string exePath(bool isExe /*= true*/) {
char buffer[PATH_MAX * 2 + 1] = {0};
int n = -1;
#if defined(_WIN32)
n = GetModuleFileNameA(isExe?nullptr:(HINSTANCE)&__ImageBase, buffer, sizeof(buffer));
#elif defined(__MACH__) || defined(__APPLE__)
n = sizeof(buffer);
if (uv_exepath(buffer, &n) != 0) {
n = -1;
}
#elif defined(__linux__)
n = readlink("/proc/self/exe", buffer, sizeof(buffer));
#endif
string filePath;
if (n <= 0) {
filePath = "./";
} else {
filePath = buffer;
}
#if defined(_WIN32)
//windows下把路径统一转换层unix风格因为后续都是按照unix风格处理的
for (auto &ch : filePath) {
if (ch == '\\') {
ch = '/';
}
}
#endif //defined(_WIN32)
return filePath;
}
string exeDir(bool isExe /*= true*/) {
auto path = exePath(isExe);
return path.substr(0, path.rfind('/') + 1);
}
string exeName(bool isExe /*= true*/) {
auto path = exePath(isExe);
return path.substr(path.rfind('/') + 1);
}
// string转小写
std::string &strToLower(std::string &str) {
transform(str.begin(), str.end(), str.begin(), towlower);
return str;
}
// string转大写
std::string &strToUpper(std::string &str) {
transform(str.begin(), str.end(), str.begin(), towupper);
return str;
}
// string转小写
std::string strToLower(std::string &&str) {
transform(str.begin(), str.end(), str.begin(), towlower);
return std::move(str);
}
// string转大写
std::string strToUpper(std::string &&str) {
transform(str.begin(), str.end(), str.begin(), towupper);
return std::move(str);
}
vector<string> split(const string &s, const char *delim) {
vector<string> ret;
size_t last = 0;
auto index = s.find(delim, last);
while (index != string::npos) {
if (index - last > 0) {
ret.push_back(s.substr(last, index - last));
}
last = index + strlen(delim);
index = s.find(delim, last);
}
if (!s.size() || s.size() - last > 0) {
ret.push_back(s.substr(last));
}
return ret;
}
#define TRIM(s, chars) \
do{ \
string map(0xFF, '\0'); \
for (auto &ch : chars) { \
map[(unsigned char &)ch] = '\1'; \
} \
while( s.size() && map.at((unsigned char &)s.back())) s.pop_back(); \
while( s.size() && map.at((unsigned char &)s.front())) s.erase(0,1); \
}while(0);
//去除前后的空格、回车符、制表符
std::string &trim(std::string &s, const string &chars) {
TRIM(s, chars);
return s;
}
std::string trim(std::string &&s, const string &chars) {
TRIM(s, chars);
return std::move(s);
}
void replace(string &str, const string &old_str, const string &new_str,std::string::size_type b_pos) {
if (old_str.empty() || old_str == new_str) {
return;
}
auto pos = str.find(old_str,b_pos);
if (pos == string::npos) {
return;
}
str.replace(pos, old_str.size(), new_str);
replace(str, old_str, new_str,pos + new_str.length());
}
bool start_with(const string &str, const string &substr) {
return str.find(substr) == 0;
}
bool end_with(const string &str, const string &substr) {
auto pos = str.rfind(substr);
return pos != string::npos && pos == str.size() - substr.size();
}
bool isIP(const char *str) {
return SockUtil::is_ipv4(str) || SockUtil::is_ipv6(str);
}
#if defined(_WIN32)
void sleep(int second) {
Sleep(1000 * second);
}
void usleep(int micro_seconds) {
this_thread::sleep_for(std::chrono::microseconds(micro_seconds));
}
int gettimeofday(struct timeval *tp, void *tzp) {
auto now_stamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
tp->tv_sec = (decltype(tp->tv_sec))(now_stamp / 1000000LL);
tp->tv_usec = now_stamp % 1000000LL;
return 0;
}
const char *strcasestr(const char *big, const char *little){
string big_str = big;
string little_str = little;
strToLower(big_str);
strToLower(little_str);
auto pos = strstr(big_str.data(), little_str.data());
if (!pos){
return nullptr;
}
return big + (pos - big_str.data());
}
int vasprintf(char **strp, const char *fmt, va_list ap) {
// _vscprintf tells you how big the buffer needs to be
int len = _vscprintf(fmt, ap);
if (len == -1) {
return -1;
}
size_t size = (size_t)len + 1;
char *str = (char*)malloc(size);
if (!str) {
return -1;
}
// _vsprintf_s is the "secure" version of vsprintf
int r = vsprintf_s(str, len + 1, fmt, ap);
if (r == -1) {
free(str);
return -1;
}
*strp = str;
return r;
}
int asprintf(char **strp, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int r = vasprintf(strp, fmt, ap);
va_end(ap);
return r;
}
#endif //WIN32
static long s_gmtoff = 0; //时间差
static onceToken s_token([]() {
#ifdef _WIN32
TIME_ZONE_INFORMATION tzinfo;
DWORD dwStandardDaylight;
long bias;
dwStandardDaylight = GetTimeZoneInformation(&tzinfo);
bias = tzinfo.Bias;
if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) {
bias += tzinfo.StandardBias;
}
if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) {
bias += tzinfo.DaylightBias;
}
s_gmtoff = -bias * 60; //时间差(分钟)
#else
local_time_init();
s_gmtoff = getLocalTime(time(nullptr)).tm_gmtoff;
#endif // _WIN32
});
long getGMTOff() {
return s_gmtoff;
}
static inline uint64_t getCurrentMicrosecondOrigin() {
#if !defined(_WIN32)
struct timeval tv;
gettimeofday(&tv, nullptr);
return tv.tv_sec * 1000000LL + tv.tv_usec;
#else
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
#endif
}
static atomic<uint64_t> s_currentMicrosecond(0);
static atomic<uint64_t> s_currentMillisecond(0);
static atomic<uint64_t> s_currentMicrosecond_system(getCurrentMicrosecondOrigin());
static atomic<uint64_t> s_currentMillisecond_system(getCurrentMicrosecondOrigin() / 1000);
static inline bool initMillisecondThread() {
static std::thread s_thread([]() {
setThreadName("stamp thread");
DebugL << "Stamp thread started";
uint64_t last = getCurrentMicrosecondOrigin();
uint64_t now;
uint64_t microsecond = 0;
while (true) {
now = getCurrentMicrosecondOrigin();
//记录系统时间戳,可回退
s_currentMicrosecond_system.store(now, memory_order_release);
s_currentMillisecond_system.store(now / 1000, memory_order_release);
//记录流逝时间戳,不可回退
int64_t expired = now - last;
last = now;
if (expired > 0 && expired < 1000 * 1000) {
//流逝时间处于0~1000ms之间那么是合理的说明没有调整系统时间
microsecond += expired;
s_currentMicrosecond.store(microsecond, memory_order_release);
s_currentMillisecond.store(microsecond / 1000, memory_order_release);
} else if (expired != 0) {
WarnL << "Stamp expired is abnormal: " << expired;
}
//休眠0.5 ms
usleep(500);
}
});
static onceToken s_token([]() {
s_thread.detach();
});
return true;
}
uint64_t getCurrentMillisecond(bool system_time) {
static bool flag = initMillisecondThread();
if (system_time) {
return s_currentMillisecond_system.load(memory_order_acquire);
}
return s_currentMillisecond.load(memory_order_acquire);
}
uint64_t getCurrentMicrosecond(bool system_time) {
static bool flag = initMillisecondThread();
if (system_time) {
return s_currentMicrosecond_system.load(memory_order_acquire);
}
return s_currentMicrosecond.load(memory_order_acquire);
}
string getTimeStr(const char *fmt, time_t time) {
if (!time) {
time = ::time(nullptr);
}
auto tm = getLocalTime(time);
size_t size = strlen(fmt) + 64;
string ret;
ret.resize(size);
size = std::strftime(&ret[0], size, fmt, &tm);
if (size > 0) {
ret.resize(size);
}
else{
ret = fmt;
}
return ret;
}
struct tm getLocalTime(time_t sec) {
struct tm tm;
#ifdef _WIN32
localtime_s(&tm, &sec);
#else
no_locks_localtime(&tm, sec);
#endif //_WIN32
return tm;
}
static thread_local string thread_name;
static string limitString(const char *name, size_t max_size) {
string str = name;
if (str.size() + 1 > max_size) {
auto erased = str.size() + 1 - max_size + 3;
str.replace(5, erased, "...");
}
return str;
}
void setThreadName(const char *name) {
assert(name);
#if defined(__linux) || defined(__linux__) || defined(__MINGW32__)
pthread_setname_np(pthread_self(), limitString(name, 16).data());
#elif defined(__MACH__) || defined(__APPLE__)
pthread_setname_np(limitString(name, 32).data());
#elif defined(_MSC_VER)
// SetThreadDescription was added in 1607 (aka RS1). Since we can't guarantee the user is running 1607 or later, we need to ask for the function from the kernel.
using SetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PCWSTR lpThreadDescription);
static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "SetThreadDescription"));
if (setThreadDescription) {
// Convert the thread name to Unicode
wchar_t threadNameW[MAX_PATH];
size_t numCharsConverted;
errno_t wcharResult = mbstowcs_s(&numCharsConverted, threadNameW, name, MAX_PATH - 1);
if (wcharResult == 0) {
HRESULT hr = setThreadDescription(::GetCurrentThread(), threadNameW);
if (!SUCCEEDED(hr)) {
int i = 0;
i++;
}
}
} else {
// For understanding the types and values used here, please see:
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
struct THREADNAME_INFO {
DWORD dwType = 0x1000; // Must be 0x1000
LPCSTR szName; // Pointer to name (in user address space)
DWORD dwThreadID; // Thread ID (-1 for caller thread)
DWORD dwFlags = 0; // Reserved for future use; must be zero
};
#pragma pack(pop)
THREADNAME_INFO info;
info.szName = name;
info.dwThreadID = (DWORD) - 1;
__try{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<const ULONG_PTR *>(&info));
} __except(GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) {
}
}
#else
thread_name = name ? name : "";
#endif
}
string getThreadName() {
#if ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) || (defined(__MACH__) || defined(__APPLE__)) || (defined(ANDROID) && __ANDROID_API__ >= 26) || defined(__MINGW32__)
string ret;
ret.resize(32);
auto tid = pthread_self();
pthread_getname_np(tid, (char *) ret.data(), ret.size());
if (ret[0]) {
ret.resize(strlen(ret.data()));
return ret;
}
return to_string((uint64_t) tid);
#elif defined(_MSC_VER)
using GetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PWSTR * ppszThreadDescription);
static auto getThreadDescription = reinterpret_cast<GetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription"));
if (!getThreadDescription) {
std::ostringstream ss;
ss << std::this_thread::get_id();
return ss.str();
} else {
PWSTR data;
HRESULT hr = getThreadDescription(GetCurrentThread(), &data);
if (SUCCEEDED(hr) && data[0] != '\0') {
char threadName[MAX_PATH];
size_t numCharsConverted;
errno_t charResult = wcstombs_s(&numCharsConverted, threadName, data, MAX_PATH - 1);
if (charResult == 0) {
LocalFree(data);
std::ostringstream ss;
ss << threadName;
return ss.str();
} else {
if (data) {
LocalFree(data);
}
return to_string((uint64_t) GetCurrentThreadId());
}
} else {
if (data) {
LocalFree(data);
}
return to_string((uint64_t) GetCurrentThreadId());
}
}
#else
if (!thread_name.empty()) {
return thread_name;
}
std::ostringstream ss;
ss << std::this_thread::get_id();
return ss.str();
#endif
}
bool setThreadAffinity(int i) {
#if (defined(__linux) || defined(__linux__)) && !defined(ANDROID)
cpu_set_t mask;
CPU_ZERO(&mask);
if (i >= 0) {
CPU_SET(i, &mask);
} else {
for (auto j = 0u; j < thread::hardware_concurrency(); ++j) {
CPU_SET(j, &mask);
}
}
if (!pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask)) {
return true;
}
WarnL << "pthread_setaffinity_np failed: " << get_uv_errmsg();
#endif
return false;
}
#ifndef HAS_CXA_DEMANGLE
// We only support some compilers that support __cxa_demangle.
// TODO: Checks if Android NDK has fixed this issue or not.
#if defined(__ANDROID__) && (defined(__i386__) || defined(__x86_64__))
#define HAS_CXA_DEMANGLE 0
#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
!defined(__mips__)
#define HAS_CXA_DEMANGLE 1
#elif defined(__clang__) && !defined(_MSC_VER)
#define HAS_CXA_DEMANGLE 1
#else
#define HAS_CXA_DEMANGLE 0
#endif
#endif
#if HAS_CXA_DEMANGLE
#include <cxxabi.h>
#endif
// Demangle a mangled symbol name and return the demangled name.
// If 'mangled' isn't mangled in the first place, this function
// simply returns 'mangled' as is.
//
// This function is used for demangling mangled symbol names such as
// '_Z3bazifdPv'. It uses abi::__cxa_demangle() if your compiler has
// the API. Otherwise, this function simply returns 'mangled' as is.
//
// Currently, we support only GCC 3.4.x or later for the following
// reasons.
//
// - GCC 2.95.3 doesn't have cxxabi.h
// - GCC 3.3.5 and ICC 9.0 have a bug. Their abi::__cxa_demangle()
// returns junk values for non-mangled symbol names (ex. function
// names in C linkage). For example,
// abi::__cxa_demangle("main", 0, 0, &status)
// returns "unsigned long" and the status code is 0 (successful).
//
// Also,
//
// - MIPS is not supported because abi::__cxa_demangle() is not defined.
// - Android x86 is not supported because STLs don't define __cxa_demangle
//
string demangle(const char *mangled) {
int status = 0;
char *demangled = nullptr;
#if HAS_CXA_DEMANGLE
demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status);
#endif
string out;
if (status == 0 && demangled) { // Demangling succeeeded.
out.append(demangled);
#ifdef ASAN_USE_DELETE
delete [] demangled; // 开启asan后用free会卡死
#else
free(demangled);
#endif
} else {
out.append(mangled);
}
return out;
}
string getEnv(const string &key) {
auto ekey = key.c_str();
if (*ekey == '$') {
++ekey;
}
auto value = *ekey ? getenv(ekey) : nullptr;
return value ? value : "";
}
void Creator::onDestoryException(const type_info &info, const exception &ex) {
ErrorL << "Invoke " << demangle(info.name()) << "::onDestory throw a exception: " << ex.what();
}
} // namespace toolkit
extern "C" {
void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line, const char *str) {
if (failed) {
toolkit::_StrPrinter printer;
printer << "Assertion failed: (" << exp ;
if(str && *str){
printer << ", " << str;
}
printer << "), function " << func << ", file " << file << ", line " << line << ".";
throw toolkit::AssertFailedException(printer);
}
}
}