diff --git a/README.en.md b/README.en.md index 37a43a4..8dbadb6 100644 --- a/README.en.md +++ b/README.en.md @@ -13,7 +13,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with | module | version | usage | path | describe | |:-------------|:---------|:-----------------------------|:--------------------------------------|:--------------------------------------| -| overall | 00.01.00 | [link](README.md) | ./ | Overall +| overall | 00.01.01 | [link](README.md) | ./ | Overall | init | 01.00.00 | [link](/doc/init.md) | ./source/00_application | Initialize export module | console | 01.00.00 | [link](/doc/console.md) | ./source/00_application/console | Console command input, combined with the 'command' module, parsing commands entered in the console | arg | 01.00.00 | [link](/doc/arg.md) | ./source/01_general | Indefinite parameters, obtain the number of indefinite parameters and specified parameters @@ -28,7 +28,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with | sList | 01.00.00 | [link](/doc/sList.md) | ./source/01_general | Universal single-link list controller | tool | 01.00.00 | [link](/doc/tool.md) | ./source/01_general | General tools code | valloc | 01.00.00 | [link](/doc/valloc.md) | ./source/01_general | Dynamic memory usage testing tool -| vlog | 01.00.00 | [link](/doc/vlog.md) | ./source/01_general | Log output module +| vlog | 01.01.00 | [link](/doc/vlog.md) | ./source/01_general | Log output module | vctype | 01.00.00 | [link](/doc/vctype.md) | ./source/02_vstd | Similar to the C standard library ctype | vmath | 01.00.00 | [link](/doc/vmath.md) | ./source/02_vstd | Similar to the C standard library math | vmem | 01.00.00 | [link](/doc/vmem.md) | ./source/02_vstd | Simple implementation of memory pool diff --git a/README.md b/README.md index ee0fe8a..4979f8f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | module | version | usage | path | describe | |:-------------|:---------|:-----------------------------|:--------------------------------------|:--------------------------------------| -| overall | 00.01.00 | [link](README.md) | ./ | Overall +| overall | 00.01.01 | [link](README.md) | ./ | Overall | init | 01.00.00 | [link](/doc/init.md) | ./source/00_application | Initialize export module | console | 01.00.00 | [link](/doc/console.md) | ./source/00_application/console | Console command input, combined with the 'command' module, parsing commands entered in the console | arg | 01.00.00 | [link](/doc/arg.md) | ./source/01_general | Indefinite parameters, obtain the number of indefinite parameters and specified parameters @@ -28,7 +28,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | sList | 01.00.00 | [link](/doc/sList.md) | ./source/01_general | Universal single-link list controller | tool | 01.00.00 | [link](/doc/tool.md) | ./source/01_general | General tools code | valloc | 01.00.00 | [link](/doc/valloc.md) | ./source/01_general | Dynamic memory usage testing tool -| vlog | 01.00.00 | [link](/doc/vlog.md) | ./source/01_general | Log output module +| vlog | 01.01.00 | [link](/doc/vlog.md) | ./source/01_general | Log output module | vctype | 01.00.00 | [link](/doc/vctype.md) | ./source/02_vstd | Similar to the C standard library ctype | vmath | 01.00.00 | [link](/doc/vmath.md) | ./source/02_vstd | Similar to the C standard library math | vmem | 01.00.00 | [link](/doc/vmem.md) | ./source/02_vstd | Simple implementation of memory pool diff --git a/doc/cQueue.md b/doc/cQueue.md new file mode 100644 index 0000000..16118be --- /dev/null +++ b/doc/cQueue.md @@ -0,0 +1,180 @@ +## 介绍 + +队列是一种先进先出(First In First Out,FIFO)的特殊数据结构,一般情况下它只有一个出口一个入口,从队尾进入从队头出,入队push,出队pop。 + +cQueue为一个队列控制体,自身本不是容器,和varch的queue有区别,但是使用上却很类似。 + +## 接口 + +### 定义队列类型 + +因为cQueue本身并不是数据容器,所以需要对其存储的数据类型定义一个新的数据结构,如下例子 + +```c +typedef struct +{ + cQueue queue; // 第一个成员,默认为这个,无需改动 + int data[10]; // 第二个成员,定义实际存储的数据数组,数组类型为任意类型,数组名都为 data , 容量根据实际所需 +} intQueueType; // 定义一个新的数据结构体类型 +intQueueType intQueue; // 定义一个新的结构体变量 +``` + +### 初始化队列 + +```c +#define cQueue_init(qObject) +``` + +宏定义方法,能适配各种数据类型 + +```c +void test_int(void) +{ + typedef struct + { + cQueue queue; + int data[10]; + } intQueueType; + intQueueType intQueue; + + cQueue_init(intQueue); +} +``` + +### 队列空队和满队 + +```c +#define cQueue_empty(qObject) +#define cQueue_full(qObject) +``` + +这两个方法实际就是queue的`size`的大小关系,等于0为空,等于容量则满。 + +### 队列数据入队和出队 + +```c +#define cQueue_push(qObject, d) +#define cQueue_pop(qObject, d) +``` + +这两个方法分别将数据d入队,和从队列出队数据到d + +```c +void test_int(void) +{ + typedef struct + { + cQueue queue; + int data[10]; + } intQueueType; + intQueueType intQueue; + + cQueue_init(intQueue); + + for (int i = 0; i < intQueue.queue.cap; i++) + { + cQueue_push(intQueue, i); + } + + while (intQueue.queue.size > 0) + { + int data; + cQueue_pop(intQueue, data); + printf("cQueue_pop %d\r\n", data); + } +} +``` +结果: +``` +cQueue_pop 0 +cQueue_pop 1 +cQueue_pop 2 +cQueue_pop 3 +cQueue_pop 4 +cQueue_pop 5 +cQueue_pop 6 +cQueue_pop 7 +cQueue_pop 8 +cQueue_pop 9 +``` + +### 队列数据访问 + +```c +#define cQueue_at(qObject, i) +``` + +随机访问队列中的数据。 + +```c +void test_int(void) +{ + typedef struct + { + cQueue queue; + int data[10]; + } intQueueType; + intQueueType intQueue; + + cQueue_init(intQueue); + + for (int i = 0; i < intQueue.queue.cap; i++) + { + cQueue_push(intQueue, i); + } + + printf("cQueue[5] = %d\r\n", cQueue_at(intQueue, 5)); +} +``` +结果: +``` +cQueue[5] = 5 +``` + +## 特殊数据结构体例子 + +```c +void test_struct(void) +{ + typedef struct + { + char *name; + int age; + } Stu; + typedef struct + { + cQueue queue; + Stu data[10]; + } StuQueueType; + StuQueueType StuQueue; + + Stu s = {"Zhang", 18}; + + cQueue_init(StuQueue); + + for (int i = 0; i < StuQueue.queue.cap; i++) + { + s.age = 18 + i; + cQueue_push(StuQueue, s); + } + + while (StuQueue.queue.size > 0) + { + cQueue_pop(StuQueue, s); + printf("cQueue_pop name: %s age %d\r\n", s.name, s.age); + } +} +``` +结果: +``` +cQueue_pop name: Zhang age 18 +cQueue_pop name: Zhang age 19 +cQueue_pop name: Zhang age 20 +cQueue_pop name: Zhang age 21 +cQueue_pop name: Zhang age 22 +cQueue_pop name: Zhang age 23 +cQueue_pop name: Zhang age 24 +cQueue_pop name: Zhang age 25 +cQueue_pop name: Zhang age 26 +cQueue_pop name: Zhang age 27 +``` diff --git a/doc/dList.md b/doc/dList.md new file mode 100644 index 0000000..aba5170 --- /dev/null +++ b/doc/dList.md @@ -0,0 +1,558 @@ +## 介绍 + +链表是一种数据结构,其中的数据元素逻辑上连续,但在物理上可以分散存储。链表能够通过指针将多个相同类型的数据块链接成一个完整的序列,在数据结构的实现中具有重要作用。 + +dList模块为通用的双向链表模块,其与sList模块非常相似,区别在于指针域由单向链表的一个指向改为两个指向(一个指向下一个,一个指向上一个),如此在存储结构上也有差异,sList为单向开环结构,dList为双向闭环结构(首尾相连,形成环形)。 +在API方面,使用是和sList基本一致的(底层实现不太一样),其余性能和优缺点对比在下文展示。 + +## 接口 + +### 创建dList + +```c +dList *dList_create(void); +``` + +在dList中,dList既是一个链表也是一个结点(因为结点也就是长度为1的链表)。所以此方法是创建一个为空的并且长度为1的链表(也就是创建一个空结点)。 + +### 删除dList + +```c +void dList_delete(dList *list); +``` + +此方法会删除该链表(包含其所有的结点)。 + +### 设置和获取dList结点内容 + +```c +int dList_set(dList *list, void* data, int size); +int dList_get(dList *list, void* data, int size); +``` + +当一个结点被创建好之后,数据域是不具备内容的,需要通过`dList_set`方法设置其数据域内容,并且可以使用`dList_get`方法来获取结点数据域内容。 +`dList_set`方法会将原来的数据覆盖掉,同时也是指定`size`为 **0** 来删除dList结点数据内容。 + +```c +static void test_set(void) +{ + dList *list = NULL; + int dataInt = 3; + char *dataString = "Hello dList"; + + list = dList_create(); + if (!list) + { + printf("dList_create Fail!\r\n"); + return; + } + + printf("dList_create Success!\r\n"); + + dList_set(list, &dataInt, sizeof(dataInt)); + printf("list->data %d\r\n", dList_ref(list, int)); + + dList_set(list, dataString, strlen(dataString) + 1); + printf("list->data %s\r\n", ((char *)(list->data))); + + dList_delete(list); +} +``` +结果: +``` +dList_create Success! +list->data 3 +list->data Hello dList +``` + +示例中的`dList_ref`为数据引用,具体用法在下文 + +### dList插入数据 + +```c +dList *dList_insert(dList **listRef, int index, void *data, int size); +``` + +插入数据方法使用起来会更加简便,省去创建结点和设置数据的环节(即使是链表表头也可以省略创建,而由此方法内部完成),可以灵活的将指定数据插入到指定的位置上。 + +```c +static void test_insert(void) +{ + dList *list = NULL; + + for (int i = 0; i < 2; i++) + { + if (!dList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + } + + int i = 100; + if (!dList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + + printf("------------\r\n"); + + dList_forEachReverse(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +data 0 +data 1 +data 100 +------------ +data 100 +data 1 +data 0 +``` + +示例中的`dList_forEachForward`和`dList_forEachReverse`为遍历方法,具体用法在下文。 +对于传入dList引用为空时,会在首次创建结点并生成表头,传入`index`为负数表示插入到尾部。可以使用默认定义好的`dList_front`和`dList_back`宏定义来代表头部和尾部。 + +### dList擦除数据 + +```c +int dList_erase(dList **listRef, int index, dList **outPrev); +``` + +此方法对照`dList_insert`方法,擦除指定位置数据(会将该结点从链表中删除),同时为了更灵活使用,也支持获取被擦除的上一个结点(可以更便利高效得完成连续擦除)。 + +```c +static void test_erase(void) +{ + dList *list = NULL; + + for (int i = 0; i < 5; i++) + { + if (!dList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + } + + dList_erase(&list, 0, NULL); + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +data 1 +data 2 +data 3 +data 4 +``` + +对照前面插入的例子,擦除表头。 + +### dList推入和弹出 + +```c +int dList_pushFront(dList **listRef, void *data, int size); +int dList_pushBack(dList **listRef, void *data, int size); +int dList_popFront(dList **listRef); +int dList_popBack(dList **listRef); +``` + +分别就是头插、尾插、头删、尾删方法,其实就是在`dList_insert`和`dList_erase`方法基础上针对常用场景进行封装,使用更简便。 + +```c +static void test_pop(void) +{ + dList *list = NULL; + + for (int i = 0; i < 5; i++) + { + if (!dList_pushFront(&list, &i, sizeof(i))) goto FAIL; + } + for (int i = 0; i < 5; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + dList_popBack(&list); + dList_popBack(&list); + dList_popFront(&list); + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +data 3 +data 2 +data 1 +data 0 +data 0 +data 1 +data 2 +``` + +### dList追加 + +```c +int dList_append(dList *list, dList **append); +``` + +此方法可以将两个链表拼接成一个链表,`append`链表在拼接成功后会失效。 + +**注意** `append`需为表头,虽然即使不是表头也能拼接成功,但是其还属于原来的链表中,在操作时会出现一些意外。 + +```c +static void test_append(void) +{ + dList *list = NULL, *ap = NULL; + + for (int i = 0; i < 10; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + if (!dList_pushBack(&ap, &i, sizeof(i))) goto FAIL; + } + + if (!dList_append(list, &ap)) goto FAIL; + + printPoint(ap); + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); + dList_delete(ap); +} +``` +结果: +``` +ap: 00000000 +data 0 +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +data 0 +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +### dList链接结点 + +```c +dList *dList_attach(dList **listRef, int index, dList *attach); +``` + +这个方法是将一个结点(或者一个链表)链接到现有的一个链表当中,可以通过index来指定具体链接到哪个位置,这个方法很灵活,直接操作链表结构,**一般情况使用不上此方法**,而是使用此方法所封装的`dList_insert`等方法。此方法可以搭配其他方法灵活二次封装成其他方法。 + +```c +static void test_attach(void) +{ + dList *list = NULL, *a = NULL; + + for (int i = 0; i < 5; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + for (int i = 0; i < 3; i++) + { + if (!dList_pushBack(&a, &i, sizeof(i))) goto FAIL; + } + + dList_attach(&list, -1, a); + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +data 0 +data 1 +data 2 +``` + +### dList断链结点 + +```c +dList *dList_detach(dList **listRef, int begin, int end, dList **outPrev); +``` + +这个方法与`dList_attach`方法为对照方法,可以从链表中断链出来若干个结点(子链表),可以通过index来指定具体断链哪个位置和count指定个数,这个方法很灵活,直接操作链表结构,**一般情况使用不上此方法**,而是使用此方法所封装的`dList_erase`等方法。此方法可以搭配其他方法灵活二次封装成其他方法。 + +```c +static void test_detach(void) +{ + dList *list = NULL, *node = NULL; + + for (int i = 0; i < 10; i++) + { + if (!dList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + } + +#if 1 + node = dList_detach(&list, 0, 3, NULL); + if (!node) + { + printf("dList_detach fail\r\n"); + } +#endif + + dList_forEachForward(node, n) + { + printf("node data %d\r\n", dList_ref(n, int)); + } + + dList_delete(node); + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +node data 0 +node data 1 +node data 2 +node data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +### dList复制 + +```c +dList *dList_copy(dList *list, int begin, int end); +``` + +这个方法是会根据源链表的指定区间进行深拷贝一份新的链表。 + +```c +static void test_copy(void) +{ + dList *list = NULL, *copy = NULL; + + for (int i = 0; i < 10; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + copy = dList_copy(list, -5, -1); + if (!copy) + { + printf("dList_copy fail\r\n"); + } + + dList_forEachForward(copy, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); + dList_delete(copy); +} +``` +结果: +``` +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +### dList区间翻转 + +```c +int dList_reverse(dList *list, int begin, int end); +``` + +这个方法是会根据源链表的指定区间进行翻转。 + +```c +static void test_reverse(void) +{ + dList *list = NULL; + + for (int i = 0; i < 10; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + if (!dList_reverse(list, 1, 5)) + { + printf("dList_reverse fail\r\n"); + } + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +data 0 +data 5 +data 2 +data 3 +data 4 +data 1 +data 6 +data 7 +data 8 +data 9 +``` + +### dList获取指定结点 + +```c +dList *dList_to(dList *list, int index); +``` + +这个方法可以根据表头当前位置获取偏移指定位置的结点,传入负数可以从末端往回找。 + +```c +static void test_to(void) +{ + dList *list = NULL, *node; + + for (int i = 0; i < 10; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + node = dList_to(list, -6); + if (!node) + { + printf("dList_to fail\r\n"); + goto FAIL; + } + + printf("dList_to data %d\r\n", dList_ref(node, int)); + + dList_forEachForward(list, n) + { + printf("data %d\r\n", dList_ref(n, int)); + } + +FAIL: + dList_delete(list); +} +``` +结果: +``` +dList_to data 4 +data 0 +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +### dList大小 + +```c +int dList_size(dList *list); +``` + +这个方法获取链表的数据个数。 + +```c +static void test_size(void) +{ + dList *list = NULL; + + for (int i = 0; i < 10; i++) + { + if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + printf("size %d\r\n", dList_size(list)); + printf("size %d\r\n", dList_size(dList_to(list, 3))); + +FAIL: + dList_delete(list); +} +``` +结果: +``` +size 10 +size 7 +``` + +### dList遍历 + +```c +#define dList_forEach(list, node) // 从前往后遍历 +#define dList_forEachForward(list, node) // 从前往后遍历 +#define dList_forEachReverse(list, node) // 从后往前遍历 +``` + +这个方法为遍历链表的方法,具体例子可以参考上文其他使用例子。 + +### dList结点数据引用 + +```c +#define dList_ref(node, type) +``` + +这个方法类似C++的引用,实则是操作指针(只是将其隐藏起来),读写更方便,但要注意的是数据操作别越界,比如本来该结点存储的是`char`型数据(分配空间也就只分配1个字节大小),如果当作`int`型使用那就越界了。具体例子可以参考上文其他使用例子。 + diff --git a/doc/sList.md b/doc/sList.md new file mode 100644 index 0000000..63a537a --- /dev/null +++ b/doc/sList.md @@ -0,0 +1,533 @@ +## 介绍 + +链表是一种数据结构,其中的数据元素逻辑上连续,但在物理上可以分散存储。链表能够通过指针将多个相同类型的数据块链接成一个完整的序列,在数据结构的实现中具有重要作用。 + +sList模块为通用的单向链表模块,它通过一系列相互链接的节点来存储线性的数据集合。每个节点包含数据域和指向下一个节点的指针域。 + +## 接口 + +### 创建sList + +```c +sList *sList_create(void); +``` + +在sList中,sList既是一个链表也是一个结点(因为结点也就是长度为1的链表)。所以此方法是创建一个为空的并且长度为1的链表(也就是创建一个空结点)。 + +### 删除sList + +```c +void sList_delete(sList *list); +``` + +此方法会删除该链表(包含其所有的结点)。 + +### 设置和获取sList结点内容 + +```c +int sList_set(sList *list, void* data, int size); +int sList_get(sList *list, void* data, int size); +``` + +当一个结点被创建好之后,数据域是不具备内容的,需要通过`sList_set`方法设置其数据域内容,并且可以使用`sList_get`方法来获取结点数据域内容。 +`sList_set`方法会将原来的数据覆盖掉,同时也是指定`size`为 **0** 来删除sList结点数据内容。 + +```c +static void test_set(void) +{ + sList *list = NULL; + int dataInt = 3; + char *dataString = "Hello sList"; + char outBuffer[32]; + + list = sList_create(); + if (!list) + { + printf("sList_create Fail!\r\n"); + return; + } + + printf("sList_create Success!\r\n"); + + sList_set(list, &dataInt, sizeof(dataInt)); + printf("list->data %d\r\n", sList_ref(list, int)); + + sList_set(list, dataString, strlen(dataString) + 1); + printf("list->data %s\r\n", ((char *)(list->data))); + + sList_get(list, outBuffer, sizeof(outBuffer)); + printf("get data %s\r\n", outBuffer); + + sList_delete(list); +} +``` +结果: +``` +sList_create Success! +list->data 3 +list->data Hello sList +get data Hello sList +``` + +示例中的`sList_ref`为数据引用,具体用法在下文 + +### sList插入数据 + +```c +sList *sList_insert(sList **listRef, int index, void *data, int size); +``` + +插入数据方法使用起来会更加简便,省去创建结点和设置数据的环节(即使是链表表头也可以省略创建,而由此方法内部完成),可以灵活的将指定数据插入到指定的位置上。 + +```c +static void test_insert(void) +{ + sList *list = NULL; + + for (int i = 0; i < 5; i++) + { + if (!sList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + } + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); +} +``` +结果: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +``` + +示例中的`sList_forEach`为遍历方法,具体用法在下文。 +对于传入sList引用为空时,会在首次创建结点并生成表头,传入`index`为负数表示插入到尾部。可以使用默认定义好的`sList_front`和`sList_back`宏定义来代表头部和尾部。 + +### sList擦除数据 + +```c +int sList_erase(sList **listRef, int index, sList **outPrev); +``` + +此方法对照`sList_insert`方法,擦除指定位置数据(会将该结点从链表中删除),同时为了更灵活使用,也支持获取被擦除的上一个结点(可以更便利高效得完成连续擦除)。 + +```c +static void test_erase(void) +{ + sList *list = NULL; + + for (int i = 0; i < 5; i++) + { + if (!sList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + } + + sList_erase(&list, 0, NULL); + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); +} +``` +结果: +``` +data 1 +data 2 +data 3 +data 4 +``` + +对照前面插入的例子,擦除表头。 + +### sList推入和弹出 + +```c +int sList_pushFront(sList **listRef, void *data, int size); +int sList_pushBack(sList **listRef, void *data, int size); +int sList_popFront(sList **listRef); +int sList_popBack(sList **listRef); +``` + +分别就是头插、尾插、头删、尾删方法,其实就是在`sList_insert`和`sList_erase`方法基础上针对常用场景进行封装,使用更简便。 + +```c +static void test_pop(void) +{ + sList *list = NULL; + + for (int i = 0; i < 5; i++) + { + if (!sList_pushFront(&list, &i, sizeof(i))) goto FAIL; + } + for (int i = 0; i < 5; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + sList_popBack(&list); + sList_popBack(&list); + sList_popFront(&list); + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); +} +``` +结果: +``` +data 3 +data 2 +data 1 +data 0 +data 0 +data 1 +data 2 +``` + +### sList追加 + +```c +int sList_append(sList *list, sList **append); +``` + +此方法可以将两个链表拼接成一个链表,`append`链表在拼接成功后会失效。 + +**注意** `append`需为表头,虽然即使不是表头也能拼接成功,但是其还属于原来的链表中,在操作时会出现一些意外。 + +```c +static void test_append(void) +{ + sList *list = NULL, *ap = NULL; + + for (int i = 0; i < 10; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + if (!sList_pushBack(&ap, &i, sizeof(i))) goto FAIL; + } + + if (!sList_append(list, &ap)) goto FAIL; + + printPoint(ap); + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); + sList_delete(ap); +} +``` +结果: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +data 0 +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +### sList链接结点 + +```c +sList *sList_attach(sList **listRef, int index, sList *attach); +``` + +这个方法是将一个结点(或者一个链表)链接到现有的一个链表当中,可以通过index来指定具体链接到哪个位置,这个方法很灵活,直接操作链表结构,**一般情况使用不上此方法**,而是使用此方法所封装的`sList_insert`等方法。此方法可以搭配其他方法灵活二次封装成其他方法。 + +```c +static void test_attach(void) +{ + sList *list = NULL, *a = NULL; + + for (int i = 0; i < 5; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + for (int i = 0; i < 3; i++) + { + if (!sList_pushBack(&a, &i, sizeof(i))) goto FAIL; + } + + sList_attach(&list, -1, a); + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); +} +``` +结果: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +data 0 +data 1 +data 2 +``` + +### sList断链结点 + +```c +sList *sList_detach(sList **listRef, int index, int count, sList **outPrev); +``` + +这个方法与`sList_attach`方法为对照方法,可以从链表中断链出来若干个结点(子链表),可以通过index来指定具体断链哪个位置和count指定个数,这个方法很灵活,直接操作链表结构,**一般情况使用不上此方法**,而是使用此方法所封装的`sList_erase`等方法。此方法可以搭配其他方法灵活二次封装成其他方法。 + +```c +static void test_detach(void) +{ + sList *list = NULL, *node; + + for (int i = 0; i < 10; i++) + { + if (!sList_insert(&list, -1, &i, sizeof(i))) goto FAIL; + } + + node = sList_detach(&list, 3, -1, NULL); + if (!node) + { + printf("sList_detach fail\r\n"); + } + + sList_forEach(node, n) + { + printf("node data %d\r\n", sList_ref(n, int)); + } + + sList_delete(node); + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); +} +``` +结果: +``` +node data 3 +node data 4 +node data 5 +node data 6 +node data 7 +node data 8 +node data 9 +data 0 +data 1 +data 2 +``` + +### sList复制 + +```c +sList *sList_copy(sList *list, int begin, int end); +``` + +这个方法是会根据源链表的指定区间进行深拷贝一份新的链表。 + +```c +static void test_copy(void) +{ + sList *list = NULL, *copy = NULL; + + for (int i = 0; i < 10; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + copy = sList_copy(list, 2, -1); + if (!copy) + { + printf("sList_copy fail\r\n"); + } + + sList_forEach(copy, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); + sList_delete(copy); +} +``` +结果: +``` +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +### sList区间翻转 + +```c +int sList_reverse(sList *list, int begin, int end); +``` + +这个方法是会根据源链表的指定区间进行翻转。 + +```c +static void test_reverse(void) +{ + sList *list = NULL; + + for (int i = 0; i < 10; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + if (!sList_reverse(list, sList_front, sList_back)) + { + printf("sList_reverse fail\r\n"); + } + + sList_forEach(list, n) + { + printf("data %d\r\n", sList_ref(n, int)); + } + +FAIL: + sList_delete(list); +} +``` +结果: +``` +data 9 +data 8 +data 7 +data 6 +data 5 +data 4 +data 3 +data 2 +data 1 +data 0 +``` + +### sList获取指定结点 + +```c +sList *sList_to(sList *list, int index); +``` + +这个方法可以根据表头当前位置获取偏移指定位置的结点。 + +```c +static void test_to(void) +{ + sList *list = NULL, *node; + + for (int i = 0; i < 10; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + node = sList_to(list, 6); + if (!node) + { + printf("sList_to fail\r\n"); + goto FAIL; + } + + printf("data %d\r\n", sList_ref(node, int)); + +FAIL: + sList_delete(list); +} +``` +结果: +``` +data 6 +``` + +### sList大小 + +```c +int sList_size(sList *list); +``` + +这个方法获取链表的数据个数。 + +```c +static void test_size(void) +{ + sList *list = NULL; + + for (int i = 0; i < 10; i++) + { + if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; + } + + printf("size %d\r\n", sList_size(list)); + printf("size %d\r\n", sList_size(sList_to(list, 3))); + +FAIL: + sList_delete(list); +} +``` +结果: +``` +size 10 +size 7 +``` + +### sList遍历 + +```c +#define sList_forEach(list, node) +``` + +这个方法为遍历链表的方法,具体例子可以参考上文其他使用例子。 + +### sList结点数据引用 + +```c +#define sList_ref(node, type) +``` + +这个方法类似C++的引用,实则是操作指针(只是将其隐藏起来),读写更方便,但要注意的是数据操作别越界,比如本来该结点存储的是`char`型数据(分配空间也就只分配1个字节大小),如果当作`int`型使用那就越界了。具体例子可以参考上文其他使用例子。 + diff --git a/doc/tool.md b/doc/tool.md new file mode 100644 index 0000000..c855b49 --- /dev/null +++ b/doc/tool.md @@ -0,0 +1,4 @@ +## 介绍 + +此工具模块包含了一些常用的代码工具,如常用的宏定义等。 + diff --git a/doc/valloc.md b/doc/valloc.md new file mode 100644 index 0000000..6cb44ef --- /dev/null +++ b/doc/valloc.md @@ -0,0 +1,74 @@ +## 介绍 + +内存动态分配是指在程序运行过程中,根据程序的需要动态地分配内存空间,以便存储数据或创建对象。内存动态分配通常使用指针来实现,通过调用系统提供的内存分配函数(如malloc、calloc等)来申请内存空间,申请成功后,返回一个指向该内存空间的指针,进而在程序中使用该指针来访问分配的内存空间。 + +内存动态分配的优点是可以动态地分配内存空间,避免浪费,提高内存使用效率。同时,动态分配的内存空间也可以随着程序的需求进行动态的释放,避免内存泄漏和出错。但是,使用内存动态分配也存在一些缺点,如容易出现内存泄漏、空间碎片等问题,需要程序员具有一定的技巧和注意事项才能正确、高效地使用内存动态分配。 + +这里,介绍一种可以检查动态内存使用情况的小工具`valloc`,以便统计动态内存的使用情况,以及检查是否有空间碎片忘记释放。用其方法代替的内存分配方法,可以计算动态内存使用的情况。 + +## 接口 + +### 检查未释放内存 + +```c +void v_check_unfree(void); +``` + +### 获取已分配内存块计数 + +```c +int v_check_count(void); +``` + +### 获取已使用内存总大小 + +```c +int v_check_used(void); +``` + +## 使用 + +在使用上非常简单,如下几步。 + +**1** 在需要统计的源文件中添加 `valloc.h` 头文件,添加到 `stdlib.h` 后面,不管哪一行只要在其后面,`valloc.h`中的内存函数(`malloc()等`)会将`stdlib.h`中的覆盖掉,而使用`valloc.h`的函数。 + +**2** 在需要输出的地方,调用 valloc 的API即可。 + +### 例子 + +```c +#include +#include +#include +#include "init.h" +#include "valloc.h" + +static void test(void) +{ + void* p = NULL; + printf("malloc %p\r\n", malloc(0)); + p = realloc(NULL, 100); + p = malloc(64); + p = malloc(50); + printf("realloc %p\r\n", realloc(NULL, 0)); + printf("%d\r\n", *(int *)p); + free(p); + + v_check_unfree(); + printf("count = %d\r\n", v_check_count()); + printf("used = %d\r\n", v_check_used()); +} +init_export_app(test); +``` +结果: +``` +malloc 007B5B50 +realloc 007B5C20 +8086040 +address: 007B5B50, size: 0, file: test/test_valloc.c, line: 10 +address: 007B5B88, size: 100, file: test/test_valloc.c, line: 11 +address: 007B5C20, size: 0, file: test/test_valloc.c, line: 14 +address: 007B5C68, size: 64, file: test/test_valloc.c, line: 12 +count = 4 +used = 164 +``` diff --git a/doc/vlog.md b/doc/vlog.md new file mode 100644 index 0000000..36c54e3 --- /dev/null +++ b/doc/vlog.md @@ -0,0 +1,124 @@ +## 介绍 + +vlog是一个简单的日志输出模块,采用的是通道过滤模式,也就是提供了多个通道的日志输出,只有该通道使能了才会有日志输出。 + +## 接口 + +### vlog日志输出 + +```c +int vlog(vlogChnType channel, const char *format, ...); +``` + +channel提供了8个通道 +``` +#define VLOG_CHANNEL_0 +#define VLOG_CHANNEL_1 +#define VLOG_CHANNEL_2 +#define VLOG_CHANNEL_3 +#define VLOG_CHANNEL_4 +#define VLOG_CHANNEL_5 +#define VLOG_CHANNEL_6 +#define VLOG_CHANNEL_7 +``` + +可以进行多通道的输出,通过 `|` 连接则进行多通道的输出。 +默认是只打开了 `VLOG_CHANNEL_0`。 + +```c +static void test_vlog(void) +{ + vlog(VLOG_CHANNEL_0, "[VLOG_CHANNEL_0] vlog!\r\n"); + vlog(VLOG_CHANNEL_1, "[VLOG_CHANNEL_1] vlog!\r\n"); + vlog(VLOG_CHANNEL_2, "[VLOG_CHANNEL_2] vlog!\r\n"); +} +``` +结果: +``` +[VLOG_CHANNEL_0] vlog! +``` + +### vlog设置和获取通道过滤器 + +```c +void vlog_set_filter(vlogChnType mask); +vlogChnType vlog_get_filter(void); +``` + +其分别可以设置和获取开放的通道过滤器。 +为了更简便,提供了如下宏定义方法可以打开和关闭指定通道。 + +```c +#define VLOG_ENABALE(c) +#define VLOG_DISABALE(c) +``` + +```c +static void test_channel(void) +{ + VLOG_DISABALE(VLOG_CHANNEL_0); + VLOG_ENABALE(VLOG_CHANNEL_1); + + vlog(VLOG_CHANNEL_0, "[VLOG_CHANNEL_0] vlog!\r\n"); + vlog(VLOG_CHANNEL_1, "[VLOG_CHANNEL_1] vlog!\r\n"); + vlog(VLOG_CHANNEL_2, "[VLOG_CHANNEL_2] vlog!\r\n"); +} +``` +结果: +``` +[VLOG_CHANNEL_1] vlog! +``` + +### vlog控制台输出 + +```c +int vlog_set_console(vlogChnType channel, int console); +``` + +此方法是设置指定通道是否开启控制台输出,默认各个通道都已经打开控制台输出。 + +### vlog离线保存 + +```c +int vlog_set_offline(vlogChnType channel, const char *filename); +``` + +此方法可以开始离线保存和停止离线保存(传入空文件名)。 + +```c +static void test_offline(void) +{ + vlog_set_offline(VLOG_CHANNEL_0, "./test/file/log.txt"); + + for (int i = 0; i < 10; i++) + { + vlog(VLOG_CHANNEL_0, "vlog %d!\r\n", i); + } + + vlog_set_offline(VLOG_CHANNEL_0, NULL); +} +``` +结果: +[link](/test/file/log.txt) + +### vlog函数回调 + +```c +int vlog_set_func(vlogChnType channel, vlogFuncType func); +``` + +此方法设置vlog回调函数,提供了更大的灵活度。也就是函数vlog内容会输出到回调函数。 + +```c +typedef void (*vlogFuncType)(char *buf, int len); +``` + +### vlog获取通道状态 + +```c +vlogChnType vlog_get_console(void); +vlogChnType vlog_get_offline(void); +vlogChnType vlog_get_func(void); +``` + +其分别可以获取3种输出方向的各个通道状态掩码。 diff --git a/makefile b/makefile index 56d6aed..9b85227 100644 --- a/makefile +++ b/makefile @@ -42,9 +42,9 @@ SOURCES += $(wildcard $(PARSER_PATH)/*.c) # SOURCES += $(TESTSPACE)/test_init.c # SOURCES += $(TESTSPACE)/test_kern.c # SOURCES += $(TESTSPACE)/test_valloc.c -SOURCES += $(TESTSPACE)/test_arg.c +# SOURCES += $(TESTSPACE)/test_arg.c # SOURCES += $(TESTSPACE)/test_vstd.c -# SOURCES += $(TESTSPACE)/test_vlog.c +SOURCES += $(TESTSPACE)/test_vlog.c # SOURCES += $(TESTSPACE)/test_ini.c # SOURCES += $(TESTSPACE)/test_txls.c # SOURCES += $(TESTSPACE)/test_xml.c diff --git a/source/01_general/cQueue.h b/source/01_general/cQueue.h index db76546..7490342 100644 --- a/source/01_general/cQueue.h +++ b/source/01_general/cQueue.h @@ -89,7 +89,7 @@ int cQueue_index(cQueue *q, unsigned int index); * \param[in] i: index starting from queue header * \return Reference to queue data */ -#define cQueue_at(qObject, i) ((qObject).data[cQueue_index(&((qObject).queue))]) +#define cQueue_at(qObject, i) ((qObject).data[cQueue_index(&((qObject).queue), (i))]) #if 0 /* cQueue demo */ void test_int(void) diff --git a/source/01_general/dList.h b/source/01_general/dList.h index 7d1a4a0..722cccc 100644 --- a/source/01_general/dList.h +++ b/source/01_general/dList.h @@ -200,6 +200,7 @@ int dList_get(dList *list, void* data, int size); * \param[in] list The head of the doubly linked list. * \param[in] node The iterator variable to represent each node in the list. */ +#define dList_forEach(list, node) dList_forEachForward(list, node) #define dList_forEachForward(list, node) \ for (dList* (node) = (list); (node); (node) = (((node)->next==(list))?NULL:(node)->next)) diff --git a/source/01_general/vlog.c b/source/01_general/vlog.c index d8e0a39..8c02311 100644 --- a/source/01_general/vlog.c +++ b/source/01_general/vlog.c @@ -6,107 +6,214 @@ * \unit vlog * \brief This is a simple log module for C language * \author Lamdonn - * \version v1.0.0 + * \version v1.1.0 * \license GPL-2.0 * \copyright Copyright (C) 2023 Lamdonn. ********************************************************************************************************/ #include "vlog.h" #include -static char channel_table = VLOG_CHANNEL_0; /* Channel configuration, default only opens channel 0 */ -static char vlog_buffer[VLOG_BUFFER_SIZE]; /* vlog output buffer */ -static vlog_func_t vlog_func = 0; /* Additional vlog callback function */ -static FILE *vlog_file = 0; /* Offline recorded files */ +#define VLOG_CHANNEL_MAX (8) -/** - * \brief output log information - * \param[in] channel: VLOG_CHANNEL_XXX, from 0 to 7 - * \param[in] format: format string - * \param[in] ...: format parameters - * \return log string length - */ -int vlog(char channel, const char *format, ...) +typedef struct { - int len = 0; + vlogChnType channel; /**< channel mask */ + char console; /**< flag whether to output to the console */ + vlogFuncType func; /**< flag whether to output to the callback function */ + FILE *file; /**< flag whether to output to the offline file, protected ones cannot be modified directly */ +} vlogChnParaType; + +/* Channel configuration, default only opens channel 0 */ +static vlogChnType cmask = VLOG_CHANNEL_0; + +/* vlog output buffer */ +static char vlog_buffer[VLOG_BUFFER_SIZE]; + +/* vlog channel parameters */ +static vlogChnParaType chn[VLOG_CHANNEL_MAX] = { +/* channel console func file*/ + {VLOG_CHANNEL_0, 1, 0, 0}, + {VLOG_CHANNEL_1, 1, 0, 0}, + {VLOG_CHANNEL_2, 1, 0, 0}, + {VLOG_CHANNEL_3, 1, 0, 0}, + {VLOG_CHANNEL_4, 1, 0, 0}, + {VLOG_CHANNEL_5, 1, 0, 0}, + {VLOG_CHANNEL_6, 1, 0, 0}, + {VLOG_CHANNEL_7, 1, 0, 0}, +}; + +int vlog(vlogChnType channel, const char *format, ...) +{ + int len = -1; + vlogChnType c; va_list args; - /* Check channel effectiveness */ - if (channel & channel_table) + for (c = 0; c < VLOG_CHANNEL_MAX; c++) { - va_start(args, format); + /* match channel */ + if ((chn[c].channel & cmask) && (chn[c].channel & channel)) + { + /* The current channel has a concrete output object */ + if (chn[c].console || chn[c].file || chn[c].func) + { + /* If the output isn't already formatted, it needs to be. And format it only once. */ + if (len == -1) + { + va_start(args, format); + len = vsnprintf(vlog_buffer, sizeof(vlog_buffer), format, args); + va_end(args); + if (len < 0) break; + } + + /* Output to default console */ + if (chn[c].console) printf(vlog_buffer); - len = vsnprintf(vlog_buffer, sizeof(vlog_buffer), format, args); + /* Output to offline file */ + if (chn[c].file) fwrite(vlog_buffer, sizeof(char), len, chn[c].file); - /* Output to default console */ - printf(vlog_buffer); - - /* Output to offline file */ - if (vlog_file) fwrite(vlog_buffer, sizeof(char), len, vlog_file); - - /* Output to callback function */ - if (vlog_func) vlog_func(vlog_buffer, len); - - va_end(args); + /* Output to callback function */ + if (chn[c].func) chn[c].func(vlog_buffer, len); + } + } } return len; } -/** - * \brief set vlog channel configuration - * \param[in] channel: VLOG_CHANNEL_XXX, from 0 to 7, multiple channels can be selected through `|` - * \return none - */ -void vlog_set_channel(char channel) +void vlog_set_filter(vlogChnType mask) { - channel_table = channel; + cmask = mask; } -/** - * \brief get vlog channel configuration - * \return channel configuration - */ -char vlog_get_channel(void) +vlogChnType vlog_get_filter(void) { - return channel_table; + return cmask; } -/** - * \brief start vlog offline save, need to be used in pairs with vlog_stop_offline() - * \param[in] *filename: offline save file name - * \return 1 success or 0 fail - */ -int vlog_start_offline(const char *filename) +int vlog_set_console(vlogChnType channel, int console) { + int count = 0; + vlogChnType c; + + for (c = 0; c < VLOG_CHANNEL_MAX; c++) + { + /* match channel */ + if (chn[c].channel & channel) + { + chn[c].console = (console ? 1: 0); + + count++; + } + } + + return count; +} + +vlogChnType vlog_get_console(void) +{ + vlogChnType mask = 0; + vlogChnType c; + + for (c = 0; c < VLOG_CHANNEL_MAX; c++) + { + /* match channel */ + if (chn[c].console) + { + mask |= chn[c].channel; + } + } + + return mask; +} + +int vlog_set_offline(vlogChnType channel, const char *filename) +{ + int count = 0; + vlogChnType c; FILE *file; - file = fopen(filename, "a+"); - if (!file) return 0; - - vlog_file = file; - - return 1; -} - -/** - * \brief stop vlog offline save, need to be used in pairs with vlog_start_offline() - * \return none - */ -void vlog_stop_offline(void) -{ - if (vlog_file) + for (c = 0; c < VLOG_CHANNEL_MAX; c++) { - fclose(vlog_file); - vlog_file = 0; + /* match channel */ + if (chn[c].channel & channel) + { + /* If the name passed is empty, then the default is to turn off offline output */ + if (!filename) + { + fclose(chn[c].file); + chn[c].file = 0; + continue; + } + + /* If a file has been defined for offline saving, it needs to be closed to open a new file */ + if (chn[c].file) + { + fclose(chn[c].file); + } + + /* Open an offline save file as an append */ + file = fopen(filename, "a+"); + if (!file) continue; + + chn[c].file = file; + + count++; + } } + + return count; } -/** - * \brief set additional vlog callback function - * \return none - */ -void vlog_set_func(vlog_func_t func) +vlogChnType vlog_get_offline(void) { - vlog_func = func; + vlogChnType mask = 0; + vlogChnType c; + + for (c = 0; c < VLOG_CHANNEL_MAX; c++) + { + /* match channel */ + if (chn[c].file) + { + mask |= chn[c].channel; + } + } + + return mask; +} + +int vlog_set_func(vlogChnType channel, vlogFuncType func) +{ + int count = 0; + vlogChnType c; + + for (c = 0; c < VLOG_CHANNEL_MAX; c++) + { + /* match channel */ + if (chn[c].channel & channel) + { + chn[c].func = func; + + count++; + } + } + + return count; +} + +vlogChnType vlog_get_func(void) +{ + vlogChnType mask = 0; + vlogChnType c; + + for (c = 0; c < VLOG_CHANNEL_MAX; c++) + { + /* match channel */ + if (chn[c].func) + { + mask |= chn[c].channel; + } + } + + return mask; } diff --git a/source/01_general/vlog.h b/source/01_general/vlog.h index 9e3bd79..f9b779b 100644 --- a/source/01_general/vlog.h +++ b/source/01_general/vlog.h @@ -6,7 +6,7 @@ * \unit vlog * \brief This is a simple log module for C language * \author Lamdonn - * \version v1.0.0 + * \version v1.1.0 * \license GPL-2.0 * \copyright Copyright (C) 2023 Lamdonn. ********************************************************************************************************/ @@ -15,31 +15,100 @@ #include -/* vlog buffer size */ -#define VLOG_BUFFER_SIZE (1024) +/* Version infomation */ -/* vlog channel */ -#define VLOG_CHANNEL_0 (0x01) -#define VLOG_CHANNEL_1 (0x02) -#define VLOG_CHANNEL_2 (0x04) -#define VLOG_CHANNEL_3 (0x08) -#define VLOG_CHANNEL_4 (0x10) -#define VLOG_CHANNEL_5 (0x20) -#define VLOG_CHANNEL_6 (0x40) -#define VLOG_CHANNEL_7 (0x80) -#define VLOG_CHANNEL_ALL (0xFF) +#define VLOG_V_MAJOR 1 +#define VLOG_V_MINOR 1 +#define VLOG_V_PATCH 0 -#define VLOG_ENABALE(c) vlog_set_channel(vlog_get_channel() | (1 << (c))) -#define VLOG_DISABALE(c) vlog_set_channel(vlog_get_channel() & ~(1 << (c))) +/* channel type define */ + +typedef unsigned char vlogChnType; /* callback function define */ -typedef void (*vlog_func_t)(char *buf, int len); -int vlog(char channel, const char *format, ...); -void vlog_set_channel(char channel); -char vlog_get_channel(void); -int vlog_start_offline(const char *filename); -void vlog_stop_offline(void); -void vlog_set_func(vlog_func_t func); +typedef void (*vlogFuncType)(char *buf, int len); + +/* vlog buffer size */ + +#define VLOG_BUFFER_SIZE (1024) + +/* vlog channel */ + +#define VLOG_CHANNEL_0 ((vlogChnType)(0x01)) +#define VLOG_CHANNEL_1 ((vlogChnType)(0x02)) +#define VLOG_CHANNEL_2 ((vlogChnType)(0x04)) +#define VLOG_CHANNEL_3 ((vlogChnType)(0x08)) +#define VLOG_CHANNEL_4 ((vlogChnType)(0x10)) +#define VLOG_CHANNEL_5 ((vlogChnType)(0x20)) +#define VLOG_CHANNEL_6 ((vlogChnType)(0x40)) +#define VLOG_CHANNEL_7 ((vlogChnType)(0x80)) +#define VLOG_CHANNEL_ALL ((vlogChnType)(0xFF)) + +/* Enable and disable a channel */ + +#define VLOG_ENABALE(c) vlog_set_filter(vlog_get_filter() | (c)) +#define VLOG_DISABALE(c) vlog_set_filter(vlog_get_filter() & ~(c)) + +/** + * \brief output log information + * \param[in] channel: VLOG_CHANNEL_XXX, from 0 to 7 + * \param[in] format: format string + * \param[in] ...: format parameters + * \return log string length + */ +int vlog(vlogChnType channel, const char *format, ...); + +/** + * \brief set vlog channel filter configuration + * \param[in] mask: VLOG_CHANNEL_XXX, from 0 to 7, multiple channels can be selected through `|` + * \return none + */ +void vlog_set_filter(vlogChnType mask); + +/** + * \brief get vlog channel filter configuration + * \return channel filter configuration mask + */ +vlogChnType vlog_get_filter(void); + +/** + * \brief turns vlog output to the console on or off + * \param[in] channel: offline save file name + * \param[in] console: 0 off or others on + * \return 1 success or 0 fail + */ +int vlog_set_console(vlogChnType channel, int console); + +/** + * \brief gets the channel console open status + * \return status mask + */ +vlogChnType vlog_get_console(void); + +/** + * \brief start vlog offline save, need to be used in pairs with stop vlog offline save + * \param[in] filename: offline save file name, or NULL(0) stop vlog offline save + * \return 1 success or 0 fail + */ +int vlog_set_offline(vlogChnType channel, const char *filename); + +/** + * \brief gets the channel offline open status + * \return status mask + */ +vlogChnType vlog_get_offline(void); + +/** + * \brief set additional vlog callback function + * \return none + */ +int vlog_set_func(vlogChnType channel, vlogFuncType func); + +/** + * \brief gets the channel callback function open status + * \return status mask + */ +vlogChnType vlog_get_func(void); #endif diff --git a/test/file/log.txt b/test/file/log.txt new file mode 100644 index 0000000..49916b6 --- /dev/null +++ b/test/file/log.txt @@ -0,0 +1,10 @@ +vlog 0! +vlog 1! +vlog 2! +vlog 3! +vlog 4! +vlog 5! +vlog 6! +vlog 7! +vlog 8! +vlog 9! diff --git a/test/test_cQueue.c b/test/test_cQueue.c index 4b297d3..a3eacf5 100644 --- a/test/test_cQueue.c +++ b/test/test_cQueue.c @@ -22,6 +22,8 @@ void test_int(void) cQueue_push(intQueue, i); } + printf("cQueue[5] = %d\r\n", cQueue_at(intQueue, 5)); + while (intQueue.queue.size > 0) { int data; diff --git a/test/test_dList.c b/test/test_dList.c index 2dff1ba..f907a72 100644 --- a/test/test_dList.c +++ b/test/test_dList.c @@ -208,7 +208,7 @@ static void test_to(void) if (!dList_pushBack(&list, &i, sizeof(i))) goto FAIL; } - node = dList_to(list, -11); + node = dList_to(list, -6); if (!node) { printf("dList_to fail\r\n"); @@ -326,9 +326,9 @@ static void test(void) // test_detach(); // test_push(); // test_pop(); - // test_to(); + test_to(); // test_size(); - test_append(); + // test_append(); // test_copy(); // test_reverse(); diff --git a/test/test_sList.c b/test/test_sList.c index b53cbf9..610911e 100644 --- a/test/test_sList.c +++ b/test/test_sList.c @@ -41,6 +41,7 @@ static void test_set(void) sList *list = NULL; int dataInt = 3; char *dataString = "Hello sList"; + char outBuffer[32]; list = sList_create(); if (!list) @@ -57,6 +58,9 @@ static void test_set(void) sList_set(list, dataString, strlen(dataString) + 1); printf("list->data %s\r\n", ((char *)(list->data))); + sList_get(list, outBuffer, sizeof(outBuffer)); + printf("get data %s\r\n", outBuffer); + sList_delete(list); } @@ -210,7 +214,7 @@ static void test_to(void) if (!sList_pushBack(&list, &i, sizeof(i))) goto FAIL; } - node = sList_to(list, 10); + node = sList_to(list, 6); if (!node) { printf("sList_to fail\r\n"); @@ -320,10 +324,10 @@ static void test(void) // test_insert(); // test_erase(); // test_attach(); - test_detach(); + // test_detach(); // test_push(); // test_pop(); - // test_to(); + test_to(); // test_size(); // test_append(); // test_copy(); diff --git a/test/test_valloc.c b/test/test_valloc.c index a81c95b..0560eb4 100644 --- a/test/test_valloc.c +++ b/test/test_valloc.c @@ -1,10 +1,10 @@ -#include "test.h" #include #include #include +#include "init.h" #include "valloc.h" -void test_main(void) +static void test(void) { void* p = NULL; printf("malloc %p\r\n", malloc(0)); @@ -19,10 +19,4 @@ void test_main(void) printf("count = %d\r\n", v_check_count()); printf("used = %d\r\n", v_check_used()); } - -std_return test_init(void) -{ - test_main(); - - return E_OK; -} \ No newline at end of file +init_export_app(test); \ No newline at end of file diff --git a/test/test_vlog.c b/test/test_vlog.c index c4700e0..419618e 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -7,14 +7,40 @@ static void vlog_callback(char *buf, int len) printf("vlog_callback[%d]: %s", len, buf); } +static void test_vlog(void) +{ + vlog(VLOG_CHANNEL_0, "[VLOG_CHANNEL_0] vlog!\r\n"); + vlog(VLOG_CHANNEL_1, "[VLOG_CHANNEL_1] vlog!\r\n"); + vlog(VLOG_CHANNEL_2, "[VLOG_CHANNEL_2] vlog!\r\n"); +} + +static void test_channel(void) +{ + VLOG_DISABALE(VLOG_CHANNEL_0); + VLOG_ENABALE(VLOG_CHANNEL_1); + + vlog(VLOG_CHANNEL_0, "[VLOG_CHANNEL_0] vlog!\r\n"); + vlog(VLOG_CHANNEL_1, "[VLOG_CHANNEL_1] vlog!\r\n"); + vlog(VLOG_CHANNEL_2, "[VLOG_CHANNEL_2] vlog!\r\n"); +} + +static void test_offline(void) +{ + vlog_set_offline(VLOG_CHANNEL_0, "./test/file/log.txt"); + + for (int i = 0; i < 10; i++) + { + vlog(VLOG_CHANNEL_0, "vlog %d!\r\n", i); + } + + vlog_set_offline(VLOG_CHANNEL_0, NULL); +} + static void test(void) { - // vlog_start_offline("log_20231227.txt"); - vlog_set_func(vlog_callback); - vlog(VLOG_CHANNEL_0, "Hello vlog!\r\n"); - vlog(VLOG_CHANNEL_0, "Hello vlog!\r\n"); - vlog(VLOG_CHANNEL_0, "Hello vlog!\r\n"); - vlog(VLOG_CHANNEL_0, "Hello vlog!\r\n"); - // vlog_stop_offline(); + // vlog_set_func(vlog_callback); + // test_vlog(); + // test_channel(); + test_offline(); } init_export_app(test); \ No newline at end of file