// utilities for platform ... // // Date: 2023-06-30 // #pragma once #include "plat_types.h" #include #include #include #include #include #include #include #include #define USE_SAFE_THREAD enum log_type { LOG_TYPE_NONE = 0, // no logging LOG_TYPE_CONSOLE, // print to console LOG_TYPE_FILE, // write log into file }; enum log_level { LOG_LEVEL_ALL = 0, LOG_LEVEL_DEBUG, LOG_LEVEL_WARNING, LOG_LEVEL_FATAL, }; namespace utils { std::string utf82ansi(const char* utf8); std::string ansi2utf8(const char* ansi); // Function: parse ONE character from a utf8 string // // Parameters: str - [in]: head of utf8 string // // [out]: beginning of the next character if success or no changes on failure // // ch - to receive the character, at least 8 bytes // // Return: 0 - success // EINVAL - invalid utf8 string // ENODATA - need more data int parse_utf8_char(const char*& str, char ch[8]); // Function: transfere from utf16 to utf8 // // Parameters: in - [in]: head of utf16 beginning // // [out]: beginning of the next character if success or no changes on failure // ch - to receive the character, at least 8 bytes // // Return: 0 - success // EINVAL - invalid utf8 string // ENODATA - need more data int utf16_2_8(unsigned short* &in, char ch[8]); std::string get_command_result(const char* cmd, int len = -1, int *err = nullptr); std::string get_local_data_path(void); std::string temporary_path(void); std::string format_current_time(void); std::string get_module_full_path(const char* part_name = nullptr/*nullptr to get main-pe/first module's full path*/); std::string find_file(const char* root_dir, const char* part_name, bool recursive = false); std::string target_file_from_link(const char* lnk_file); std::string from_console(const char* tips = nullptr, console_color clr = console_color::COLOR_CHAR_GREEN); std::string get_ini_value(const char* seg, const char* key, const char* cfg_file); // return "" if not found std::string load_mini_file(const char* file, int* err); // <= 1MB int save_2_file(void* data, size_t len, const char* file, bool append = false/*append or new*/, size_t max_size = -1/*in append mode, truncate file if size is exceeded this value if was not -1*/); void printf_with_color(const char* msg, console_color text_clr, console_color bkg_clr = COLOR_BKG_NONE, console_display_mode mode = console_display_mode::MODE_DEFAULT); bool is_match_pattern(const char* text, const char* pattern); const char* to_lower(std::string& str); // return str.c_str() const char* trim(std::string& str, const char* sp = "\r\n\t "); // return str.c_str() int get_stack_size(void); bool from_hex_string(const char* hex, uint8_t* val); bool from_hex_string(const char* hex, uint16_t* val); bool from_hex_string(const char* hex, uint32_t* val); bool from_hex_string(const char* hex, uint64_t* val); HMODULE load_dll(const char* path_file, int flag); bool create_folder(const char* folder); void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file); int enum_file(const char* folder, bool recursive, bool/*return false to stop enumeration*/(STDCALL* found)(const char* path_name, bool dir, void* param), void* param); int move_file(const char* from, const char* to); int make_file_size(const char* file, uint64_t size); // truncate or extend file size to 'size', create if not exist int get_file_time(const char* file, uint64_t* born, uint64_t* modify, uint64_t* last_access); // all time are both in seconds from 1970-01-01 00:00:00 int get_memory_usage(uint64_t* peak, uint64_t* now, uint64_t* phymem, uint32_t pid = -1); void print_memory_usage(const char* tips, bool to_log_file); int get_disk_space(const char* path, unsigned long long* total, unsigned long long* avail, unsigned long long* block); unsigned int get_page_size(unsigned int* map_unit = nullptr); // return logging file path if 'type' was LOG_TYPE_FILE std::string init_log(log_type type, log_level level = LOG_LEVEL_ALL, const char* fn_appendix = nullptr/*appendix to default log-file-name*/); void uninit(void); void log_info(const char* info, int level = LOG_LEVEL_ALL); void log_mem_info(const char* desc, const void* data, size_t bytes, int level = LOG_LEVEL_ALL); // log as 0x12345678 00 01 02 ... int get_log_type(void); int get_log_level(void); int copy_log_file_to(const char* dst); int clear_log_file(void); // unit convert ... int mm_2_pixel(float mm, int dpi = 200); float pixel_2_mm(int px, int dpi = 200); // bitmap header ... std::string bitmap_info_header(int pixel_w, int pixel_h, int bpp, int dpix, int dpiy = 0); // return BITMPINFOHEADER + pallete if need. dpiy same as dpix if was ZERO std::string bitmap_file_header(BITMAPINFOHEADER *lpbi); // return BITMAPFILEHEADER #if OS_WIN // Function: pump message recycle (GetMessageW) // // Parameters: hwnd - target window // // filter_min - the minimal message // // filter_max - the maximal message // // msghandler - custom message processor // MSG*, received message by GetMessage // bool*, return whether user handled the message, no TranslateMessage and DispatchMessage if this was true // // Return: whether quit the message recycle bool run_get_message(HWND hwnd, UINT filter_min = 0, UINT filter_max = 0, std::function msghandler = std::function()); #endif template std::string format_string(const char* fmt, Args ... args) { size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; std::unique_ptr buf(new char[size]); snprintf(buf.get(), size, fmt, args ...); return buf.get(); } template void to_log(int level, const char* fmt, Args ... args) { if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level) { size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; std::unique_ptr buf(new char[size]); snprintf(buf.get(), size, fmt, args ...); log_info(buf.get(), (log_level)level); } } template void to_log_with_api(bool(*is_enable)(int), void(*log_api)(const char*, int), int level, const char* fmt, Args ... args) { if (is_enable(level)) { size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; std::unique_ptr buf(new char[size]); snprintf(buf.get(), size, fmt, args ...); log_api(buf.get(), (log_level)level); } } template T swap_half(T v) { T mask = (1 << (sizeof(T) * 4)) - 1, h = v & mask; v >>= sizeof(T) * 4; v &= mask; h <<= sizeof(T) * 4; v |= h; return v; } class base64 { char base64_ind_[128]; char base64_char_[80]; char padding_char_; bool is_valid_base64_table(const char* table); bool initialize_base64_table(const char* table); public: base64(); ~base64(); public: bool set_base64_table(const char* table = nullptr); std::string encode(const char* data, size_t bytes, unsigned int line_bytes = -1, bool need_padding = true); std::string decode(const char* data, size_t bytes); }; }; #if OS_WIN struct _time_val { time_t tv_sec; /* Seconds. */ time_t tv_usec; /* Microseconds. */ }; typedef struct _time_val TIMEV; struct timezone { int tz_minuteswest; /* Minutes west of GMT. */ int tz_dsttime; /* Nonzero if DST is ever in effect. */ }; int gettimeofday(TIMEV* tv, struct timezone* tz); #else #include typedef struct timeval TIMEV; #endif // object life referer // // derived from 'refer' if your class used in multi-threads // #define MUTEX std::mutex #define LOCK_WITH_NAME(n, v) std::lock_guard n(v) #define SIMPLE_LOCK(v) LOCK_WITH_NAME(locker, v) class refer { volatile int32_t ref_; MUTEX mutex_; protected: refer(); virtual ~refer(); virtual void on_born(void); virtual void on_dead(void); public: virtual int32_t add_ref(void); virtual int32_t release(void); }; template class refer_guard { T *obj_ = nullptr; void clear(void) { if(obj_) obj_->release(); obj_ = nullptr; } public: refer_guard() {} refer_guard(T* obj) : obj_(obj) {} ~refer_guard() { clear(); } public: void reset(T* obj) { clear(); obj_ = obj; } T* operator->(void) { return obj_; } }; // time utility class chronograph { TIMEV bgn_; public: chronograph(); ~chronograph(); static bool now(TIMEV* tv); static bool now(uint64_t* seconds, uint64_t* u_seconds); static std::string now(bool with_ms = true/*whether with milliseconds*/); // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false public: uint64_t elapse_s(void); uint64_t elapse_ms(void); uint64_t elapse_us(void); void reset(void); }; // global info class global_info { public: global_info(); ~global_info(); public: static uint32_t page_size; static uint32_t page_map_size; static uint32_t cluster_size; static uint32_t stack_size; static uint32_t gray_pallete[256]; }; // event class platform_event : public refer { sem_t sem_; volatile bool waiting_; std::string dbg_info_; bool log_ = true; public: platform_event(const char* info = ""); ~platform_event(); public: bool try_wait(void); bool wait(unsigned timeout = USB_TIMEOUT_INFINITE/*ms*/); // USB_TIMEOUT_INFINITE is waiting unfinite, true when watied and false for wait timeout void trigger(void); void reset(void); bool is_waiting(void); void enable_log(bool enable); void set_debug_info(const char* info); }; // share memory class shared_memory : public refer { unsigned long long key_; void* obj_; bool first_; size_t bytes_; size_t len_; void init(void); void clear(void); char* get_buf(void); void release_buf(void* buf); #if !defined(WIN32) && !defined(_WIN64) static std::string get_proc_name_by_pid(pid_t pid); #endif public: shared_memory(unsigned long long key, size_t size = 1024); protected: ~shared_memory(); public: bool is_ok(void); bool is_first(void); std::string read(void); int write(const char* data, size_t len); }; template class safe_fifo { MUTEX lock_; std::deque que_; platform_event* wait_; public: safe_fifo(const char* who) : wait_(new platform_event(who && *who ? who : "fifo")) {} ~safe_fifo() { wait_->release(); } public: size_t save(const T& t, bool notify = false) { SIMPLE_LOCK(lock_); size_t cnt = que_.size(); que_.push_back(std::move(t)); if (notify) wait_->trigger(); return cnt + 1; } bool take(T& t, bool wait = false, uint32_t to_ms = USB_TIMEOUT_INFINITE) { if (wait && size() == 0) { if(!wait_->wait(to_ms)) return false; } { SIMPLE_LOCK(lock_); if (que_.size()) { t = std::move(que_.front()); que_.pop_front(); return true; } else { return false; } } } T front(void) { SIMPLE_LOCK(lock_); return que_.front(); } void pop_front(void) { SIMPLE_LOCK(lock_); que_.pop_front(); } size_t size(void) { SIMPLE_LOCK(lock_); return que_.size(); } void clear(void) { SIMPLE_LOCK(lock_); que_.clear(); wait_->reset(); } void trigger(void) { wait_->trigger(); } void enable_wait_log(bool enable) { wait_->enable_log(enable); } }; class safe_thread { typedef struct _safe_thrd { bool raw; std::string name; std::shared_ptr thread; // valid when !raw pthread_t thread_raw; // valid when raw }SAFETHRD; volatile bool run_ = true; MUTEX lock_; std::unique_ptr notify_thread_; std::vector threads_; safe_fifo excep_que_; std::function excep_handler_ = std::function(); void thread_worker(std::function func, std::string name, void* addr); void thread_notify_exception(void); typedef struct _cr_raw_thread { safe_thread* obj; std::function func; std::string name; void* addr; }CRAWTHRD, *LPCRAWTHRD; static #if OS_WIN DWORD WINAPI #else void* #endif raw_thread(void* lp); public: safe_thread(void); ~safe_thread(); public: void set_exception_handler(std::function on_exception = std::function()); int start(std::function f, std::size_t stack = 0, const char* thread_name = nullptr, void* addr = nullptr); int stop(const char* thread_name); }; // Purpose: safe file ensure the integrity of small data files // // Format: (uint16_t)raw data len + (uint32_t)checksum + raw data // // Flow: when save, write into file 'name.new' first, and rename to 'name' at last // when read, check 'name.new' first, and open name if '.new' not exists class safe_file { typedef struct _sf_head // ensure this struct size be multiple of size uint32_t { uint32_t chksum; uint32_t time; uint64_t len : 16; uint64_t iotimes : 48; }SFHEAD, *LPSFHEAD; const std::string tmp_appendix_ = ".new"; std::string path_; uint64_t iotimes_ = 0; uint32_t checksum(const void* data, size_t bytes, uint32_t prev = 0); std::string load_with_check(const char* file, bool ignore_lost); public: safe_file(const char* path); ~safe_file(); public: void set_file_path(const char* path); std::string load(void); int save(const void* data, uint16_t bytes); };