574 lines
14 KiB
C++
574 lines
14 KiB
C++
// utilities for platform ...
|
|
//
|
|
// Date: 2023-06-30
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include "plat_types.h"
|
|
#include <string>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <deque>
|
|
#include <functional>
|
|
|
|
#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]);
|
|
|
|
// Function: transfere from utf8 to utf16
|
|
//
|
|
// Parameters: in - [in]: head of utf8 beginning
|
|
//
|
|
// [out]: beginning of the next character if success or no changes on failure
|
|
// ch - to receive the character
|
|
//
|
|
// Return: 0 - success
|
|
// EINVAL - invalid utf8 string
|
|
int utf8_2_16(uint8_t* &in, uint16_t& ch);
|
|
int utf8_2_web(uint8_t* &in, std::string& web); // convert 'E5 A5 BD' to '\u597D'
|
|
|
|
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 set_log_file_max_size(int size = SIZE_MB(10));
|
|
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
|
|
int save_bitmap(const char* file, int w, int h, int bpp, int dpix, int dpiy, void* bits);
|
|
|
|
#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<bool(MSG*, bool*)> msghandler = std::function<bool(MSG*, bool*)>());
|
|
#endif
|
|
|
|
template<typename ... Args>
|
|
std::string format_string(const char* fmt, Args ... args)
|
|
{
|
|
size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
|
|
std::unique_ptr<char[]> buf(new char[size]);
|
|
|
|
snprintf(buf.get(), size, fmt, args ...);
|
|
|
|
return buf.get();
|
|
}
|
|
|
|
template<typename ... Args>
|
|
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<char[]> buf(new char[size]);
|
|
|
|
snprintf(buf.get(), size, fmt, args ...);
|
|
log_info(buf.get(), (log_level)level);
|
|
}
|
|
}
|
|
|
|
template<typename ... Args>
|
|
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<char[]> buf(new char[size]);
|
|
|
|
snprintf(buf.get(), size, fmt, args ...);
|
|
log_api(buf.get(), (log_level)level);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
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 <sys/time.h>
|
|
|
|
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<MUTEX> 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 T>
|
|
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_;
|
|
}
|
|
T* get(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 T>
|
|
class safe_fifo
|
|
{
|
|
MUTEX lock_;
|
|
std::deque<T> 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<std::thread> thread; // valid when !raw
|
|
pthread_t thread_raw; // valid when raw
|
|
}SAFETHRD;
|
|
volatile bool run_ = true;
|
|
MUTEX lock_;
|
|
std::unique_ptr<std::thread> notify_thread_;
|
|
std::vector<SAFETHRD> threads_;
|
|
safe_fifo<std::string> excep_que_;
|
|
std::function<void(const char*)> excep_handler_ = std::function<void(const char*)>();
|
|
|
|
void thread_worker(std::function<void(void)> func, std::string name, void* addr);
|
|
void thread_notify_exception(void);
|
|
|
|
typedef struct _cr_raw_thread
|
|
{
|
|
safe_thread* obj;
|
|
std::function<void(void)> 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<void(const char*)> on_exception = std::function<void(const char*)>());
|
|
int start(std::function<void(void)> 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);
|
|
};
|
|
|
|
#include <exception>
|
|
class exception_ex : public std::exception
|
|
{
|
|
std::string what_;
|
|
|
|
public:
|
|
exception_ex();
|
|
exception_ex(const char* msg);
|
|
exception_ex(const exception_ex& r);
|
|
~exception_ex();
|
|
|
|
public:
|
|
virtual const char*
|
|
what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT override;
|
|
void set_info(const char* info);
|
|
};
|