#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 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--=====================================\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 defined(WIN32) || defined(_WIN64) 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 defined(WIN32) || defined(_WIN64) 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--=====================================\n\n\n\n", 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; } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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_); }