#include "utils.h" #include "huagao/brand.h" #include "ini_file.h" #include #include #if OS_WIN #include #include #include #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #include static std::mutex ini_lock_; static std::map 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 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; } int GetCurrentThreadId(void) { return pthread_self(); } int GetCurrentProcessId(void) { return 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 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 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 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) { std::string result(""); #if OS_WIN #else FILE* src = popen(cmd, "r"); 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); } pclose(src); } #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(); } 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 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) { utils::to_log(LOG_LEVEL_DEBUG, " Total: %lld, Free: %lld, Avail: %lld, block size: %lld\n", fs.f_blocks, fs.f_bfree, fs.f_bavail, fs.f_bsize); 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; } void 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"; } log_cls::instance()->set_log_type(type, &file[0]); log_cls::instance()->set_log_level(level); } 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 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; 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; } 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 : ""; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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; }