newtx/sdk/base/utils.cpp

2047 lines
42 KiB
C++

#include "utils.h"
#include "ini_file.h"
#include <mutex>
#include <algorithm>
#if OS_WIN
#include <direct.h>
#include <Windows.h>
#include <time.h>
#include <Psapi.h>
#include <Tlhelp32.h>
#pragma comment(lib, "Psapi.lib")
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 // microseconds from '1601-01-01 00:00:00' to '1970-01-01 00:00:00'
int gettimeofday(TIMEV* tv, struct timezone* tz)
{
FILETIME ft = { 0 };
uint64_t ui64 = 0;
static bool set_tz = true;
GetSystemTimeAsFileTime(&ft); // 100 ns - from 1601-01-01 00:00:00
ui64 = ft.dwHighDateTime;
ui64 <<= 32;
ui64 |= ft.dwLowDateTime;
// convert to microseconds ...
ui64 += 5;
ui64 /= 10;
// move to 1970-01-01 00:00:00
ui64 -= DELTA_EPOCH_IN_MICROSECS;
if (tv)
{
tv->tv_sec = ui64 / 1000000;
tv->tv_usec = ui64 % 1000000;
}
if (tz)
{
if (set_tz)
{
set_tz = false;
_tzset();
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
#else
#include <map>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <thread>
#include <iconv.h>
#include <sys/vfs.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#define PRODUCT_VENDOR "HuaGo"
static std::mutex ini_lock_;
static std::map<std::string, simple_ini*> ini_files_;
static std::string debug_cfg_file_ = "";
static void init_default_config_file_path(void)
{
if(debug_cfg_file_.empty())
{
debug_cfg_file_ = utils::get_local_data_path() + PATH_SEPARATOR + "config" + PATH_SEPARATOR + "debug.cfg";
}
}
static simple_ini* get_ini_object(const char* file)
{
if(!file || file[0] == 0)
{
init_default_config_file_path();
file = debug_cfg_file_.c_str();
}
{
std::lock_guard<std::mutex> lock(ini_lock_);
if (ini_files_.count(file) == 0)
{
simple_ini* ini = new simple_ini();
ini->load(file);
ini_files_[file] = ini;
}
}
return ini_files_[file];
}
DWORD GetPrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, DWORD nSize, const char* lpFileName)
{
simple_ini* ini = get_ini_object(lpFileName);
std::string str(ini->get(lpAppName, lpKeyName, lpDefault));
if (nSize)
{
if (nSize < str.length())
{
memcpy(lpReturnedString, str.c_str(), nSize);
}
else
{
strcpy(lpReturnedString, str.c_str());
nSize = str.length();
}
}
return nSize;
}
BOOL WritePrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpString, const char* lpFileName)
{
simple_ini* ini = get_ini_object(lpFileName);
ini->set(lpAppName, lpKeyName, lpString);
ini->save(lpFileName);
return TRUE;
}
DWORD GetLastError(void)
{
return errno;
}
DWORD GetPrivateProfileIntA(const char* app, const char* key, DWORD def, const char* file)
{
std::string val(get_ini_object(file)->get(app, key));
return val.empty() ? def : atoi(val.c_str());
}
DWORD GetPrivateProfileStringA(const char* app, const char* key, const char* init, char* buf, size_t len, const char* file)
{
std::string val(get_ini_object(file)->get(app, key));
if(val.empty())
{
if(init)
{
strcpy(buf, init);
len = strlen(init);
}
else
{
len = 0;
}
}
else
{
if(len < val.length())
memcpy(buf, val.c_str(), len);
else
{
strcpy(buf, val.c_str());
len = val.length();
}
}
return len;
}
void Sleep(DWORD milliseconds)
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
int GetModuleFileNameA(HMODULE module, char* buf, size_t len)
{
std::string name(""),
val(utils::get_module_full_path((char*)module));
val += PATH_SEPARATOR + name;
if(len < val.length())
memcpy(buf, val.c_str(), len);
else
{
strcpy(buf, val.c_str());
len = val.length();
}
return len;
}
uint64_t GetCurrentThreadId(void)
{
return (uint64_t)pthread_self();
}
uint64_t GetCurrentProcessId(void)
{
return (uint64_t)getpid();
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// log class
#define MAX_LOG_FILE_SIZE SIZE_MB(10)
class log_cls
{
void(*log_)(const char*, void*, void*);
std::string path_file_;
FILE* file_;
int level_;
int type_;
std::mutex lock_;
static log_cls* inst_;
static FILE* create_log_file(const char* path_file, bool truncate)
{
FILE* file_ = fopen(path_file, "a+b");
if (file_)
{
fseek(file_, 0, SEEK_END);
if (ftell(file_) == 0)
{
unsigned char bom[] = { 0x0ef, 0x0bb, 0x0bf };
fwrite(bom, sizeof(bom), 1, file_);
}
else
{
std::string sep("\n\n========================================================================================================================\n");
fwrite(sep.c_str(), sizeof(sep[0]), sep.length(), file_);
}
std::string now("[" + utils::format_current_time() + "]: =====================================");
now += truncate ? "--truncated--=====================================\n" : "--started--" + std::to_string(GetCurrentProcessId()) + "=====================================\n";
fwrite(now.c_str(), sizeof(now[0]), now.length(), file_);
}
return file_;
}
static void log_none(const char* info, void* param, void* param2)
{}
static void log_consonle(const char* info, void* param, void* param2)
{
printf(info);
}
static void log_file(const char* info, void* param, void* param2)
{
FILE** file = (FILE**)param;
if (*file == nullptr)
*file = create_log_file(((std::string*)param2)->c_str(), false);
if (*file)
{
fwrite(info, 1, strlen(info), *file);
fflush(*file);
if (ftell(*file) >= MAX_LOG_FILE_SIZE)
{
fclose(*file);
remove(((std::string*)param2)->c_str());
*file = create_log_file(((std::string*)param2)->c_str(), true);
}
}
}
protected:
log_cls() : path_file_(""), file_(0), log_(&log_cls::log_consonle), level_(LOG_LEVEL_ALL)
{}
~log_cls()
{
if (file_)
{
fclose(file_);
file_ = 0;
}
}
public:
static log_cls* instance(void)
{
if (!log_cls::inst_)
log_cls::inst_ = new log_cls();
return log_cls::inst_;
}
static void clear(void)
{
if(log_cls::inst_)
{
delete log_cls::inst_;
log_cls::inst_ = nullptr;
}
}
int set_log_type(int type, void* param)
{
int ret = 0;
if (file_)
{
fclose(file_);
file_ = 0;
}
type_ = type;
if (type == LOG_TYPE_NONE)
log_ = &log_cls::log_none;
else if (type == LOG_TYPE_CONSOLE)
log_ = &log_cls::log_consonle;
else if (type == LOG_TYPE_FILE)
{
log_ = &log_cls::log_file;
ret = -1;
if (param)
{
path_file_ = (char*)param;
file_ = create_log_file(path_file_.c_str(), false);
if (file_)
ret = 0;
}
}
if (ret != 0)
{
log_ = &log_cls::log_none;
type_ = LOG_TYPE_NONE;
}
return ret;
}
void set_log_level(int level)
{
level_ = level;
}
int level(void)
{
return level_;
}
int type(void)
{
return type_;
}
void log(const char* info)
{
std::lock_guard<std::mutex> lock(lock_);
log_(info, &file_, &path_file_);
}
std::string get_log_file_path(const char* dst = nullptr)
{
std::string file("");
if (log_ == &log_cls::log_file && file_)
{
file = path_file_;
if (dst)
{
file = dst;
FILE* dst = fopen(file.c_str(), "wb");
if (!dst)
file = "";
else
{
std::lock_guard<std::mutex> lock(lock_);
char buf[1024] = { 0 };
size_t l = 0;
fseek(file_, 0, SEEK_SET);
while ((l = fread(buf, 1, sizeof(buf), file_)))
fwrite(buf, 1, l, dst);
fclose(dst);
}
}
}
return file;
}
void clear_log(void)
{
if (log_ == &log_cls::log_file && file_)
{
std::lock_guard<std::mutex> lock(lock_);
fclose(file_);
remove(path_file_.c_str());
file_ = create_log_file(path_file_.c_str(), true);
}
}
};
log_cls* log_cls::inst_ = NULL;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// util
namespace utils
{
typedef struct _match_part
{
std::string pattern;
std::string found;
bool(*match)(const char*, const char*);
}MATCHPART;
static bool find_sub_str(const char* text, const char* pattern)
{
return strstr(text, pattern) != nullptr;
}
static bool STDCALL match_part_filename(const char* path_name, bool dir, void* param)
{
MATCHPART* partn = (MATCHPART*)param;
if (!dir)
{
if (partn->pattern.empty())
{
partn->found = path_name;
}
else
{
const char* name = strrchr(path_name, PATH_SEPARATOR[0]);
if (name++ == nullptr)
name = path_name;
std::string n(name);
to_lower(n);
// if (strstr(n.c_str(), partn[0].c_str()))
if(partn->match(n.c_str(), partn->pattern.c_str()))
partn->found = path_name;
else
dir = true;
}
}
return dir;
}
#if OS_WIN
static std::string u2m(const wchar_t* u, int page)
{
char* ansi = NULL;
int len = 0;
std::string mb("");
len = WideCharToMultiByte(page, 0, u, lstrlenW(u), NULL, 0, NULL, NULL);
ansi = new char[len + 2];
len = WideCharToMultiByte(page, 0, u, lstrlenW(u), ansi, len, NULL, NULL);
ansi[len--] = 0;
mb = ansi;
delete[] ansi;
return mb;
}
static std::wstring m2u(const char* m, int page)
{
wchar_t* unic = NULL;
int len = 0;
std::wstring u(L"");
len = MultiByteToWideChar(page, 0, m, lstrlenA(m), NULL, 0);
unic = new wchar_t[len + 2];
len = MultiByteToWideChar(page, 0, m, lstrlenA(m), unic, len);
unic[len--] = 0;
u = unic;
delete[] unic;
return u;
}
std::string utf82ansi(const char* utf8)
{
return u2m(m2u(utf8, CP_UTF8).c_str(), CP_ACP);
}
std::string ansi2utf8(const char* ansi)
{
return u2m(m2u(ansi, CP_ACP).c_str(), CP_UTF8);
}
#else
// This function will return 'in' string if failed !
static std::string transform_between_gbk_and_utf8(const char* in, bool to_utf8, int *err, const char* ansi = "GBK")
{
size_t len = strlen(in) + 8, ol = len * 2;
char *buf = (char*)malloc(len), *oper = buf, *out = nullptr, *oper1 = nullptr;
iconv_t conv;
memset(buf, 0, len);
strcpy(buf, in);
if(to_utf8)
conv = iconv_open("UTF-8", ansi);
else
conv = iconv_open(ansi, "UTF-8");
if(conv == (iconv_t)-1)
{
if(err)
*err = errno;
free(buf);
return in;
}
oper1 = out = (char*)malloc(ol);
memset(out, 0, ol);
len -= 8;
if(iconv(conv, &oper, &len, &oper1, &ol))
{
if(err)
*err = errno;
}
else if(err)
*err = 0;
std::string ret(out);
free(buf);
free(out);
iconv_close(conv);
return ret.empty() ? in : std::move(ret);
}
std::string utf82ansi(const char* utf8)
{
// fix me ...
return transform_between_gbk_and_utf8(utf8, false, nullptr);
}
std::string ansi2utf8(const char* ansi)
{
// fix me ...
return transform_between_gbk_and_utf8(ansi, true, nullptr);
}
#endif
std::string get_command_result(const char* cmd, int len, int *err)
{
std::string result("");
#if OS_WIN
#else
FILE* src = popen(cmd, "r");
if(err)
*err = 0;
if (src)
{
char buf[128] = { 0 };
int rv = fread(buf, 1, sizeof(buf) - 1, src);
while (rv > 0)
{
buf[rv] = 0;
result += buf;
if (len != -1 && result.length() >= len)
{
result.erase(len);
break;
}
rv = fread(buf, 1, sizeof(buf) - 1, src);
}
if(rv == -1 && err)
{
*err = errno;
utils::to_log(LOG_LEVEL_DEBUG, "Failed to excute shell command '%s' in read pipe: %d - %s\n", cmd, errno, strerror(errno));
}
pclose(src);
}
else if(err)
{
*err = errno;
utils::to_log(LOG_LEVEL_DEBUG, "Failed to excute shell command '%s' in open pipe: %d - %s\n", cmd, errno, strerror(errno));
}
#endif
return std::move(result);
}
std::string get_local_data_path(void)
{
static std::string ldp("");
if (ldp.empty())
{
#if OS_WIN
const char* path(getenv("LOCALAPPDATA"));
if (path)
{
ldp = path;
ldp += PATH_SEPARATOR;
}
#else
const char* path(getenv("HOME"));
if (path)
{
if (strstr(path, "/root"))
{
std::string usr(get_command_result("logname"));
ldp = std::string("/home/") + trim(usr);
if (!opendir(ldp.c_str()))
{
printf("opendir(%s) failed: %s\n", ldp.c_str(), strerror(errno));
ldp = path;
}
}
else
{
ldp = path;
}
ldp += std::string(PATH_SEPARATOR) + ".";
}
#endif
ldp += PRODUCT_VENDOR;
ldp += "Scan";
create_folder(ldp.c_str());
std::string first("[" + format_current_time() + "]: Process "),
ff(temporary_path() + PATH_SEPARATOR + PRODUCT_VENDOR + "scanner-first.log");
first += std::to_string(GetCurrentProcessId()) + " root of local data path is " + ldp + "\n";
save_2_file(&first[0], first.length(), ff.c_str(), true, SIZE_KB(1));
}
return ldp;
}
std::string temporary_path(void)
{
return std::move(simple_ini::temporary_path());
}
std::string format_current_time(void)
{
return std::move(chronograph::now());
}
std::string get_module_full_path(const char* part_name/*nullptr to get main-pe/first module's full path*/)
{
MATCHPART file = {part_name ? part_name : "", "", find_sub_str};
to_lower(file.pattern);
if(file.pattern.find("*") != std::string::npos)
file.match = is_match_pattern;
#if OS_WIN
if (part_name && *part_name)
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32W pei = { 0 };
if (h != INVALID_HANDLE_VALUE)
{
pei.dwSize = sizeof(pei);
if (Module32FirstW(h, &pei))
{
do
{
char path[256] = { 0 };
GetModuleFileNameA(pei.hModule, path, _countof(path) - 1);
if (!match_part_filename(path, false, (void*)&file))
break;
pei.dwSize = sizeof(pei);
} while (Module32NextW(h, &pei));
}
CloseHandle(h);
}
}
else
{
char path[256] = { 0 };
GetModuleFileNameA(NULL, path, _countof(path) - 1);
file.found = path;
}
#else
char path[128] = { 0 };
sprintf(path, "/proc/%u/map_files/", getpid());
enum_file(path, false, match_part_filename, (void*)&file);
#endif
return std::move(file.found);
}
std::string find_file(const char* root_dir, const char* part_name, bool recursive)
{
MATCHPART file = {part_name ? part_name : "", "", find_sub_str};
to_lower(file.pattern);
if(file.pattern.find("*") != std::string::npos)
file.match = is_match_pattern;
enum_file(root_dir, recursive, match_part_filename, (void*)&file);
return std::move(file.found);
}
std::string target_file_from_link(const char* lnk_file)
{
#if OS_WIN
std::string ret("");
return ret;
#else
char path[256] = { 0 };
int len = readlink(lnk_file, path, sizeof(path) - 1);
return len > 0 ? path : lnk_file;
#endif
}
std::string get_ini_value(const char* seg, const char* key, const char* cfg_file)
{
char buf[512] = { 0 };
GetPrivateProfileStringA(seg, key, "", buf, sizeof(buf) - 1, cfg_file);
return buf;
}
std::string load_mini_file(const char* file, int* err)
{
std::string cont("");
FILE* src = fopen(file, "rb");
int en = 0;
if (src)
{
long len = 0;
fseek(src, 0, SEEK_END);
len = ftell(src);
fseek(src, 0, SEEK_SET);
if (len > SIZE_MB(1))
{
en = E2BIG;
}
else
{
char* buf = new char[len];
if (buf)
{
len = fread(buf, 1, len, src);
cont = std::string(buf, len);
en = 0;
delete[] buf;
}
else
en = ENOMEM;
}
fclose(src);
}
else
en = errno;
if(err)
*err = en;
return std::move(cont);
}
int save_2_file(void* data, size_t len, const char* file, bool append, size_t max_size)
{
FILE* dst = fopen(file, append ? "a+b" : "wb");
int err = 0;
if (!dst)
return errno;
if(append && max_size != -1 && ftell(dst) >= max_size)
fseek(dst, 0, SEEK_SET);
if (fwrite(data, 1, len, dst) < len)
err = ENOSPC;
fclose(dst);
return err;
}
bool is_match_pattern(const char* text, const char* pattern)
{
int str_ind = 0, pattern_ind = 0, star = -1, m = 0,
str_len = strlen(text),
patt_len = strlen(pattern);
bool ok = true;
while (str_ind < str_len)
{
if (pattern_ind < patt_len && (text[str_ind] == pattern[pattern_ind] || pattern[pattern_ind] == '?'))
{
str_ind++;
pattern_ind++;
}
else if (pattern_ind < patt_len && pattern[pattern_ind] == '*')
{
star = pattern_ind++;
m = str_ind;
}
else if (star != -1)
{
pattern_ind = star + 1;
str_ind = ++m;
}
else
{
ok = false;
break;
}
}
if(ok)
{
while (pattern_ind < patt_len && pattern[pattern_ind] == '*')
pattern_ind++;
ok = pattern_ind == patt_len;
}
return ok;
}
const char* to_lower(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), tolower);
return str.c_str();
}
const char* trim(std::string& str, const char* sp)
{
int pos = 0;
char ch[2] = { 0 };
for (; pos < str.length(); ++pos)
{
ch[0] = str[pos];
if (!strstr(sp, ch))
break;
}
if (pos)
str.erase(0, pos);
pos = str.length() - 1;
for (; pos >= 0; --pos)
{
ch[0] = str[pos];
if (!strstr(sp, ch))
break;
}
if (++pos < str.length())
str.erase(pos);
return str.c_str();
}
bool from_hex_string(const char* hex, uint8_t* val)
{
*val = 0;
if(*hex >= '0' && *hex <= '9')
*val = *hex - '0';
else if(*hex >= 'a' && *hex <= 'f')
*val = *hex - 'a' + 10;
else if(*hex >= 'A' && *hex <= 'F')
*val = *hex - 'A' + 10;
else
return *hex == 0;
hex++;
if(*hex)
{
*val <<= 4;
if(*hex >= '0' && *hex <= '9')
*val += *hex - '0';
else if(*hex >= 'a' && *hex <= 'f')
*val += *hex - 'a' + 10;
else if(*hex >= 'A' && *hex <= 'F')
*val += *hex - 'A' + 10;
else
return false;
}
return true;
}
bool from_hex_string(const char* hex, uint16_t* val)
{
if(from_hex_string(hex, (uint8_t*)val))
{
hex += 2;
*val = swap_half(*val);
return from_hex_string(hex, (uint8_t*)val);
}
else
return false;
}
bool from_hex_string(const char* hex, uint32_t* val)
{
if(from_hex_string(hex, (uint16_t*)val))
{
hex += 4;
*val = swap_half(*val);
return from_hex_string(hex, (uint16_t*)val);
}
else
return false;
}
bool from_hex_string(const char* hex, uint64_t* val)
{
if(from_hex_string(hex, (uint32_t*)val))
{
hex += 8;
*val = swap_half(*val);
return from_hex_string(hex, (uint32_t*)val);
}
else
return false;
}
HMODULE load_dll(const char* path_file, int flag)
{
#if OS_WIN
HMODULE h = LoadLibraryA(path_file);
int ret = GetLastError();
utils::to_log(1, "[TWAIN]Load: LoadLibraryA(%s) = %d\r\n", path_file, ret);
if (!h && (ret == ERROR_MOD_NOT_FOUND || ret == ERROR_BAD_EXE_FORMAT))
{
std::string dir(path_file);
size_t pos = dir.rfind('\\');
char path[MAX_PATH] = { 0 };
GetDllDirectoryA(_countof(path) - 1, path);
if (pos != std::wstring::npos)
dir.erase(pos);
utils::to_log(LOG_LEVEL_FATAL, "[TWAIN]Load: change directory to '%s' and retry LoadLibraryA(%s) ...\r\n", dir.c_str(), path_file);
SetDllDirectoryA(dir.c_str());
h = LoadLibraryA(path_file);
// h = LoadLibraryExW(path_dll, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
ret = GetLastError();
utils::to_log(1, "[TWAIN]Load: trying LoadLibraryA(%s) = %d, restore directory to '%s'\r\n", path_file, ret, path);
SetDllDirectoryA(path);
}
errno = GetLastError();
return h;
#else
return dlopen(path_file, flag);
#endif
}
bool create_folder(const char* folder)
{
int ret = MKDIR(folder, S_IREAD | S_IWRITE | S_IEXEC);
return ret == 0 || errno == EEXIST;
}
void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file)
{
WritePrivateProfileStringA(seg, key, val, cfg_file);
}
int enum_file(const char* folder, bool recursive, bool(STDCALL* found)(const char* path_name, bool dir, void* param), void* param)
{
int ret = EACCES;
#if OS_WIN
WIN32_FIND_DATAA fd = { 0 };
std::string root(folder);
HANDLE hf = FindFirstFileA((root + "\\*").c_str(), &fd);
root += "\\";
if (hf == INVALID_HANDLE_VALUE)
ret = GetLastError();
else
{
do
{
bool is_dir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
if (!found((root + fd.cFileName).c_str(), is_dir, param))
{
ret = ERROR_CANCELLED;
break;
}
if (is_dir && recursive)
{
char* name = strrchr(fd.cFileName, PATH_SEPARATOR[0]);
if (name++ == NULL)
name = fd.cFileName;
if (strcmp(name, ".") && strcmp(name, ".."))
{
if (enum_file(fd.cFileName, recursive, found, param) == ERROR_CANCELLED)
{
ret = ERROR_CANCELLED;
break;
}
}
}
} while (FindNextFileA(hf, &fd));
FindClose(hf);
}
#else
DIR* pdir = nullptr;
struct dirent* ent = nullptr;
pdir = opendir(folder);
if (!pdir)
ret = errno;
else
{
while ((ent = readdir(pdir)))
{
std::string file(folder);
file += PATH_SEPARATOR;
file += ent->d_name;
if (!found(target_file_from_link(file.c_str()).c_str(), ent->d_type & DT_DIR, param))
{
ret = ERROR_CANCELLED;
break;
}
if (ent->d_type & DT_DIR)
{
if (recursive)
{
if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
{
std::string sub(folder);
sub += PATH_SEPARATOR;
sub += ent->d_name;
if (enum_file(sub.c_str(), recursive, found, param) == ERROR_CANCELLED)
{
ret = ERROR_CANCELLED;
break;
}
}
}
}
}
}
#endif
return ret;
}
int move_file(const char* from, const char* to)
{
return rename(from, to);
}
int make_file_size(const char* file, uint64_t size)
{
int err = 0;
get_command_result(("fallocate -l " + std::to_string(size) + " " + file).c_str(), -1, &err);
if(err == 0)
{
get_command_result(("truncate -s " + std::to_string(size) + " " + file).c_str(), -1, &err);
if(err == 0)
{
FILE* dst = fopen(file, "rb");
if(dst)
{
uint64_t rs = 0;
FSEEK(dst, 0, SEEK_END);
rs = FTELL(dst);
fclose(dst);
if(rs != size)
err = ENOSPC;
}
else
err = errno;
}
}
if(err)
remove(file);
return err;
}
int get_disk_space(const char* path, unsigned long long* total, unsigned long long* avail, unsigned long long* block)
{
int ret = 0;
#if OS_WIN
ULARGE_INTEGER av = { 0 },
all = { 0 };
if (GetDiskFreeSpaceExA(path, &av, &all, NULL))
{
if (total)
*total = all.QuadPart;
if (avail)
*avail = av.QuadPart;
if (block)
{
DWORD sec = 0,
clu = 0;
std::string root(path);
size_t pos = root.find(":\\");
if (pos != std::string::npos)
root.erase(pos + 2);
if (GetDiskFreeSpaceA(root.c_str(), &clu, &sec, NULL, NULL))
{
*block = clu * sec;
}
}
}
else
ret = GetLastError();
#else
struct statfs fs = { 0 };
ret = statfs(path, &fs);
if (ret == 0)
{
if (total)
*total = fs.f_blocks * fs.f_bsize;
if (avail)
*avail = fs.f_bavail * fs.f_bsize;
if (block)
*block = fs.f_bsize;
}
#endif
return ret;
}
unsigned int get_page_size(unsigned int* map_unit)
{
unsigned int ps = 1024;
#if OS_WIN
SYSTEM_INFO si = { 0 };
GetSystemInfo(&si);
ps = si.dwPageSize;
if (map_unit)
*map_unit = si.dwAllocationGranularity;
#else
ps = sysconf(_SC_PAGESIZE);
if(ps < 1024 || (ps & 0x0fe0000ff)) // nKB && < 16MB
ps = getpagesize();
if (map_unit)
*map_unit = ps;
#endif
if (ps < 1024 || (ps & 0x0fe0000ff)) // nKB && < 16MB
ps = 1024;
return ps;
}
std::string init_log(log_type type, log_level level, const char* fn_appendix)
{
std::string file("");
if (type == LOG_TYPE_FILE)
{
file = get_local_data_path() + PATH_SEPARATOR + "Log";
create_folder(file.c_str());
std::string pe(get_module_full_path());
size_t pos = pe.rfind(PATH_SEPARATOR[0]);
if (pos++ == std::string::npos)
pos = 0;
file += PATH_SEPARATOR + pe.substr(pos);
if (fn_appendix)
file += fn_appendix;
file += ".log";
printf("log file : %s\n", file.c_str());
}
log_cls::instance()->set_log_type(type, &file[0]);
log_cls::instance()->set_log_level(level);
return std::move(file);
}
void uninit(void)
{
log_info(("=====================================--Exited--" + std::to_string(GetCurrentProcessId()) + "=====================================\n\n\n\n").c_str(), LOG_LEVEL_FATAL);
log_cls::clear();
}
void log_info(const char* info, int level)
{
if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level)
{
log_cls::instance()->log(("[" + format_current_time() + "]: " + info).c_str());
}
}
void log_mem_info(const char* desc, const void* data, size_t bytes, int level)
{
if (get_log_type() == LOG_TYPE_NONE || get_log_level() > level)
return;
std::string line(desc);
char buf[40] = {0};
utils::log_info((line + "\n").c_str(), level);
line = "";
for(size_t i = 0; i < bytes; ++i)
{
if((i % 16) == 0)
{
if(line.length())
utils::log_info((line + "\n").c_str(), level);
sprintf(buf, "%p ", (const char*)data + i);
line = buf;
}
else if((i % 8) == 0)
line += " ";
sprintf(buf, "%02x ", ((const unsigned char*)data)[i]);
line += buf;
}
if(line.length())
utils::log_info((line + "\n").c_str(), level);
}
int get_log_type(void)
{
return log_cls::instance()->type();
}
int get_log_level(void)
{
return log_cls::instance()->level();
}
int copy_log_file_to(const char* dst)
{
log_cls::instance()->get_log_file_path(dst);
return 0;
}
int clear_log_file(void)
{
log_cls::instance()->clear_log();
return 0;
}
#if OS_WIN
bool run_get_message(HWND hwnd, UINT filter_min, UINT filter_max, std::function<bool(MSG*, bool*)> msghandler)
{
MSG msg = { 0 };
BOOL ret = GetMessageW(&msg, hwnd, filter_min, filter_max);
bool handled = false;
if ((DWORD)ret == -1 || !ret)
return false;
if (msghandler)
ret = msghandler(&msg, &handled) == true;
if (!handled)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return ret == TRUE;
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// base64 util ..
static char base64_default_table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" };
base64::base64() : padding_char_('=')
{
base64_ind_[0] = base64_char_[0] = 0;
initialize_base64_table(base64_default_table);
}
base64::~base64()
{}
bool base64::is_valid_base64_table(const char* table)
{
bool valid = false;
if (table && strlen(table) >= 64)
{
char repeat[4] = { 0 };
valid = true;
for (int i = 0; i < 63; ++i)
{
repeat[0] = table[i];
if (strstr(table + i + 1, repeat))
{
valid = false;
break;
}
}
}
return valid;
}
bool base64::initialize_base64_table(const char* table)
{
if (!table || strlen(table) < 64)
{
if (memcmp(base64_default_table, base64_char_, 64) == 0)
{
return !table;
}
memcpy(base64_char_, base64_default_table, 64);
}
else
{
if (memcmp(base64_char_, table, 64) == 0)
{
return true;
}
else if (!is_valid_base64_table(table))
return false;
memcpy(base64_char_, table, 64);
}
base64_char_[64] = base64_char_[65] = 0;
// initialize base64_index
memset(base64_ind_, 0, sizeof(base64_ind_));
for (int i = 0; i < 64; ++i)
{
base64_ind_[base64_char_[i]] = i;
}
// padding char
padding_char_ = '=';
if (base64_ind_[padding_char_])
{
for (padding_char_ = 0x21; padding_char_ < 0x7e && base64_ind_[padding_char_] && padding_char_ != base64_char_[0]; ++padding_char_);
}
return padding_char_ < 0x7e;
}
bool base64::set_base64_table(const char* table)
{
return initialize_base64_table(table ? table : base64_default_table);
}
std::string base64::encode(const char* data, size_t bytes, unsigned int line_bytes, bool need_padding)
{
char* str = (char*)malloc(bytes * 2 + 3);
unsigned char c1 = 0, c2 = 0, c3 = 0;
unsigned long line_len = 0;
unsigned long words = bytes / 3;
int rest = bytes % 3,
pos = 0;
std::string ret("");
for (unsigned long i = 0; i < words; ++i)
{
// fetch 3 letters
c1 = *data++;
c2 = *data++;
c3 = *data++;
// encoding into 4-bytes
str[pos++] = base64_char_[c1 >> 2];
str[pos++] = base64_char_[((c1 << 4) | (c2 >> 4)) & 0x3f];
str[pos++] = base64_char_[((c2 << 2) | (c3 >> 6)) & 0x3f];
str[pos++] = base64_char_[c3 & 0x3f];
line_len += 4;
// new line ...
if ((unsigned int)line_len > line_bytes - 4)
{
str[pos++] = '\r';
str[pos++] = '\n';
line_len = 0;
}
}
// rest ...
if (rest == 1)
{
c1 = *data++;
str[pos++] = base64_char_[(c1 & 0xfc) >> 2];
str[pos++] = base64_char_[((c1 & 0x03) << 4)];
if (need_padding)
{
str[pos++] = padding_char_;
str[pos++] = padding_char_;
}
}
else if (rest == 2)
{
c1 = *data++;
c2 = *data++;
str[pos++] = base64_char_[(c1 & 0xfc) >> 2];
str[pos++] = base64_char_[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
str[pos++] = base64_char_[((c2 & 0x0f) << 2)];
if (need_padding)
{
str[pos++] = padding_char_;
}
}
if (pos > 0)
{
str[pos++] = 0;
ret = std::string(str, pos - 1);
}
free(str);
return ret;
}
std::string base64::decode(const char* data, size_t bytes)
{
char* str = (char*)malloc(bytes + 1);
int pos = 0,
shifts = 18,
value = 0;
std::string ret("");
for (int i = 0; i < (int)bytes; ++i)
{
if (*data != '\r' && *data != '\n')
{
if (*data == padding_char_)
{
break;
}
value += base64_ind_[*data] << shifts;
if (shifts == 0)
{
shifts = 18;
str[pos++] = (value >> 16) & 0x0ff;
str[pos++] = (value >> 8) & 0x0ff;
str[pos++] = (value >> 0) & 0x0ff;
value = 0;
}
else
{
shifts -= 6;
}
}
data++;
}
if (shifts == 12 || shifts == 6)
{
str[pos++] = (value >> 16) & 0x0ff;
}
else if (shifts == 0)
{
str[pos++] = (value >> 16) & 0x0ff;
str[pos++] = (value >> 8) & 0x0ff;
}
if (pos > 0)
{
str[pos++] = 0;
ret = std::string(str, pos - 1);
}
free(str);
return ret;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// refer
refer::refer() : ref_(1)
{
on_born();
}
refer::~refer()
{
on_dead();
}
void refer::on_born(void)
{}
void refer::on_dead(void)
{}
int32_t refer::add_ref(void)
{
SIMPLE_LOCK(mutex_);
return ++ref_;
}
int32_t refer::release(void)
{
int32_t ref = 0;
{
SIMPLE_LOCK(mutex_);
ref = --ref_;
}
if (ref == 0)
delete this;
return ref;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// util
chronograph::chronograph()
{
reset();
}
chronograph::~chronograph()
{}
bool chronograph::now(TIMEV* tv)
{
struct timezone tz = { 0 };
return gettimeofday(tv, &tz) == 0;
}
bool chronograph::now(uint64_t* seconds, uint64_t* u_seconds)
{
TIMEV tv = { 0 };
struct timezone tz = { 0 };
if (gettimeofday(&tv, &tz) == 0)
{
if (seconds)
*seconds = tv.tv_sec;
if (u_seconds)
*u_seconds = tv.tv_usec;
return true;
}
else
{
return false;
}
}
std::string chronograph::now(bool with_ms/*whether with milliseconds*/) // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false
{
TIMEV tv = { 0 };
if (!chronograph::now(&tv))
return "";
char buf[40] = { 0 };
time_t t = tv.tv_sec;
struct tm* l = localtime(&t);
if (with_ms)
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday
, l->tm_hour, l->tm_min, l->tm_sec, tv.tv_usec);
else
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday
, l->tm_hour, l->tm_min, l->tm_sec);
return buf;
}
uint64_t chronograph::elapse_s(void)
{
TIMEV tv = { 0 };
chronograph::now(&tv);
return tv.tv_sec - bgn_.tv_sec;
}
uint64_t chronograph::elapse_ms(void)
{
TIMEV tv = { 0 };
uint64_t dif = 0;
chronograph::now(&tv);
dif = SEC_2_MS(tv.tv_sec - bgn_.tv_sec);
dif += tv.tv_usec / MSEC_2_US(1);
dif -= bgn_.tv_usec / MSEC_2_US(1);
return dif;
}
uint64_t chronograph::elapse_us(void)
{
TIMEV tv = { 0 };
uint64_t dif = 0;
chronograph::now(&tv);
dif = SEC_2_US(tv.tv_sec - bgn_.tv_sec);
dif += tv.tv_usec;
dif -= bgn_.tv_usec;
return dif;
}
void chronograph::reset()
{
chronograph::now(&bgn_);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// event
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// windows event ...
#if OS_WIN
int __stdcall sem_init(sem_t* handle, int, int)
{
if (!handle)
{
errno = EINVAL;
return -1;
}
*handle = CreateEvent(NULL, TRUE, FALSE, NULL);
if (*handle)
return 0;
else
{
errno = GetLastError();
return -1;
}
}
void __stdcall sem_destroy(sem_t* handle)
{
if (*handle)
{
CloseHandle(*handle);
*handle = NULL;
}
}
int __stdcall sem_trywait(sem_t* handle)
{
return WaitForSingleObject(*handle, 1) == WAIT_TIMEOUT ? -1 : 0;
}
void __stdcall sem_wait(sem_t* handle)
{
if (WaitForSingleObject(*handle, INFINITE) == WAIT_OBJECT_0)
ResetEvent(*handle);
}
int __stdcall sem_timedwait(sem_t* handle, struct timespec* to)
{
DWORD elapse = to->tv_sec * 1000;
elapse += to->tv_nsec / (1000 * 1000);
int ret = WaitForSingleObject(*handle, elapse) == WAIT_TIMEOUT ? -1 : 0;
if (ret == 0)
ResetEvent(*handle);
return ret;
}
void __stdcall sem_post(sem_t* handle)
{
SetEvent(*handle);
}
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// platform_event (base on semaphore)
platform_event::platform_event(const char* info) : waiting_(false), dbg_info_(info ? info : "")
{
int err = sem_init(&sem_, 0, 0);
if (err == -1)
{
err = errno;
utils::to_log(LOG_LEVEL_FATAL, "(%p)sem_init failed: %d\n", this, err);
}
}
platform_event::~platform_event()
{
sem_destroy(&sem_);
}
bool platform_event::try_wait(void)
{
return sem_trywait(&sem_) == 0;
}
bool platform_event::wait(unsigned timeout)
{
bool waited = true;
if(log_)
utils::to_log(LOG_LEVEL_DEBUG, "platform_event(%p - %s) --> waiting...\n", this, dbg_info_.c_str());
waiting_ = true;
if (timeout == USB_TIMEOUT_INFINITE)
sem_wait(&sem_);
else
{
struct timespec to;
to.tv_sec = timeout / 1000;
to.tv_nsec = (long)((timeout % 1000) * 1000 * 1000);
waited = sem_timedwait(&sem_, &to) == 0;
}
if (log_)
utils::to_log(LOG_LEVEL_DEBUG, "platform_event(%p - %s) --> %s.\n", this, dbg_info_.c_str(), waited ? "waited" : "wait timeout");
waiting_ = false;
return waited;
}
void platform_event::trigger(void)
{
sem_post(&sem_);
}
bool platform_event::is_waiting(void)
{
return waiting_;
}
void platform_event::set_debug_info(const char* info)
{
dbg_info_ = info ? info : "";
}
void platform_event::enable_log(bool enable)
{
log_ = enable;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// shared_memory
shared_memory::shared_memory(unsigned long long key, size_t size) : key_(key), obj_((void*)-1), first_(true), bytes_(size), len_(0)
{
unsigned int* ptr = (unsigned int*)&key_;
utils::to_log(LOG_LEVEL_DEBUG, "shared memory key = 0x%x%08x\n", ptr[1], ptr[0]);
init();
}
shared_memory::~shared_memory()
{
clear();
}
void shared_memory::init(void)
{
#if OS_WIN
char name[40] = { 0 };
DWORD* key = (DWORD*)&key_;
HANDLE h = NULL;
sprintf(name, "scanner_0x%08x-%08x", key[1], key[0]);
h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bytes_, name);
if (h == NULL)
return;
first_ = !(GetLastError() == ERROR_ALREADY_EXISTS);
obj_ = (void*)h;
#else
int obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0666);
if (obj < 0)
{
unsigned int* v = (unsigned int*)&key_;
if (errno == EEXIST)
{
first_ = false;
obj = shmget(key_, bytes_, 0600);
if (obj == -1)
obj = shmget(key_, bytes_, 0);
utils::to_log(LOG_LEVEL_DEBUG, "open existing: shmget(0x%x%08x) = %d\n", v[1], v[0], obj);
obj_ = (void*)obj;
std::string prev(read()), proc("");
utils::to_log(LOG_LEVEL_DEBUG, "shared memory content: %s\n", prev.c_str());
if (prev.length())
{
proc = prev;
size_t pos = proc.find("pid: ");
if (pos != std::string::npos)
proc.erase(0, pos + 5);
pos = proc.find(")");
if (pos != std::string::npos)
proc.erase(pos);
proc = shared_memory::get_proc_name_by_pid(atoi(proc.c_str()));
if (proc.length())
{
pos = prev.find("(");
if (pos == std::string::npos)
pos = prev.length();
if (strcasecmp(proc.c_str(), prev.substr(0, pos).c_str()))
proc = "";
}
}
if (proc.empty())
{
first_ = true;
clear();
obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600);
utils::to_log(LOG_LEVEL_DEBUG, "%s is not existing and reopen it\n", prev.c_str());
}
}
else
{
utils::to_log(LOG_LEVEL_DEBUG, "shmget(0x%x%08x) = %d\n", v[1], v[0], errno);
return;
}
}
obj_ = (void*)obj;
utils::to_log(LOG_LEVEL_DEBUG, "shared memory id = %d[%s], \n", obj, first_ ? "created" : "opened");
#endif
if (first_)
{
pid_t pid = getpid();
std::string me("");
char buf[40] = { 0 };
unsigned int* pn = (unsigned int*)&pid;
if (sizeof(pid) > 4 && pn[1])
sprintf(buf, "(pid: 0x%x%08x)", pn[1], pn[0]);
else
sprintf(buf, "(pid: %u)", pn[0]);
me = utils::get_module_full_path();
pid = me.rfind(PATH_SEPARATOR[0]);
if ((size_t)pid != std::string::npos)
me.erase(0, pid + 1);
me += buf;
write(me.c_str(), me.length());
}
}
void shared_memory::clear(void)
{
if (obj_ != (void*)-1)
#if OS_WIN
CloseHandle((HANDLE)obj_);
#else
{
if (first_)
{
struct shmid_ds ds = { 0 };
int* h = (int*)&obj_;
shmctl(*h, IPC_RMID, &ds);
}
}
#endif
obj_ = (void*)-1;
}
char* shared_memory::get_buf(void)
{
#if OS_WIN
char* buf = (char*)MapViewOfFile((HANDLE)obj_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
if (!buf)
buf = (char*)-1;
#else
int* h = (int*)&obj_;
char* buf = (char*)shmat(*h, 0, 0);
utils::to_log(LOG_LEVEL_DEBUG, "shared memory %d buffer = %p, error = %d\n", *h, buf, errno);
#endif
return buf;
}
void shared_memory::release_buf(void* buf)
{
#if OS_WIN
UnmapViewOfFile(buf);
#else
shmdt(buf);
#endif
}
#if !OS_WIN
std::string shared_memory::get_proc_name_by_pid(pid_t pid)
{
char path[512] = { 0 };
unsigned int* v = (unsigned int*)&pid;
std::string ret("");
if (sizeof(pid) > 4 && v[1])
sprintf(path, "/proc/%lld/status", pid);
else
sprintf(path, "/proc/%u/status", pid);
FILE* src = fopen(path, "rb");
if (src)
{
char val[512] = { 0 };
memset(path, 0, sizeof(path));
fgets(path, sizeof(path) - 1, src);
fclose(src);
sscanf(path, "%*s %s", val);
ret = val;
}
if (sizeof(pid) > 4 && v[1])
{
utils::to_log(LOG_LEVEL_DEBUG, "PID(%lld) name is: %s\n", pid, ret.c_str());
}
else
{
utils::to_log(LOG_LEVEL_DEBUG, "PID(%u) name is: %s\n", pid, ret.c_str());
}
return ret;
}
#endif
bool shared_memory::is_ok(void)
{
return obj_ != (void*)-1;
}
bool shared_memory::is_first(void)
{
return is_ok() && first_;
}
std::string shared_memory::read(void)
{
if (obj_ == (void*)-1)
return "";
char* buf = get_buf();
if (buf == (char*)-1)
return "";
std::string ret("");
size_t len = 0;
int off = 4; // sizeof(size_t);
memcpy(&len, buf, off);
ret = std::string(buf + off, len);
release_buf(buf);
return ret;
}
int shared_memory::write(const char* data, size_t len)
{
if (len > bytes_)
return E2BIG;
char* buf = get_buf();
int off = 4; // sizeof(len);
if (buf == (char*)-1)
return errno;
memcpy(buf, &len, off);
memcpy(buf + off, data, len);
len_ = len;
release_buf(buf);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// safe_thread
safe_thread::safe_thread() : excep_que_("thread-exception")
{
notify_thread_.reset(new std::thread(&safe_thread::thread_notify_exception, this));
}
safe_thread::~safe_thread()
{
run_ = false;
excep_que_.trigger();
if(notify_thread_->joinable())
notify_thread_->join();
for(auto& v: threads_)
{
if(v.thread.get() && v.thread->joinable())
v.thread->join();
}
threads_.clear();
}
void safe_thread::thread_worker(std::function<void(void)> func, std::string name, void* addr)
{
try
{
utils::to_log(LOG_LEVEL_DEBUG, "+++ safe_thread of '%s(%p) - %p' is running ...\n", name.c_str(), addr, GetCurrentThreadId());
func();
utils::to_log(LOG_LEVEL_DEBUG, "--- safe_thread of '%s - %p' exited.\n", name.c_str(), GetCurrentThreadId());
return;
}
catch (std::exception e)
{
utils::to_log(LOG_LEVEL_FATAL, "Exception in thread '%p - %s': %s\n", GetCurrentThreadId(), name.c_str(), e.what());
}
catch (...)
{
utils::to_log(LOG_LEVEL_FATAL, "Unknown exception in thread '%p - %s'!\n", GetCurrentThreadId(), name.c_str());
}
excep_que_.save(name, true);
}
void safe_thread::thread_notify_exception(void)
{
while(run_)
{
std::string name("");
if(excep_que_.take(name, true))
{
if(excep_handler_)
excep_handler_(name.c_str());
}
}
}
void safe_thread::set_exception_handler(std::function<void(const char*)> on_exception)
{
excep_handler_ = on_exception;
}
int safe_thread::start(std::function<void(void)> f, const char* thread_name, void* addr)
{
SAFETHRD st;
st.name = thread_name ? thread_name : "";
st.thread.reset(new std::thread(&safe_thread::thread_worker, this, f, thread_name, addr));
{
SIMPLE_LOCK(lock_);
threads_.push_back(st);
}
return 0;
}
int safe_thread::stop(const char* thread_name)
{
int ret = ENOENT;
SIMPLE_LOCK(lock_);
for(int i = 0; i < threads_.size(); ++i)
{
if(threads_[i].name == thread_name)
{
if(threads_[i].thread.get() && threads_[i].thread->joinable())
threads_[i].thread->join();
threads_.erase(threads_.begin() + i);
ret = 0;
break;
}
}
return ret;
}