【C语言开源库】C语言必备实用第三方库Melon(包括数据结构算法)
簡介
Melon 是一個用于簡化開發的 C 框架庫。 它包括許多數據結構、算法、體系結構和許多其他有用的組件。 您可以根據自己的需求選擇一些組件或整個框架。 Melon 通過模塊開發支持多進程和多線程模型。
在 Windows 上,框架無法激活,但其他組件仍在工作。
melon GitHub地址:Melon
melon中文幫助文檔:中文手冊
安裝
Windows與UNIX環境的安裝并無差異,僅需要先行安裝并配置mingw、git bash以及make即可。
執行如下命令安裝Melon:
$ git clone https://github.com/Water-Melon/Melon.git $ ./configure $ make $ sudo make installMelon會同時生成動態庫與靜態庫。對于Linux系統,在使用Melon的動態庫時,需要將該庫的路徑加入到系統配置中:
$ sudo echo "/usr/local/melon/lib/" >> /etc/ld.so.conf $ sudo ldconfig默認情況下,UNIX中Melon會被安裝在/usr/local/melon下,Windows中會安裝于$HOME/libmelon中。
快速入門
Melon的使用并不繁瑣,大致步驟可分為:
組件使用示例
下面看一個使用內存池的例子:
#include <stdio.h> #include "mln_core.h" #include "mln_alloc.h" #include <mln_log.h>int main(int argc, char *argv[]) {char *ptr;mln_alloc_t *pool;struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {fprintf(stderr, "Melon init failed.\n");return -1;}pool = mln_alloc_init();ptr = mln_alloc_m(pool, 1024);mln_log(debug, "%X\n", ptr);mln_alloc_free(ptr);mln_alloc_destroy(pool);return 0; }其中,mln_core_init就是Melon庫的初始化函數,函數參數是一個結構體,該結構體用于傳入程序參數、全局變量初始化函數以及工作進程處理函數。由于這個例子中并不打算啟用多進程框架,也不需要初始化一些全局變量,因此兩個函數指針都被置空了。
隨后的代碼中:
- mln_alloc_init:用于創建內存池
- mln_alloc_m: 用于從內存池中分配一塊指定大小的內存
- mln_alloc_free:用于將內存池分配的內存釋放回池中
- mln_alloc_destroy:用于銷毀內存池并釋放資源
內存池相關的函數及結構定義都在mln_alloc.h中。
mln_log為Melon的日志輸出函數,本例中以十六進制輸出內存池分配的內存起始地址。
事實上,在GCC編譯器的情況下,上面的代碼還可以再簡化:
#include <stdio.h> #include "mln_alloc.h" #include "mln_log.h" #define MLN_SIMPLE_INIT #include "mln_core.h"int main(int argc, char *argv[]) {char *ptr;mln_alloc_t *pool;mln_simple_init = 1;pool = mln_alloc_init();ptr = mln_alloc_m(pool, 1024);mln_log(debug, "%X\n", ptr);mln_alloc_free(ptr);mln_alloc_destroy(pool);return 0; }這里的簡化方式是:將mln_core_init相關內容去掉,然后換成mln_simple_init = 1;。這種方式是利用了GCC的contructor特性做到的。事實上,這個特性使用有個前提:代碼不需要使用多進程多線程框架的情況下方可使用,因為無法設置global_init和worker_process回調函數進行處理。
隨后對代碼進行編譯,這里以UNIX系統為例:
$ cc -o test test.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelonWindows用戶也可以在git bash中執行:
$ gcc -o test test.c -I $HOME/libmelon/include/ -L $HOME/libmelon/lib/ -llibmelon -lWs2_32此時,還不可以啟動這個test程序,因為我們先要檢查Melon庫的配置文件,確保配置不會使得程序啟動多進程或者多線程框架(Windows用戶可以忽略此步驟)。
$ vim /usr/local/melon/conf/melon.conflog_level "none"; //user "root"; daemon off; core_file_size "unlimited"; //max_nofile 1024; worker_proc 1; thread_mode off; framework off; log_path "/usr/local/melon/logs/melon.log"; /** Configurations in the 'exec_proc' are the* processes which are customized by user.** Here is an example to show you how to* spawn a program.* keepalive "/tmp/a.out" ["arg1" "arg2" ...]* The command in this example is 'keepalive' that* indicate master process to supervise this* process. If process is killed, master process* would restart this program.* If you don't want master to restart it, you can* default "/tmp/a.out" ["arg1" "arg2" ...]** But you should know that there is another* arugment after the last argument you write here.* That is the file descriptor which is used to* communicate with master process.*/ exec_proc {// keepalive "/tmp/a"; } thread_exec { // restart "hello" "hello" "world"; // default "haha"; }這里我們需要確保framework這一項為off,因為本例不需要啟動框架功能。
此時,我們就可以執行這個例子了。
$ ./test此時可以看到類似如下輸出內容:
03/27/2021 04:36:26 GMT DEBUG: test.c:main:25: PID:24077 1e29950多進程框架使用示例
框架功能在Windows中,暫時不支持,本例將在UNIX系統中進行演示。
#include <stdio.h> #include "mln_core.h" #include "mln_log.h" #include "mln_event.h"char text[1024];static int global_init(void); static void worker_process(mln_event_t *ev); static void print_handler(mln_event_t *ev, void *data);int main(int argc, char *argv[]) {struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = global_init;cattr.worker_process = worker_process;return mln_core_init(&cattr); }static int global_init(void) {//global variable init functionint n = snprintf(text, sizeof(text)-1, "hello world\n");text[n] = 0;return 0; }static void worker_process(mln_event_t *ev) {//we can set event handler here//let's set a timermln_event_set_timer(ev, 1000, text, print_handler); }static void print_handler(mln_event_t *ev, void *data) {mln_log(debug, "%s\n", (char *)data);mln_event_set_timer(ev, 1000, data, print_handler); }在本例中,我們增加了global_init和worker_process的處理函數。global_init用于初始化一個全局的字符數組text。而worker_process則是子進程(或稱為工作進程)的處理函數。在工作進程處理函數中,我們使用到了Melon事件模塊的定時器函數,用于每1秒中(1000毫秒),調用一次print_handler函數將字符數組text中的內容進行日志輸出。
生成可執行程序:
$ cc -o hello hello.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelon接著,依舊是檢查配置文件,但這一次我們要確保framework須為on,thread_mode為off。這樣的配置表明,我們啟用Melon的框架功能,但不啟用多線程模式,那么Melon就會啟用多進程模式。然后,根據需要修改worker_proc的數量,例如:3。
log_level "none"; //user "root"; daemon off; core_file_size "unlimited"; //max_nofile 1024; worker_proc 3; thread_mode off; framework on; log_path "/usr/local/melon/logs/melon.log"; /** Configurations in the 'exec_proc' are the* processes which are customized by user.** Here is an example to show you how to* spawn a program.* keepalive "/tmp/a.out" ["arg1" "arg2" ...]* The command in this example is 'keepalive' that* indicate master process to supervise this* process. If process is killed, master process* would restart this program.* If you don't want master to restart it, you can* default "/tmp/a.out" ["arg1" "arg2" ...]** But you should know that there is another* arugment after the last argument you write here.* That is the file descriptor which is used to* communicate with master process.*/ exec_proc {// keepalive "/tmp/a"; } thread_exec { // restart "hello" "hello" "world"; // default "haha"; }此時,我們可以啟動程序了:
$ ./hello我們可以看到類似如下的輸出:
Start up worker process No.1 Start up worker process No.2 Start up worker process No.3 03/27/2021 04:53:44 GMT DEBUG: d.c:print_handler:39: PID:27620 hello world03/27/2021 04:53:44 GMT DEBUG: d.c:print_handler:39: PID:27621 hello world03/27/2021 04:53:44 GMT DEBUG: d.c:print_handler:39: PID:27622 hello world03/27/2021 04:53:45 GMT DEBUG: d.c:print_handler:39: PID:27620 hello world03/27/2021 04:53:45 GMT DEBUG: d.c:print_handler:39: PID:27621 hello world03/27/2021 04:53:45 GMT DEBUG: d.c:print_handler:39: PID:27622 hello world...到此,快速入門部分就告一段落了,下面我們會針對Melon所提供的每一個功能進行說明并給予示例。
初始化
頭文件
#include "mln_core.h"相關結構
struct mln_core_attr {int argc; //一般為main的argcchar **argv; //一般為main的argvmln_core_init_t global_init; //初始化回調函數,一般用于初始化全局變量,該回調會在配置加載完成后被調用mln_core_worker_process_t worker_process; //工作進程處理函數,我們將在多進程框架部分深入 };typedef int (*mln_core_init_t)(void); typedef void (*mln_core_worker_process_t)(mln_event_t *);一般情況下,在Melon的各個組件中,被用來作為初始化參數的結構體都以_attr結尾,且不會被typedef定義為某一類型。
函數
mln_core_init
int mln_core_init(struct mln_core_attr *attr);描述:該函數是Melon庫的整體初始化函數,會加載配置,并根據配置啟用或停用Melon中框架部分功能,以及其他額外功能。
返回值:成功則返回0,否則返回-1。
舉例:
int main(int argc, char *argv[]) {struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;return mln_core_init(&cattr); }配置
由于配置文件的加載是在Melon的初始化函數中被自動加載的,因此多數數據結構及函數是無需開發者關心的,這里僅給出開發者所需要的結構定義和函數聲明。
在Melon中,配置被劃分為兩層,每一層為一個域。最外層為main域,在main中允許出現以名稱{...}擴住的子域,然而子域中不允許再出現子域。但在程序中,main與子域其實為同級關系,而非包含關系。
每個域內是若干配置項,配置項由配置指令名與配置參數組成。
Melon的配置文件melon.conf會被安裝在安裝路徑下的conf子目錄中。
頭文件
#include "mln_conf.h"相關結構
typedef struct mln_conf_item_s mln_conf_item_t; struct mln_conf_item_s {enum {CONF_NONE = 0,CONF_STR,CONF_CHAR,CONF_BOOL,CONF_INT,CONF_FLOAT} type; //配置項參數類型union {mln_string_t *s;mln_s8_t c;mln_u8_t b;mln_sauto_t i;float f;} val; //配置項參數數據 };在Melon中,配置參數分為5種類型(忽略NONE),分別為:
- 字符串:以""擴住的字符集
- 字符:以''擴住的字符
- 布爾開關:on或off
- 整型:十進制整數
- 浮點型:十進制形式的實數
函數
mln_get_conf
mln_conf_t *mln_get_conf(void);描述:獲取全局配置結構。
返回值:mln_conf_t指針,若為NULL,則表明Melon并未進行初始化。
search
typedef mln_conf_domain_t *(*search_domain) (mln_conf_t *, char *); typedef mln_conf_cmd_t *(*search_cmd) (mln_conf_domain_t *, char *); typedef mln_conf_item_t *(*search_item) (mln_conf_cmd_t *, mln_u32_t);描述:
在Melon中,所有配置都會被加載進mln_conf_t結構中,隨后的獲取則是通過這三個search函數進行的。這三個search函數指針依次分別為mln_conf_t,mln_conf_domain_t以及mln_conf_cmd_t中的search成員。故在使用時,則是形如:
mln_conf_domain_t *d = conf->search(conf, "main");這三個search函數分別是:
- 從全局mln_conf_t中獲取某個域結構mln_conf_domain_t
- 從某個域(mln_conf_domain_t)中獲取對應的配置指令項mln_conf_cmd_t
- 從某個指令(mln_conf_cmd_t)中獲取某個參數mln_conf_item_t
其中,最后一個search的第二個參數為參數下標,且下標從1開始而非0。
在本篇末尾處將給出使用示例。
返回值:
正常情況下,只要所需配置在配置文件中,且配置被正常初始化,那么返回值則必不為NULL。
mln_conf_set_hook
mln_conf_hook_t *mln_conf_set_hook(reload_handler reload, void *data);typedef int (*reload_handler)(void *);描述:
Melon配置支持重載,重載的方式是設置重載回調函數,且允許設置多個。當執行重載時,新配置加載后,將調用這些回調函數。
回調函數的參數即為mln_conf_set_hook的第二個參數data。
返回值:
- mln_conf_set_hook:成功返回mln_conf_hook_t回調句柄,否則返回NULL。
- 回調函數:成功返回0,否則返回-1。
mln_conf_unset_hook
void mln_conf_unset_hook(mln_conf_hook_t *hook);描述:刪除已設置的配置重載回調函數。
返回值:無
mln_conf_get_ncmd
mln_u32_t mln_conf_get_cmdNum(mln_conf_t *cf, char *domain);描述:獲取某個域下配置項的個數。
返回值:配置項個數
mln_conf_get_cmds
void mln_conf_get_cmds(mln_conf_t *cf, char *domain, mln_conf_cmd_t **vector);描述:獲取某個域內的全部配置項,這些配置項將被存放在vector中,vector需要在調用前分配好,配置項個數可以通過mln_conf_get_ncmd預先獲取到。
返回值:無
mln_conf_get_narg
mln_u32_t mln_conf_get_argNum(mln_conf_cmd_t *cc);描述:獲取某個配置項的參數個數。
返回值:指令項參數個數
示例
#include <stdio.h> #include "mln_core.h"static int global_init(void) {mln_conf_t *cf;mln_conf_domain_t *d;mln_conf_cmd_t *c;mln_conf_item_t *i;cf = mln_get_conf();d = cf->search(cf, "main"); //如果main都不存在,那說明配追初始化有嚴重問題c = d->search(cf, "daemon"); //這里我們獲取daemon配置項if(c == NULL) {mln_log(error, "daemon not exist.\n");return -1;//出錯返回}i = c->search(c, 1); //daemon在配置文件中只有一個參數,配置參數的下標從1開始if (i == NULL) {mln_log(error, "Invalid daemon argument.\n");return -1;}if (i->type != CONF_BOOL) { //daemon 參數應該為布爾開關量mln_log(error, "Invalid type of daemon argument.\n");return -1;}mln_log(debug, "%u\n", i->val.b); //輸出布爾量的值return 0; }int main(int argc, char *argv[]) {struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = global_init;cattr.worker_process = NULL;return mln_core_init(&cattr); }在使用日志輸出時,請確保Melon配置及文件中日志文件的父目錄路徑是否存在。
日志
頭文件
#include "mln_log.h"函數
mln_log
enum log_level {none,report,debug,warn,error };void _mln_sys_log(enum log_level level, const char *file, const char *func, int line, char *msg, ...);#define mln_log(err_lv,msg,...) _mln_sys_log(err_lv, __FILE__, __FUNCTION__, __LINE__, msg, ## __VA_ARGS__)描述:
日常開發中,經常被用到的是宏mln_log,它會將輸出日志的文件、函數、行數都自行附加上。
日志分為5個等級,其級別由上至下依次增加。在配置文件中,有一配置項用于控制輸出日志的級別,低于該級別的日志將不會進行輸出:
log_level "none";默認情況下為最低級別none。
none與其他級別有所不同,該級別下,所有日志輸出的內容完全為msg的內容,而不帶有任何前綴信息,如:日期、進程號、文件名、函數名、行號等。
該函數需要在mln_core_init之后或其回調函數中使用,在mln_core_init之前使用將會出錯,因為此時日志相關組件尚未被初始化。
字符串
頭文件
mln_string.h主要數據結構
typedef struct {mln_u8ptr_t data; //數據存放的內存起始地址mln_u64_t len; //數據字節長度mln_uauto_t data_ref:1; //data是否是引用mln_uauto_t pool:1; //本結構是否是由內存池分配mln_uauto_t ref:30; //本結構所被引用的次數 } mln_string_t;函數/宏列表
mln_string
mln_string(str)描述:利用字符串常量str創建一個mln_string_t對象。用于定義mln_string_t變量的同時對其進行初始化。
返回值:mln_string_t類型結構體
舉例:
void foo() {mln_string_t s = mln_string("Hello"); }mln_string_set
mln_string_set(pstr, s)描述:用于將s這個字符串賦值給pstr這個mln_string_t指針所指向的結構。此時,data_ref成員會被置1。
返回值:無
舉例:
void foo() {char text[] = "hello";mln_string_t s;mln_string_set(&s, text); }mln_string_nset
mln_string_nset(pstr, s, n)描述:與mln_string_set功能一樣,只是pstr所指向的mln_string_t僅記錄了s的前n個字節。
返回值:無
舉例:
void foo() {char text[] = "hello world";mln_string_t s;mln_string_nset(&s, text, 5); //利用mln_log的%S進行輸出時,僅會輸出hello }mln_string_ref
mln_string_ref(pstr)描述:將pstr所指向的mln_string_t結構的ref成員累加1,用于直接引用pstr這個內存結構。在釋放內存時,引用計數大于1時是不會實際釋放內存的。
返回值:mln_string_t類型指針
void foo(mln_string_t s) {mln_string_t *ref = mln_string_ref(s); //此時ref與s的內存地址完全相同... }mln_string_free
mln_string_free(pstr)描述:釋放ptrs所指向的mln_string_t結構內存,若ref大于1則僅遞減引用計數,若data_ref為1,則不釋放data成員指向的內存,否則釋放data成員內存,隨后釋放pstr內存。釋放時,會根據pool成員判斷是釋放回內存池,還是返還malloc庫。
返回值:無
mln_string_new
mln_string_t *mln_string_new(const char *s);描述:根據字符串常量s創建字符串結構,此時新字符串結構及其數據部分內存均由malloc庫進行分配,并將s的內容拷貝進data成員中。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_pool_new
mln_string_t *mln_string_pool_new(mln_alloc_t *pool, const char *s);描述:與mln_string_new功能一致,僅內存是由pool所指向的內存池中分配而來。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_dup
mln_string_t *mln_string_dup(mln_string_t *str);描述:完全復制一份str,其內存均由malloc進行分配。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_pool_dup
mln_string_t *mln_string_pool_dup(mln_alloc_t *pool, mln_string_t *str);描述:與mln_string_dup功能一致,僅內存是從pool所指向的內存池中分配而來。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_ndup
mln_string_t *mln_string_ndup(mln_string_t *str, mln_s32_t size);描述:創建一個新字符串對象,并僅復制str中前size個字節數據。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_const_ndup
mln_string_t *mln_string_const_ndup(char *str, mln_s32_t size);描述:創建一個新字符串對象,并僅復制str中前size個字節數據。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_ref_dup
mln_string_t *mln_string_ref_dup(mln_string_t *str);描述:創建一個新的字符串結構,但結構中的data成員指向str中data成員所指向的地址,且新結構中data_ref會被置位。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_const_ref_dup
mln_string_t *mln_string_const_ref_dup(char *s);描述:創建一個新的字符串結構,但結構中的data成員指向s,且新結構中data_ref會被置位。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_strseqcmp
int mln_string_strseqcmp(mln_string_t *s1, mln_string_t *s2);描述:比較s1與s2的數據,如果短的一方剛好與長的一方的前面完全匹配,則長的一方大于短的一方。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
舉例:
int main(void) {mln_string_t s1 = mln_string("abcd");mln_string_t s2 = mln_string("abcdefg");printf("%d", mln_string_strseqcmp(&s1, &s2)); //-1return 0; }mln_string_strcmp
int mln_string_strcmp(mln_string_t *s1, mln_string_t *s2);描述:比較s1與s2中數據的大小。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_const_strcmp
int mln_string_const_strcmp(mln_string_t *s1, char *s2);描述:比較s1所記錄的數據與s2的大小。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_strncmp
int mln_string_strncmp(mln_string_t *s1, mln_string_t *s2, mln_u32_t n);描述:比較s1與s2的前n個字節的大小。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_const_strncmp
int mln_string_const_strncmp(mln_string_t *s1, char *s2, mln_u32_t n);描述:比較s1所記錄的數據與s2的前n個字節的大小。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_strcasecmp
int mln_string_strcasecmp(mln_string_t *s1, mln_string_t *s2);描述:比較s1與s2數據的大小,且忽略大小寫。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_const_strcasecmp
int mln_string_const_strcasecmp(mln_string_t *s1, char *s2);描述:比較s1所記錄的數據與s2的大小,且忽略大小寫。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_const_strncasecmp
int mln_string_const_strncasecmp(mln_string_t *s1, char *s2, mln_u32_t n);描述:比較s1所記錄的數據與s2的前n個字節的大小,且忽略大小寫。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_strncasecmp
int mln_string_strncasecmp(mln_string_t *s1, mln_string_t *s2, mln_u32_t n);描述:比較s1與s2所記錄數據的前n個字節的大小,且忽略大小寫。
返回值:
- -1 - s1比s2小
- 1 - s1比s2大
- 0 - 二者相同
mln_string_strstr
char *mln_string_strstr(mln_string_t *text, mln_string_t *pattern);描述:匹配text所記錄的數據中與pattern中數據一樣的起始地址。
返回值:若匹配成功,則返回text的data成員所指向地址中的對應地址;否則返回NULL。
mln_string_const_strstr
char *mln_string_const_strstr(mln_string_t *text, char *pattern);描述:匹配text所記錄的數據中與pattern一樣的起始地址。
返回值:若匹配成功,則返回text的data成員所指向地址中的對應地址;否則返回NULL。
mln_string_new_strstr
mln_string_t *mln_string_new_strstr(mln_string_t *text, mln_string_t *pattern);描述:與mln_string_strstr功能一致,但返回的是由mln_string_t結構包裝后的字符串。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_new_const_strstr
mln_string_t *mln_string_new_const_strstr(mln_string_t *text, char *pattern);描述:與mln_string_const_strstr功能一致,但返回的是由mln_string_t結構包裝后的字符串。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_kmp
char *mln_string_kmp(mln_string_t *text, mln_string_t *pattern);描述:與mln_string_strstr功能一致,但是是由KMP算法實現的。KMP算法適用場景是,text中有較多與pattern前綴相同的字符串的情況。例如: text中包含aaaaaaaaaabc,pattern中包含ab,此時,KMP算法性能將高于樸素算法。
返回值:若匹配成功,則返回text的data成員所指向地址中的對應地址;否則返回NULL。
mln_string_const_kmp
char *mln_string_const_kmp(mln_string_t *text, char *pattern);描述:與mln_string_kmp功能一致,但pattern為字符指針類型。
返回值:若匹配成功,則返回text的data成員所指向地址中的對應地址;否則返回NULL。
mln_string_new_kmp
mln_string_t *mln_string_new_kmp(mln_string_t *text, mln_string_t *pattern);描述:與mln_string_kmp功能一致,但返回的是由mln_string_t結構包裝后的數據。
返回值:成功則返回mln_string_t指針,失敗則返回NULL。
mln_string_new_const_kmp
mln_string_t *mln_string_new_const_kmp(mln_string_t *text, char *pattern);描述:與mln_string_const_kmp功能一致,但返回的是由mln_string_t結構包裝后的數據。
返回值:成功則返回mln_string_t指針,失敗則返回NULL。
mln_string_slice
mln_string_t *mln_string_slice(mln_string_t *s, const char *sep_array/*ended by \0*/);描述:seq_array是一個字符數組且以0結尾,該數組的每一個字符都是一個分隔標志。函數會掃描s的數據部分,當數據中遇到seq_array中的任意一個字符時都會被進行分割,連續遇到多個時僅分割一次,且分割后,分隔符不會出現在被分割后的字符串中。
返回值:成功則返回mln_string_t數組,否則返回NULL。數組的最后一個元素的len為0,且data為NULL。
舉例:
int main(void) {mln_string_t s = mln_string("abc-def-=ghi");mln_string_t *str, *arr = mln_string_slice(&s, "-=");for (str = arr; str->data != NULL; ++str) {mln_log(debug, "%S", str);}mln_string_slice_free(arr);return 0; }mln_string_slice_free
void mln_string_slice_free(mln_string_t *array);描述:釋放由mln_string_slice函數創建的mln_string_t數組。
返回值:無
mln_string_strcat
mln_string_t *mln_string_strcat(mln_string_t *s1, mln_string_t *s2);描述:創建一個新的mln_string_t結構,其數據為s1和s2依此順序拼接后的結果。
返回值:成功則返回mln_string_t指針,否則返回NULL。
mln_string_pool_strcat
mln_string_t *mln_string_pool_strcat(mln_alloc_t *pool, mln_string_t *s1, mln_string_t *s2);描述:與mln_string_strcat功能一致,僅新的結構所使用內存由pool指向的內存池分配。
返回值:成功則返回mln_string_t指針,否則返回NULL。
雙向鏈表
頭文件
#include "mln_defs.h"函數/宏
MLN_CHAIN_FUNC_DECLARE
MLN_CHAIN_FUNC_DECLARE(prefix,type,ret_attr,func_attr);描述:本宏用于對雙向鏈表的添加操作和刪除操作函數進行聲明,其中:
- prefix:為兩個函數名的前綴,這是為了允許在一個源文件內為多個雙向鏈表進行函數聲明。
- type:鏈表節點的類型
- ret_attr:兩個函數的操作域類型和返回值類型
- func_attr:對函數參數的約束(僅限于Linux中),若無則留空即可
MLN_CHAIN_FUNC_DEFINE
MLN_CHAIN_FUNC_DEFINE(prefix,type,ret_attr,prev_ptr,next_ptr);ret_attr prefix##_chain_add(type **head, type **tail, type *node); ret_attr prefix##_chain_del(type **head, type **tail, type *node);描述:本宏用于定義雙向鏈表的添加和刪除操作函數,其中:
- prefix:為兩個函數名的前綴,這是為了允許在一個源文件內為多個雙向鏈表進行函數聲明。
- type:鏈表節點的類型
- ret_attr:兩個函數的操作域類型和返回值類型
- prev_ptr:鏈表節點中指向前一節點的指針名
- next_ptr:鏈表節點中指向后一節點的指針名
chain_add和chain_del分別為添加和刪除節點函數,兩個函數的參數為:
- head:二級指針,用于在操作函數內對頭指針自動修改
- tail:二級指針,用于在操作函數內對尾指針自動修改
- node:被加入的節點指針,其前后指向的指針可能會被修改
示例
#include <stdio.h> #include <stdlib.h> #include "mln_defs.h"typedef struct chain_s {int val;struct chain_s *prev;struct chain_s *next; } chain_t;MLN_CHAIN_FUNC_DECLARE(test, chain_t, static inline void, ); MLN_CHAIN_FUNC_DEFINE(test, chain_t, static inline void, prev, next);int main(void) {int i;chain_t *head = NULL, *tail = NULL, *c;for (i = 0; i < 10; ++i) {c = (chain_t *)malloc(sizeof(chain_t));if (c == NULL) {fprintf(stderr, "malloc failed.\n");return -1;}c->val = i;c->prev = c->next = NULL;test_chain_add(&head, &tail, c);}for (c = head; c != NULL; c = c->next) {printf("%d\n", c->val);}return 0; }棧
頭文件
#include "mln_stack.h"函數/宏
mln_stack_init
mln_stack_t *mln_stack_init(struct mln_stack_attr *attr);struct mln_stack_attr {stack_free free_handler;//棧節點數據釋放函數stack_copy copy_handler;//棧節點數據復制函數mln_u32_t cache:1;//是否緩存棧節點結構 };typedef void (*stack_free)(void *); typedef void *(*stack_copy)(void *, void *);描述:
初始化棧結構。
free_handler:是入棧數據的釋放函數,由于入棧數據可能為自定義數據結構,因此若需釋放,可對此進行設置否則置NULL。
copy_handler:復制棧節點數據。
cache:是否緩存全部棧節點結構內存以提升效率(非用戶數據)。
stack_free的參數為用戶自定義數據的數據結構指針。
stack_copy的參數分別為:被復制的棧節點數據的數據結構指針 和 mln_stack_dup函數的第二個參數(即用戶自定義數據),這個回調函數僅在mln_stack_dup函數中被調用。
返回值:成功則返回棧指針,否則為NULL
mln_stack_destroy
void mln_stack_destroy(mln_stack_t *st);描述:銷毀棧結構,并釋放棧節點內數據資源。
返回值:無
mln_stack_push
int mln_stack_push(mln_stack_t *st, void *data);描述:將數據data壓入棧st中。
返回值:成功返回0,否則返回-1
mln_stack_pop
void *mln_stack_pop(mln_stack_t *st);描述:將棧st的棧頂元素數據彈出。
返回值:若棧內無元素則為NULL,否則為棧節點內的數據指針
mln_stack_empty
mln_stack_empty(s)描述:判斷棧是否為空。
返回值:空為非0,否則為0
mln_stack_top
mln_stack_top(st)描述:獲取棧頂元素數據。
返回值:若棧st為空則返回NULL,否則為棧頂節點內的數據指針
mln_stack_dup
mln_stack_t *mln_stack_dup(mln_stack_t *st, void *udata);描述:完全復制棧st。udata為用戶提供的額外數據。
返回值:若成功則返回新棧指針,否則返回NULL
mln_stack_scan_all
int mln_stack_scan_all(mln_stack_t *st, stack_scan scanner, void *data);typedef int (*stack_scan)(void *, void *);描述:
從棧頂向棧底遍歷棧st的每一個棧內元素數據。scanner為數據訪問函數,data為遍歷時的額外用戶數據。
stack_scan有兩個參數,分別為:棧節點內數據指針 和 data參數。
返回值:
- mln_stack_scan_all:全部遍歷完則返回0,否則返回-1
- stack_scan:若想中斷遍歷則返回小于0的值,否則返回值大于等于0
隊列
頭文件
#include "mln_queue.h"函數/宏
mln_queue_init
mln_queue_t *mln_queue_init(struct mln_queue_attr *attr);struct mln_queue_attr {mln_uauto_t qlen; //隊列長度queue_free free_handler; //隊列節點數據的釋放函數 }; typedef void (*queue_free)(void *);描述:創建隊列。
本隊列為固定長度隊列,因此attr.qlen就是隊列的長度。free_handler為釋放函數,用于釋放隊列內每個成員中的數據。若不需要釋放則置NULL即可。
釋放函數的參數即為隊列每個成員的數據結構指針。
返回值:成功則返回mln_queue_t類型的隊列指針,失敗則返回NULL
mln_queue_destroy
void mln_queue_destroy(mln_queue_t *q);描述:銷毀隊列。
隊列銷毀時,會根據free_handler的設置而自動釋放隊列成員的數據。
返回值:無
mln_queue_append
int mln_queue_append(mln_queue_t *q, void *data);描述:將數據data追加進隊列q的末尾。
返回值:若隊列已滿則返回-1,成功返回0
mln_queue_get
void *mln_queue_get(mln_queue_t *q);描述:獲取隊首成員的數據。
返回值:成功則返回數據指針,若隊列為空則返回NULL
mln_queue_remove
void mln_queue_remove(mln_queue_t *q);描述:刪除隊首元素,但不釋放資源。
返回值:無
mln_queue_search
void *mln_queue_search(mln_queue_t *q, mln_uauto_t index);描述:查找并返回從隊列q隊首開始的第index成員的數據,下標從0開始。
返回值:成功則返回數據指針,否則為NULL
mln_queue_free_index
void mln_queue_free_index(mln_queue_t *q, mln_uauto_t index);描述:釋放隊列q內指定下標index的成員,并根據free_handler釋放其數據。
返回值:無
mln_queue_scan_all
int mln_queue_scan_all(mln_queue_t *q, queue_scan scan_handler, void *udata);typedef int (*queue_scan)(void *, void *);描述:遍歷每一個隊列成員。
udata為輔助遍歷的自定義結構指針,若不需要可置NULL。
scan_handler的兩個參數分別為:成員數據,udata。
返回值:遍歷完成返回0,被中斷則返回-1
mln_queue_empty
mln_queue_empty(q)描述:判斷隊列q是否為空隊列。
返回值:空則為非0,否則為0
mln_queue_full
mln_queue_full(q)描述:判斷隊列是否已滿。
返回值:滿則為非0,否則為0
mln_queue_length
mln_queue_length(q)描述:獲取隊列q的總長度。
返回值:無符號整型長度值
mln_queue_element
mln_queue_element(q)描述:獲取隊列q中當前的成員數量。
返回值:無符號整型數量值
示例
#include <stdio.h> #include <stdlib.h> #include "mln_core.h" #include "mln_log.h" #include "mln_queue.h"int main(int argc, char *argv[]) {int i = 10;mln_queue_t *q;struct mln_queue_attr qattr;struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {fprintf(stderr, "init failed\n");return -1;}qattr.qlen = 10;qattr.free_handler = NULL;q = mln_queue_init(&qattr);if (q == NULL) {mln_log(error, "queue init failed.\n");return -1;}mln_queue_append(q, &i);mln_log(debug, "%d\n", *(int *)mln_queue_get(q));mln_queue_destroy(q);return 0; }內存池
Melon中,內存池分為兩類:
- 堆內存
- 共享內存
其中,共享內存內存池只允許主子進程之間共享數據(兄弟進程之間也共享)。即使用時,由主進程創建共享內存內存池,然后創建子進程。
頭文件
#include "mln_alloc.h"函數
mln_alloc_init
mln_alloc_t *mln_alloc_init(void);描述:創建堆內存內存池。
返回值:成功則返回內存池結構指針,否則返回NULL
mln_alloc_shm_init
mln_alloc_t *mln_alloc_shm_init(mln_size_t size);描述:創建共享內存內存池。本池建立時需要給出池大小size(單位字節),一旦創建完畢后則后續無法再擴大。
返回值:成功則返回內存池結構指針,否則返回NULL
mln_alloc_destroy
void mln_alloc_destroy(mln_alloc_t *pool);描述:銷毀內存池。銷毀操作會將內存池中管理的所有內存進行統一釋放。
返回值:無
mln_alloc_m
void *mln_alloc_m(mln_alloc_t *pool, mln_size_t size);描述:從內存池pool中分配一個size大小的內存。如果內存池是共享內存內存池,則會從共享內存中進行分配,否則從堆內存中進行分配。
返回值:成功則返回內存起始地址,否則返回NULL
mln_alloc_c
void *mln_alloc_c(mln_alloc_t *pool, mln_size_t size);描述:從內存池pool中分配一個size大小的內存,且該內存會被清零。
返回值:成功則返回內存起始地址,否則返回NULL
mln_alloc_re
void *mln_alloc_re(mln_alloc_t *pool, void *ptr, mln_size_t size);描述:從內存池pool中分配一個size大小的內存,并將ptr指向的內存中的數據拷貝到新的內存中。
ptr必須為內存池分配的內存起始地址。若size為0,ptr指向的內存會被釋放。
返回值:成功則返回內存起始地址,否則返回NULL
mln_alloc_free
void mln_alloc_free(void *ptr);描述:釋放ptr指向的內存。注意:ptr必須為分配函數返回的地址,而不可以是分配的內存中某一個位置。
返回值:無
mln_alloc_shm_rdlock
int mln_alloc_shm_rdlock(mln_alloc_t *pool);描述:讀鎖定。本函數會等待直到鎖資源可用,并將之鎖定。
本函數及后續鎖相關函數均用于共享內存內存池。
出于對讀多寫少的場景考慮,給共享內存配備的是讀寫鎖,而非互斥鎖。
返回值:成功返回0,否則返回非0
mln_alloc_shm_tryrdlock
int mln_alloc_shm_tryrdlock(mln_alloc_t *pool);描述:嘗試讀鎖定。本函數不會掛起等待鎖資源可用。
返回值:成功返回0,否則返回非0
mln_alloc_shm_wrlock
int mln_alloc_shm_wrlock(mln_alloc_t *pool);描述:寫鎖定。本函數會等待直到鎖資源可用,并將之鎖定。
返回值:成功返回0,否則返回非0
mln_alloc_shm_trywrlock
int mln_alloc_shm_trywrlock(mln_alloc_t *pool);描述:嘗試寫鎖定。本函數不會掛起等待鎖資源可用。
返回值:成功返回0,否則返回非0
mln_alloc_shm_unlock
int mln_alloc_shm_unlock(mln_alloc_t *pool);描述:解除鎖定。
返回值:成功返回0,否則返回非0
示例
#include <stdio.h> #include <stdlib.h> #include "mln_core.h" #include "mln_log.h" #include "mln_alloc.h"int main(int argc, char *argv[]) {char *p;mln_alloc_t *pool;struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {fprintf(stderr, "init failed\n");return -1;}pool = mln_alloc_init();if (pool == NULL) {mln_log(error, "pool init failed\n");return -1;}p = (char *)mln_alloc_m(pool, 6);if (p == NULL) {mln_log(error, "alloc failed\n");return -1;}memcpy(p, "hello", 5);p[5] = 0;mln_log(debug, "%s\n", p);mln_alloc_free(p);return 0; }線程池
在Melon中支持兩種多線程模式,線程池是其中一種,另一種請參見后續的多線程框架文章。
注意:在每個進程中僅允許存在一個線程池。
頭文件
#include "mln_thread_pool.h"函數
mln_thread_pool_run
int mln_thread_pool_run(struct mln_thread_pool_attr *tpattr);struct mln_thread_pool_attr {void *main_data;mln_thread_process child_process_handler;mln_thread_process main_process_handler;mln_thread_dataFree free_handler;mln_u64_t cond_timeout; /*ms*/mln_u32_t max;mln_u32_t concurrency; }; typedef int (*mln_thread_process)(void *); typedef void (*mln_thread_dataFree)(void *);描述:創建并運行內存池。
線程池由主線程進行管理和做一部分處理后下發任務,子線程組則接受任務進行處理。
初始狀態下,是不存在子線程的,當有任務需要下發時會自動創建子線程。當任務處理完后,子線程會延遲釋放,避免頻繁分配釋放資源。
其中參數結構體的每個成員含義如下:
- main_data 為主線程的用戶自定義數據。
- child_process_handler 每個子線程的處理函數,該函數有一個參數為主線程下發任務時給出的數據結構指針,返回值為0表示處理正常,非0表示處理異常,異常時會有日志輸出。
- main_process_handler 主線程的處理函數,該函數有一個參數為main_data,返回值為0表示處理正常,非0表示處理異常,異常時會有日志輸出。一般情況下,主線程處理函數不應隨意自行返回,一旦返回代表線程池處理結束,線程池會被銷毀。
- free_handler 為資源釋放函數。其資源為主線程下發給子線程的數據結構指針所指向的內容。
- cond_timeout為閑置子線程回收定時器,單位為毫秒。當子線程無任務處理,且等待時間超過該定時器時長后,會自行退出。
- max線程池允許的最大子線程數量。
- concurrency用于pthread_setconcurrency設置并行級別參考值,但部分系統并為實現該功能,因此不應該過多依賴該值。在Linux下,該值設為零表示交由本系統實現自行確定并行度。
返回值:本函數返回值與主線程處理函數的返回值保持一致
mln_thread_pool_addResource
int mln_thread_pool_addResource(void *data);描述:將資源data放入到資源池中。本函數僅應由主線程調用,用于主線程向子線程下發任務所用。
返回值:成功則返回0,否則返回非0
mln_thread_quit
void mln_thread_quit(void);描述:本函數用于告知線程池,關閉并銷毀線程池。
返回值:無
mln_thread_ResourceInfo
void mln_thread_ResourceInfo(struct mln_thread_pool_info *info);struct mln_thread_pool_info {mln_u32_t max_num;mln_u32_t idle_num;mln_u32_t cur_num;mln_size_t res_num; };描述:獲取當前線程池信息。信息會寫入參數結構體中,結構體每個參數含義如下:
- max_num:線程池最大子線程數量
- idle_num:當前閑置子線程數量
- cur_num:當前子線程數量(包含閑置和工作中的子線程)
- res_num:當前尚未被處理的資源數量
返回值:無
示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "mln_core.h" #include "mln_thread_pool.h" #include "mln_log.h"static int main_process_handler(void *data); static int child_process_handler(void *data); static void free_handler(void *data);int main(int argc, char *argv[]) {struct mln_core_attr cattr;struct mln_thread_pool_attr tpattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {return -1;}tpattr.dataForMain = NULL;tpattr.child_process_handler = child_process_handler;tpattr.main_process_handler = main_process_handler;tpattr.free_handler = free_handler;tpattr.condTimeout = 10;tpattr.max = 10;tpattr.concurrency = 10;return mln_thread_pool_run(&tpattr); }static int child_process_handler(void *data) {mln_log(none, "%s\n", (char *)data);return 0; }static int main_process_handler(void *data) {int n;char *text;while (1) {if ((text = (char *)malloc(16)) == NULL) {return -1;}n = snprintf(text, 15, "hello world");text[n] = 0;mln_thread_pool_addResource(text);usleep(1000);} }static void free_handler(void *data) {free(data); }JSON
頭文件
#include "mln_json.h"函數/宏
mln_json_new
mln_json_t *mln_json_new(void);描述:新建json節點,用于生成json字符串之用。
返回值:成功則返回mln_json_t指針,否則返回NULL
mln_json_parse
mln_json_t *mln_json_parse(mln_string_t *jstr);描述:將JSON字符串jstr解析成數據結構。
返回值:成功則返回mln_json_t指針,否則返回NULL
mln_json_free
void mln_json_free(void *json);描述:釋放mln_json_t類型的json節點內存。
返回值:無
mln_json_dump
void mln_json_dump(mln_json_t *j, int n_space, char *prefix);描述:將json節點j的詳細信息輸出到標準輸出。n_space表示當前縮進空格數,prefix為輸出內容的前綴。
返回值:無
mln_json_generate
mln_string_t *mln_json_generate(mln_json_t *j);描述:由mln_json_t節點結構生成JSON字符串。
返回值:成功返回mln_string_t字符串指針,否則返回NULL
mln_json_search_value
mln_json_t *mln_json_search_value(mln_json_t *j, mln_string_t *key);描述:從節點j中搜索key為key的value內容。此時,j必須為對象類型(有key: value對的字典)。
返回值:成功則返回mln_json_t類型的value,否則返回NULL
mln_json_search_element
mln_json_t *mln_json_search_element(mln_json_t *j, mln_uauto_t index);描述:從節點j中搜索下標為index的元素內容。此時,j必須為數組類型。
返回值:成功則返回mln_json_t類型的元素節點,否則返回NULL
mln_json_get_array_length
mln_uauto_t mln_json_get_array_length(mln_json_t *j);描述:獲取數組的長度。此時j必須為數組類型。
返回值:數組長度
mln_json_update_obj
int mln_json_update_obj(mln_json_t *j, mln_json_t *key, mln_json_t *val);描述:將key與val對添加到j JSON節點中。此時,j需為對象類型。若key已經存在,則將原本value替換為val。
返回值:成功則返回0,否則返回-1
mln_json_add_element
int mln_json_add_element(mln_json_t *j, mln_json_t *value);描述:將value加入到數組類型的JSON結構j中。
返回值:成功則返回0,否則返回-1
mln_json_update_element
int mln_json_update_element(mln_json_t *j, mln_json_t *value, mln_uauto_t index);描述:將value更新到數組類型JSON結構j的下標為index的位置上。
返回值:成功則返回0,否則返回-1
mln_json_reset
void mln_json_reset(mln_json_t *j);描述:重置JSON節點j數據結構,將其內存進行釋放。
返回值:無
mln_json_remove_object
mln_json_t *mln_json_remove_object(mln_json_t *j, mln_string_t *key);描述:將key值為key的鍵值對從對象類型的JSON結構j中刪除,并將相應value返回。
返回值:存在則返回對應value部分的JSON節點,否則返回NULL
mln_json_remove_element
mln_json_t *mln_json_remove_element(mln_json_t *j, mln_uauto_t index);描述:將下標為index的元素從數組類型JSON節點上刪除并返回。
返回值:存在則返回元素指針,否則返回NULL
is_type
M_JSON_IS_OBJECT(json) M_JSON_IS_ARRAY(json) M_JSON_IS_STRING(json) M_JSON_IS_NUMBER(json) M_JSON_IS_TRUE(json) M_JSON_IS_FALSE(json) M_JSON_IS_NULL(json) M_JSON_IS_NONE(json)描述:判斷mln_json_t結構的json類型,依次分別為:對象、數組、字符串、數字、布爾真、布爾假、NULL、無類型。
返回值:滿足條件返回非0,否則返回0
set_type
M_JSON_SET_TYPE_NONE(json) M_JSON_SET_TYPE_OBJECT(json) M_JSON_SET_TYPE_ARRAY(json) M_JSON_SET_TYPE_STRING(json) M_JSON_SET_TYPE_NUMBER(json) M_JSON_SET_TYPE_TRUE(json) M_JSON_SET_TYPE_FALSE(json) M_JSON_SET_TYPE_NULL(json)描述:給mln_json_t類型的json節點設置類型,依次分別為:無類型、對象、數組、字符串、數字、布爾真、布爾假、NULL。
返回值:無
get_data
M_JSON_GET_DATA_OBJECT(json) M_JSON_GET_DATA_ARRAY(json) M_JSON_GET_DATA_STRING(json) M_JSON_GET_DATA_NUMBER(json) M_JSON_GET_DATA_TRUE(json) M_JSON_GET_DATA_FALSE(json) M_JSON_GET_DATA_NULL(json)描述:獲取mln_json_t類型的json節點中對應類型的數據部分。類型依次為:對象、數組、字符串、數字、布爾真、布爾假、NULL。
返回值:
- 對象類型為mln_hash_t類型指針
- 數組類型為mln_rbtree_t類型指針
- 字符串類型為mln_string_t類型指針
- 數字類型為double類型值
- 布爾真為mln_u8_t類型值
- 布爾假為mln_u8_t類型值
- NULL類型為mln_u8ptr_t類型的NULL值
set_data
M_JSON_SET_DATA_STRING(json,str) M_JSON_SET_DATA_NUMBER(json,num) M_JSON_SET_DATA_TRUE(json) M_JSON_SET_DATA_FALSE(json) M_JSON_SET_DATA_NULL(json)描述:給不同類型的JSON節點json設置數據值。對象和數組類型分別使用哈希表和紅黑樹函數進行操作,其余類型用上述宏進行設置。
注意:這里設置的字符串必須是從內存池或堆中分配的內存,棧中內存會出現段錯誤,因為賦值時不會在宏內自動復制一份而是直接使用。
返回值:無
M_JSON_SET_INDEX
M_JSON_SET_INDEX(json,i)描述:設置mln_json_t類型節點json的下標為index。該宏用于生成JSON字符串中數組的部分。
返回值:無
示例
#include <stdio.h> #include <stdlib.h> #include "mln_core.h" #include "mln_log.h" #include "mln_string.h" #include "mln_json.h"int main(int argc, char *argv[]) {mln_json_t *j = NULL, *key = NULL, *val = NULL;mln_string_t s1 = mln_string("name");mln_string_t s2 = mln_string("Tom");mln_string_t *res;struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {fprintf(stderr, "init failed\n");return -1;}key = mln_json_new();if (key == NULL) {mln_log(error, "init key failed\n");goto err;}M_JSON_SET_TYPE_STRING(key);M_JSON_SET_DATA_STRING(key, mln_string_dup(&s1));//注意,一定是要自行分配內存,不可直接使用棧中內存val = mln_json_new();if (val == NULL) {mln_log(error, "init val failed\n");goto err;}M_JSON_SET_TYPE_STRING(val);M_JSON_SET_DATA_STRING(val, mln_string_dup(&s2));//注意,一定是要自行分配內存,不可直接使用棧中內存j = mln_json_new();if (j == NULL) {mln_log(error, "init object failed\n");goto err;}if (mln_json_update_obj(j, key, val) < 0) {mln_log(error, "update object failed\n");goto err;}key = val = NULL;res = mln_json_generate(j);mln_json_free(j);if (res == NULL) {mln_log(error, "generate failed\n");goto err;}mln_log(debug, "%S\n", res);j = mln_json_parse(res);mln_string_free(res);mln_json_dump(j, 0, NULL);mln_json_free(j);return 0;err:if (j != NULL) mln_json_free(j);if (key != NULL) mln_json_free(key);if (val != NULL) mln_json_free(val);return -1; }HTTP
頭文件
#include "mln_http.h"函數/宏
mln_http_init
mln_http_t *mln_http_init(mln_tcp_conn_t *connection, void *data, mln_http_handler body_handler);typedef int (*mln_http_handler)(mln_http_t *, mln_chain_t **, mln_chain_t **);描述:創建并初始化mln_http_t結構。connection是TCP結構,內含TCP套接字。data為體處理函數的用戶自定義數據部分,用于輔助請求或響應體的處理。body_handler是體處理函數,該函數會在每次調用mln_http_parse或mln_http_generate函數時被調用。體處理函數有三個參數,分別為:http結構,用于解析或生成HTTP報文的雙向鏈表的頭和尾節點。
返回值:成功則返回mln_http_t結構指針,否則返回NULL
mln_http_destroy
void mln_http_destroy(mln_http_t *http);描述:銷毀http結構并釋放資源。
返回值:無
mln_http_reset
void mln_http_reset(mln_http_t *http);描述:重置http結構,但不會將結構釋放,可用于下一次處理。
返回值:無
mln_http_parse
int mln_http_parse(mln_http_t *http, mln_chain_t **in);描述:用于解析HTTP報文,并將解析的結果寫入http中。
返回值:
- M_HTTP_RET_DONE 解析完成
- M_HTTP_RET_OK 解析未完成但未出錯,繼續傳入新的數據使解析完成
- M_HTTP_RET_ERROR 解析失敗
mln_http_generate
int mln_http_generate(mln_http_t *http, mln_chain_t **out_head, mln_chain_t **out_tail);描述:將http中HTTP相關信息生成HTTP報文。報文可能不會一次性生成完全,因此可以多次調用。已生成的報文將會存放在out_head和out_tail指定的雙向鏈表中。
返回值:
- M_HTTP_RET_DONE 生成完成
- M_HTTP_RET_OK 生成未完成但未出錯
- M_HTTP_RET_ERROR 生成失敗
mln_http_set_field
int mln_http_set_field(mln_http_t *http, mln_string_t *key, mln_string_t *val);描述:設置HTTP頭字段。若頭字段key存在,則會將val替換原有值。
返回值:
- M_HTTP_RET_OK 處理成功
- M_HTTP_RET_ERROR處理失敗
mln_http_get_field
mln_string_t *mln_http_get_field(mln_http_t *http, mln_string_t *key);描述:獲取HTTP頭字段中鍵為key的值。
返回值:成功則返回值字符串結構指針,否則返回NULL
mln_http_field_iterator
mln_string_t *mln_http_field_iterator(mln_http_t *http, mln_string_t *key);描述:每次返回一個鍵為key的頭字段值(即假設存在多個相同鍵名的頭字段)。
返回值:成功則返回值字符串結構指針,否則返回NULL
mln_http_drop_field
void mln_http_drop_field(mln_http_t *http, mln_string_t *key);描述:移除頭字段key及其值。
返回值:無
mln_http_dump
void mln_http_dump(mln_http_t *http);描述:將HTTP信息輸出到標準輸出,用于調試之用。
返回值:無
mln_http_get_connection
mln_http_get_connection(h)描述:獲取類型為mln_http_t的h中TCP鏈接結構。
返回值:mln_tcp_conn_t類型指針
mln_http_set_connection
mln_http_set_connection(h,c)描述:將mln_http_t類型的h中TCP鏈接結構設置為mln_tcp_conn_t類型的c。
返回值:無
mln_http_get_pool
mln_http_get_pool(h)描述:獲取類型為mln_http_t的h中內存池結構。
返回值:mln_alloc_t類型指針
mln_http_set_pool
mln_http_set_pool(h,p)描述:將mln_http_t類型的h中內存池設置為mln_alloc_t類型的p。
返回值:無
mln_http_get_data
mln_http_get_data(h)描述:獲取類型為mln_http_t的h中輔助體處理函數的用戶自定義數據。
返回值:用戶自定義數據指針
mln_http_set_data
mln_http_set_data(h,d)描述:將mln_http_t類型的h中輔助體處理函數的用戶自定義數據設置為d。
返回值:無
mln_http_get_uri
mln_http_get_uri(h)描述:獲取類型為mln_http_t的h中URI字符串。
返回值:mln_string_t類型指針
mln_http_set_uri
mln_http_set_uri(h,u)描述:將mln_http_t類型的h中URI設置為mln_string_t類型指針的u。
返回值:無
mln_http_get_args
mln_http_get_args(h)描述:獲取類型為mln_http_t的h中參數字符串。
返回值:mln_string_t類型指針
mln_http_set_args
mln_http_set_args(h,a)描述:將mln_http_t類型的h中參數設置為mln_string_t類型指針的a。
返回值:無
mln_http_get_status
mln_http_get_status(h)描述:獲取類型為mln_http_t的h中響應狀態字,例如200 400等。
返回值:整型狀態字
mln_http_set_status
mln_http_set_status(h,s)描述:將mln_http_t類型的h中響應狀態字設置為整型的s。
返回值:無
mln_http_get_method
mln_http_get_method(h)描述:獲取類型為mln_http_t的h中方法字段
返回值:
- M_HTTP_GET
- M_HTTP_POST
- M_HTTP_HEAD
- M_HTTP_PUT
- M_HTTP_DELETE
- M_HTTP_TRACE
- M_HTTP_CONNECT
- M_HTTP_OPTIONS
mln_http_set_method
mln_http_set_method(h,m)描述:將mln_http_t類型的h中請求方法設置為m,m的可用值參考mln_http_get_method的返回值部分。
返回值:無
mln_http_get_version
mln_http_get_version(h)描述:獲取類型為mln_http_t的h中HTTP版本
返回值:
- M_HTTP_VERSION_1_0 HTTP 1.0
- M_HTTP_VERSION_1_1 HTTP 1.1
mln_http_set_version
mln_http_set_version(h,v)描述:將mln_http_t類型的h中的HTTP版本號為v,v的取值參考mln_http_get_version的返回值。
返回值:無
mln_http_get_type
mln_http_get_type(h)描述:獲取類型為mln_http_t的h中HTTP類型,即請求還是響應。
返回值:
- M_HTTP_UNKNOWN未知類型
- M_HTTP_REQUEST請求
- M_HTTP_RESPONSE響應
mln_http_set_type
mln_http_set_type(h,t)描述:將mln_http_t類型的h中報文類型設置為t,t的取值參考mln_http_get_type的返回值。
返回值:無
mln_http_get_handler
mln_http_get_handler(h)描述:獲取類型為mln_http_t的h中體處理函數指針。
返回值:類型為mln_http_handler的函數指針
mln_http_set_handler
mln_http_set_handler(h,hlr)描述:將mln_http_t類型的h中提處理函數設置為mln_http_handler類型的hlr。
返回值:無
mln_http_get_response_msg
mln_http_get_response_msg(h)描述:獲取類型為mln_http_t的h中響應信息,即類似:Bad Request 或 Internal Server Error等字符串。
返回值:mln_string_t類型指針
mln_http_set_response_msg
mln_http_set_response_msg(h,m)描述:將mln_http_t類型的h中響應信息設置為mln_string_t類型指針的m。
返回值:無
mln_http_get_error
mln_http_get_error(h)#define M_HTTP_CONTINUE 100 #define M_HTTP_SWITCHING_PROTOCOLS 101 #define M_HTTP_PROCESSING 102 #define M_HTTP_OK 200 #define M_HTTP_CREATED 201 #define M_HTTP_ACCEPTED 202 #define M_HTTP_NON_AUTHORITATIVE_INFORMATION 203 #define M_HTTP_NO_CONTENT 204 #define M_HTTP_RESET_CONTENT 205 #define M_HTTP_PARTIAL_CONTENT 206 #define M_HTTP_MULTI_STATUS 207 #define M_HTTP_MULTIPLE_CHOICES 300 #define M_HTTP_MOVED_PERMANENTLY 301 #define M_HTTP_MOVED_TEMPORARILY 302 #define M_HTTP_SEE_OTHER 303 #define M_HTTP_NOT_MODIFIED 304 #define M_HTTP_USE_PROXY 305 #define M_HTTP_SWITCH_PROXY 306 #define M_HTTP_TEMPORARY_REDIRECT 307 #define M_HTTP_BAD_REQUEST 400 #define M_HTTP_UNAUTHORIZED 401 #define M_HTTP_PAYMENT_REQUIRED 402 #define M_HTTP_FORBIDDEN 403 #define M_HTTP_NOT_FOUND 404 #define M_HTTP_METHOD_NOT_ALLOWED 405 #define M_HTTP_NOT_ACCEPTABLE 406 #define M_HTTP_PROXY_AUTHENTICATION_REQUIRED 407 #define M_HTTP_REQUEST_TIMEOUT 408 #define M_HTTP_CONFLICT 409 #define M_HTTP_GONE 410 #define M_HTTP_LENGTH_REQUIRED 411 #define M_HTTP_PRECONDITION_FAILED 412 #define M_HTTP_REQUEST_ENTITY_TOO_LARGE 413 #define M_HTTP_REQUEST_URI_TOO_LARGE 414 #define M_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define M_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE 416 #define M_HTTP_EXPECTATION_FAILED 417 #define M_HTTP_TOO_MANY_CONNECTIONS 421 #define M_HTTP_UNPROCESSABLE_ENTITY 422 #define M_HTTP_LOCKED 423 #define M_HTTP_FAILED_DEPENDENCY 424 #define M_HTTP_UNORDERED_COLLECTION 425 #define M_HTTP_UPGRADE_REQUIRED 426 #define M_HTTP_RETRY_WITH 449 #define M_HTTP_INTERNAL_SERVER_ERROR 500 #define M_HTTP_NOT_IMPLEMENTED 501 #define M_HTTP_BAD_GATEWAY 502 #define M_HTTP_SERVICE_UNAVAILABLE 503 #define M_HTTP_GATEWAY_TIMEOUT 504 #define M_HTTP_VERSION_NOT_SUPPORTED 505 #define M_HTTP_VARIANT_ALSO_NEGOTIATES 506 #define M_HTTP_INSUFFICIENT_STORAGE 507 #define M_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509 #define M_HTTP_NOT_EXTENDED 510 #define M_HTTP_UNPARSEABLE_RESPONSE_HEADERS 600描述:獲取類型為mln_http_t的h中錯誤信息。
返回值:宏定義的錯誤值
mln_http_set_error
mln_http_set_error(h,e)描述:將mln_http_t類型的h中錯誤信息設置為e,e的取值參見mln_http_get_error中的宏定義。
返回值:無
mln_http_get_header
mln_http_get_header(h)描述:獲取類型為mln_http_t的h中頭字段結構。
返回值:mln_hash_t類型結構
示例
#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <arpa/inet.h> #include <sys/time.h> #include "mln_core.h" #include "mln_log.h" #include "mln_http.h" #include "mln_file.h"static void mln_accept(mln_event_t *ev, int fd, void *data); static int mln_http_recv_body_handler(mln_http_t *http, mln_chain_t **in, mln_chain_t **nil); static void mln_recv(mln_event_t *ev, int fd, void *data); static void mln_quit(mln_event_t *ev, int fd, void *data); static void mln_send(mln_event_t *ev, int fd, void *data); static int mln_http_send_body_handler(mln_http_t *http, mln_chain_t **body_head, mln_chain_t **body_tail);static void worker_process(mln_event_t *ev) {mln_u16_t port = 1234;mln_s8_t ip[] = "0.0.0.0";struct sockaddr_in addr;int val = 1;int listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd < 0) {mln_log(error, "listen socket error\n");return;}if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {mln_log(error, "setsockopt error\n");close(listenfd);return;}addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip);if (bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {mln_log(error, "bind error\n");close(listenfd);return;}if (listen(listenfd, 511) < 0) {mln_log(error, "listen error\n");close(listenfd);return;}if (mln_event_set_fd(ev, \listenfd, \M_EV_RECV|M_EV_NONBLOCK, \M_EV_UNLIMITED, \NULL, \mln_accept) < 0){mln_log(error, "listen sock set event error\n");close(listenfd);return;} }static void mln_accept(mln_event_t *ev, int fd, void *data) {mln_tcp_conn_t *connection;mln_http_t *http;int connfd;socklen_t len;struct sockaddr_in addr;while (1) {memset(&addr, 0, sizeof(addr));len = sizeof(addr);connfd = accept(fd, (struct sockaddr *)&addr, &len);if (connfd < 0) {if (errno == EAGAIN) break;if (errno == EINTR) continue;perror("accept");exit(1);}connection = (mln_tcp_conn_t *)malloc(sizeof(mln_tcp_conn_t));if (connection == NULL) {fprintf(stderr, "3No memory.\n");close(connfd);continue;}if (mln_tcp_conn_init(connection, connfd) < 0) {fprintf(stderr, "4No memory.\n");close(connfd);free(connection);continue;}http = mln_http_init(connection, NULL, mln_http_recv_body_handler);if (http == NULL) {fprintf(stderr, "5No memory.\n");mln_tcp_conn_destroy(connection);free(connection);close(connfd);continue;}if (mln_event_set_fd(ev, \connfd, \M_EV_RECV|M_EV_NONBLOCK, \M_EV_UNLIMITED, \http, \mln_recv) < 0){fprintf(stderr, "6No memory.\n");mln_http_destroy(http);mln_tcp_conn_destroy(connection);free(connection);close(connfd);continue;}} }static void mln_quit(mln_event_t *ev, int fd, void *data) {mln_http_t *http = (mln_http_t *)data;mln_tcp_conn_t *connection = mln_http_get_connection(http);mln_event_set_fd(ev, fd, M_EV_CLR, M_EV_UNLIMITED, NULL, NULL);mln_http_destroy(http);mln_tcp_conn_destroy(connection);free(connection);close(fd); }static void mln_recv(mln_event_t *ev, int fd, void *data) {mln_http_t *http = (mln_http_t *)data;mln_tcp_conn_t *connection = mln_http_get_connection(http);int ret, rc;mln_chain_t *c;while (1) {ret = mln_tcp_conn_recv(connection, M_C_TYPE_MEMORY);if (ret == M_C_FINISH) {continue;} else if (ret == M_C_NOTYET) {c = mln_tcp_conn_remove(connection, M_C_RECV);if (c != NULL) {rc = mln_http_parse(http, &c);if (c != NULL) {mln_tcp_conn_append_chain(connection, c, NULL, M_C_RECV);}if (rc == M_HTTP_RET_OK) {return;} else if (rc == M_HTTP_RET_DONE) {mln_send(ev, fd, data);} else {fprintf(stderr, "Http parse error. error_code:%u\n", mln_http_get_error(http));mln_quit(ev, fd, data);return;}}break;} else if (ret == M_C_CLOSED) {c = mln_tcp_conn_remove(connection, M_C_RECV);if (c != NULL) {rc = mln_http_parse(http, &c);if (c != NULL) {mln_tcp_conn_append_chain(connection, c, NULL, M_C_RECV);}if (rc == M_HTTP_RET_ERROR) {fprintf(stderr, "Http parse error. error_code:%u\n", mln_http_get_error(http));}}mln_quit(ev, fd, data);return;} else if (ret == M_C_ERROR) {mln_quit(ev, fd, data);return;}} }static int mln_http_recv_body_handler(mln_http_t *http, mln_chain_t **in, mln_chain_t **nil) {mln_u32_t method = mln_http_get_method(http);if (method == M_HTTP_GET)return M_HTTP_RET_DONE;mln_http_set_error(http, M_HTTP_NOT_IMPLEMENTED);return M_HTTP_RET_ERROR; }static void mln_send(mln_event_t *ev, int fd, void *data) {mln_http_t *http = (mln_http_t *)data;mln_tcp_conn_t *connection = mln_http_get_connection(http);mln_chain_t *c = mln_tcp_conn_get_head(connection, M_C_SEND);int ret;if (c == NULL) {mln_http_reset(http);mln_http_set_status(http, M_HTTP_OK);mln_http_set_version(http, M_HTTP_VERSION_1_0);mln_http_set_type(http, M_HTTP_RESPONSE);mln_http_set_handler(http, mln_http_send_body_handler);mln_chain_t *body_head = NULL, *body_tail = NULL;if (mln_http_generate(http, &body_head, &body_tail) == M_HTTP_RET_ERROR) {fprintf(stderr, "mln_http_generate() failed. %u\n", mln_http_get_error(http));mln_quit(ev, fd, data);return;}mln_tcp_conn_append_chain(connection, body_head, body_tail, M_C_SEND);}while ((c = mln_tcp_conn_get_head(connection, M_C_SEND)) != NULL) {ret = mln_tcp_conn_send(connection);if (ret == M_C_FINISH) {mln_quit(ev, fd, data);break;} else if (ret == M_C_NOTYET) {mln_chain_pool_release_all(mln_tcp_conn_remove(connection, M_C_SENT));mln_event_set_fd(ev, fd, M_EV_SEND|M_EV_APPEND|M_EV_NONBLOCK, M_EV_UNLIMITED, data, mln_send);return;} else if (ret == M_C_ERROR) {mln_quit(ev, fd, data);return;} else {fprintf(stderr, "Shouldn't be here.\n");abort();}} }static int mln_http_send_body_handler(mln_http_t *http, mln_chain_t **body_head, mln_chain_t **body_tail) {mln_u8ptr_t buf;mln_alloc_t *pool = mln_http_get_pool(http);mln_string_t cttype_key = mln_string("Content-Type");mln_string_t cttype_val = mln_string("text/html");buf = mln_alloc_m(pool, 5);if (buf == NULL) {mln_http_set_error(http, M_HTTP_INTERNAL_SERVER_ERROR);return M_HTTP_RET_ERROR;}memcpy(buf, "hello", 5);if (mln_http_set_field(http, &cttype_key, &cttype_val) == M_HTTP_RET_ERROR) {mln_http_set_error(http, M_HTTP_INTERNAL_SERVER_ERROR);return M_HTTP_RET_ERROR;}mln_string_t ctlen_key = mln_string("Content-Length");mln_string_t ctlen_val = mln_string("5");if (mln_http_set_field(http, &ctlen_key, &ctlen_val) == M_HTTP_RET_ERROR) {mln_http_set_error(http, M_HTTP_INTERNAL_SERVER_ERROR);return M_HTTP_RET_ERROR;}mln_chain_t *c = mln_chain_new(pool);if (c == NULL) {mln_http_set_error(http, M_HTTP_INTERNAL_SERVER_ERROR);return M_HTTP_RET_ERROR;}mln_buf_t *b = mln_buf_new(pool);if (b == NULL) {mln_chain_pool_release(c);mln_http_set_error(http, M_HTTP_INTERNAL_SERVER_ERROR);return M_HTTP_RET_ERROR;}c->buf = b;b->left_pos = b->pos = b->start = buf;b->last = b->end = buf + 5;b->in_memory = 1;b->last_buf = 1;b->last_in_chain = 1;if (*body_head == NULL) {*body_head = *body_tail = c;} else {(*body_tail)->next = c;*body_tail = c;}return M_HTTP_RET_DONE; }int main(int argc, char *argv[]) {struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = worker_process;return mln_core_init(&cattr); }矩陣運算
頭文件
#include "mln_matrix.h"相關結構
typedef struct { mln_size_t row;//矩陣的行數mln_size_t col;//矩陣的列數double *data;//一個一維數組,包含了矩陣內所有元素,按行一次排列mln_u32_t is_ref:1;//標識data是否為外部引用,該標記用于釋放矩陣結構時忽略對data的釋放 } mln_matrix_t;函數
mln_matrix_new
mln_matrix_t *mln_matrix_new(mln_size_t row, mln_size_t col, double *data, mln_u32_t is_ref);描述:創建一個row行col列,數據為data的矩陣。若is_ref為0則表示矩陣結構會完全復制一個data在其中,否則直接引用data。
返回值:成功則返回矩陣結構指針,否則返回NULL
mln_matrix_free
void mln_matrix_free(mln_matrix_t *matrix);描述:釋放矩陣結構內存。
返回值:無
mln_matrix_mul
mln_matrix_t *mln_matrix_mul(mln_matrix_t *m1, mln_matrix_t *m2);描述:矩陣乘法。
返回值:成功則返回結果矩陣指針,否則返回NULL
mln_matrix_inverse
mln_matrix_t *mln_matrix_inverse(mln_matrix_t *matrix);描述:矩陣求逆。注意:矩陣求逆要求是該矩陣為方陣。
返回值:成功則返回結果矩陣指針,否則返回NULL
mln_matrix_dump
void mln_matrix_dump(mln_matrix_t *matrix);描述:將矩陣的信息輸出到標準輸出中。僅用于調試。
返回值:無
示例
#include <stdio.h> #include <errno.h> #include <stdlib.h> #include "mln_core.h" #include "mln_log.h" #include "mln_matrix.h"int main(int argc, char *argv[]) {mln_matrix_t *a, *b;double data[] = {1, 1, 1, 1, 2, 4, 2, 8, 64};struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {fprintf(stderr, "init failed\n");return -1;}a = mln_matrix_new(3, 3, data, 1);if (a == NULL) {mln_log(error, "init matrix failed\n");return -1;}mln_matrix_dump(a);b = mln_matrix_inverse(a);mln_matrix_free(a);if (b == NULL) {mln_log(error, "inverse failed: %s\n", strerror(errno));return -1;}mln_matrix_dump(b);mln_matrix_free(b);return 0; }大數計算
頭文件
#include "mln_bignum.h"相關結構
typedef struct {mln_u32_t tag;//正負數標記 mln_u32_t length;//當前大數數值所使用到的data的元素個數mln_u64_t data[M_BIGNUM_SIZE];//大數數值 } mln_bignum_t;#define M_BIGNUM_POSITIVE 0 //正數 #define M_BIGNUM_NEGATIVE 1 //負數#define M_BIGNUM_SIZE 257Melon中大數的實現是定長的,即大數是有上限的,目前支持到最大2048位。
函數/宏
mln_bignum_init
mln_bignum_t *mln_bignum_init(void);描述:創建并初始化大數結構mln_bignum_t,該結構由malloc分配而來。
返回值:成功則返回大數結構指針,否則返回NULL
mln_bignum_pool_init
mln_bignum_t *mln_bignum_pool_init(mln_alloc_t *pool);描述:創建并初始化大數結構mln_bignum_t,該結構由pool指定的內存池分配而來。
返回值:成功則返回大數結構指針,否則返回NULL
mln_bignum_free
void mln_bignum_free(mln_bignum_t *bn);描述:釋放大數結構bn,該結構應由mln_bignum_init分配而來。
返回值:無
mln_bignum_pool_free
void mln_bignum_pool_free(mln_bignum_t *bn);描述:釋放大數結構bn,該結構應由mln_bignum_pool_init分配而來。
返回值:無
mln_bignum_dup
mln_bignum_t *mln_bignum_dup(mln_bignum_t *bn);描述:完全復制一份大數結構bn,復制品由malloc分配內存。
返回值:成功則返回大數結構指針,否則返回NULL
mln_bignum_pool_dup
mln_bignum_t *mln_bignum_pool_dup(mln_alloc_t *pool, mln_bignum_t *bn);描述:完全復制一份大數結構bn,復制品由pool指定的內存池上分配內存。
返回值:成功則返回大數結構指針,否則返回NULL
mln_bignum_assign
int mln_bignum_assign(mln_bignum_t *bn, mln_s8ptr_t sval, mln_u32_t len);描述:將sval和len表示的字符串形式的大數賦值給大數結構bn。
返回值:成功則返回0,否則返回-1
mln_bignum_add
void mln_bignum_add(mln_bignum_t *dest, mln_bignum_t *src);描述:大數加法,計算結果會放入dest中。
返回值:無
mln_bignum_sub
void mln_bignum_sub(mln_bignum_t *dest, mln_bignum_t *src);描述:大數減法,計算結果會放入dest中。
返回值:無
mln_bignum_mul
void mln_bignum_mul(mln_bignum_t *dest, mln_bignum_t *src);描述:大數乘法,計算結果會放入dest中。
返回值:無
mln_bignum_div
int mln_bignum_div(mln_bignum_t *dest, mln_bignum_t *src, mln_bignum_t *quotient);描述:大數除法,商會放入dest中,若quotient不為空,則余數將放入其中。
返回值:成功則返回0,否則返回-1
mln_bignum_pwr
int mln_bignum_pwr(mln_bignum_t *dest, mln_bignum_t *exponent, mln_bignum_t *mod);描述:大數冪運算,計算dest的exponent次方。若mod不為空,則結果會對mod取模。最終計算結果會放入dest中。
返回值:成功則返回0,否則返回-1
mln_bignum_compare
int mln_bignum_compare(mln_bignum_t *bn1, mln_bignum_t *bn2);描述:帶符號的比較大數大小值。
返回值:
- 1 - bn1 > bn2
- -1 -bn1 < bn2
- 0 - bn1 = bn2
mln_bignum_abs_compare
int mln_bignum_abs_compare(mln_bignum_t *bn1, mln_bignum_t *bn2);描述:絕對值比大小。
返回值:
- 1 - bn1 > bn2
- -1 -bn1 < bn2
- 0 - bn1 = bn2
mln_bignum_bit_test
int mln_bignum_bit_test(mln_bignum_t *bn, mln_u32_t index);描述:檢測index指定的大數bn中該比特是否為1。
返回值:為1則返回1,否則返回0
mln_bignum_left_shift
void mln_bignum_left_shift(mln_bignum_t *bn, mln_u32_t n);描述:將大數bn左移n位。
返回值:無
mln_bignum_right_shift
void mln_bignum_right_shift(mln_bignum_t *bn, mln_u32_t n);描述:將大數bn右移n位。
返回值:無
mln_bignum_prime
int mln_bignum_prime(mln_bignum_t *res, mln_u32_t bitwidth);描述:計算一個bitwidth位的大素數,并將結果寫入res中。
返回值:成功則返回0,否則返回-1
mln_bignum_extend_eulid
int mln_bignum_extend_eulid(mln_bignum_t *a, mln_bignum_t *b, mln_bignum_t *x, mln_bignum_t *y);描述:大數版本的擴展歐幾里得算法。
返回值:成功則返回0,否則返回-1
mln_bignum_i2osp
int mln_bignum_i2osp(mln_bignum_t *n, mln_u8ptr_t buf, mln_size_t len);描述:將n的二進制值寫入buf與len指代的內存中。
返回值:成功則返回0,否則返回-1
mln_bignum_os2ip
int mln_bignum_os2ip(mln_bignum_t *n, mln_u8ptr_t buf, mln_size_t len);描述:將buf與len指代的內存中的二進制值賦予n。與mln_bignum_i2osp是相反操作。
返回值:成功則返回0,否則返回-1
mln_bignum_i2s
int mln_bignum_i2s(mln_bignum_t *n, mln_u8ptr_t buf, mln_size_t len);描述:與mln_bignum_i2osp功能相同,推薦使用mln_bignum_i2osp。
返回值:成功則返回0,否則返回-1
mln_bignum_s2i
int mln_bignum_s2i(mln_bignum_t *n, mln_u8ptr_t buf, mln_size_t len);描述:與mln_bignum_os2ip功能相同,推薦使用mln_bignum_os2ip。
返回值:成功則返回0,否則返回-1
mln_bignum_positive
mln_bignum_positive(pbn)描述:將mln_bignum_t *的大數pbn設置為正數。
返回值:無
mln_bignum_negative
mln_bignum_negative(pbn)描述:將mln_bignum_t *的大數pbn設置為負數。
返回值:無
mln_bignum_is_positive
mln_bignum_is_positive(pbn)描述:判斷mln_bignum_t *的大數pbn是否為為正數。
返回值:為正則返回非0,否則返回0
mln_bignum_is_negative
mln_bignum_is_negative(pbn)描述:判斷mln_bignum_t *的大數pbn是否為為負數。
返回值:為負則返回非0,否則返回0
mln_bignum_get_length
mln_bignum_get_length(pbn)描述:獲取mln_bignum_t *的大數pbn的值占data多少個數組元素。
返回值:元素個數
mln_bignum_zero
mln_bignum_zero()描述:返回一個值為0的大數。
返回值:大數值為0的常量
示例
#include <stdio.h> #include <errno.h> #include <stdlib.h> #include "mln_core.h" #include "mln_log.h" #include "mln_bignum.h"int main(int argc, char *argv[]) {mln_bignum_t *n1 = NULL, *n2 = NULL;struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;if (mln_core_init(&cattr) < 0) {fprintf(stderr, "init failed\n");return -1;}n1 = mln_bignum_init();n2 = mln_bignum_init();if (n1 == NULL || n2 == NULL) {mln_log(error, "init bignum failed\n");goto err;}if (mln_bignum_assign(n1, "10", 2) < 0) {mln_log(error, "assign failed\n");goto err;}if (mln_bignum_assign(n2, "20", 2) < 0) {mln_log(error, "assign failed\n");goto err;}mln_bignum_add(n1, n2);mln_bignum_dump(n1);err:if (n1 != NULL) mln_bignum_free(n1);if (n2 != NULL) mln_bignum_free(n2);return 0; }多線程框架
注意:Windows下目前不支持本功能。
多線程框架與前面介紹的線程池不同,是一種模塊化線程。模塊化線程是指,每一個線程都是一個獨立的代碼模塊,都有各自對應的入口函數(類似于每一個 C 語言程序有一個 main 函數一樣)。
模塊要存放于 Melon/threads/目錄下。在現有的Melon代碼中,包含了兩個示例模塊——haha 和 hello (名字起得有點隨意)。下面,我們以這兩個模塊為例說明模塊化線程的開發和使用流程。
開發流程
這里有幾點注意事項:
可以看到,在這個例子中,模塊的入口函數名為haha_main。對于每一個線程模塊來說,他們的入口函數就是他們模塊的名稱(即文件名)+下劃線+main組成的。
這個例子也很簡單,就是利用 select 持續關注主線程消息,當從主線程接收到消息后,就進行日志輸出,然后釋放資源。
與之功能對應的就是 hello 這個模塊:
//hello 模塊 #include <assert.h>static void hello_cleanup(void *data) {mln_log(debug, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); }int hello_main(int argc, char **argv) {mln_thread_cleanup_set(hello_cleanup, NULL);int i;for (i = 0; i < 1; ++i) {int fd = atoi(argv[argc-1]);mln_thread_msg_t msg;memset(&msg, 0, sizeof(msg));msg.dest = mln_string_new("haha");assert(msg.dest);msg.sauto = 9736;msg.c = 'N';msg.type = ITC_REQUEST;msg.need_clear = 1; #if defined(WINNT)int n = send(fd, (char *)&msg, sizeof(msg), 0); #elseint n = send(fd, &msg, sizeof(msg), 0); #endifif (n != sizeof(msg)) {mln_log(debug, "send error. n=%d. %s\n", n, strerror(errno));mln_string_free(msg.dest);return -1;}}usleep(100000);return 0; }這個模塊的功能也很簡單,就是向主線程發送消息,而消息的接收方是 haha 模塊,即主線程是一個中轉站,它將 hello 模塊的消息轉發給 haha 模塊。
在 hello 這個模塊中,調用了mln_thread_cleanup_set函數,這個函數的作用是:在從當前線程模塊的入口函數返回至上層函數后,將會被調用,用于清理自定義資源。
每一個線程模塊的清理函數只能被設置一個,多次設置會被覆蓋,清理函數是線程獨立的,因此不會出現覆蓋其他線程處理函數的情況(當然,你也可以故意這樣來構造,比如傳一個處理函數指針給別的模塊,然后那個模塊再進行設置)。
使用流程
使用流程遵循如下步驟:
我們逐個步驟進行操作。
我們先編寫啟動器:
//launcher.c#include "mln_core.h"int main(int argc, char *argv[]) {struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = NULL;cattr.worker_process = NULL;return mln_core_init(&cattr); }這里,我們不初始化任何全局變量,也不需要工作進程,因此都置空即可。
$ cc -o launcher launcher.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelon -lpthread生成名為 launcher 的可執行程序。
此時,我們的線程尚不能執行,我們需要修改配置文件:
log_level "none"; //user "root"; daemon off; core_file_size "unlimited"; //max_nofile 1024; worker_proc 1; thread_mode off; framework off; log_path "/usr/local/melon/logs/melon.log"; /** Configurations in the 'exec_proc' are the* processes which are customized by user.** Here is an example to show you how to* spawn a program.* keepalive "/tmp/a.out" ["arg1" "arg2" ...]* The command in this example is 'keepalive' that* indicate master process to supervise this* process. If process is killed, master process* would restart this program.* If you don't want master to restart it, you can* default "/tmp/a.out" ["arg1" "arg2" ...]** But you should know that there is another* arugment after the last argument you write here.* That is the file descriptor which is used to* communicate with master process.*/ exec_proc {// keepalive "/tmp/a"; } thread_exec { // restart "hello" "hello" "world"; // default "haha"; }上面是默認配置文件,我們要進行如下修改:
- thread_mode off; -> thread_mode on;
- framework off; -> framework on;
- thread_exec配置塊中的兩項注釋去掉
這里,需要額外說明一下:
thread_exec配置塊專門用于模塊化線程之用,其內部每一個配置項均為線程模塊。
以 hello 為例:
restart "hello" "hello" "world";restart或者default是指令,restart表示線程退出主函數后,再次啟動線程。而default則表示一旦退出便不再啟動。其后的hello字符串就是模塊的名稱,其余則為模塊參數,即入口函數的argc和argv的部分。而與主線程通信的套接字則不必寫在此處,而是線程啟動后進入入口函數前自動添加的。
現在,就來啟動程序吧。
$ ./launcherStart up worker process No.1 Start thread 'hello' Start thread 'haha' 02/14/2021 04:07:48 GMT DEBUG: ./src/mln_thread_module.c:haha_main:42: PID:9309 !!!src:hello auto:9736 char:N 02/14/2021 04:07:49 GMT DEBUG: ./src/mln_thread_module.c:hello_cleanup:53: PID:9309 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 02/14/2021 04:07:49 GMT REPORT: PID:9309 Thread 'hello' return 0. 02/14/2021 04:07:49 GMT REPORT: PID:9309 Child thread 'hello' exit. 02/14/2021 04:07:49 GMT REPORT: PID:9309 child thread pthread_join's exit code: 0 02/14/2021 04:07:49 GMT DEBUG: ./src/mln_thread_module.c:haha_main:42: PID:9309 !!!src:hello auto:9736 char:N 02/14/2021 04:07:49 GMT DEBUG: ./src/mln_thread_module.c:hello_cleanup:53: PID:9309 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 02/14/2021 04:07:49 GMT REPORT: PID:9309 Thread 'hello' return 0. 02/14/2021 04:07:49 GMT REPORT: PID:9309 Child thread 'hello' exit. 02/14/2021 04:07:49 GMT REPORT: PID:9309 child thread pthread_join's exit code: 0 ...可以看到,事實上 Melon 中會啟動工作進程來拉起其子線程,而工作進程數量由worker_proc配置項控制,如果多于一個,則每個工作進程都會拉起一組haha和hello線程。此外,我們也看到,hello線程退出后,清理函數被調用。
IPC模塊開發
注意:Windows下暫時不支持本功能。
Melon支持多進程框架,那么自然主子進程之間會涉及通信問題,這部分就由IPC進行處理。
Melon提供了一種簡單的IPC擴展方式,它不需要對現有源文件進行修改即可完成(但需要重新編譯)。為此,在Melon目錄中有一個專門的目錄提供給這個擴展之用——ipc_handlers。
在這個目錄下的文件,每一個即為一個消息以及消息的處理函數。文件的命名是有規范的:處理函數前綴.消息類型名。而處理函數又分為:主進程處理函數 和 子進程處理函數,他們的名字分別為:處理函數前綴_master和處理函數前綴_worker。消息類型會在configure時自動加入到mln_ipc.h頭文件中,用戶自己的代碼直接使用即可。
處理函數的函數原型如下:
void prefix_master(mln_event_t *ev, void *f_ptr, void *buf, mln_u32_t len, void **udata_ptr);和
void prefix_worker(mln_event_t *ev, void *f_ptr, void *buf, mln_u32_t len, void **udata_ptr);第一個參數為與本消息相關的事件結構。第二個參數是一個mln_fork_t類型的指針,我們可以通過這里個指針獲取一些和子進程相關的信息,例如通信的鏈接結構。第三個參數為主/子進程本次收到的消息內容。第四個參數為消息的長度。第五個參數是一個可自定義的用戶數據。我們可以在本次調用中對其進行賦值,而在下次調用時它會被傳遞回來繼續使用,框架不會對其進行初始化和釋放操作。
關于mln_fork_t類型:
struct mln_fork_s {struct mln_fork_s *prev;struct mln_fork_s *next;mln_s8ptr_t *args; //子進程參數mln_tcp_conn_t conn; //通信的鏈接結構,socketpair被當作tcp使用pid_t pid; //子進程的pidmln_u32_t n_args;//參數個數mln_u32_t state;//子進程狀態mln_u32_t msg_len;//消息長度mln_u32_t msg_type;//消息類型mln_size_t error_bytes;//錯誤消息類型數據大小void *msg_content;//消息內容enum proc_exec_type etype;//子進程是需要被替換執行映像的(exec)還是不需要的enum proc_state_type stype;//子進程退出后是否需要被重新拉起 };由于Melon是一主多從模式,因此需要一個方法能夠遍歷子進程列表,并對其下發消息,因此提供了一個函數用于遍歷所有子進程結構:
int mln_fork_scan_all(mln_event_t *ev, scan_handler handler, void *data);typedef int (*scan_handler)(mln_event_t *, mln_fork_t *, void *);- ev為主進程消息相關的事件處理結構。
- handler為每個子進程節點的處理函數,函數有三個參數,分別為:消息相關的事件處理結構、子進程的mln_fork_t結構以及用戶自定義數據(即mln_fork_scan_all的第三個參數)。
- data用戶自定義數據。
Melon中還提供了其他函數:
-
mln_fork_get_master_connection
mln_tcp_conn_t *mln_fork_get_master_connection(void);用于在子進程中獲取與主進程通信的TCP鏈接結構(socketpair被當作TCP處理)。
-
mln_ipc_master_send_prepare
int mln_ipc_master_send_prepare(mln_event_t *ev, mln_u32_t type, void *buf, mln_size_t len, mln_fork_t *f_child);用于主進程將長度為len類行為type的消息buf發送給f_child指定的子進程。
-
mln_ipc_worker_send_prepare
int mln_ipc_worker_send_prepare(mln_event_t *ev, mln_u32_t type, void *msg, mln_size_t len);用于子進程將長度為len類行為type的消息buf發送給主進程。
目前,在ipc_handlers中有一個已經定義了的消息,用于配置熱重載的,極其簡單,用戶可以在閱讀完本文后與之對照。
多進程模型
注意:Windows下目前不支持本功能。
Melon開發之初便是要支持多進程模型的,這一點也主要源于Nginx以及以往用戶態網絡程序開發經歷。因此,Melon中的多進程也延續了類似Nginx的異步事件模式。
下面我們來使用Melon來完成一個多進程例子。這個例子雖然簡單,但用戶會發現,事實上Melon并不干涉甚至是基于用戶極大的自由發揮空間。
在安裝好后,我們首先要創建一個名為hello.c的源文件來完成我們期望的功能:
#include <stdio.h> #include "mln_core.h" #include "mln_log.h" #include "mln_event.h"char text[1024];static int global_init(void); static void worker_process(mln_event_t *ev); static void print_handler(mln_event_t *ev, void *data);int main(int argc, char *argv[]) {struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = global_init;cattr.worker_process = worker_process;return mln_core_init(&cattr); }static int global_init(void) {//global variable init functionint n = snprintf(text, sizeof(text)-1, "hello world\n");text[n] = 0;return 0; }static void worker_process(mln_event_t *ev) {//we can set event handler here//let's set a timermln_event_set_timer(ev, 1000, text, print_handler); }static void print_handler(mln_event_t *ev, void *data) {mln_log(debug, "%s\n", (char *)data);mln_event_set_timer(ev, 1000, data, print_handler); }這段代碼主要是初始化了一個全局變量,然后給每一個子進程創建了一個定時事件,即每一秒中輸出一個 hello world 。
我們對該源文件進行編譯鏈接生成可執行程序:
$ gcc -o hello hello.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelon然后,我們需要先修改 Melon 庫的配置文件:
$ sudo vim /usr/local/melon/conf/melon.conflog_level "none"; //user "root"; daemon off; core_file_size "unlimited"; //max_nofile 1024; worker_proc 1; thread_mode off; framework off; log_path "/usr/local/melon/logs/melon.log"; /** Configurations in the 'exec_proc' are the* processes which are customized by user.** Here is an example to show you how to* spawn a program.* keepalive "/tmp/a.out" ["arg1" "arg2" ...]* The command in this example is 'keepalive' that* indicate master process to supervise this* process. If process is killed, master process* would restart this program.* If you don't want master to restart it, you can* default "/tmp/a.out" ["arg1" "arg2" ...]** But you should know that there is another* arugment after the last argument you write here.* That is the file descriptor which is used to* communicate with master process.*/ exec_proc {// keepalive "/tmp/a"; } thread_exec { // restart "hello" "hello" "world"; // default "haha"; }我們做如下修改:
- framework off; –> framework on;
- worker_proc 1; –> worker_proc 3;
這樣,多進程框架將被啟用,且會產生三個子進程。
最后,程序啟動后如下:
$ ./hello Start up worker process No.1 Start up worker process No.2 Start up worker process No.3 02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25322 hello world02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25323 hello world02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25324 hello world02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25322 hello world02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25323 hello world02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25324 hello world...這時,可以ps看一下,一共存在四個 hello 進程,一個為主,其余三個為子進程。
到此,我們的例子已經看完。我們可以看到,在全局的初始化函數中,我們可以自由的對全局變量進行處理,甚至可以在其中改寫當前框架的配置以強制框架按照我們的期望進行初始化。而在工作進程處理函數中,我們可以自由的編寫我們的程序邏輯,而不會擔心會有其他繁雜的邏輯會對此產生干擾。
總結
以上是生活随笔為你收集整理的【C语言开源库】C语言必备实用第三方库Melon(包括数据结构算法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux php muma,php实现
- 下一篇: ios使用theos tweak log