diff --git a/.gitignore b/.gitignore index 026730a..bf1bb21 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,5 @@ node_modules dist dist-ssr *.local -# .vscode/ +.vscode/ built/ \ No newline at end of file diff --git a/README.en.md b/README.en.md index f0a0e80..d82ef91 100644 --- a/README.en.md +++ b/README.en.md @@ -18,60 +18,60 @@ It has the characteristics of **simplicity, universality, and efficiency**, with | module | version | usage | path | describe | |:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------| -| overall | 00.02.00 | [link](README.md) | [path](./) | Overall -| init | 01.00.00 | [link](/doc/init.md) | [path](./source/00_application) | Initialize export module -| console | 01.00.00 | [link](/doc/console.md) | [path](./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) | [path](./source/01_general) | Indefinite parameters, obtain the number of indefinite parameters and specified parameters -| calculate | 01.00.00 | [link](/doc/calculate.md) | [path](./source/01_general) | Calculation module, input calculation expression to obtain calculation result -| command | 01.00.00 | [link](/doc/command.md) | [path](./source/01_general) | Command parsing module, input string commands (similar to shell commands), execute corresponding command functions -| cPatten | 01.00.00 | [link](/doc/cPatten.md) | [path](./source/01_general) | Artistic character patterns -| cQueue | 01.00.00 | [link](/doc/cQueue.md) | [path](./source/01_general) | Universal queue controller -| sList | 01.00.01 | [link](/doc/sList.md) | [path](./source/01_general) | Universal single-link list controller -| dList | 01.00.01 | [link](/doc/dList.md) | [path](./source/01_general) | Universal double-link list controller -| fsm | 01.00.00 | [link](/doc/fsm.md) | [path](./source/01_general) | Universal finite state machine module -| kern | 01.00.00 | [link](/doc/kern.md) | [path](./source/01_general) | The kernel module for scheduling periodic tasks is mostly used in varch testing -| oscp | 01.00.00 | [link](/doc/oscp.md) | [path](./source/01_general) | Analog oscilloscope module, can easily monitor the waveform of variable changes -| tool | 01.00.00 | [link](/doc/tool.md) | [path](./source/01_general) | General tools code -| valloc | 01.00.00 | [link](/doc/valloc.md) | [path](./source/01_general) | Dynamic memory usage testing tool -| vlog | 01.01.00 | [link](/doc/vlog.md) | [path](./source/01_general) | Log output module -| intl | 01.00.00 | [link](/doc/intl.md) | [path](./source/01_general) | Large integer arithmetic module -| date | 01.00.00 | [link](/doc/date.md) | [path](./source/01_general) | Date calculation module, calculating date differences, printing calendars, etc -| vctype | 01.00.00 | [link](/doc/vctype.md) | [path](./source/02_vstd) | Similar to the C standard library ctype -| vmath | 01.00.00 | [link](/doc/vmath.md) | [path](./source/02_vstd) | Similar to the C standard library math -| vmem | 01.00.00 | [link](/doc/vmem.md) | [path](./source/02_vstd) | Simple implementation of memory pool -| vstddef | 01.00.00 | [link](/doc/vstddef.md) | [path](./source/02_vstd) | Similar to the C standard library stddef -| vstdint | 01.00.00 | [link](/doc/vstdint.md) | [path](./source/02_vstd) | Similar to the C standard library stdint -| vstdlib | 01.00.00 | [link](/doc/vstdlib.md) | [path](./source/02_vstd) | Similar to the C standard library stdlib -| vstring | 01.00.00 | [link](/doc/vstring.md) | [path](./source/02_vstd) | Similar to the C standard library string -| queue | 01.00.00 | [link](/doc/queue.md) | [path](./source/03_container) | Universal queue container -| stack | 01.00.00 | [link](/doc/stack.md) | [path](./source/03_container) | Universal stack container -| deque | 01.00.00 | [link](/doc/deque.md) | [path](./source/03_container) | Universal double-end queue container -| list | 01.00.00 | [link](/doc/list.md) | [path](./source/03_container) | Universal list container, single-link and internal iteration -| vector | 01.00.00 | [link](/doc/vector.md) | [path](./source/03_container) | Universal vector(array) container -| str | 01.00.00 | [link](/doc/str.md) | [path](./source/03_container) | String class -| dict | 01.00.00 | [link](/doc/dict.md) | [path](./source/03_container) | Universal dictionarie container, implementation based on hash table -| heap | 01.00.00 | [link](/doc/heap.md) | [path](./source/03_container) | Universal heap container -| set | 01.00.00 | [link](/doc/set.md) | [path](./source/03_container) | Universal set container, implementation based on RB-tree -| map | 01.00.00 | [link](/doc/map.md) | [path](./source/03_container) | Universal map container, implementation based on RB-tree -| tree | 01.00.00 | [link](/doc/tree.md) | [path](./source/03_container) | Universal tree container -| graph | 01.00.00 | [link](/doc/graph.md) | [path](./source/03_container) | Universal graph container -| check | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | Verification algorithm, sum check, parity check, XOR check, LRC check -| crc | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | Universal and standard CRC algorithms -| encrypt | 01.00.00 | [link](/doc/encrypt.md) | [path](./source/04_algorithm) | Encryption and decryption algorithms -| filter | 01.00.00 | [link](/doc/filter.md) | [path](./source/04_algorithm) | Filter algorithms, median, kalman, average -| hash | 01.00.00 | [link](/doc/hash.md) | [path](./source/04_algorithm) | Hash algorithms, bkdr, ap, djb, js, rs, sdbm, pjw, elf, dek, bp, fnv, jdk6 -| pid | 01.00.00 | [link](/doc/pid.md) | [path](./source/04_algorithm) | PID control algorithm calculator -| search | 01.00.00 | [link](/doc/search.md) | [path](./source/04_algorithm) | Universal search algorithms, linear, binary -| sort | 01.00.00 | [link](/doc/sort.md) | [path](./source/04_algorithm) | Universal sorting algorithms (various data structures), bubble, select, insert, hill, quick, heap -| csv | 01.00.00 | [link](/doc/csv.md) | [path](./source/05_parser) | CSV file parsing generator -| ini | 01.00.00 | [link](/doc/ini.md) | [path](./source/05_parser) | INI configuration file parsing generator -| json | 01.00.00 | [link](/doc/json.md) | [path](./source/05_parser) | JSON file parsing generator -| txls | 01.00.00 | [link](/doc/txls.md) | [path](./source/05_parser) | TXLS file parsing generator -| xml | 01.00.00 | [link](/doc/xml.md) | [path](./source/05_parser) | XML file parsing generator -| ramt | 01.00.00 | [link](/doc/ramt.md) | [path](./source/06_performance) | RAM test module -| romt | 01.00.00 | [link](/doc/romt.md) | [path](./source/06_performance) | ROM test module -| cpul | 01.00.00 | [link](/doc/cpul.md) | [path](./source/06_performance) | CPU load test module, including obtaining and increasing load -| unitt | 01.00.00 | [link](/doc/unitt.md) | [path](./source/06_performance) | Simple unit test module +| overall | 00.02.01 | [link](README.en.md) | [path](./) | Overall +| init | 01.00.00 | [link](/doc/init.en.md) | [path](./source/00_application) | Initialize export module +| console | 01.00.00 | [link](/doc/console.en.md) | [path](./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.en.md) | [path](./source/01_general) | Indefinite parameters, obtain the number of indefinite parameters and specified parameters +| calculate | 01.00.00 | [link](/doc/calculate.en.md) | [path](./source/01_general) | Calculation module, input calculation expression to obtain calculation result +| command | 01.00.00 | [link](/doc/command.en.md) | [path](./source/01_general) | Command parsing module, input string commands (similar to shell commands), execute corresponding command functions +| cPatten | 01.00.00 | [link](/doc/cPatten.en.md) | [path](./source/01_general) | Artistic character patterns +| cQueue | 01.00.00 | [link](/doc/cQueue.en.md) | [path](./source/01_general) | Universal queue controller +| sList | 01.00.01 | [link](/doc/sList.en.md) | [path](./source/01_general) | Universal single-link list controller +| dList | 01.00.01 | [link](/doc/dList.en.md) | [path](./source/01_general) | Universal double-link list controller +| fsm | 01.00.00 | [link](/doc/fsm.en.md) | [path](./source/01_general) | Universal finite state machine module +| kern | 01.00.00 | [link](/doc/kern.en.md) | [path](./source/01_general) | The kernel module for scheduling periodic tasks is mostly used in varch testing +| oscp | 01.00.00 | [link](/doc/oscp.en.md) | [path](./source/01_general) | Analog oscilloscope module, can easily monitor the waveform of variable changes +| tool | 01.00.00 | [link](/doc/tool.en.md) | [path](./source/01_general) | General tools code +| valloc | 01.00.00 | [link](/doc/valloc.en.md) | [path](./source/01_general) | Dynamic memory usage testing tool +| vlog | 01.01.00 | [link](/doc/vlog.en.md) | [path](./source/01_general) | Log output module +| intl | 01.00.00 | [link](/doc/intl.en.md) | [path](./source/01_general) | Large integer arithmetic module +| date | 01.00.00 | [link](/doc/date.en.md) | [path](./source/01_general) | Date calculation module, calculating date differences, printing calendars, etc +| vctype | 01.00.00 | [link](/doc/vctype.en.md) | [path](./source/02_vstd) | Similar to the C standard library ctype +| vmath | 01.00.00 | [link](/doc/vmath.en.md) | [path](./source/02_vstd) | Similar to the C standard library math +| vmem | 01.00.00 | [link](/doc/vmem.en.md) | [path](./source/02_vstd) | Simple implementation of memory pool +| vstddef | 01.00.00 | [link](/doc/vstddef.en.md) | [path](./source/02_vstd) | Similar to the C standard library stddef +| vstdint | 01.00.00 | [link](/doc/vstdint.en.md) | [path](./source/02_vstd) | Similar to the C standard library stdint +| vstdlib | 01.00.00 | [link](/doc/vstdlib.en.md) | [path](./source/02_vstd) | Similar to the C standard library stdlib +| vstring | 01.00.00 | [link](/doc/vstring.en.md) | [path](./source/02_vstd) | Similar to the C standard library string +| queue | 01.00.00 | [link](/doc/queue.en.md) | [path](./source/03_container) | Universal queue container +| stack | 01.00.00 | [link](/doc/stack.en.md) | [path](./source/03_container) | Universal stack container +| deque | 01.00.00 | [link](/doc/deque.en.md) | [path](./source/03_container) | Universal double-end queue container +| list | 01.00.00 | [link](/doc/list.en.md) | [path](./source/03_container) | Universal list container, single-link and internal iteration +| vector | 01.00.00 | [link](/doc/vector.en.md) | [path](./source/03_container) | Universal vector(array) container +| str | 01.00.00 | [link](/doc/str.en.md) | [path](./source/03_container) | String class +| dict | 01.00.00 | [link](/doc/dict.en.md) | [path](./source/03_container) | Universal dictionarie container, implementation based on hash table +| heap | 01.00.00 | [link](/doc/heap.en.md) | [path](./source/03_container) | Universal heap container +| set | 01.00.00 | [link](/doc/set.en.md) | [path](./source/03_container) | Universal set container, implementation based on RB-tree +| map | 01.00.00 | [link](/doc/map.en.md) | [path](./source/03_container) | Universal map container, implementation based on RB-tree +| tree | 01.00.00 | [link](/doc/tree.en.md) | [path](./source/03_container) | Universal tree container +| graph | 01.00.00 | [link](/doc/graph.en.md) | [path](./source/03_container) | Universal graph container +| check | 01.00.00 | [link](/doc/check.en.md) | [path](./source/04_algorithm) | Verification algorithm, sum check, parity check, XOR check, LRC check +| crc | 01.00.00 | [link](/doc/crc.en.md) | [path](./source/04_algorithm) | Universal and standard CRC algorithms +| encrypt | 01.00.00 | [link](/doc/encrypt.en.md) | [path](./source/04_algorithm) | Encryption and decryption algorithms +| filter | 01.00.00 | [link](/doc/filter.en.md) | [path](./source/04_algorithm) | Filter algorithms, median, kalman, average +| hash | 01.00.00 | [link](/doc/hash.en.md) | [path](./source/04_algorithm) | Hash algorithms, bkdr, ap, djb, js, rs, sdbm, pjw, elf, dek, bp, fnv, jdk6 +| pid | 01.00.00 | [link](/doc/pid.en.md) | [path](./source/04_algorithm) | PID control algorithm calculator +| search | 01.00.00 | [link](/doc/search.en.md) | [path](./source/04_algorithm) | Universal search algorithms, linear, binary +| sort | 01.00.00 | [link](/doc/sort.en.md) | [path](./source/04_algorithm) | Universal sorting algorithms (various data structures), bubble, select, insert, hill, quick, heap +| csv | 01.00.00 | [link](/doc/csv.en.md) | [path](./source/05_parser) | CSV file parsing generator +| ini | 01.00.00 | [link](/doc/ini.en.md) | [path](./source/05_parser) | INI configuration file parsing generator +| json | 01.00.00 | [link](/doc/json.en.md) | [path](./source/05_parser) | JSON file parsing generator +| txls | 01.00.00 | [link](/doc/txls.en.md) | [path](./source/05_parser) | TXLS file parsing generator +| xml | 01.00.00 | [link](/doc/xml.en.md) | [path](./source/05_parser) | XML file parsing generator +| ramt | 01.00.00 | [link](/doc/ramt.en.md) | [path](./source/06_performance) | RAM test module +| romt | 01.00.00 | [link](/doc/romt.en.md) | [path](./source/06_performance) | ROM test module +| cpul | 01.00.00 | [link](/doc/cpul.en.md) | [path](./source/06_performance) | CPU load test module, including obtaining and increasing load +| unitt | 01.00.00 | [link](/doc/unitt.en.md) | [path](./source/06_performance) | Simple unit test module ## Usage diff --git a/README.md b/README.md index 2a2e591..fd42bc1 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | module | version | usage | path | describe | |:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------| -| overall | 00.02.00 | [link](README.md) | [path](./) | 整体 +| overall | 00.02.01 | [link](README.md) | [path](./) | 整体 | init | 01.00.00 | [link](/doc/init.md) | [path](./source/00_application) | 初始化导出模块 | console | 01.00.00 | [link](/doc/console.md) | [path](./source/00_application/console) | 控制台命令输入,结合 `command` 模块,解析在控制台中输入的命令 | arg | 01.00.00 | [link](/doc/arg.md) | [path](./source/01_general) | 不定参数,获取不定参数和指定参数的个数 @@ -56,7 +56,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | tree | 01.00.00 | [link](/doc/tree.md) | [path](./source/03_container) | 通用树容器 | graph | 01.00.00 | [link](/doc/graph.md) | [path](./source/03_container) | 通用图容器 | check | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | 校验算法,求和校验,奇偶校验,异或校验,LRC校验 -| crc | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | 通用标准CRC算法 +| crc | 01.00.00 | [link](/doc/crc.md) | [path](./source/04_algorithm) | 通用标准CRC算法 | encrypt | 01.00.00 | [link](/doc/encrypt.md) | [path](./source/04_algorithm) | 加密解密算法 | filter | 01.00.00 | [link](/doc/filter.md) | [path](./source/04_algorithm) | 滤波算法,中值,卡尔曼,平均值 | hash | 01.00.00 | [link](/doc/hash.md) | [path](./source/04_algorithm) | 哈希算法,bkdr、ap、djb、js、rs、sdbm、pjw、elf、dek、bp、fnv、jdk6 diff --git a/doc/arg.en.md b/doc/arg.en.md new file mode 100644 index 0000000..a972bd5 --- /dev/null +++ b/doc/arg.en.md @@ -0,0 +1,53 @@ +## Introduction + +The `arg` module is primarily designed for scenarios that involve variable numbers of arguments, allowing you to retrieve the count of those arguments and access specific positional arguments. + +## Interface + +### Functions + +```c +#define ARG_MAX // Maximum number of supported variable arguments +#define ARGC(...) // Current number of variable arguments, does not support distinguishing empty arguments +#define ARGC0(...) // Current number of variable arguments, supports distinguishing empty arguments but requires more compile-time space; use as needed +#define ARGS(x, ...) // Retrieve the argument at a specified position +``` +In usage, you only need to focus on the macro definitions above; the rest can be ignored. + +Example usage: +```c +static void test(void) +{ + printf("ARG_MAX %d\r\n", ARG_MAX); + + printf("ARGC %d\r\n", ARGC()); + printf("ARGC %d\r\n", ARGC(A)); + printf("ARGC %d\r\n", ARGC(A, B)); + printf("ARGC %d\r\n", ARGC(A, B, C)); + + printf("ARGC %d\r\n", ARGC0()); + printf("ARGC %d\r\n", ARGC0(A)); + printf("ARGC %d\r\n", ARGC0(A, B)); + printf("ARGC %d\r\n", ARGC0(A, B, C)); + + printf("ARGS %s\r\n", ARGS(0, "arg0", "arg1", "arg2")); + printf("ARGS %s\r\n", ARGS(1, "arg0", "arg1", "arg2")); + printf("ARGS %s\r\n", ARGS(2, "arg0", "arg1", "arg2")); +} +``` +Results: +``` +ARG_MAX 124 +ARGC 1 +ARGC 1 +ARGC 2 +ARGC 3 +ARGC 0 +ARGC 1 +ARGC 2 +ARGC 3 +ARGS arg0 +ARGS arg1 +ARGS arg2 +``` + diff --git a/doc/arg.md b/doc/arg.md index 2474883..588eecd 100644 --- a/doc/arg.md +++ b/doc/arg.md @@ -1,6 +1,6 @@ ## 介绍 -arg模块主要针对应用布丁参数的场合,可以获取不定参数的个数及指定位置的参数。 +arg模块主要针对应用不定参数的场合,可以获取不定参数的个数及指定位置的参数。 ## 接口 diff --git a/doc/cPatten.en.md b/doc/cPatten.en.md new file mode 100644 index 0000000..5b60b80 --- /dev/null +++ b/doc/cPatten.en.md @@ -0,0 +1,52 @@ +## Introduction + +cPatten is a simple character art module that forms artistic characters with characters. + +## Interface + +### Functions + +```c +int cPatten_setMask(char c); // Set the characters that make up the artistic characters +void cPatten_showChar(char c); // Display the artistic character +void cPatten_showString(char *s); // Display the artistic string +``` + +Usage Example: +```c +static void show(void) +{ + cPatten_setMask('`'); + cPatten_showString("Varch"); + cPatten_setMask('*'); + cPatten_showString("Varch"); + cPatten_setMask('0'); + cPatten_showString("Varch"); + cPatten_setMask('#'); + cPatten_showString("Varch"); +} +``` +Result: +``` + ` ` ``` ``` ``` ` ` + ` ` ` ` ` ` ` ` ` ` + ` ` ````` ``` ` ````` + ` ` ` ` ` ` ` ` ` ` + ` ` ` ` ` ``` ` ` + * * *** *** *** * * + * * * * * * * * * * + * * ***** *** * ***** + * * * * * * * * * * + * * * * * *** * * + 0 0 000 000 000 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 00000 000 0 00000 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 000 0 0 + # # ### ### ### # # + # # # # # # # # # # + # # ##### ### # ##### + # # # # # # # # # # + # # # # # ### # # + +``` diff --git a/doc/cQueue.en.md b/doc/cQueue.en.md new file mode 100644 index 0000000..647e17b --- /dev/null +++ b/doc/cQueue.en.md @@ -0,0 +1,185 @@ +## Introduction + +A queue is a special data structure with the characteristic of First In First Out (FIFO). Generally, it has only one entrance and one exit. Data enters from the rear of the queue and exits from the front. The operations of adding an element to the queue is called "push", and removing an element from the queue is called "pop". + +cQueue is a queue control entity. It is not a container by itself and is different from the queue in varch, but its usage is quite similar. + +## Interface + +### Defining the Queue Type + +Since cQueue itself is not a data container, a new data structure needs to be defined for the data type it stores. Here is an example: + +```c +typedef struct +{ + cQueue queue; // The first member, which is default and usually doesn't need to be changed + int data[10]; // The second member, defining the actual data array to be stored. The array type can be any type, and the array name is always "data". The capacity depends on the actual requirements. +} intQueueType; // Defining a new data structure type +intQueueType intQueue; // Defining a new structure variable +``` + +There is also a simpler definition method. This method defines an anonymous structure type and defines a variable based on this structure. Therefore, this method can only be used locally. +``` +cQueue(int, 64) queue; +``` + +### Initializing the Queue + +```c +#define cQueue_init(qObject) +``` + +It is a macro definition method and can be adapted to various data types. + +```c +void test_int(void) +{ + typedef struct + { + cQueue queue; + int data[10]; + } intQueueType; + intQueueType intQueue; + + cQueue_init(intQueue); +} +``` + +### Checking if the Queue is Empty or Full + +```c +#define cQueue_empty(qObject) +#define cQueue_full(qObject) +``` + +These two methods are actually related to the size of the queue's `size`. If it equals 0, the queue is empty. If it equals the capacity, the queue is full. + +### Pushing and Popping Data in the Queue + +```c +#define cQueue_push(qObject, d) +#define cQueue_pop(qObject, d) +``` + +These two methods are used to push data `d` into the queue and pop data from the queue to `d` respectively. + +```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); + } +} +``` +Result: +``` +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 +``` + +### Accessing Data in the Queue + +```c +#define cQueue_at(qObject, i) +``` + +It is used to randomly access data in the queue. + +```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)); +} +``` +Result: +``` +cQueue[5] = 5 +``` + +## Example of Special Data Structure + +```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); + } +} +``` +Result: +``` +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/cQueue.md b/doc/cQueue.md index 16118be..64bd28a 100644 --- a/doc/cQueue.md +++ b/doc/cQueue.md @@ -19,6 +19,11 @@ typedef struct intQueueType intQueue; // 定义一个新的结构体变量 ``` +也可以使用更简便的定义方法,此方法定义了一个匿名结构体类型,根据此结构体定义一个变量。因此,此方方法也只能局部使用。 +``` +cQueue(int, 64) queue; +``` + ### 初始化队列 ```c diff --git a/doc/calculate.en.md b/doc/calculate.en.md new file mode 100644 index 0000000..b09ca79 --- /dev/null +++ b/doc/calculate.en.md @@ -0,0 +1,118 @@ +## Introduction + +This module is a simple parser and calculator for mathematical expressions. By passing in a simple arithmetic expression, it calculates the result. + +## Interface + +### Expression Calculation +```c +double calculate(const char *expression); +``` +This function is very simple to use: pass in the expression, and it returns the calculation result. The default return type is double precision floating-point, which has a wide range and is suitable for floating-point calculations. When there is a calculation error, it returns `NAN`. +The supported operators include: addition `+`, subtraction `-`, multiplication `*`, division `/`, exponentiation `^`, and modulus `%`. The operator precedence is as follows: +| Operator | Precedence | +|:--------:|:----------:| +| ^ | 0 | +| * / % | 1 | +| + - | 2 | + +In addition to basic arithmetic operations, built-in functions are also supported. The function name is followed by `()`, where the function parameters are passed inside the parentheses, and multiple parameters are separated by commas `,`. +| Function | Parameters | Description | +|:----------:|:-----------:|:-------------:| +| abs | 1 | Absolute value | +| sqrt | 1 | Square root | +| exp | 1 | Natural exponential | +| ln | 1 | Natural logarithm | +| sin | 1 | Sine | +| cos | 1 | Cosine | +| tan | 1 | Tangent | +| cot | 1 | Cotangent | +| asin | 1 | Arcsine | +| acos | 1 | Arccosine | +| atan | 1 | Arctangent | +| acot | 1 | Arccotangent | +| ceil | 1 | Ceiling | +| floor | 1 | Floor | +| min | 2 | Minimum of two | +| max | 2 | Maximum of two | +| pow | 2 | Power function, same as `^` | +| log | 2 | Logarithm function | + +Additionally, there are constants `pi` and `e`, which are case-insensitive. +Spaces may be retained between operators and numbers for clarity, **but numbers must be tightly combined into a single number**. + +Simple examples: +```c +static void test_base(void) +{ + const char *expression[] = { + " ( 99 * 3 ) ", + " min (12, 3)", + "sin ( 11 / 2 * pi ) + 100 ", + }; + + for (int i = 0; i < sizeof(expression) / sizeof(expression[0]); i++) + { + printf("cal: %s = %lf\r\n", expression[i], calculate(expression[i])); + } +} +``` +Calculation results: +``` +cal: ( 99 * 3 ) = 297.000000 +cal: min (12, 3) = 3.000000 +cal: sin ( 11 / 2 * Pi ) + 100 = 99.000000 +``` + +You can export this calculation module as a command to execute calculations in command form. This command function has already removed unnecessary trailing zeros after the decimal point for a more aesthetically pleasing display. +```c +static int command_calculate(const char *expression) +{ + double r = NAN; + if (!expression) return 0; + r = calculate(expression); + if (fabs(floor(r) - r) <= DBL_EPSILON && fabs(r) < 1.0e60) printf("%.0lf\r\n", r); + else if (fabs(r) < 1.0e-6 || fabs(r) > 1.0e9) printf("%e\r\n", r); + else + { + char p[64]; + int len = 0; + len = sprintf(p, "%lf", r); + while (len > 0 && p[len-1] == '0' && p[len-2] != '.') {p[--len] = 0;} + printf("%s\r\n", p); + } + return 1; +} +``` +Input command: +``` +calculate -c "sin( 11 / 2 * Pi ) + 2 ^ 3" +``` +Calculation result: +``` +7 +``` + +### Exporting + +```c +int calculate_function(const char *name, double (*func)(), int argc); // Export function +int calculate_constant(const char *name, double value); // Export constant +``` + +These two methods export functions and constants, respectively. + +To export a function, you pass in the function name, function pointer, and the number of parameters. It supports exporting up to `CALCULATE_EXFUNC_MAX` functions. + +To export a constant, you pass in the constant name and constant value, supporting up to `CALCULATE_CONSTV_MAX` constants. + +### List Key Identifiers + +```c +const char* calculate_ls_func(int *argc); // List functions +const char* calculate_ls_const(double *value); // List constants +``` + +These two methods list functions and constants, respectively. + +Both use an iterative list method that continues until `NULL` is returned. \ No newline at end of file diff --git a/doc/calculate.md b/doc/calculate.md index 40f56a3..fa9e905 100644 --- a/doc/calculate.md +++ b/doc/calculate.md @@ -43,49 +43,76 @@ double calculate(const char *expression); 简单的例子: ```c -void test(void) +static void test_base(void) { - printf("mul %lf\r\n", calculate(" ( 99 * 3 ) ")); - printf("min %lf\r\n", calculate(" min (12, 3)")); - printf("sin %lf\r\n", calculate("sin ( 11 / 2 * pi ) + 100 ")); + const char *expression[] = { + " ( 99 * 3 ) ", + " min (12, 3)", + "sin ( 11 / 2 * pi ) + 100 ", + }; + + for (int i = 0; i < sizeof(expression) / sizeof(expression[0]); i++) + { + printf("cal: %s = %lf\r\n", expression[i], calculate(expression[i])); + } } ``` 运算结果: ``` -mul 297.000000 -min 3.000000 -sin 99.000000 +cal: ( 99 * 3 ) = 297.000000 +cal: min (12, 3) = 3.000000 +cal: sin ( 11 / 2 * Pi ) + 100 = 99.000000 ``` 可以将这个计算模块作为命令导出,以命令形式来计算。这个命令函数,已经将小数点后面没用的`0`去掉,更美观的显示。 ```c -int command_calculate(int argc, char *argv[]) +static int command_calculate(const char *expression) { - double r = NAN; - if (argc < 2) return 0; - r = calculate(argv[1]); - if (fabs(floor(r) - r) <= DBL_EPSILON && fabs(r) < 1.0e60) printf("%.0lf\r\n", r); - else if (fabs(r) < 1.0e-6 || fabs(r) > 1.0e9) printf("%e\r\n", r); - else - { - char p[64]; - int len = 0; - len = sprintf(p, "%lf", r); - while (len > 0 && p[len-1] == '0' && p[len-2] != '.') {p[--len] = 0;} - printf("%s\r\n", p); - } - return 1; -} -void test(void) -{ - command_export("cal", command_calculate); + double r = NAN; + if (!expression) return 0; + r = calculate(expression); + if (fabs(floor(r) - r) <= DBL_EPSILON && fabs(r) < 1.0e60) printf("%.0lf\r\n", r); + else if (fabs(r) < 1.0e-6 || fabs(r) > 1.0e9) printf("%e\r\n", r); + else + { + char p[64]; + int len = 0; + len = sprintf(p, "%lf", r); + while (len > 0 && p[len-1] == '0' && p[len-2] != '.') {p[--len] = 0;} + printf("%s\r\n", p); + } + return 1; } ``` 输入命令: ``` -cal "sin( 11 / 2 * pi ) + 2 ^ 3" +calculate -c "sin( 11 / 2 * Pi ) + 2 ^ 3" ``` 运算结果: ``` 7 ``` + +### 导出 + +```c +int calculate_function(const char *name, double (*func)(), int argc); // 导出函数 +int calculate_constant(const char *name, double value); // 导出常数 +``` + +这两个方法分别导出函数和常熟。 + +导出函数分别传进:函数名,函数指针和函数的参数个数,最大可支持导出 `CALCULATE_EXFUNC_MAX` 个函数。 + +导出常数分别传进:常数名和常数值,最大可支持导出 `CALCULATE_CONSTV_MAX` 个常数。 + +### 列举关键标识 + +```c +const char* calculate_ls_func(int *argc); // 列举函数 +const char* calculate_ls_const(double *value); // 列举常数 +``` + +这两个方法分别列举函数和常熟。 + +都采用迭代列举方式,直到返回 `NULL` 才列举结束。 diff --git a/doc/check.en.md b/doc/check.en.md new file mode 100644 index 0000000..8f06acf --- /dev/null +++ b/doc/check.en.md @@ -0,0 +1,15 @@ +## Introduction + +Data may be subject to errors during the transmission process due to various reasons. In order to control errors in the transmission process, communication systems often adopt data verification to ensure the integrity of data. + +Common data verification algorithms include sum check, parity check, exclusive OR check, Longitudinal Redundancy Check (LRC), and Cyclic Redundancy Check (CRC). The code for commonly used verification algorithms is also provided here. + +## Interface + +```c +uint8_t check_sum(uint8_t* data, uint32_t len); // Sum check algorithm +uint8_t check_parity(uint8_t* data, uint32_t len); // Parity check +uint8_t check_lrc(uint8_t* data, uint32_t len); // Longitudinal Redundancy Check +uint8_t check_xor(uint8_t* data, uint32_t len); // Exclusive OR check +``` +The usage methods of these several verification algorithms are the same. They all take the data address and data length as input parameters and return the calculated verification value. diff --git a/doc/check.md b/doc/check.md index 5d6bbe3..e6753a7 100644 --- a/doc/check.md +++ b/doc/check.md @@ -12,97 +12,3 @@ uint8_t check_lrc(uint8_t* data, uint32_t len); // LRC校验 uint8_t check_xor(uint8_t* data, uint32_t len); // 异或校验 ``` 这几种校验算法使用方法一致,都是传入数据地址和数据长度,返回计算出来的校验值。 - -### 通用crc -```c -// 通用CRC算法 -uint32_t check_crc(uint8_t* data, uint32_t len, uint8_t width, uint32_t poly, uint32_t init, uint8_t refin, uint8_t refout, uint32_t xorout); -``` -这个CRC算法是32byte以内的通用CRC算法,除了传入数据地址和数据长度,后面依次跟着CRC校验码宽度、多项式、初始值、输入翻转、输出翻转、异或输出。 -这个算法和 [CRC(循环冗余校验)在线计算](http://www.ip33.com/crc.html) 的使用方法一致。 -在`check_crc`随机计算个crc,与在线工具对比 -```c -printf("crc 0x%X\r\n", check_crc("Hello", 5, 8, 0x5A, 0xAA, 1, 0, 0x4A)); -``` -结果都是 -``` -crc 0xE0 -``` - -根据常用的crc算法,可以通过`check_crc`宏定义实现 -```c -/* -|-------------------------------------------------------------------------------------------| -|CRC name | width | poly | init | xorout | refin | refout | -|-------------------------------------------------------------------------------------------| -|CRC-4/ITU | 4 | 03 | 00 | 00 | true | true | -|CRC-5/EPC | 5 | 9 | 09 | 00 | false | false | -|CRC-5/ITU | 5 | 5 | 00 | 00 | true | true | -|CRC-5/USB | 5 | 5 | 1F | 1F | true | true | -|CRC-6/ITU | 6 | 3 | 00 | 00 | true | true | -|CRC-7/MMC | 7 | 9 | 00 | 00 | false | false | -|CRC-8 | 8 | 7 | 00 | 00 | false | false | -|CRC-8/ITU | 8 | 7 | 00 | 55 | false | false | -|CRC-8/ROHC | 8 | 7 | FF | 00 | true | true | -|CRC-8/MAXIM | 8 | 1 | 00 | 00 | true | true | -|CRC-16/IBM | 16 | 005 | 0000 | 0000 | true | true | -|CRC-16/MAXIM | 16 | 005 | 0000 | FFFF | true | true | -|CRC-16/USB | 16 | 005 | FFFF | FFFF | true | true | -|CRC-16/MODBUS | 16 | 005 | FFFF | 0000 | true | true | -|CRC-16/CCITT | 16 | 021 | 0000 | 0000 | true | true | -|CRC-16/CCITT-FALSE | 16 | 021 | FFFF | 0000 | false | false | -|CRC-16/X25 | 16 | 021 | FFFF | FFFF | true | true | -|CRC-16/XMODEM | 16 | 021 | 0000 | 0000 | false | false | -|CRC-16/DNP | 16 | D65 | 0000 | FFFF | true | true | -|CRC-32 | 32 | 4C11DB7 | FFFFFFFF | FFFFFFFF | true | true | -|CRC-32/MPEG-2 | 32 | 4C11DB7 | FFFFFFFF | 00000000 | false | false | -|-------------------------------------------------------------------------------------------| - */ -#define check_crc4_itu(data, len) check_crc(data, len, 4, 0x03, 0x00, 1, 1, 0x00) -#define check_crc5_epc(data, len) check_crc(data, len, 5, 0x09, 0x09, 0, 0, 0x00) -#define check_crc5_usb(data, len) check_crc(data, len, 5, 0x05, 0x1F, 1, 1, 0x1F) -#define check_crc6_itu(data, len) check_crc(data, len, 6, 0x03, 0x00, 1, 1, 0x00) -#define check_crc7_mmc(data, len) check_crc(data, len, 7, 0x09, 0x00, 0, 0, 0x00) -#define check_crc8(data, len) check_crc(data, len, 8, 0x07, 0x00, 0, 0, 0x00) -#define check_crc8_itu(data, len) check_crc(data, len, 8, 0x07, 0x00, 0, 0, 0x55) -#define check_crc8_rohc(data, len) check_crc(data, len, 8, 0x07, 0xFF, 1, 1, 0x00) -#define check_crc8_maxim(data, len) check_crc(data, len, 8, 0x31, 0x00, 1, 1, 0x00) -#define check_crc16_ibm(data, len) check_crc(data, len, 16, 0x8005, 0x0000, 1, 1, 0x0000) -#define check_crc16_maxim(data, len) check_crc(data, len, 16, 0x8005, 0x0000, 1, 1, 0xFFFF) -#define check_crc16_usb(data, len) check_crc(data, len, 16, 0x8005, 0xFFFF, 1, 1, 0xFFFF) -#define check_crc16_modbus(data, len) check_crc(data, len, 16, 0x8005, 0xFFFF, 1, 1, 0x0000) -#define check_crc16_ccitt(data, len) check_crc(data, len, 16, 0x1021, 0x0000, 1, 1, 0x0000) -#define check_crc16_ccitt_false(data, len) check_crc(data, len, 16, 0x1021, 0xFFFF, 0, 0, 0x0000) -#define check_crc16_x25(data, len) check_crc(data, len, 16, 0x1021, 0xFFFF, 1, 1, 0xFFFF) -#define check_crc16_xmodem(data, len) check_crc(data, len, 16, 0x1021, 0x0000, 0, 0, 0x0000) -#define check_crc16_dnp(data, len) check_crc(data, len, 16, 0x3D65, 0x0000, 1, 1, 0xFFFF) -#define check_crc32(data, len) check_crc(data, len, 32, 0x04C11DB7, 0xFFFFFFFF, 1, 1, 0xFFFFFFFF) -#define check_crc32_mpeg_2(data, len) check_crc(data, len, 32, 0x04C11DB7, 0xFFFFFFFF, 0, 0, 0x00000000) -``` - -### 标准crc - -如上述通用的crc也能实现标准的crc,但是运算起来效率必然有所下降,下面是根据标准crc进行的专有的实现 -```c -uint8_t crc4_itu(uint8_t* data, uint32_t len); -uint8_t crc5_epc(uint8_t* data, uint32_t len); -uint8_t crc5_itu(uint8_t* data, uint32_t len); -uint8_t crc5_usb(uint8_t* data, uint32_t len); -uint8_t crc6_itu(uint8_t* data, uint32_t len); -uint8_t crc7_mmc(uint8_t* data, uint32_t len); -uint8_t crc8(uint8_t* data, uint32_t len); -uint8_t crc8_itu(uint8_t* data, uint32_t len); -uint8_t crc8_rohc(uint8_t* data, uint32_t len); -uint8_t crc8_maxim(uint8_t* data, uint32_t len); -uint16_t crc16_ibm(uint8_t* data, uint32_t len); -uint16_t crc16_maxim(uint8_t* data, uint32_t len); -uint16_t crc16_usb(uint8_t* data, uint32_t len); -uint16_t crc16_modbus(uint8_t* data, uint32_t len); -uint16_t crc16_ccitt(uint8_t* data, uint32_t len); -uint16_t crc16_ccitt_false(uint8_t* data, uint32_t len); -uint16_t crc16_x25(uint8_t* data, uint32_t len); -uint16_t crc16_xmodem(uint8_t* data, uint32_t len); -uint16_t crc16_dnp(uint8_t* data, uint32_t len); -uint32_t crc32(uint8_t* data, uint32_t len); -uint32_t crc32_mpeg_2(uint8_t* data, uint32_t len); -``` diff --git a/doc/command.en.md b/doc/command.en.md new file mode 100644 index 0000000..07c4f5f --- /dev/null +++ b/doc/command.en.md @@ -0,0 +1,112 @@ +## Introduction + +The command prompt is a working prompt in the operating system that prompts for command input. It is a convenient human-computer interaction interface. +This command parsing module does not involve receiving commands entered from the console for the time being, but rather processes string commands. + +## Interface + +### Command Export +```c +int command_export(const char *name, command_handle_t handle); +``` +Before introducing the command parsing function, let's first look at the command export function. +This function is used to add supported functions, matching the name with the corresponding callback function. The `name` is the function identification name (generally, the function name is used as the identification name for command parsing), and the `handle` is the corresponding callback processing function. It returns 1 if the export is successful and 0 if it fails. +```c +typedef int (*command_handle_t)(int argc, char *argv[]); +``` +The definition of the `command_handle_t` function type is consistent with our commonly used `int main(int argc, char *argv[])`. Parameters are passed in through `argc` and `argv`. + +### Command Execution +```c +int command(const char *line); +``` +The command execution function is also very simple. Just pass in the command line. It returns the return value of the function corresponding to the recognized command. -1 is the return value when the parsing fails. Therefore, when defining the processing function, **the return value cannot be -1**. +The input format of the command: +``` +argv[0] argv[1] argv[2]... +``` +Among them, `argv[0]` is the command identifier, and several parameters can follow it. The number is passed to the function through `argc`. +Command parameters are separated by spaces. When you want to input spaces within a parameter, you need to use the escape character backslash `\` for escaping. "\ " represents a space. Besides, the **escape characters** that are also supported include: double quotation marks `"` and backslash `\`. +To simplify the use of escape characters, you can use **a pair of double quotation marks** to enclose commands that do not need escape parsing. +Usage Example: +```c +int func1(int argc, char *argv[]) +{ + printf("I am func1!\r\n"); + printf("argc = %d\r\n", argc); + for (int i = 0; i < argc; i++) + { + printf("argv[%d] = %s\r\n", i, argv[i]); + } + return 1; +} + +int func2(int argc, char *argv[]) +{ + printf("I am func2!\r\n"); + return 1; +} + +int func3(int argc, char *argv[]) +{ + printf("I am func3!\r\n"); + return 1; +} + +static void test(void) +{ + /* Export commands */ + command_export("func1", func1); + command_export("func2", func2); + command_export("func3", func3); + + command("cmd -l"); // The built-in command of the module, used to view the currently supported commands + printf("--------------------------\r\n"); + command("func2"); + printf("--------------------------\r\n"); + command("func3"); + printf("--------------------------\r\n"); + command("func1 1 2 3"); // Without escaping, each space-separated part will become a separate parameter + printf("--------------------------\r\n"); + command("func1 1\\ 2 3"); // The space after 1 is escaped, and 1 and 2 will be combined with the space in between + printf("--------------------------\r\n"); + command("func1 \"1 2 3\""); // "1 2 3" between double quotation marks will be parsed as one parameter +} +``` +Result: +``` +cmd +func1 +func2 +func3 +count = 4 +-------------------------- +I am func2! +-------------------------- +I am func3! +-------------------------- +I am func1! +argc = 4 +argv[0] = func1 +argv[1] = 1 +argv[2] = 2 +argv[3] = 3 +-------------------------- +I am func1! +argc = 3 +argv[0] = func1 +argv[1] = 1 2 +argv[2] = 3 +-------------------------- +I am func1! +argc = 2 +argv[0] = func1 +argv[1] = 1 2 3 +``` +Among them, `cmd` is a built-in command that counts the currently supported commands and returns the number of supported commands. + +### Command Clear +```c +void command_clear(void); +``` +Clear all exported commands. diff --git a/doc/console.en.md b/doc/console.en.md new file mode 100644 index 0000000..7131c0b --- /dev/null +++ b/doc/console.en.md @@ -0,0 +1,41 @@ +## Introduction + +The console module plays a crucial role in program development and operation. It is mainly used to manage the input and output of character-based applications. Through the console module, developers can achieve effective interaction with users and customize and manage the command-line interface at the same time. + +## Interface + +### Functions + +```c +void console_init(void); +``` + +This is the initialization function of the console module. It can be called manually or through the export call of the init module (by default, it is called through export). It can be directly used after the program runs. + +Usage Example: +``` +>> cmd -h + +Usage: +Enter the command line to execute the corresponding command + +OPTIONS +[-l] : Print currently supported commands +[-n] : Print the number of currently supported commands +[-h] : Print help +[-v] : Print version +[-c] : Print the configuration information of the current command module + `argc` : The maximum number of parameters supported for parsing in the input command + `line` : The maximum length supported for parsing in the input command + `count` : The maximum command count supported +>> +>> +>> cmd -l + +command list: +@ cmd +@ into +>> +>> + +``` diff --git a/doc/cpul.en.md b/doc/cpul.en.md new file mode 100644 index 0000000..73fe52d --- /dev/null +++ b/doc/cpul.en.md @@ -0,0 +1,67 @@ +### Introduction +`cpul` is a simple CPU load (cpuload) module for the C language. It defines a series of data structures, macros, and function interfaces related to CPU load management, aiming to facilitate developers to control and monitor CPU load in C language projects. + +### Interfaces + +#### Functions + +##### `cpul_init` Function +```c +int cpul_init(CPUL *cpul); +``` +**Function Description**: This function is used to initialize an instance of `CPUL`. It initializes the `CPUL` structure to its default values and checks the validity of the passed-in pointer. For the initialization, a function method for obtaining the original load needs to be provided. For the Linux cpuload, you can refer to [test_cpul.c](/test/test_cpul.c). + +##### `cpul_get` Function +```c +int cpul_get(CPUL *cpul, uint16_t *load); +``` +**Function Description**: This function retrieves the current CPU load value from the `CPUL` instance. It also checks the validity of the parameters and the status. + +##### `cpul_set` Function +```c +int cpul_set(CPUL *cpul, uint16_t load); +``` +**Function Description**: This function is used to set the target CPU load value for the `CPUL` instance. It checks the validity of the passed-in parameter. + +##### `cpul_task` Function +```c +int cpul_task(CPUL *cpul); +``` +**Function Description**: This function executes a CPU load management task. It manages the CPU load based on the current load value and the target load value. + +**Usage Example**: +```c +void *loadgen_entry(void *ptr) +{ + #define PERIOD 10 + const int coreid = *(int *)ptr; + CPUL cpul; + int ret = 0; + uint32_t count = 0; + uint16_t load = 0; + + cpul.coreid = coreid; + cpul.resolution = 10; + cpul.raw = cpul_raw; + ret = cpul_init(&cpul); + + ret = cpul_set(&cpul, 5000); + + while(1) + { + count += PERIOD; + if (count >= 25200000) count = 0; + + if (count % 1000 == 0) + { + ret = cpul_get(&cpul, &load); + printf("cpul_get<%d> %2d.%02d%%, %d, %d\r\n", coreid, (uint16_t)(load / 100), load % 100, cpul.ctrl.tload, cpul.ctrl.refine); + } + + cpul_task(&cpul); + usleep(1000 * PERIOD); + } +} +``` + +Through these function interfaces, developers can conveniently implement functions such as initialization, retrieval, setting, and management of CPU load in C language programs. diff --git a/doc/cpul.md b/doc/cpul.md new file mode 100644 index 0000000..25d96de --- /dev/null +++ b/doc/cpul.md @@ -0,0 +1,68 @@ +## 介绍 + +`cpul` 是一个用于C语言的简单CPU负载(cpuload)模块,定义了一系列与CPU负载管理相关的数据结构、宏以及函数接口,旨在方便开发者在C语言项目中对CPU负载进行控制和监测等操作。 + +## 接口 + +### 函数 + +#### `cpul_init`函数 +```c +int cpul_init(CPUL *cpul); +``` +**功能描述**:用于初始化`CPUL`实例,将`CPUL`结构初始化为默认值,并检查传入指针的有效性。初始化需要提供获取原始的负载的函数方法,针对linux的cpuload可以参考 [test_cpul.c](/test/test_cpul.c)。 + +#### `cpul_get`函数 +```c +int cpul_get(CPUL *cpul, uint16_t *load); +``` +**功能描述**:从`CPUL`实例中获取当前的CPU负载值,同时会检查参数以及状态的有效性。 + +#### `cpul_set`函数 +```c +int cpul_set(CPUL *cpul, uint16_t load); +``` +**功能描述**:设置`CPUL`实例的目标CPU负载值,会对传入的参数进行有效性检查。 + +#### `cpul_task`函数 +```c +int cpul_task(CPUL *cpul); +``` +**功能描述**:执行一个CPU负载管理任务,根据当前负载值和目标负载值来对CPU负载进行管理操作。 + +**使用例子**: +```c +void *loadgen_entry(void *ptr) +{ + #define PERIOD 10 + const int coreid = *(int *)ptr; + CPUL cpul; + int ret = 0; + uint32_t count = 0; + uint16_t load = 0; + + cpul.coreid = coreid; + cpul.resolution = 10; + cpul.raw = cpul_raw; + ret = cpul_init(&cpul); + + ret = cpul_set(&cpul, 5000); + + while(1) + { + count += PERIOD; + if (count >= 25200000) count = 0; + + if (count % 1000 == 0) + { + ret = cpul_get(&cpul, &load); + printf("cpul_get<%d> %2d.%02d%%, %d, %d\r\n", coreid, (uint16_t)(load / 100), load % 100, cpul.ctrl.tload, cpul.ctrl.refine); + } + + cpul_task(&cpul); + usleep(1000 * PERIOD); + } +} +``` + +通过这些函数接口,开发者可以方便地在C语言程序中实现对CPU负载的初始化、获取、设置以及管理等功能。 \ No newline at end of file diff --git a/doc/crc.en.md b/doc/crc.en.md new file mode 100644 index 0000000..ec1261b --- /dev/null +++ b/doc/crc.en.md @@ -0,0 +1,100 @@ +## Introduction + +Data may encounter errors during the transmission process due to various reasons. To control errors in the transmission process, communication systems often adopt data verification to ensure the integrity of data. +Common data verification algorithms include sum check, parity check, exclusive OR check, Longitudinal Redundancy Check (LRC), and Cyclic Redundancy Check (CRC). The code for commonly used verification algorithms is also provided here. + +## Interface + +### General CRC +```c +// General CRC algorithm +uint32_t check_crc(uint8_t* data, uint32_t len, uint8_t width, uint32_t poly, uint32_t init, uint8_t refin, uint8_t refout, uint32_t xorout); +``` +This CRC algorithm is a general CRC algorithm for data within 32 bytes. Besides passing in the data address and data length, it is followed by the width of the CRC check code, polynomial, initial value, input inversion, output inversion, and exclusive OR output in sequence. +This algorithm has the same usage method as [CRC (Cyclic Redundancy Check) Online Calculator](http://www.ip33.com/crc.html). +Randomly calculate a CRC using `check_crc` and compare it with the online tool. +```c +printf("crc 0x%X\r\n", check_crc("Hello", 5, 8, 0x5A, 0xAA, 1, 0, 0x4A)); +``` +The result is: +``` +crc 0xE0 +``` + +Based on commonly used CRC algorithms, they can be implemented through the `check_crc` macro definition. +```c +/* +|-------------------------------------------------------------------------------------------| +|CRC name | width | poly | init | xorout | refin | refout | +|-------------------------------------------------------------------------------------------| +|CRC-4/ITU | 4 | 03 | 00 | 00 | true | true | +|CRC-5/EPC | 5 | 9 | 09 | 00 | false | false | +|CRC-5/ITU | 5 | 5 | 00 | 00 | true | true | +|CRC-5/USB | 5 | 5 | 1F | 1F | true | true | +|CRC-6/ITU | 6 | 3 | 00 | 00 | true | true | +|CRC-7/MMC | 7 | 9 | 00 | 00 | false | false | +|CRC-8 | 8 | 7 | 00 | 00 | false | false | +|CRC-8/ITU | 8 | 7 | 00 | 55 | false | false | +|CRC-8/ROHC | 8 | 7 | FF | 00 | true | true | +|CRC-8/MAXIM | 8 | 1 | 00 | 00 | true | true | +|CRC-16/IBM | 16 | 005 | 0000 | 0000 | true | true | +|CRC-16/MAXIM | 16 | 005 | 0000 | FFFF | true | true | +|CRC-16/USB | 16 | 005 | FFFF | FFFF | true | true | +|CRC-16/MODBUS | 16 | 005 | FFFF | 0000 | true | true | +|CRC-16/CCITT | 16 | 021 | 0000 | 0000 | true | true | +|CRC-16/CCITT-FALSE | 16 | 021 | FFFF | 0000 | false | false | +|CRC-16/X25 | 16 | 021 | FFFF | FFFF | true | true | +|CRC-16/XMODEM | 16 | 021 | 0000 | 0000 | false | false | +|CRC-16/DNP | 16 | D65 | 0000 | FFFF | true | true | +|CRC-32 | 32 | 4C11DB7 | FFFFFFFF | FFFFFFFF | true | true | +|CRC-32/MPEG-2 | 32 | 4C11DB7 | FFFFFFFF | 00000000 | false | false | +|-------------------------------------------------------------------------------------------| + */ +#define check_crc4_itu(data, len) check_crc(data, len, 4, 0x03, 0x00, 1, 1, 0x00) +#define check_crc5_epc(data, len) check_crc(data, len, 5, 0x09, 0x09, 0, 0, 0x00) +#define check_crc5_usb(data, len) check_crc(data, len, 5, 0x05, 0x1F, 1, 1, 0x1F) +#define check_crc6_itu(data, len) check_crc(data, len, 6, 0x03, 0x00, 1, 1, 0x00) +#define check_crc7_mmc(data, len) check_crc(data, len, 7, 0x09, 0x00, 0, 0, 0x00) +#define check_crc8(data, len) check_crc(data, len, 8, 0x07, 0x00, 0, 0, 0x00) +#define check_crc8_itu(data, len) check_crc(data, len, 8, 0x07, 0x00, 0, 0, 0x55) +#define check_crc8_rohc(data, len) check_crc(data, len, 8, 0x07, 0xFF, 1, 1, 0x00) +#define check_crc8_maxim(data, len) check_crc(data, len, 8, 0x31, 0x00, 1, 1, 0x00) +#define check_crc16_ibm(data, len) check_crc(data, len, 16, 0x8005, 0x0000, 1, 1, 0x0000) +#define check_crc16_maxim(data, len) check_crc(data, len, 16, 0x8005, 0x0000, 1, 1, 0xFFFF) +#define check_crc16_usb(data, len) check_crc(data, len, 16, 0x8005, 0xFFFF, 1, 1, 0xFFFF) +#define check_crc16_modbus(data, len) check_crc(data, len, 16, 0x8005, 0xFFFF, 1, 1, 0x0000) +#define check_crc16_ccitt(data, len) check_crc(data, len, 16, 0x1021, 0x0000, 1, 1, 0x0000) +#define check_crc16_ccitt_false(data, len) check_crc(data, len, 16, 0x1021, 0xFFFF, 0, 0, 0x0000) +#define check_crc16_x25(data, len) check_crc(data, len, 16, 0x1021, 0xFFFF, 1, 1, 0xFFFF) +#define check_crc16_xmodem(data, len) check_crc(data, len, 16, 0x1021, 0x0000, 0, 0, 0x0000) +#define check_crc16_dnp(data, len) check_crc(data, len, 16, 0x3D65, 0x0000, 1, 1, 0xFFFF) +#define check_crc32(data, len) check_crc(data, len, 32, 0x04C11DB7, 0xFFFFFFFF, 1, 1, 0xFFFFFFFF) +#define check_crc32_mpeg_2(data, len) check_crc(data, len, 32, 0x04C11DB7, 0xFFFFFFFF, 0, 0, 0x00000000) +``` + +### Standard CRC + +Although the above general CRC can also implement standard CRC, the calculation efficiency will inevitably decrease. The following is the proprietary implementation based on standard CRC. +```c +uint8_t crc4_itu(uint8_t* data, uint32_t len); +uint8_t crc5_epc(uint8_t* data, uint32_t len); +uint8_t crc5_itu(uint8_t* data, uint32_t len); +uint8_t crc5_usb(uint8_t* data, uint32_t len); +uint8_t crc6_itu(uint8_t* data, uint32_t len); +uint8_t crc7_mmc(uint8_t* data, uint32_t len); +uint8_t crc8(uint8_t* data, uint32_t len); +uint8_t crc8_itu(uint8_t* data, uint32_t len); +uint8_t crc8_rohc(uint8_t* data, uint32_t len); +uint8_t crc8_maxim(uint8_t* data, uint32_t len); +uint16_t crc16_ibm(uint8_t* data, uint32_t len); +uint16_t crc16_maxim(uint8_t* data, uint32_t len); +uint16_t crc16_usb(uint8_t* data, uint32_t len); +uint16_t crc16_modbus(uint8_t* data, uint32_t len); +uint16_t crc16_ccitt(uint8_t* data, uint32_t len); +uint16_t crc16_ccitt_false(uint8_t* data, uint32_t len); +uint16_t crc16_x25(uint8_t* data, uint32_t len); +uint16_t crc16_xmodem(uint8_t* data, uint32_t len); +uint16_t crc16_dnp(uint8_t* data, uint32_t len); +uint32_t crc32(uint8_t* data, uint32_t len); +uint32_t crc32_mpeg_2(uint8_t* data, uint32_t len); +``` diff --git a/doc/crc.md b/doc/crc.md new file mode 100644 index 0000000..d8debed --- /dev/null +++ b/doc/crc.md @@ -0,0 +1,100 @@ +## 介绍 + +数据在传输过程中可能会因为各种原因导致产生了差错,为了能够控制传输过程的差错,通信系统往往会采用数据校验来保证数据的完整性。 +常见的数据校验算法就包含,求和校验、奇偶校验、异或校验、LRC校验,这里也给出了常用的校验算法的代码。 + +## 接口 + +### 通用crc +```c +// 通用CRC算法 +uint32_t check_crc(uint8_t* data, uint32_t len, uint8_t width, uint32_t poly, uint32_t init, uint8_t refin, uint8_t refout, uint32_t xorout); +``` +这个CRC算法是32byte以内的通用CRC算法,除了传入数据地址和数据长度,后面依次跟着CRC校验码宽度、多项式、初始值、输入翻转、输出翻转、异或输出。 +这个算法和 [CRC(循环冗余校验)在线计算](http://www.ip33.com/crc.html) 的使用方法一致。 +在`check_crc`随机计算个crc,与在线工具对比 +```c +printf("crc 0x%X\r\n", check_crc("Hello", 5, 8, 0x5A, 0xAA, 1, 0, 0x4A)); +``` +结果都是 +``` +crc 0xE0 +``` + +根据常用的crc算法,可以通过`check_crc`宏定义实现 +```c +/* +|-------------------------------------------------------------------------------------------| +|CRC name | width | poly | init | xorout | refin | refout | +|-------------------------------------------------------------------------------------------| +|CRC-4/ITU | 4 | 03 | 00 | 00 | true | true | +|CRC-5/EPC | 5 | 9 | 09 | 00 | false | false | +|CRC-5/ITU | 5 | 5 | 00 | 00 | true | true | +|CRC-5/USB | 5 | 5 | 1F | 1F | true | true | +|CRC-6/ITU | 6 | 3 | 00 | 00 | true | true | +|CRC-7/MMC | 7 | 9 | 00 | 00 | false | false | +|CRC-8 | 8 | 7 | 00 | 00 | false | false | +|CRC-8/ITU | 8 | 7 | 00 | 55 | false | false | +|CRC-8/ROHC | 8 | 7 | FF | 00 | true | true | +|CRC-8/MAXIM | 8 | 1 | 00 | 00 | true | true | +|CRC-16/IBM | 16 | 005 | 0000 | 0000 | true | true | +|CRC-16/MAXIM | 16 | 005 | 0000 | FFFF | true | true | +|CRC-16/USB | 16 | 005 | FFFF | FFFF | true | true | +|CRC-16/MODBUS | 16 | 005 | FFFF | 0000 | true | true | +|CRC-16/CCITT | 16 | 021 | 0000 | 0000 | true | true | +|CRC-16/CCITT-FALSE | 16 | 021 | FFFF | 0000 | false | false | +|CRC-16/X25 | 16 | 021 | FFFF | FFFF | true | true | +|CRC-16/XMODEM | 16 | 021 | 0000 | 0000 | false | false | +|CRC-16/DNP | 16 | D65 | 0000 | FFFF | true | true | +|CRC-32 | 32 | 4C11DB7 | FFFFFFFF | FFFFFFFF | true | true | +|CRC-32/MPEG-2 | 32 | 4C11DB7 | FFFFFFFF | 00000000 | false | false | +|-------------------------------------------------------------------------------------------| + */ +#define check_crc4_itu(data, len) check_crc(data, len, 4, 0x03, 0x00, 1, 1, 0x00) +#define check_crc5_epc(data, len) check_crc(data, len, 5, 0x09, 0x09, 0, 0, 0x00) +#define check_crc5_usb(data, len) check_crc(data, len, 5, 0x05, 0x1F, 1, 1, 0x1F) +#define check_crc6_itu(data, len) check_crc(data, len, 6, 0x03, 0x00, 1, 1, 0x00) +#define check_crc7_mmc(data, len) check_crc(data, len, 7, 0x09, 0x00, 0, 0, 0x00) +#define check_crc8(data, len) check_crc(data, len, 8, 0x07, 0x00, 0, 0, 0x00) +#define check_crc8_itu(data, len) check_crc(data, len, 8, 0x07, 0x00, 0, 0, 0x55) +#define check_crc8_rohc(data, len) check_crc(data, len, 8, 0x07, 0xFF, 1, 1, 0x00) +#define check_crc8_maxim(data, len) check_crc(data, len, 8, 0x31, 0x00, 1, 1, 0x00) +#define check_crc16_ibm(data, len) check_crc(data, len, 16, 0x8005, 0x0000, 1, 1, 0x0000) +#define check_crc16_maxim(data, len) check_crc(data, len, 16, 0x8005, 0x0000, 1, 1, 0xFFFF) +#define check_crc16_usb(data, len) check_crc(data, len, 16, 0x8005, 0xFFFF, 1, 1, 0xFFFF) +#define check_crc16_modbus(data, len) check_crc(data, len, 16, 0x8005, 0xFFFF, 1, 1, 0x0000) +#define check_crc16_ccitt(data, len) check_crc(data, len, 16, 0x1021, 0x0000, 1, 1, 0x0000) +#define check_crc16_ccitt_false(data, len) check_crc(data, len, 16, 0x1021, 0xFFFF, 0, 0, 0x0000) +#define check_crc16_x25(data, len) check_crc(data, len, 16, 0x1021, 0xFFFF, 1, 1, 0xFFFF) +#define check_crc16_xmodem(data, len) check_crc(data, len, 16, 0x1021, 0x0000, 0, 0, 0x0000) +#define check_crc16_dnp(data, len) check_crc(data, len, 16, 0x3D65, 0x0000, 1, 1, 0xFFFF) +#define check_crc32(data, len) check_crc(data, len, 32, 0x04C11DB7, 0xFFFFFFFF, 1, 1, 0xFFFFFFFF) +#define check_crc32_mpeg_2(data, len) check_crc(data, len, 32, 0x04C11DB7, 0xFFFFFFFF, 0, 0, 0x00000000) +``` + +### 标准crc + +如上述通用的crc也能实现标准的crc,但是运算起来效率必然有所下降,下面是根据标准crc进行的专有的实现 +```c +uint8_t crc4_itu(uint8_t* data, uint32_t len); +uint8_t crc5_epc(uint8_t* data, uint32_t len); +uint8_t crc5_itu(uint8_t* data, uint32_t len); +uint8_t crc5_usb(uint8_t* data, uint32_t len); +uint8_t crc6_itu(uint8_t* data, uint32_t len); +uint8_t crc7_mmc(uint8_t* data, uint32_t len); +uint8_t crc8(uint8_t* data, uint32_t len); +uint8_t crc8_itu(uint8_t* data, uint32_t len); +uint8_t crc8_rohc(uint8_t* data, uint32_t len); +uint8_t crc8_maxim(uint8_t* data, uint32_t len); +uint16_t crc16_ibm(uint8_t* data, uint32_t len); +uint16_t crc16_maxim(uint8_t* data, uint32_t len); +uint16_t crc16_usb(uint8_t* data, uint32_t len); +uint16_t crc16_modbus(uint8_t* data, uint32_t len); +uint16_t crc16_ccitt(uint8_t* data, uint32_t len); +uint16_t crc16_ccitt_false(uint8_t* data, uint32_t len); +uint16_t crc16_x25(uint8_t* data, uint32_t len); +uint16_t crc16_xmodem(uint8_t* data, uint32_t len); +uint16_t crc16_dnp(uint8_t* data, uint32_t len); +uint32_t crc32(uint8_t* data, uint32_t len); +uint32_t crc32_mpeg_2(uint8_t* data, uint32_t len); +``` diff --git a/doc/csv.en.md b/doc/csv.en.md new file mode 100644 index 0000000..b31560a --- /dev/null +++ b/doc/csv.en.md @@ -0,0 +1,282 @@ +## Introduction + +### What is a CSV file? + +CSV (Comma-Separated Values) is a common file format used for storing and exchanging simple data tables. A CSV file consists of text lines, where each line represents a row of data in the table, and each data field is separated by a comma. + +### Characteristics of CSV files + +- **Simple and easy to use**: CSV files use a plain text format, making them easy to create and edit. Almost all spreadsheet software and text editors support reading and writing operations on CSV files. +- **Cross-platform compatibility**: CSV files are a universal data exchange format that can be read and processed on different operating systems, such as Windows, Mac, and Linux. +- **Flexibility**: CSV files can contain any number of rows and columns and can store various types of data, such as text, numbers, and dates. +- **Readability**: Since CSV files adopt a plain text format, they are easy for humans to read and understand, and also convenient for data analysis and processing. + +### Uses of CSV files + +CSV files are widely used in scenarios such as data import, export, and exchange, including: + +- **Data import and export**: CSV files are often used to export data from one application to another, or from a database to spreadsheet software, and vice versa. +- **Data exchange**: As a universal data exchange format, CSV files are commonly used for data exchange between different systems, such as data integration and data synchronization. +- **Data backup and storage**: CSV files can serve as a simple data backup and storage format, facilitating the preservation of data in plain text form and enabling recovery when needed. +- **Data analysis and processing**: CSV files can be conveniently analyzed and processed. Various data analysis tools (such as Excel, Python, etc.) can be used to operate on and calculate CSV files. + +### How to create and edit CSV files? + +CSV files can be created and edited using text editors, spreadsheet software, or programming languages. Here are some common methods: + +- **Text editors**: Text editors (such as Notepad++, Sublime Text, etc.) can be used to create and edit CSV files by separating each data field with commas. +- **Spreadsheet software**: Common spreadsheet software (such as Microsoft Excel, Google Sheets, etc.) provides functions for importing, exporting, and editing CSV files. You can use spreadsheet software to create and edit CSV files and save them in CSV format. +- **Programming languages**: Programming languages (such as Python, Java, etc.) can be used to read, write, and process CSV files. Many programming languages provide specialized CSV libraries and functions to facilitate operations and processing on CSV files. + +### Precautions + +When creating and processing CSV files, the following points need to be noted: + +- **Data format**: Ensure that the data in the CSV file is stored in the correct format. For example, dates, numbers, etc. need to be entered according to the agreed format. +- **Data encoding**: Select the appropriate character encoding as needed to ensure the encoding consistency of CSV files in different operating systems and applications. +- **Data escaping**: When data fields contain special characters such as commas and line breaks, appropriate escaping or quoting needs to be performed to ensure data correctness. + +### C language version CSV library + +The CSV library provided by varch is simple and easy to use and can complete most of the basic operations on tables, including loading and saving of CSV files, as well as addition, deletion, modification, and query operations for rows, columns, and cells. + +## Interface + +### Creating and deleting csv objects +```c +csv_t csv_create(unsigned int row, unsigned int col, const void *array); +void csv_delete(csv_t csv); +``` +Here, **csv_t** is the structure of csv. The creation method will generate a table with specified rows and columns and initialize it with the specified array at the same time. The deletion method deletes the specified csv object. + +### Loading csv objects +```c +csv_t csv_loads(const char* text); +csv_t csv_file_load(const char* filename); +``` +A csv object can be loaded from a string text or from a file. If the loading is successful, a csv object will be returned; otherwise, NULL will be returned. +When the loading of a csv object fails, the function `int csv_error_info(int* line, int* column);` can be called to locate the error. +Error types include +``` +#define CSV_E_OK (0) /* no error */ +#define CSV_E_MEMORY (1) /* memory allocation failed */ +#define CSV_E_OPEN (2) /* fail to open file */ +``` + +### Dumping csv objects +```c +char* csv_dumps(csv_t csv, int* len); +int csv_file_dump(csv_t csv, const char* filename); +``` +Firstly, for the **csv_dumps** method, it dumps the csv object into a string according to the format. *len is the length of the converted string. When NULL is passed in, the length will not be obtained. The return value is the converted string, which is allocated by the function and **needs to be freed after use**. +The **csv_file_dump** method dumps the csv into a file based on **csv_dumps**. The filename is passed in as the file name, and the return value is the length of the dump. A negative value indicates that the dump failed. + +### Getting row, column, and cell counts of csv +```c +unsigned int csv_row(csv_t csv); +unsigned int csv_col(csv_t csv); +unsigned int csv_cell(csv_t csv); +``` +These functions are used to obtain the number of rows and columns in the csv table and the count of non-empty cells respectively. + +### Deep copying csv +```c +csv_t csv_duplicate(csv_t csv); +``` +A new csv object is deeply copied from the source csv object. + +### Converting csv to an array +```c +int csv_to_array(csv_t csv, unsigned int o_row, unsigned int o_col, void *array, unsigned int row_size, unsigned int col_size); +``` +Starting from [o_row, o_col], the content of the selected area with the size of (row_size, col_size) is transferred to the array. + +### Minifying csv +```c +void csv_minify(csv_t csv); +``` +This method will not affect the actual stored content of csv. It will remove the invalid empty cells at the end of rows, thereby reducing the storage space. + +### Setting cell content of csv +```c +int csv_set_text(csv_t csv, unsigned int row, unsigned int col, const char* text); +``` +Overwrite and write the text into the cell at (row, col). When the cell does not exist, a new cell will also be created for writing. + +### Getting cell content of csv +```c +const char* csv_get_text(csv_t csv, unsigned int row, unsigned int col); +``` +Get the content of the cell at (row, col). NULL will be returned if the cell does not exist. + +### Clearing cell content of csv +```c +void csv_clean_text(csv_t csv, unsigned int row, unsigned int col); +``` +Clear the content of the cell at (row, col). + +### Inserting rows and columns into csv +```c +int csv_insert_row(csv_t csv, unsigned int pos, const char **array, unsigned int count); +int csv_insert_col(csv_t csv, unsigned int pos, const char **array, unsigned int count); +``` +Insert rows or columns at the position of pos (when pos is 0, it defaults to inserting at the end). If array and count are specified, the inserted rows or columns will be initialized with the array, and count specifies the number of initializations. + +### Deleting rows and columns from csv +```c +int csv_delete_row(csv_t csv, unsigned int pos); +int csv_delete_col(csv_t csv, unsigned int pos); +``` +Delete the row or column at the position of pos (when pos is 0, it defaults to deleting at the end). + +### Moving rows and columns in csv +```c +int csv_move_row_to(csv_t csv, unsigned int pos, unsigned int dest); +int csv_move_col_to(csv_t csv, unsigned int pos, unsigned int dest); +``` +Move the row or column at the position of pos (when pos is 0, it defaults to deleting at the end) to the position of dest. + +### Copying rows and columns in csv +```c +int csv_copy_row_to(csv_t csv, unsigned int pos, unsigned int dest); +int csv_copy_col_to(csv_t csv, unsigned int pos, unsigned int dest); +``` +Copy the row or column at the position of pos (when pos is 0, it defaults to deleting at the end) to the position of dest. + +### Inserting cells into csv +```c +int csv_insert_cell(csv_t csv, unsigned int row, unsigned int col, int move_down); +``` +Insert an empty cell at the position of (row, col). If move_down is non-zero, the content below will move down; otherwise, it will move to the right. + +### Deleting cells from csv +```c +int csv_delete_cell(csv_t csv, unsigned int row, unsigned int col, int move_up); +``` +Delete the cell at the position of (row, col). If move_up is non-zero, the content below will move up; otherwise, it will move to the left. + +### Copying cells in csv +```c +int csv_copy_cell_to(csv_t csv, unsigned int s_row, unsigned int s_col, unsigned int d_row, unsigned int d_col); +``` +Copy the content of the cell at (s_row, s_col) to the cell at (d_row, d_col). + +### Cutting cells in csv +```c +int csv_cut_cell_to(csv_t csv, unsigned int s_row, unsigned int s_col, unsigned int d_row, unsigned int d_col); +``` +Cut the content of the cell at (s_row, s_col) to the cell at (d_row, d_col). + +### Searching in csv +```c +int csv_find(csv_t csv, const char* text, int flag, unsigned int* row, unsigned int* col); +``` +Search for `text` in the entire table. If a matching cell is found, 1 will be returned, and the matching position is (row, col). After the search is completed, -1 will be returned. +The search rules are controlled by `flag`. +```c +#define CSV_F_FLAG_MatchCase (0x01) /* match case sensitive */ +#define CSV_F_FLAG_MatchEntire (0x02) /* match the entire cell content */ +#define CSV_F_FLAG_MatchByCol (0x04) /* match by column */ +#define CSV_F_FLAG_MatchForward (0x08) /* match from back to front */ +``` + +### Traversing non-empty cells in csv +```c +#define csv_for_each(csv, row, col, text) +``` +Traverse all non-empty cells from top to bottom by row. +```c +const char *text = NULL; +csv_for_each(csv, row, col, text) +{ + printf("[%d, %d]: %s\r\n", row, col, text); +} +``` + +## Reference Examples + +### Generating a csv file +```c +static void dump_demo(void) +{ + csv_t csv; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + csv = csv_create(3, 5, array); + if (!csv) + { + printf("create csv fail!\r\n"); + return; + } + + if (csv_file_dump(csv, "info.csv") < 0) + { + printf("csv dump fail!\r\n"); + } + else + { + printf("csv dump success!\r\n"); + } + + csv_delete(csv); +} +``` +The dumped file **info.csv** +```csv +ID,Name,Gender,Age,Height +20240107001,ZhangSan,Man,18,178 +20240107002,LiSi,Woman,24,162 +``` + +### Loading a csv file +Load the same csv file **info.csv** +```csv +ID,Name,Gender,Age,Height +20240107001,ZhangSan,Man,18,178 +20240107002,LiSi,Woman,24,162 +``` + +```c +static void load_demo(void) +{ + csv_t csv; + + csv = csv_file_load("info.csv"); + if (!csv) + { + printf("csv load fail!\r\n"); + return; + } + + unsigned int row, col; + const char *text = NULL; + csv_for_each(csv, row, col, text) + { + printf("[%u, %u]: %s\r\n", row, col, text); + } + + csv_delete(csv); +} +``` +Running result: +``` +[1, 1]: ID +[1, 2]: Name +[1, 3]: Gender +[1, 4]: Age +[1, 5]: Height +[2, 1]: 20240107001 +[2, 2]: ZhangSan +[2, 3]: Man +[2, 4]: 18 +[2, 5]: 178 +[3, 1]: 20240107002 +[3, 2]: LiSi +[3, 3]: Woman +[3, 4]: 24 +[3, 5]: 162 +``` diff --git a/doc/dList.en.md b/doc/dList.en.md new file mode 100644 index 0000000..9dd3dd3 --- /dev/null +++ b/doc/dList.en.md @@ -0,0 +1,523 @@ +### Introduction + +A linked list is a data structure in which data elements are logically continuous but can be physically stored in a dispersed manner. It can link multiple data blocks of the same type into a complete sequence through pointers and plays an important role in the implementation of data structures. + +The dList module is a general-purpose doubly linked list module, which is very similar to the sList module. The difference lies in that the pointer field has been changed from a single pointer in the singly linked list to two pointers (one pointing to the next element and one pointing to the previous element). As a result, there are also differences in the storage structure. The sList is a unidirectional open-loop structure, while the dList is a bidirectional closed-loop structure (with the head and tail connected to form a ring). + +In terms of the API, its usage is basically the same as that of sList (although the underlying implementation is different). The comparison of the rest of the performance, advantages, and disadvantages is shown below. + +### Interface + +#### Creating a dList +```c +dList *dList_create(void); +``` +In dList, dList is both a linked list and a node (because a node can be regarded as a linked list with a length of 1). So this method is used to create an empty linked list with a length of 1 (that is, to create an empty node). + +#### Deleting a dList +```c +void dList_delete(dList *list); +``` +This method will delete the linked list (including all of its nodes). + +#### Setting and Getting the Content of a dList Node +```c +int dList_set(dList *list, void* data, int size); +int dList_get(dList *list, void* data, int size); +``` +After a node is created, its data field does not have content initially. You need to use the `dList_set` method to set the content of its data field, and you can use the `dList_get` method to get the content of the node's data field. The `dList_set` method will overwrite the original data, and you can also specify `size` as 0 to delete the data content of the dList node. + +```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); +} +``` +Result: +``` +dList_create Success! +list->data 3 +list->data Hello dList +``` +The `dList_ref` in the example is for data reference, and its specific usage is described below. + +#### Inserting Data into a dList +```c +dList *dList_insert(dList **listRef, int index, void *data, int size); +``` +The method for inserting data is more convenient to use. It saves the steps of creating a node and setting data (even when inserting at the head of the linked list, the creation can be omitted and completed internally by this method), and it can flexibly insert specified data into the specified position. + +```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); +} +``` +Result: +``` +data 0 +data 1 +data 100 +------------ +data 100 +data 1 +data 0 +``` +The `dList_forEachForward` and `dList_forEachReverse` in the example are traversal methods, and their specific usages are described below. When the reference to dList passed in is NULL, a node will be created for the first time and the head of the list will be generated. Passing in a negative `index` means inserting at the tail. You can use the predefined `dList_front` and `dList_back` macros to represent the head and tail. + +#### Erasing Data from a dList +```c +int dList_erase(dList **listRef, int index, dList **outPrev); +``` +This method corresponds to the `dList_insert` method. It erases the data at the specified position (it will remove the node from the linked list). At the same time, in order to make it more flexible to use, it also supports getting the previous node of the erased node (which can make it more convenient and efficient to perform continuous erasing). + +```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); +} +``` +Result: +``` +data 1 +data 2 +data 3 +data 4 +``` +This is compared with the previous insertion example, erasing the head of the list. + +#### Pushing and Popping in a 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); +``` +These are methods for inserting at the head, inserting at the tail, deleting from the head, and deleting from the tail respectively. In fact, they are encapsulations based on the `dList_insert` and `dList_erase` methods for common scenarios, making them easier to use. + +```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); +} +``` +Result: +``` +data 3 +data 2 +data 1 +data 0 +data 0 +data 1 +data 2 +``` + +#### Appending in a dList +```c +int dList_append(dList *list, dList **append); +``` +This method can concatenate two linked lists into one. The `append` linked list will become invalid after the concatenation is successful. + +**Note**: The `append` should be the head of the list. Although it can still be concatenated successfully even if it is not the head, it still belongs to the original linked list, and some unexpected situations may occur during operations. + +```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); +} +``` +Result: +``` +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 +``` + +#### Attaching Nodes in a dList +```c +dList *dList_attach(dList **listRef, int index, dList *attach); +``` +This method can attach a node (or a linked list) to an existing linked list. You can use the `index` to specify the specific position to attach. This method is very flexible and directly operates on the structure of the linked list. **Generally, this method is not commonly used**. Instead, methods encapsulated based on this one, such as `dList_insert`, are usually used. This method can be combined with other methods to be flexibly encapsulated into other methods again. + +```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); +} +``` +Result: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +data 0 +data 1 +data 2 +``` + +#### Detaching Nodes in a dList +```c +dList *dList_detach(dList **listRef, int begin, int end, dList **outPrev); +``` +This method is the counterpart of the `dList_attach` method. It can detach several nodes (a sub-linked list) from the linked list. You can use the `index` to specify which positions to detach and the `count` to specify the number of nodes. This method is very flexible and directly operates on the structure of the linked list. **Generally, this method is not commonly used**. Instead, methods encapsulated based on this one, such as `dList_erase`, are usually used. This method can be combined with other methods to be flexibly encapsulated into other methods again. + +```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); +} +``` +Result: +``` +node data 0 +node data 1 +node data 2 +node data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +#### Copying a dList +```c +dList *dList_copy(dList *list, int begin, int end); +``` +This method will make a deep copy of a new linked list according to the specified interval of the source linked list. + +```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); +} +``` +Result: +``` +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +#### Reversing an Interval in a dList +```c +int dList_reverse(dList *list, int begin, int end); +``` +This method will reverse the specified interval of the source linked list. + +```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); +} +``` +Result: +``` +data 0 +data 5 +data 2 +data 3 +data 4 +data 1 +data 6 +data 7 +data 8 +data 9 +``` + +#### Getting a Specified Node in a dList +```c +dList *dList_to(dList *list, int index); +``` +This method can get the node with a specified offset from the current position of the head of the list. Passing in a negative number allows you to search backward from the end. + +```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); +} +``` +Result: +``` +dList_to data 4 +data 0 +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +#### Getting the Size of a dList +```c +int dList_size(dList *list); +``` +This method is used to get the number of data elements in the linked 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); +} +``` +Result: +``` +size 10 +size 7 +``` + +#### Traversing a dList +```c +#define dList_forEach(list, node) // Traverse from the front to the back +#define dList_forEachForward(list, node) // Traverse from the front to the back +#define dList_forEachReverse(list, node) // Traverse from the back to the front +``` +These are methods for traversing the linked list. For specific examples, you can refer to the other usage examples above. + +#### Referencing Node Data in a dList +```c +#define dList_ref(node, type) +``` +This method is similar to the reference in C++. In fact, it operates on pointers (but hides the pointer operations), making reading and writing more convenient. However, it should be noted to avoid out-of-bounds data operations. For example, if the node originally stores `char`-type data (only 1 byte of space is allocated), using it as `int`-type data will cause an out-of-bounds problem. For specific examples, you can refer to the other usage examples above. diff --git a/doc/date.en.md b/doc/date.en.md new file mode 100644 index 0000000..46a88c4 --- /dev/null +++ b/doc/date.en.md @@ -0,0 +1,233 @@ +### Introduction +The `date` module in C language is a simple date calculation module. It offers developers numerous practical functions, covering aspects like date validity verification, leap year determination, calculation of date differences, obtaining the corresponding day of the week based on a date, and displaying the calendar for a specified month and year. It facilitates efficient handling of various date-related business logics in C language projects. + +### Interfaces + +#### Functions + +##### `date_isleap` Function +```c +uint8_t date_isleap(uint16_t year); +``` +**Usage Example**: +```c +int test() +{ + uint16_t year = 2024; + uint8_t result = date_isleap(year); + if (result == 1) + { + printf("%d is a leap year.\n", year); + } + else + { + printf("%d is not a leap year.\n", year); + } + return 0; +} +``` +This function is used to check if a given year is a leap year. It returns 1 if it is a leap year and 0 otherwise. + +##### `date_isvalid` Function +```c +uint8_t date_isvalid(DATE date); +``` +**Usage Example**: +```c +int test() +{ + DATE date = DATE(2024, 2, 29); // Assume a date example + uint8_t result = date_isvalid(date); + if (result == 1) { + printf("The date is valid.\n"); + } else { + printf("The date is invalid.\n"); + } + return 0; +} +``` +It's used to verify the validity of a given date. If the date is valid according to the rules of the calendar, it returns 1; otherwise, it returns 0. + +##### `date_century_days` Function +```c +uint32_t date_century_days(uint16_t century); +``` +**Usage Example**: +```c +int test() +{ + uint16_t century = 21; + uint32_t result = date_century_days(century); + printf("The number of days in the %d century is: %u\n", century, result); + return 0; +} +``` +This function calculates and returns the total number of days in a specified century. + +##### `date_year_days` Function +```c +uint32_t date_year_days(uint16_t year); +``` +**Usage Example**: +```c +int test() +{ + uint16_t year = 2024; + uint32_t result = date_year_days(year); + printf("The number of days in the year %d is: %u\n", year, result); + return 0; +} +``` +It determines and returns the number of days in a particular year, taking into account whether it's a leap year or not. + +##### `date_month_days` Function +```c +uint32_t date_month_days(uint16_t year, uint8_t month); +``` +**Usage Example**: +```c +int test() +{ + uint16_t year = 2024; + uint8_t month = 2; + uint32_t result = date_month_days(year, month); + printf("The number of days in month %d of year %d is: %u\n", month, year, result); + return 0; +} +``` +This function figures out and returns the number of days in a specific month of a given year. + +##### `date_get_dow` Function +```c +uint32_t date_get_dow(DATE date); +``` +**Usage Example**: +```c +int test() +{ + DATE date = DATE(2024, 12, 31); + uint32_t result = date_get_dow(date); + switch (result) { + case 1: + printf("The day of week is Monday.\n"); + break; + case 2: + printf("The day of week is Tuesday.\n"); + break; + case 3: + printf("The day of week is Wednesday.\n"); + break; + case 4: + printf("The day of week is Thursday.\n"); + break; + case 5: + printf("The day of week is Friday.\n"); + break; + case 6: + printf("The day of week is Saturday.\n"); + break; + case 7: + printf("The day of week is Sunday.\n"); + break; + default: + printf("The input date is invalid.\n"); + } + return 0; +} +``` +It's used to obtain the day of the week corresponding to a given date. + +##### `date_current_days` Function +```c +uint32_t date_current_days(DATE date); +``` +**Usage Example**: +```c +int test() +{ + DATE date = DATE(2024, 12, 31); + uint32_t result = date_current_days(date); + printf("The total number of days from the base date to the given date is: %u\n", result); + return 0; +} +``` +This function calculates the total number of days from a base date (presumably some predefined starting point) to the provided date. + +##### `date_diff_days` Function +```c +int32_t date_diff_days(DATE date1, DATE date2); +``` +**Usage Example**: +```c +int test() +{ + DATE date1 = DATE(2024, 1, 1); + DATE date2 = DATE(2024, 12, 31); + int32_t result = date_diff_days(date1, date2); + printf("The difference in days between the two dates is: %d\n", result); + return 0; +} +``` +It calculates the difference in days between two given dates. + +##### `date_from_days` Function +```c +DATE date_from_days(uint32_t days); +``` +**Usage Example**: +```c +int test() +{ + uint32_t days = 366; // Assume corresponding number of days + DATE result = date_from_days(days); + printf("The date converted from %u days is: %d-%d-%d\n", days, result.year, result.month, result.day); + return 0; +} +``` +This function converts a given number of days into a specific date. + +##### `date_offset` Function +```c +DATE date_offset(DATE date, int32_t days); +``` +**Usage Example**: +```c +int test() +{ + DATE date = DATE(2024, 1, 1); + int32_t days = 365; + DATE result = date_offset(date, days); + printf("The offset date is: %d-%d-%d\n", result.year, result.month, result.day); + return 0; +} +``` +It calculates a new date by offsetting the given date by a specified number of days. + +##### `date_calendar` Function +```c +void date_calendar(uint16_t year, uint8_t month); +``` +**Usage Example**: +```c +int test() +{ + uint16_t year = 2024; + uint8_t month = 12; + date_calendar(year, month); + return 0; +} +``` +This function displays the calendar for a specified month and year, presenting it in a formatted table-like structure as shown in the example result: +``` ++-----------------------------------------+ +| 2024/12 | ++-----------------------------------------+ +| Sun | Mon | Tue | Wed | Thu | Fri | Sat | ++-----------------------------------------+ +| 1 | 2 | 3 | 4 | 5 | 6 | 7 | +| 8 | 9 | 10 | 11 | 12 | 13 | 14 | +| 15 | 16 | 17 | 18 | 19 | 20 | 21 | +| 22 | 23 | 24 | 25 | 26 | 27 | 28 | +| 29 | 30 | 31 | | | | | ++-----------------------------------------+ +``` diff --git a/doc/date.md b/doc/date.md new file mode 100644 index 0000000..277fa6f --- /dev/null +++ b/doc/date.md @@ -0,0 +1,223 @@ +## 介绍 +`date`是一个用于C语言的简单日期计算模块,它为开发者提供了诸多实用的功能,涵盖了日期合法性验证、闰年判断、日期差值计算、基于日期获取对应星期数以及展示指定年月的日历等多方面的日期相关操作,方便在C语言项目中高效处理各种与日期有关的业务逻辑。 + +## 接口 + +### 函数 + +#### `date_isleap`函数 +```c +uint8_t date_isleap(uint16_t year); +``` +使用例子: +```c +int test() +{ + uint16_t year = 2024; + uint8_t result = date_isleap(year); + if (result == 1) + { + printf("%d is a leap year.\n", year); + } + else + { + printf("%d is not a leap year.\n", year); + } + return 0; +} +``` + +#### `date_isvalid`函数 +```c +uint8_t date_isvalid(DATE date); +``` +使用例子: +```c +int test() +{ + DATE date = DATE(2024, 2, 29); // 假设一个日期示例 + uint8_t result = date_isvalid(date); + if (result == 1) { + printf("The date is valid.\n"); + } else { + printf("The date is invalid.\n"); + } + return 0; +} +``` + +#### `date_century_days`函数 +```c +uint32_t date_century_days(uint16_t century); +``` +使用例子: +```c +int test() +{ + uint16_t century = 21; + uint32_t result = date_century_days(century); + printf("The number of days in the %d century is: %u\n", century, result); + return 0; +} +``` + +#### `date_year_days`函数 +```c +uint32_t date_year_days(uint16_t year); +``` +使用例子: +```c +int test() +{ + uint16_t year = 2024; + uint32_t result = date_year_days(year); + printf("The number of days in the year %d is: %u\n", year, result); + return 0; +} +``` + +#### `date_month_days`函数 +```c +uint32_t date_month_days(uint16_t year, uint8_t month); +``` +使用例子: +```c +int test() +{ + uint16_t year = 2024; + uint8_t month = 2; + uint32_t result = date_month_days(year, month); + printf("The number of days in month %d of year %d is: %u\n", month, year, result); + return 0; +} +``` + +#### `date_get_dow`函数 +```c +uint32_t date_get_dow(DATE date); +``` +使用例子: +```c +int test() +{ + DATE date = DATE(2024, 12, 31); + uint32_t result = date_get_dow(date); + switch (result) { + case 1: + printf("The day of week is Monday.\n"); + break; + case 2: + printf("The day of week is Tuesday.\n"); + break; + case 3: + printf("The day of week is Wednesday.\n"); + break; + case 4: + printf("The day of week is Thursday.\n"); + break; + case 5: + printf("The day of week is Friday.\n"); + break; + case 6: + printf("The day of week is Saturday.\n"); + break; + case 7: + printf("The day of week is Sunday.\n"); + break; + default: + printf("The input date is invalid.\n"); + } + return 0; +} +``` + +#### `date_current_days`函数 +```c +uint32_t date_current_days(DATE date); +``` +使用例子: +```c +int test() +{ + DATE date = DATE(2024, 12, 31); + uint32_t result = date_current_days(date); + printf("The total number of days from the base date to the given date is: %u\n", result); + return 0; +} +``` + +#### `date_diff_days`函数 +```c +int32_t date_diff_days(DATE date1, DATE date2); +``` +使用例子: +```c +int test() +{ + DATE date1 = DATE(2024, 1, 1); + DATE date2 = DATE(2024, 12, 31); + int32_t result = date_diff_days(date1, date2); + printf("The difference in days between the two dates is: %d\n", result); + return 0; +} +``` + +#### `date_from_days`函数 +```c +DATE date_from_days(uint32_t days); +``` +使用例子: +```c +int test() +{ + uint32_t days = 366; // 假设对应天数 + DATE result = date_from_days(days); + printf("The date converted from %u days is: %d-%d-%d\n", days, result.year, result.month, result.day); + return 0; +} +``` + +#### `date_offset`函数 +```c +DATE date_offset(DATE date, int32_t days); +``` +使用例子: +```c +int test() +{ + DATE date = DATE(2024, 1, 1); + int32_t days = 365; + DATE result = date_offset(date, days); + printf("The offset date is: %d-%d-%d\n", result.year, result.month, result.day); + return 0; +} +``` + +#### `date_calendar`函数 +```c +void date_calendar(uint16_t year, uint8_t month); +``` +使用例子: +```c +int test() +{ + uint16_t year = 2024; + uint8_t month = 12; + date_calendar(year, month); + return 0; +} +``` +结果: +``` ++-----------------------------------------+ +| 2024/12 | ++-----------------------------------------+ +| Sun | Mon | Tue | Wed | Thu | Fri | Sat | ++-----------------------------------------+ +| 1 | 2 | 3 | 4 | 5 | 6 | 7 | +| 8 | 9 | 10 | 11 | 12 | 13 | 14 | +| 15 | 16 | 17 | 18 | 19 | 20 | 21 | +| 22 | 23 | 24 | 25 | 26 | 27 | 28 | +| 29 | 30 | 31 | | | | | ++-----------------------------------------+ +``` diff --git a/doc/deque.en.md b/doc/deque.en.md new file mode 100644 index 0000000..016a074 --- /dev/null +++ b/doc/deque.en.md @@ -0,0 +1,181 @@ +## Introduction + +A double-ended queue (deque) has two entrances and exits and combines the characteristics of both a queue and a stack. Data can be entered and exited from either of the two ports. +The implementation principles of varch's double-ended queue, queue, and stack are the same. They all use continuous address spaces that are connected in a ring shape, enabling efficient data entry and exit at both ends and also supporting random access. +- **Capacity**: +Capacity refers to the maximum number of queue items that can be stored during use. For example, for a queue with a capacity of 10, it can store at most 10 queue items. Once it's full, no more items can be enqueued. The queue storage in varch uses continuous addresses and is a queue with a limited capacity. +- **Access Mechanism**: +Generally, a queue has only two ways of operation, which are enqueueing and dequeueing. It can be quickly traversed and accessed based on continuous addresses. + +## Interface + +### Creating and Deleting deque Objects +```c +deque_t deque_create(int dsize, int capacity, void *base); +void deque_delete(deque_t deque); +#define deque(type, capacity) // For more convenient use, a macro definition is wrapped around deque_create +#define _deque(deque) // A macro definition is wrapped around deque_delete, and the deque is set to NULL after deletion +``` +Here, **deque_t** is the structure of deque. The creation method will return a deque object if successful, or NULL if it fails. The parameter `dsize` is used to pass in the size of the data, `capacity` is used to pass in the queue capacity, and `*base` is used to pass in the buffer address (it can be omitted, and if omitted, a space with the size of `capacity` will be automatically allocated to store the queue data). The deletion method is used to delete the input deque object. The creation and deletion methods should be used in pairs. Once created, it should be deleted when it's no longer in use. +```c +void test(void) +{ + deque_t deque = deque(int, 10); // Define and create a deque of type int with a capacity of 10 + _deque(deque); // Use them in pairs and delete it after use +} +``` + +### Enqueueing and Dequeueing of deque +```c +int deque_push_front(deque_t deque, void* data); +int deque_push_back(deque_t deque, void* data); +int deque_pop_front(deque_t deque, void* data); +int deque_pop_back(deque_t deque, void* data); +``` +These four methods can conveniently add data to the queue and pop data from the queue. For the `push` methods, the `data` parameter is used to pass in the address of the data to be enqueued. For the `pop` methods, the `data` parameter is used to pass in the address that will receive the dequeued data. For both types of methods, `data` can be set to NULL, which just serves as a placeholder. The methods return 1 if the operation is successful and 0 if it fails. +```c +void test(void) +{ + deque_t deque = deque(int, 10); + int i = 0; + + for (i = 0; i < deque_capacity(deque); i++) + { + deque_push_back(deque, &i); + } + deque_pop_front(deque, NULL); + deque_pop_back(deque, NULL); + + _deque(deque); // Use them in pairs and delete it after use +} +``` + +### Size, Capacity, and Data Size of deque +```c +int deque_size(deque_t deque); +int deque_capacity(deque_t deque); +int deque_dsize(deque_t deque); +``` +The `capacity` of deque is the capacity specified during creation, which indicates how many queue elements it can store. The `size` represents the number of elements in the queue, and `dsize` is the size of the data passed in during creation. For example, if it's `int`, `dsize` is `sizeof(int)`. +```c +void test(void) +{ + deque_t deque = deque(int, 10); + int i = 0; + + for (i = 0; i < deque_capacity(deque); i++) + { + deque_push_back(deque, &i); + } + deque_pop_front(deque, NULL); + deque_pop_back(deque, NULL); + printf("deque capacity=%d, size=%d, dsize=%d\r\n", deque_capacity(deque), deque_size(deque), deque_dsize(deque)); + + _deque(deque); +} +``` +Result: +``` +deque capacity=10, size=8, dsize=4 +``` + +### Reading and Writing of deque Data +```c +void* deque_data(deque_t deque, int index); +#define deque_at(deque, type, i) +``` +The `deque_data` method is used to obtain the address of the data according to the index, and it returns the address of the specified data. NULL will be returned if the operation fails. The `deque_at` method adds a type on the basis of `deque_data`. +```c +void test(void) +{ + deque_t deque = deque(int, 10); + int i = 0; + + for (i = 0; i < deque_capacity(deque); i++) + { + deque_push_back(deque, &i); + } + deque_pop_front(deque, NULL); + deque_pop_back(deque, NULL); + for (i = 0; i < deque_size(deque); i++) + { + printf("deque[%d] = %d\r\n", i, deque_at(deque, int, i)); + } + + _deque(deque); +} +``` +Result: +``` +deque[0] = 1 +deque[1] = 2 +deque[2] = 3 +deque[3] = 4 +deque[4] = 5 +deque[5] = 6 +deque[6] = 7 +deque[7] = 8 +``` + +### Storage Index of deque Data +```c +int deque_index(deque_t deque, int index); +``` +The queue storage structure is a circular queue, which means that the continuous address storage space is connected end to end to form a ring, and the entry and exit of queue data occur within this ring. Therefore, the index of the queue is not directly the index of the buffer. The `deque_index` method is used to map the index of the queue to the index of the buffer. -1 will be returned if the operation fails. +Generally, this method is not used frequently. It's mainly used when the `base` is passed in during the `deque_create` method, and then the `deque_index` method is used on the `base` address to obtain the queue data. + +### Checking if deque is Empty or Full +```c +int deque_empty(deque_t deque); +int deque_full(deque_t deque); +``` +These two methods are actually related to the size of deque's `size`. If it equals 0, the deque is empty. If it equals the capacity, the deque is full. + +## Source Code Analysis + +### deque Structure +All the structures of the deque container are implicit, which means that the structure members cannot be directly accessed. This way ensures the independence and security of the module and prevents external calls from modifying the structure members and thus destroying the deque storage structure. Therefore, the deque parser only leaves a single deque declaration in the header file, and the definitions of the structures are in the source file. Only the methods provided by the deque container can be used to operate on deque objects. +The deque type declaration: +```c +typedef struct DEQUE *deque_t; +``` +When using it, just use `deque_t`. +```c +typedef struct DEQUE +{ + void* base; /* base address of data */ + int cst; /* base const */ + int dsize; /* size of deque data */ + int capacity; /* capacity of deque */ + int size; /* size of deque */ + int head; /* index of deque head */ + int tail; /* index of deque tail */ +} DEQUE; +``` +The `DEQUE` structure contains seven members. `base` is the base address of the data buffer for the queue structure, `cst` indicates whether the space of `base` is passed in during the `create` method, `size` represents the size (length) of deque, `dsize` is the size of each data, `capacity` is the capacity of the queue, and `head` and `tail` are the indices pointed to by the head and tail of the circular buffer respectively. + +The main problem that the deque container needs to solve is the issue of the circular queue and the first-in-first-out characteristic of data. Other operations like creation and deletion are for basic initialization such as space initialization. The operations of `deque_push_back` and `deque_pop_front` are actually the same as those of a queue and won't be elaborated here. The main focus is on `deque_push_front` and `deque_pop_back`. +```c +int deque_push_front(deque_t deque, void* data) +{ + if (!deque) return 0; + if (deque_full(deque)) return 0; // Check if the queue is full before enqueueing + // Here, it's not simply subtracting 1 from head. Considering that when head is 0, subtracting 1 would result in -1, which is out of the capacity range. + // What we want is that when head is 0 and we push to the head, head should go back to the end of the buffer to ensure the continuity of the ring. + // So the approach here is to add capacity to head first, then subtract 1, and finally take the remainder of capacity to ensure the circularity. + deque->head = (deque->head + deque->capacity - 1) % deque->capacity; + if (data) memcpy(at(deque->head), data, deque->dsize); + deque->size++; + return 1; +} +int deque_pop_back(deque_t deque, void* data) +{ + if (!deque) return 0; + if (deque_empty(deque)) return 0; // Check if the queue is empty before dequeueing + // The operation on tail here is similar to the operation on head in the deque_push_front method. + deque->tail = (deque->tail + deque->capacity - 1) % deque->capacity; + if (data) memcpy(data, at(deque->tail), deque->dsize); + deque->size--; + return 1; +} +``` diff --git a/doc/dict.en.md b/doc/dict.en.md new file mode 100644 index 0000000..94ca067 --- /dev/null +++ b/doc/dict.en.md @@ -0,0 +1,371 @@ +## Introduction + +The `dict` dictionary is a logically discrete container, which is quite similar to the `set` container. The `set` container exists in the form of `index - data`, while the `dict` container exists in the form of `key - value`. The `index` of the `set` is an integer number, and the `key` of the `dict` is a string (here it's different from Python's `dict`, as Python's `key` can be not only a string but also an integer or a tuple, etc. In varch, something similar to Python's `dict` is called a `map` mapping). The `data` of the `set` and the `value` of the `dict` are essentially the same thing. +The `dict` container in varch adopts a **hash table** in its underlying implementation. It enables quick lookups, supports random access, and occupies relatively small space. The `dict` can be traversed through an iterator. + +## Interface + +### Creating and Deleting dict Objects +```c +dict_t dict_create(int dsize); +void dict_delete(dict_t dict); +#define dict(type) // For more convenient use, a macro definition is wrapped around dict_create +#define _dict(dict) // A macro definition is wrapped around dict_delete, and the dict is set to NULL after deletion +``` +Here, **dict_t** is the structure of `dict`. The creation method will return an empty `dict` object if successful, or NULL if it fails. The parameter `dsize` is used to pass in the size of the data. The deletion method is used to delete the input `dict` object. The creation and deletion methods should be used in pairs. Once created, it should be deleted when it's no longer in use. +```c +void test(void) +{ + dict_t dict = dict(int); // Define and create a dict of type int + _dict(dict); // Use them in pairs and delete it after use +} +``` + +### Inserting and Removing in dict +```c +void* dict_insert(dict_t dict, const char *key, void *value); +int dict_erase(dict_t dict, const char *key); +``` +This `dict` locates data through hash values and can quickly find the storage location of data based on hash values. +The insertion method is used to add a specified `key` and copy the data to this key (when `value` is passed in as NULL, only space will be allocated without assignment). During the process of inserting the `key`, duplicate checking will be performed to ensure the uniqueness of the `key`. If the insertion is successful, the address of the inserted data will be returned; otherwise, NULL will be returned. The removal method is used to remove the data of the specified key. It returns 1 if successful and 0 if it fails. + +### Reading and Writing of dict Data +```c +void* dict_value(dict_t dict, const char *key); +void* dict_error(dict_t dict); +#define dict_at(dict, type, key) +``` +The `dict_value` method is used to obtain the address of the data according to the key and returns the address of the specified data. `dict_error()` is used to indicate failure. The `dict_at` method adds a type on the basis of `dict_value`. The `dict_value` has a read-write protection mechanism. Since `dict_error()` is returned instead of NULL, when using the `dict_at` method and if the key is written incorrectly, the content pointed to by `dict_error()` will be modified instead of causing a crash. +The random access of `dict` is achieved by calculating the hash value of the key and locating the data in the hash table according to the hash value. +```c +void test(void) +{ + dict_t dict = dict(int); + int value; + + value = 100; dict_insert(dict, "hello", &value); + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + value = 4; dict_insert(dict, "SunLiu", &value); + value = 5; dict_insert(dict, "QianQi", &value); + + printf("dict[hello] = %d\r\n", dict_at(dict, int, "hello")); + printf("dict[SunLiu] = %d\r\n", dict_at(dict, int, "SunLiu")); + + _dict(dict); +} +``` +Result: +``` +dict[hello] = 100 +dict[SunLiu] = 4 +``` + +### Size and Data Size of dict +```c +int dict_size(dict_t dict); +int dict_dsize(dict_t dict); +``` +The `size` of `dict` is similar to the size of an array, and `dsize` is the size of the data passed in during creation. +```c +void test(void) +{ + dict_t dict = dict(int); + int value; + + value = 100; dict_insert(dict, "hello", &value); + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + value = 4; dict_insert(dict, "SunLiu", &value); + value = 5; dict_insert(dict, "QianQi", &value); + + printf("size = %d, value size = %d\r\n", dict_size(dict), dict_dsize(dict)); + + _dict(dict); +} +``` +Result: +``` +size = 6, value size = 4 +``` + +### dict Search +```c +int dict_find(dict_t dict, const char *key); +``` +This method is actually implemented by wrapping `dict_value`. It returns 1 if the search is successful and 0 if it fails. + +### dict Iterator +```c +void dict_it_init(dict_t dict); +void* dict_it_get(dict_t dict, char **key); +``` +The `dict` also supports a built-in iterator, which is mainly used for traversing. Since when traversing an array, we know that the keys start from 0 and can be traversed one by one in an increasing order. However, the `dict` has discrete keys, so it can't be traversed in this incremental way. Therefore, two iterator functions are provided here for traversing the `dict`. +The `dict_it_init` function initializes the iterator, and the `dict_it_get` function obtains the iteration and updates the iteration position. The `*key` is the output key (the current key, or NULL can be passed in if not needed), and the function returns the data at the iteration position. The number of iterations is controlled by `dict_size`. +```c +void test(void) +{ + dict_t dict = dict(int); + int value; + char *key; + void *data; + int i; + + value = 100; dict_insert(dict, "hello", &value); + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + value = 4; dict_insert(dict, "SunLiu", &value); + value = 5; dict_insert(dict, "QianQi", &value); + + dict_it_init(dict, DICT_HEAD); + i = dict_size(dict); + while (i--) + { + data = dict_it_get(dict, &key); + printf("dict[%s] = %d\r\n", key, *(int *)data); + } + + _dict(dict); +} +``` +Result: +``` +dict[LiSi] = 2 +dict[QianQi] = 5 +dict[SunLiu] = 4 +dict[WangWu] = 3 +dict[ZhangSan] = 1 +dict[hello] = 100 +``` + +## Source Code Analysis + +### dict Structure +All the structures of the `dict` container are implicit, which means that the structure members cannot be directly accessed. This way ensures the independence and security of the module and prevents external calls from modifying the structure members and thus destroying the `dict` storage structure. Therefore, the `dict` parser only leaves a single `dict` declaration in the header file, and the definitions of the structures are in the source file. Only the methods provided by the `dict` container can be used to operate on `dict` objects. +The `dict` type declaration: +```c +typedef struct DICT *dict_t; +``` +When using it, just use `dict_t`. +```c +/* dict type define */ +typedef struct DICT +{ + groove_t *base; /* base address for groove data */ + void *error; /* error space */ + int vsize; /* size of value */ + unsigned int size; /* size of dict */ + unsigned int capacity; /* capacity of dict */ + unsigned int it; /* iterator index */ +} DICT; +``` +The `DICT` structure contains 6 members. `base` is the base address of the hash table, `error` is the error area of `dict` (when randomly accessing a non-existent key, the address of this error area will be returned, so that when using the `at` method, it won't operate on invalid memory addresses), `size` is the size (length) of `dict`, `vsize` is the size of each data, `capacity` is the capacity of the hash table, and `it` is used to record the current accessed hash table index during iterator traversal. +The main problems that the `dict` container needs to solve are hash collisions and the adjustment of the hash table capacity. +```c +/* dict node type define */ +typedef struct +{ + unsigned int hash; /* hash value */ + char *key; /* key */ + void *value; /* value */ +} GROOVE, *groove_t; +``` +In `dict`, data is stored through an array. Each array item only stores the address of each slot (groove), and each slot stores three parts of content: the hash value of the current slot in the current hash table (for faster lookup of key-value pairs with hash collisions), and the actual stored key-value pairs. +``` ++------+------------------------+------------------------+ +| hash | key | value | ++------+------------------------+------------------------+ +|... | ... | ... | ++------+------------------------+------------------------+ +|... | ... | ... | ++------+------------------------+------------------------+ +``` + +### dict Creation and Deletion +```c +dict_t dict_create(int vsize) +{ + dict_t dict; + if (vsize <= 0) return NULL; + dict = (dict_t)malloc(sizeof(DICT)); + if (!dict) return NULL; + dict->error = malloc(vsize); + if (!dict->error) { free(dict); return NULL; } + dict->base = NULL; + dict->vsize = vsize; + dict->size = 0; + dict->capacity = 0; + dict->it = 0; + return dict; +} +``` +The creation of `dict` only creates an empty `dict` and initializes the basic parameters. The deletion method is used to release the memory space allocated during the operation of `dict`. + +### dict Insertion +```c +void* dict_insert(dict_t dict, const char *key, void *value) +{ + groove_t groove = NULL; + unsigned int hash = 0, index; + int len = 0; + + /* + Check the validity of the input parameters. + */ + if (!dict) return NULL; + if (!key) return NULL; + + /* + Check if the size has exceeded 3/4 of the hash table capacity. If it exceeds 3/4, resize the hash table. + Because when it exceeds 3/4, it means the hash table is quite full and the lookup efficiency will be greatly reduced. So some free space in the hash table should be maintained. + The resizing here changes in powers of 2, such as growing as 4, 8, 16, 32... + */ + /* the current capacity affects the search rate and needs to be expanded */ + if (dict->size >= ((dict->capacity >> 2) + (dict->capacity >> 1))) /* size exceeds 3/4 of capacity */ + { + /* + During the resizing process, first allocate a new space with a new capacity, + then rehash and insert the members in the old hash table back into the new hash table one by one. + */ + /* allocate new hash table space */ + if (!dict_resize(dict, dict->capacity < MIN_CAPACITY? MIN_CAPACITY : dict->capacity << 1)) return NULL; + } + + /* + Calculate the hash value of the inserted key (the bkdr hash algorithm is used by default), and take the modulus of the hash value with the hash table capacity. + Then check if there is a hash collision. If there is a collision, use the open addressing method, linearly adding 1 to search for free space backward. + During the backward search, check the hash value in the corresponding slot. A hash value of -1 indicates that it has been removed by the `erase` method but the actual slot has not been deleted, and it is also regarded as an available slot. + */ + /* find a free groove */ + len = strlen(key); + hash = hash_bkdr((void *)key, len) % dict->capacity; + index = hash; + while (dict->base[index] && dict->base[index]->hash!= -1) + { + index = (index + 1) % dict->capacity; + if (index == hash) return NULL; + } + + /* + Allocate the space for the slot. + */ + /* space allocation */ + groove = dict->base[index]; + if (!groove) groove = (groove_t)malloc(sizeof(GROOVE)); + if (!groove) return NULL; + groove->key = (char *)malloc(len + 1); + if (!groove->key) { if (!dict->base[index]) free(groove); return NULL; } + groove->value = malloc(dict->vsize); + if (!groove->value) { free(groove->key), groove->key = NULL; if (!dict->base[index]) free(groove); return NULL; } + + /* assign */ + groove->hash = hash; // Record the hash value of the current slot + strcpy(groove->key, key); + if (value) memcpy(groove->value, value, dict->vsize); + + /* insert */ + dict->base[index] = groove; + dict->size++; + + return groove->value; +} +``` +Actual storage example: +```c +value = 100; dict_insert(dict, "hello", &value); +value = 1; dict_insert(dict, "ZhangSan", &value); +value = 2; dict_insert(dict, "LiSi", &value); +value = 3; dict_insert(dict, "WangWu", &value); +value = 4; dict_insert(dict, "SunLiu", &value); +value = 5; dict_insert(dict, "QianQi", &value); +value = 8; dict_insert(dict, "WangBa", &value); +value = 9; dict_insert(dict, "LiuJiu", &value); +``` +Hash table: +``` ++------+------------------------+------------------------+ +| hash | key | value | ++------+------------------------+------------------------+ +| 0 | ZhangSan | 1 | ++------+------------------------+------------------------+ +| 1 | QianQi | 5 | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| 4 | SunLiu | 4 | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| 6 | WangBa | 8 | ++------+------------------------+------------------------+ +| 7 | LiSi | 2 | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| 9 | WangWu | 3 | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| | | | ++------+------------------------+------------------------+ +| 14 | hello | 100 | ++------+------------------------+------------------------+ +| 14 | LiuJiu | 9 | ++------+------------------------+------------------------+ +``` + +### Removal from the `dict` + +```c +int dict_erase(dict_t dict, const char *key) +{ + groove_t groove; + unsigned int index, next; + + // Check if the dictionary pointer is NULL. If so, return 0 to indicate failure of the removal operation. + if (!dict) return 0; + // Check if the key pointer is NULL. If so, also return 0 as the operation can't proceed without a valid key. + if (!key) return 0; + + /* + To remove an element, we first need to find the position of the key in the hash table. + The `find_index` function follows a similar logic as the insertion method described earlier. If the key is not found, it will return -1. + */ + index = find_index(dict, key); + // If the `find_index` function returns -1, meaning the key was not found in the hash table, return 0 to indicate failure of the removal. + if (index == -1) return 0; + groove = dict->base[index]; + + // If the `key` pointer in the `groove` structure is not NULL, free the memory occupied by the key string and set the pointer to NULL to avoid dangling pointers. + if (groove->key) { free(groove->key); groove->key = NULL; } + // Similarly, if the `value` pointer in the `groove` structure is not NULL, free the memory occupied by the value and set the pointer to NULL. + if (groove->value) { free(groove->value); groove->value = NULL; } + // Instead of actually freeing the entire `groove` structure, we just set its `hash` value to -1. A hash value of -1 indicates that this slot is logically removed (although the structure itself isn't deallocated yet). In the hash table, a hash value of -1 is not used for normal elements. + groove->hash = -1; + // Decrease the size of the dictionary by 1 to reflect the removal of one key-value pair. + dict->size--; + + /* + Just like with the insertion method where the size and capacity need to be adjusted, the same applies to the removal operation. + When the number of elements in the dictionary (`size`) is less than one-fourth of the capacity, we resize the hash table by reducing its capacity to half. This way, the dictionary will occupy about half of its previous capacity. + */ + if (dict->capacity > MIN_CAPACITY && dict->size <= (dict->capacity >> 2)) /* size less than 1/4 of capacity */ + { + dict_resize(dict, dict->capacity >> 1); + } + + // If all the above operations are completed successfully, return 1 to indicate that the removal operation was successful. + return 1; +} +``` + +This function `dict_erase` is mainly responsible for removing a key-value pair from the `dict` data structure. It first locates the position of the key in the hash table. Then, it frees the memory associated with the key and value, marks the slot as logically removed, adjusts the size of the dictionary, and finally may resize the hash table if necessary to optimize memory usage and maintain the efficiency of the hash table structure. diff --git a/doc/encrypt.en.md b/doc/encrypt.en.md new file mode 100644 index 0000000..3e812a8 --- /dev/null +++ b/doc/encrypt.en.md @@ -0,0 +1,144 @@ +## Introduction + +During data transmission, some sensitive data, such as usernames and passwords, need to be encrypted to ensure security. + +Encryption and decryption algorithms are generally classified into **symmetric encryption algorithms** and **asymmetric encryption algorithms**. This module provides several basic encryption and decryption algorithms: +- [x] DES encryption and decryption +- [x] DES3 encryption and decryption +- [ ] AES encryption and decryption +- [ ] SHA1 encryption +- [ ] MD5 encryption +- [ ] HMAC calculation +- [ ] blowfish encryption and decryption +- [ ] RSA encryption and decryption, public key, signature, conversion + +## Interface + +### DES +```c +int des_set_key(const uint8_t key[8]); +int des_crypt_ecb(const uint8_t input[8], uint8_t output[8], uint8_t mode); +int des_crypt_cbc(const uint8_t *input, uint8_t *output, uint32_t length, uint8_t mode); +``` +The DES algorithm commonly has ECB (Electronic Codebook) and CBC (Cipher Block Chaining) modes. In the ECB mode, each encryption block is calculated independently, while in the CBC mode, the calculation depends on the previous encryption block. + +Before performing encryption or decryption, it is necessary to set the key first using the `des_set_key` function. The key has a default length of 8 bytes. + +Both encryption and decryption algorithms use the `des_crypt_ecb` function, and whether it is encryption or decryption is set through the `mode` parameter. + +The `des_crypt_cbc` function is different from `des_crypt_ecb`. The lengths of `input` and `output` need to be the same and a multiple of 8, which is passed in through the `length` parameter. + +Example: +```c +void test_des(void) +{ + uint8_t key[8] = "hello"; + uint8_t data_block[8] = {1, 2, 3, 4, 5, 6, 7, 9}; + uint8_t processed_block[8]; + int i; + + des_set_key(key); + + printf("des ecb encrypt: "); + des_crypt_ecb(data_block, processed_block, DES_ENCRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", processed_block[i]); + } + printf("\r\n"); + + printf("des ecb decrypt: "); + des_crypt_ecb(processed_block, data_block, DES_DECRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", data_block[i]); + } + printf("\r\n"); + + /////////////////////////////////////// + printf("des cbc encrypt: "); + des_crypt_cbc(data_block, processed_block, 8, DES_ENCRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", processed_block[i]); + } + printf("\r\n"); + + printf("des cbc decrypt: "); + des_crypt_cbc(processed_block, data_block, 8, DES_DECRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", data_block[i]); + } + printf("\r\n"); +} +``` +Result: +``` +des ecb encrypt: 156 151 171 0 235 148 83 44 +des ecb decrypt: 1 2 3 4 5 6 7 9 +des cbc encrypt: 156 151 171 0 235 148 83 44 +des cbc decrypt: 1 2 3 4 5 6 7 9 +``` + +### DES3 +```c +int des3_set_key2(const uint8_t key[16]); +int des3_set_key3(const uint8_t key[24]); +int des3_crypt_ecb(const uint8_t input[8], uint8_t output[8], uint8_t mode); +int des3_crypt_cbc(const uint8_t *input, uint8_t *output, uint32_t length, uint8_t mode); +``` +In terms of usage, `DES3` is consistent with `DES`. It performs triple encryption on DES, and the length of the key also increases accordingly. Keys of 16 bytes and 24 bytes can be set. The usage of other encryption and decryption modes such as ECB and CBC is the same as that of `DES`. + +Example: +```c +void test_des3(void) +{ + uint8_t key[24] = "hello world"; + uint8_t data_block[8] = {1, 2, 3, 4, 5, 6, 7, 9}; + uint8_t processed_block[8]; + int i; + + des3_set_key2(key); + + printf("des3 ecb encrypt: "); + des3_crypt_ecb(data_block, processed_block, DES_ENCRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", processed_block[i]); + } + printf("\r\n"); + + printf("des3 ecb decrypt: "); + des3_crypt_ecb(processed_block, data_block, DES_DECRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", data_block[i]); + } + printf("\r\n"); + + /////////////////////////////////////// + printf("des3 cbc encrypt: "); + des3_crypt_cbc(data_block, processed_block, 8, DES_ENCRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", processed_block[i]); + } + printf("\r\n"); + + printf("des3 cbc decrypt: "); + des3_crypt_cbc(processed_block, data_block, 8, DES_DECRYPT); + for (i = 0; i < 8; i++) + { + printf("%d ", data_block[i]); + } + printf("\r\n"); +} +``` +Result: +``` +des3 ecb encrypt: 48 251 201 178 251 3 6 54 +des3 ecb decrypt: 1 2 3 4 5 6 7 9 +des3 cbc encrypt: 48 251 201 178 251 3 6 54 +des3 cbc decrypt: 1 2 3 4 5 6 7 9 +``` diff --git a/doc/filter.en.md b/doc/filter.en.md new file mode 100644 index 0000000..8e009c9 --- /dev/null +++ b/doc/filter.en.md @@ -0,0 +1,146 @@ +### Introduction + +Filtering algorithms are techniques used in data processing for screening and extracting specific data. Their core purpose is to identify and retain data that meets specific conditions from a large amount of data while excluding irrelevant data. + +### Median Filter Algorithm + +The median filter algorithm is a non-linear filtering technique mainly used to eliminate noise, especially salt-and-pepper noise, while preserving the edge information of images. The following will explain in detail the principle, implementation, and application of the median filter algorithm: + +- **Basic Principle** + - **Concept**: The basic principle of the median filter algorithm is to replace the gray value of a pixel with the median of the gray values of all pixels in its neighborhood. This algorithm can effectively remove isolated noise points while keeping image details from being blurred. + - **Neighborhood Selection**: Usually, a two-dimensional sliding template (such as 3×3 or 5×5) is used. The pixel values within the template are sorted, and the value in the middle position is taken as the new value of the current pixel. + +- **Implementation Method** + - **Data Sorting**: Use a sorting algorithm (such as bubble sort, quick sort, etc.) to sort the data in the neighborhood and then select the median. + - **Boundary Processing**: For pixels on the image boundary, methods such as not processing, repeating boundary values, filling with 0, or reducing the window size can be chosen for processing. + +- **Application Scenarios** + - **Image Denoising**: The median filter is especially suitable for removing salt-and-pepper noise and is widely used in the field of digital image processing. + - **Signal Processing**: In one-dimensional signal processing, the median filter can also be used to eliminate abnormal data points and smooth the signal. + +- **Advantages and Disadvantages** + - **Advantages**: The median filter can well preserve edge information while removing noise and has a remarkable effect on salt-and-pepper noise. + - **Disadvantages**: It has a relatively large computational cost, especially for large-sized windows, requiring more computing resources and time. + +### Kalman Filter Algorithm + +The Kalman filter algorithm is an efficient autoregressive data processing algorithm mainly used for data fusion, state estimation, and prediction update. It combines data from different sensors to obtain more accurate measurement values and state estimates. The following will explain in detail the principle, implementation, and application of the Kalman filter algorithm: + +- **Basic Principle** + - **Concept**: The Kalman filter is essentially a data fusion algorithm that fuses data with the same measurement purpose but from different sensors to obtain a more accurate measurement value for the target. Its core lies in using the state of the previous moment (and possible measurement values) to obtain the optimal estimate of the state at the current moment. + - **Limitations**: The Kalman filter can only fit linear Gaussian systems, but it has a small computational amount and high efficiency. + +- **Implementation Method** + - **Prediction Update**: Based on the state and motion measurement values of the previous moment, predict the state vector and covariance matrix of the current moment through the state transition equation. This step increases uncertainty because both state transitions and motion measurements have noise. + - **Measurement Update**: When new measurement values are obtained, use the measurement model to correct the predicted state to obtain a more accurate state estimate. This step requires calculating the Kalman gain and updating the state vector and covariance matrix. + +- **Application Scenarios** + - **Satellite Navigation**: In applications combining GPS and IMU, the Kalman filter is often used to fuse data from different sensors to improve navigation accuracy. + - **SLAM**: Although the mainstream trend of SLAM is graph optimization, the Kalman filter still provides a good reference for data fusion. + +- **Advantages and Disadvantages** + - **Advantages**: It has a small computational amount and is suitable for real-time applications; it can effectively fuse multi-sensor data and improve the accuracy of state estimation. + - **Disadvantages**: It is limited to linear Gaussian systems. For nonlinear or non-Gaussian systems, improved algorithms such as the extended Kalman filter or unscented Kalman filter need to be used. + +### Average Filter Algorithm + +The average filter algorithm is a simple linear filtering technique mainly used to smooth data and eliminate noise. It replaces the value of a pixel with the average of all pixel values in its surrounding neighborhood. The following will explain in detail the principle, implementation, and application of the average filter algorithm: + +- **Basic Principle** + - **Concept**: The basic principle of the average filter algorithm is to replace the gray value of a pixel with the average of the gray values of all pixels in its neighborhood. This algorithm can effectively smooth the image and eliminate high-frequency noise, but it may lead to the loss of image details. + - **Neighborhood Selection**: Usually, a two-dimensional sliding template (such as 3×3 or 5×5) is used. The pixel values within the template are averaged, and the result is taken as the new value of the current pixel. + +- **Implementation Method** + - **Data Smoothing**: Achieve data smoothing by calculating the average of the data within the sliding window. For example, for a one-dimensional data set, you can select n data points before and after, and calculate the average of these 2n + 1 points as the filtered value of the current point. + - **Boundary Processing**: For pixels on the image boundary, methods such as not processing, repeating boundary values, filling with 0, or reducing the window size can be chosen for processing. + +- **Application Scenarios** + - **Image Processing**: The average filter is widely used in image denoising and smoothing processing, especially for dealing with random white noise. + - **Signal Processing**: In one-dimensional signal processing, the average filter can also be used to eliminate abnormal data points and smooth the signal. + +- **Advantages and Disadvantages** + - **Advantages**: The algorithm is simple, has a small computational amount, and is suitable for real-time applications; it can effectively smooth data and eliminate random noise. + - **Disadvantages**: It will cause the loss of image details and lead to edge blurring; it has limited effect on non-random noise such as Gaussian noise. + +- **Improved Algorithms** + - **Weighted Average Filter**: By performing a weighted average on the pixel values in the neighborhood, different weights can be assigned according to the distance from the central pixel, so as to better preserve image details. + - **Adaptive Average Filter**: Automatically adjust the sliding window size or weights according to the local characteristics of the image to meet the needs of different regions. + +### Interface + +```c +void filter_median(double *data, int size, int window); +void filter_kalman(double *measurements, double *estimates, int numMeasurements, double processNoise, double measurementNoise); +void filter_average(double *data, int size, int window); +``` + +### Testing + +```c +static void test_median(void) +{ + double data[] = {1, 2, 3, 2.5, 3.5, 2, 3, 4, 5}; + int size = sizeof(data) / sizeof(data[0]); + int windowSize = 3; + + filter_median(data, size, windowSize); + + printf("Filtered data: \n"); + for (int i = 0; i < size; i++) + { + printf("%f ", data[i]); + } + printf("\n"); +} +``` + +**Result** + +``` +2.000000 2.000000 2.500000 2.500000 2.500000 2.500000 3.000000 4.000000 4.000000 +``` + +```c +static void test_average(void) +{ + double data[] = {1, 2, 3, 2.5, 3.5, 2, 3, 4, 5}; + int size = sizeof(data) / sizeof(data[0]); + int windowSize = 3; + + filter_average(data, size, windowSize); + + printf("Filtered data: \n"); + for (int i = 0; i < size; i++) + { + printf("%f ", data[i]); + } + printf("\n"); +} +``` + +**Result** + +``` +1.000000 2.000000 2.500000 2.833333 2.777778 2.592593 3.197531 4.065844 3.021948 +``` + +```c +static void test_kalman(void) +{ + double measurements[] = {1.0, 2.0, 3.0, 2.5, 3.5, 2.0, 3.0, 4.0, 5.0}; + double estimates[1]; + int numMeasurements = sizeof(measurements) / sizeof(measurements[0]); + double processNoise = 0.1; + double measurementNoise = 0.2; + + filter_kalman(measurements, estimates, numMeasurements, processNoise, measurementNoise); + + printf("Estimated value: %f\n", estimates[0]); +} +``` + +**Result** + +``` +Estimated value: 2.826087 +``` diff --git a/doc/fsm.en.md b/doc/fsm.en.md new file mode 100644 index 0000000..89dfe74 --- /dev/null +++ b/doc/fsm.en.md @@ -0,0 +1,100 @@ +### Introduction +The `fsm` is related to a general-purpose finite state machine module. It defines data structures related to state transitions and provides key function interfaces such as state machine initialization and execution, helping developers easily build and use finite state machines in C language projects to handle business scenarios with different states and state transition logics. + +### Interfaces + +#### Functions +```c +int fsm_init(FSM* fsm, StateTransform* trans, int count, int state); +int fsm_execute(FSM* fsm, int event); +``` + +**`fsm_init` Function**: +This function is used to initialize a finite state machine. It takes the following parameters: +- `FSM* fsm`: A pointer to the `FSM` structure that represents the state machine itself. The `FSM` structure usually holds information about the current state, the state transition table, etc. +- `StateTransform* trans`: A pointer to an array of `StateTransform` structures. This array defines the possible state transitions for the state machine. Each `StateTransform` element typically contains details such as the source state, the destination state, the triggering event, and an associated action (which is `NULL` in the provided example). +- `int count`: The number of elements in the `StateTransform` array, indicating how many possible state transitions are defined for this state machine. +- `int state`: The initial state in which the state machine should start. + +**`fsm_execute` Function**: +This function is responsible for executing the state machine based on a given event. When called with an `int event` parameter, it checks the current state of the state machine and searches through the defined state transition table (`StateTransform` array) to see if the provided event triggers a state transition. If a valid transition is found based on the current state and the input event, the state of the state machine is updated accordingly. + +**Usage Example**: +```c +#include +#include "fsm.h" + +enum +{ + TestState_Init = 0, + TestState_Stanby, + TestState_Run, + TestState_Error, + TestState_Exit, +}; + +enum +{ + TestEvent_Init = 0, + TestEvent_ToRun, + TestEvent_ToStanby, + TestEvent_Error, + TestEvent_Exit, +}; + +/* State transition diagram ++-----------+ +-----------+ +-----------+ +| | | | -----------> | | +| init | -------> | standby | | run | +| | | | <---------- | | ++-----------+ +-----------+ +-----------+ + | \ / | + | \ / | + | \ / | + | \ / | + | \ / | + | \/ | + | /\ | + | / \ | + | / \ | + | / \ | + | / \ | + | / \ | + v v v v + +-----------+ +-----------+ + | | | | + | error | -----------> | exit | + | | | | + +-----------+ +-----------+ +*/ +StateTransform TestFsmTable[] = +{ /* from, to, event, action */ + {TestState_Init, TestState_Stanby, TestEvent_Init, NULL}, + {TestState_Stanby, TestState_Run, TestEvent_ToRun, NULL}, + {TestState_Stanby, TestState_Error, TestEvent_Error, NULL}, + {TestState_Stanby, TestState_Exit, TestEvent_Exit, NULL}, + {TestState_Run, TestState_Stanby, TestEvent_ToStanby, NULL}, + {TestState_Run, TestState_Error, TestEvent_Error, NULL}, + {TestState_Run, TestState_Exit, TestEvent_Exit, NULL}, + {TestState_Error, TestState_Exit, TestEvent_Exit, NULL}, +}; + +int main(int argc, char* argv[]) +{ + FSM TestFsm; + int event[] = { TestState_Init, TestEvent_ToRun, TestState_Exit }; + + fsm_init(&TestFsm, TestFsmTable, 8, TestState_Init); + + printf("%d\r\n", TestFsm.count); + + for (int i = 0, n = sizeof(event) / sizeof(event[0]); i < n; i++) + { + fsm_execute(&TestFsm, event[i]); + printf("state %d\r\n", TestFsm.state); + } + + printf("%c", getchar()); + return 0; +} +``` diff --git a/doc/fsm.md b/doc/fsm.md new file mode 100644 index 0000000..2fb902a --- /dev/null +++ b/doc/fsm.md @@ -0,0 +1,92 @@ +## 介绍 +`fsm`是一个用于C语言的通用有限状态机模块,它通过定义状态转换相关的数据结构以及提供状态机的初始化和执行等关键函数接口,帮助开发者在C语言项目中方便地构建和运用有限状态机,以处理具有不同状态及状态转换逻辑的业务场景。 + +## 接口 + +### 函数 + +```c +int fsm_init(FSM* fsm, StateTransform* trans, int count, int state); +int fsm_execute(FSM* fsm, int event); +``` + +使用例子: + +```c +#include +#include "fsm.h" + +enum +{ + TestState_Init = 0, + TestState_Stanby, + TestState_Run, + TestState_Error, + TestState_Exit, +}; + +enum +{ + TestEvent_Init = 0, + TestEvent_ToRun, + TestEvent_ToStanby, + TestEvent_Error, + TestEvent_Exit, +}; + +/* State transition diagram ++-----------+ +-----------+ +-----------+ +| | | | -----------> | | +| init | -------> | standby | | run | +| | | | <---------- | | ++-----------+ +-----------+ +-----------+ + | \ / | + | \ / | + | \ / | + | \ / | + | \ / | + | \/ | + | /\ | + | / \ | + | / \ | + | / \ | + | / \ | + | / \ | + v v v v + +-----------+ +-----------+ + | | | | + | error | -----------> | exit | + | | | | + +-----------+ +-----------+ +*/ +StateTransform TestFsmTable[] = +{ /* from, to, event, action */ + {TestState_Init, TestState_Stanby, TestEvent_Init, NULL}, + {TestState_Stanby, TestState_Run, TestEvent_ToRun, NULL}, + {TestState_Stanby, TestState_Error, TestEvent_Error, NULL}, + {TestState_Stanby, TestState_Exit, TestEvent_Exit, NULL}, + {TestState_Run, TestState_Stanby, TestEvent_ToStanby, NULL}, + {TestState_Run, TestState_Error, TestEvent_Error, NULL}, + {TestState_Run, TestState_Exit, TestEvent_Exit, NULL}, + {TestState_Error, TestState_Exit, TestEvent_Exit, NULL}, +}; + +int main(int argc, char* argv[]) +{ + FSM TestFsm; + int event[] = { TestState_Init, TestEvent_ToRun, TestState_Exit }; + + fsm_init(&TestFsm, TestFsmTable, 8, TestState_Init); + + printf("%d\r\n", TestFsm.count); + + for (int i = 0; i < sizeof(event) / sizeof(event[0]); i++) + { + fsm_execute(&TestFsm, event[i]); + printf("state %d\r\n", TestFsm.state); + } + + printf("%c", getchar()); + return 0; +} +``` diff --git a/doc/graph.en.md b/doc/graph.en.md new file mode 100644 index 0000000..1ddf8e4 --- /dev/null +++ b/doc/graph.en.md @@ -0,0 +1,707 @@ +### Introduction + +A graph is a data structure composed of nodes (also known as vertices) and edges, and it is usually used to represent the relationships between objects. The following are the basic concepts and characteristics of graphs: + +### 1. Basic Components +- **Node (Vertex)**: It is the basic unit in a graph and represents an object. +- **Edge**: It is the line connecting two nodes and represents the relationship between nodes. + +### 2. Types +- **Directed Graph**: The edges have directions, indicating a one-way relationship from one node to another. +- **Undirected Graph**: The edges have no directions, representing a two-way relationship between nodes. +- **Weighted Graph**: The edges have weights, which represent the cost or distance between two connected nodes. + +### 3. Representation Methods +- **Adjacency Matrix**: A two-dimensional array is used to represent the connection relationships between nodes. The values in the array indicate the existence (1) or non-existence (0) of an edge. In a weighted graph, the weights of the edges are used for representation. +- **Adjacency List**: An array or linked list is used to represent the adjacent nodes of each node, which is suitable for sparse graphs. + +### 4. Applications +- **Social Networks**: Nodes represent users, and edges represent the relationships between users. +- **Network Routing**: Nodes represent routers, and edges represent connections. +- **Path Finding**: For example, finding the shortest path in map applications. + +This module implements a graph data structure, including basic graph operations such as adding and deleting vertices and edges, as well as algorithms like Depth-First Search (DFS) and Breadth-First Search (BFS). The graph supports both directed and undirected graphs and is suitable for various graph theory applications. + +### Interface + +#### Creating and Deleting a `graph` Object +```c +graph_t graph_create(int max, int directed); +``` +Creates and initializes a graph. + +**Parameters**: +- `max`: The maximum number of vertices in the graph. +- `directed`: A flag indicating whether the graph is a directed graph (non-zero) or an undirected graph (zero). + +**Returns**: Returns a pointer to the newly created graph. If the creation fails, it returns `NULL`. + +```c +void graph_delete(graph_t graph); +``` +Destroys the graph and releases related resources. + +**Parameters**: +- `graph`: A pointer to the graph to be destroyed. + +**Returns**: None + +Example: +```c +graph_t graph = graph_create(10, 1); +graph_delete(graph); +``` + +#### Adding a Vertex +```c +int graph_add_vertex(graph_t graph, void *data, int size); +``` +Adds a vertex to the graph. + +**Parameters**: +- `graph`: A pointer to the graph to which the vertex is to be added. +- `data`: A pointer to the data associated with the vertex. +- `size`: The size of the data to be copied. + +**Returns**: Returns the index of the added vertex. If the addition fails, it returns -1. + +Example: +```c +void graph_traverse_int(int index, void *data, int size) +{ + printf("graph[%d] %d\r\n", index, *(int *)data); +} + +static void test_add_vertex(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 12), sizeof(int)); + graph_add_vertex(graph, la(int, 13), sizeof(int)); + graph_add_vertex(graph, la(int, 15), sizeof(int)); + graph_add_vertex(graph, la(int, 23), sizeof(int)); + + graph_ls(graph, graph_traverse_int); + + graph_delete(graph); +} +``` +Here, `la()` is a macro definition for getting the address of a literal. For details, you can refer to the [test_graph.c](../test/test_graph.c) file. `graph_ls()` is a linear search method, which will be introduced later. + +**Result**: +``` +graph[0] 12 +graph[1] 13 +graph[2] 15 +graph[3] 23 + +``` + +#### Adding an Edge +```c +int graph_add_edge(graph_t graph, int start, int end, int weight); +``` +Adds an edge to the graph. + +**Parameters**: +- `graph`: A pointer to the graph to which the edge is to be added. +- `start`: The index of the starting vertex. +- `end`: The index of the ending vertex. +- `weight`: The weight of the edge. + +**Returns**: Returns 1 if the edge is added successfully, and 0 if it fails. + +Example: +```c +static void test_add_edge(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 12), sizeof(int)); + graph_add_vertex(graph, la(int, 13), sizeof(int)); + graph_add_vertex(graph, la(int, 15), sizeof(int)); + graph_add_vertex(graph, la(int, 23), sizeof(int)); + + printf("---------------\r\n"); + + graph_dfs(graph, 0, graph_traverse_int); + + printf("---------------\r\n"); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_dfs(graph, 0, graph_traverse_int); + + printf("---------------\r\n"); + + graph_delete(graph); +} +``` +`graph_dfs()` is the Depth-First Search method, which will be introduced later. Comparing the search situations before and after adding edges, vertices that are not connected by edges cannot be found in the search. + +**Result**: +``` +--------------- +graph[0] 12 +--------------- +graph[0] 12 +graph[3] 23 +graph[2] 15 +graph[1] 13 +--------------- +``` + +#### Removing a Vertex and an Edge +```c +int graph_remove_vertex(graph_t graph, int index); +``` +Removes the specified vertex and its associated edges from the graph. + +**Parameters**: +- `graph`: A pointer to the graph from which the vertex is to be removed. +- `index`: The index of the vertex to be removed. + +**Returns**: Returns 1 if the vertex is removed successfully, and 0 if it fails. + +```c +int graph_remove_edge(graph_t graph, int start, int end); +``` +Removes the specified edge from the graph. + +**Parameters**: +- `graph`: A pointer to the graph from which the edge is to be removed. +- `start`: The index of the starting vertex of the edge. +- `end`: The index of the ending vertex of the edge. + +**Returns**: Returns 1 if the edge is removed successfully, and 0 if it fails. + +Example: +```c +static void test_remove(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_remove_vertex(graph, 1); + graph_remove_edge(graph, 0, 2); + + graph_dfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} +``` + +**Result**: +``` +graph[0] 100 +graph[3] 500 +``` + +#### Traversal +```c +void graph_ls(graph_t graph, graph_traverse_t func); +``` +Performs a linear traversal of the graph starting from the beginning position. + +**Parameters**: +- `graph`: A pointer to the graph to be traversed. +- `func`: A callback function executed for each visited vertex. + +**Returns**: None + +```c +void graph_ls(graph_t graph, graph_traverse_t func); +``` +Performs a Depth-First Search starting from the specified starting vertex. + +**Parameters**: +- `graph`: A pointer to the graph to be traversed. +- `start`: The index of the starting vertex. +- `func`: A callback function executed for each visited vertex. + +**Returns**: None + +```c +void graph_dfs(graph_t graph, int start, graph_traverse_t func); +``` +Performs a Breadth-First Search starting from the specified starting vertex. + +**Parameters**: +- `graph`: A pointer to the graph to be traversed. +- `start`: The index of the starting vertex. +- `func`: A callback function executed for each visited vertex. + +**Returns**: None + +Example: +```c +static void test_search(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_remove_vertex(graph, 1); + graph_remove_edge(graph, 0, 2); + + printf("graph_ls ----------------------\r\n"); + graph_ls(graph, graph_traverse_int); + printf("graph_dfs ----------------------\r\n"); + graph_dfs(graph, 0, graph_traverse_int); + printf("graph_bfs ----------------------\r\n"); + graph_bfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} +``` + +**Result**: +``` +graph_ls ---------------------- +graph[0] 100 +graph[2] 300 +graph[3] 500 +graph_dfs ---------------------- +graph[0] 100 +graph[3] 500 +graph_bfs ---------------------- +graph[0] 100 +graph[3] 500 +``` + +#### Setting and Getting Data +```c +int graph_vertex_set_data(graph_t graph, int index, void *data, int size); +``` +Sets data for the specified vertex in the graph. + +**Parameters**: +- `graph`: A pointer to the graph. +- `index`: The index of the vertex. +- `data`: A pointer to the data to be set. +- `size`: The size of the data. + +**Returns**: Returns 1 if the data is set successfully, and 0 if it fails. + +```c +int graph_vertex_get_data(graph_t graph, int index, void *data, int size); +``` +Gets data from the specified vertex in the graph. + +**Parameters**: +- `graph`: A pointer to the graph. +- `index`: The index of the vertex. +- `data`: A pointer to the location where the data will be copied. +- `size`: The size of the data buffer. + +**Returns**: Returns 1 if the data is obtained successfully, and 0 if it fails. + +```c +void *graph_vertex_data(graph_t graph, int index, int *size); +``` +Gets the data of the specified vertex in the graph. + +**Parameters**: +- `graph`: A pointer to the graph. +- `index`: The index of the vertex. +- `size`: A pointer to the variable storing the size of the vertex data (if not `NULL`). + +**Returns**: Returns a pointer to the vertex data. If it fails, it returns `NULL`. + +Example: +```c +static void test_data(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_vertex_set_data(graph, 2, la(int, 1024), sizeof(int)); + + int size = 0; + printf("graph_vertex_data[3].data = %d\r\n", *(int *)graph_vertex_data(graph, 3, &size)); + printf("graph_vertex_data[3].size = %d\r\n", size); + + graph_ls(graph, graph_traverse_int); + + graph_delete(graph); +} +``` + +**Result**: +``` +graph_vertex_data[3].data = 500 +graph_vertex_data[3].size = 4 +graph[0] 100 +graph[1] 200 +graph[2] 1024 +graph[3] 500 +``` + +#### Out-degree and In-degree +```c +int graph_out_degree(graph_t graph, int index); +``` +Gets the out-degree of the specified vertex. + +**Parameters**: +- `graph`: A pointer to the graph. +- `index`: The index of the vertex. + +**Returns**: The out-degree of the vertex. If it fails, it returns -1. + +```c +int graph_in_degree(graph_t graph, int index); +``` +Gets the in-degree of the specified vertex. + +**Parameters**: +- `graph`: A pointer to the graph. +- `index`: The index of the vertex. + +**Returns**: The in-degree of the vertex. If it fails, it returns -1. + +Example: +```c +static void test_degree(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + printf("graph_out_degree %d\r\n", graph_out_degree(graph, 3)); + printf("graph_out_degree %d\r\n", graph_out_degree(graph, 2)); + + printf("graph_in_degree %d\r\n", graph_in_degree(graph, 0)); + printf("graph_in_degree %d\r\n", graph_in_degree(graph, 2)); + + graph_delete(graph); +} +``` + +**Result**: +``` +graph_out_degree 0 +graph_out_degree 1 +graph_in_degree 1 +graph_in_degree 2 +``` + +#### Adjacency +```c +int graph_is_adjacent(graph_t graph, int start, int end); +``` +Checks whether two vertices are adjacent. + +**Parameters**: +- `graph`: A pointer to the graph. +- `start`: The index of the starting vertex. +- `end`: The index of the ending vertex. + +**Returns**: Returns 1 if the vertices are adjacent, otherwise returns 0 or fails. + +### Weight +```c +int graph_get_edge_weight(graph_t graph, int start, int end); +``` +This function is used to obtain the weight of the edge between two vertices (if it exists). + +**Parameters**: +- `graph`: A pointer to the graph. +- `start`: The index of the starting vertex. +- `end`: The index of the ending vertex. + +**Returns**: It returns the weight of the edge. If the operation fails or the edge does not exist, it returns `INT_MAX`. + +```c +int graph_set_edge_weight(graph_t graph, int start, int end, int weight); +``` +This function is used to set the weight of the edge between two vertices (if the edge exists). + +**Parameters**: +- `graph`: A pointer to the graph. +- `start`: The index of the starting vertex. +- `end`: The index of the ending vertex. +- `weight`: The weight to be set for the edge. + +**Returns**: It returns 1 if the edge exists and the weight is set successfully, and 0 if the operation fails or the edge does not exist. + +Example: +```c +static void test_weight(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 10); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + printf("graph_get_edge_weight = %d\r\n", graph_get_edge_weight(graph, 0, 3)); + graph_set_edge_weight(graph, 0, 3, 1024); + printf("graph_get_edge_weight = %d\r\n", graph_get_edge_weight(graph, 0, 3)); + + graph_delete(graph); +} +``` +**Result**: +``` +graph_get_edge_weight = 10 +graph_get_edge_weight = 1024 +``` + +### Checking Vertices +```c +int graph_contains_vertex(graph_t graph, int index); +``` +This function is used to check whether a vertex exists in the graph. + +**Parameters**: +- `graph`: A pointer to the graph. +- `index`: The index of the vertex. + +**Returns**: It returns 1 if the vertex exists, and 0 if the operation fails or the vertex does not exist. + +### Topological Sorting +```c +int graph_contains_vertex(graph_t graph, int index); +``` +This function is supposed to perform a topological sorting on the graph. + +**Parameters**: +- `graph`: A pointer to the graph. + +**Returns**: It does not return a value. + +### Shortest Path +```c +void graph_shortest_path(graph_t graph, int start); +``` +This function is used to find the shortest path from the starting vertex to other vertices. + +**Parameters**: +- `graph`: A pointer to the graph. +- `start`: The index of the starting vertex. + +**Returns**: It does not return a value. + +Example: +```c +static void test_shortest_path(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + graph_shortest_path(graph, 0); + + graph_delete(graph); +} +``` +**Result**: +``` +Shortest path from 0 to 0: 0 (0) +Shortest path from 0 to 1: 3 (0 -> 1) +Shortest path from 0 to 2: 2 (0 -> 2) +Shortest path from 0 to 3: 3 (0 -> 2 -> 3) +``` + +### Connected Graph +```c +int graph_is_connected(graph_t graph); +``` +This function is used to check whether the graph is connected. + +**Parameters**: +- `graph`: A pointer to the graph. + +**Returns**: It returns 1 if the graph is connected, and 0 if it is not. + +### Complete Graph +```c +int graph_is_complete(graph_t graph); +``` +This function is used to check whether the graph is a complete graph. + +**Parameters**: +- `graph`: A pointer to the graph. + +**Returns**: It returns 1 if the graph is a complete graph, and 0 if it is not. + +### Bipartite Graph +```c +int graph_is_bipartite(graph_t graph); +``` +This function is used to check whether the graph is a bipartite graph. + +**Parameters**: +- `graph`: A pointer to the graph. + +**Returns**: It returns 1 if the graph is a bipartite graph, and 0 if it is not. + +### Eulerian Graph +```c +int graph_is_eulerian(graph_t graph); +``` +This function is used to check whether the graph is an Eulerian graph (which can traverse each edge exactly once). + +**Parameters**: +- `graph`: A pointer to the graph. + +**Returns**: It returns 1 if the graph is an Eulerian graph, and 0 if it is not. + +### Minimum Vertex Cover +```c +void graph_min_vertex_cover(graph_t graph); +``` +This function is used to calculate the minimum vertex cover of the graph. + +**Parameters**: +- `graph`: A pointer to the graph. + +**Returns**: It does not return a value. + +Example: +```c +static void test_min_cover(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + graph_min_vertex_cover(graph); + + graph_delete(graph); +} +``` +**Result**: +``` +0 3 1 2 +``` diff --git a/doc/hash.en.md b/doc/hash.en.md new file mode 100644 index 0000000..71a73e8 --- /dev/null +++ b/doc/hash.en.md @@ -0,0 +1,58 @@ +### Introduction + +A Hash algorithm is an algorithm that can discretely map data of any length to data of a specified shorter length in a way that is generally irreversible. + +### Interface + +```c +uint32_t hash_bkdr(void *data, uint32_t size); +uint32_t hash_ap(void *data, uint32_t size); +uint32_t hash_djb(void *data, uint32_t size); +uint32_t hash_js(void *data, uint32_t size); +uint32_t hash_rs(void *data, uint32_t size); +uint32_t hash_sdbm(void *data, uint32_t size); +uint32_t hash_pjw(void *data, uint32_t size); +uint32_t hash_elf(void *data, uint32_t size); +uint32_t hash_dek(void *data, uint32_t size); +uint32_t hash_bp(void *data, uint32_t size); +uint32_t hash_fnv(void *data, uint32_t size); +uint32_t hash_jdk6(void *data, uint32_t size); +``` +These several checksum algorithms are used in the same way. They all take the address of the data and the size of the data as parameters and return the calculated 32-bit hash value. + +### Testing + +```c +static void test(void) +{ + printf("hash_bkdr 0x%X\r\n", hash_bkdr("Hello", 5)); + printf("hash_ap 0x%X\r\n", hash_ap("Hello", 5)); + printf("hash_djb 0x%X\r\n", hash_djb("Hello", 5)); + printf("hash_js 0x%X\r\n", hash_js("Hello", 5)); + printf("hash_rs 0x%X\r\n", hash_rs("Hello", 5)); + printf("hash_sdbm 0x%X\r\n", hash_sdbm("Hello", 5)); + printf("hash_pjw 0x%X\r\n", hash_pjw("Hello", 5)); + printf("hash_elf 0x%X\r\n", hash_elf("Hello", 5)); + printf("hash_dek 0x%X\r\n", hash_dek("Hello", 5)); + printf("hash_bp 0x%X\r\n", hash_bp("Hello", 5)); + printf("hash_fnv 0x%X\r\n", hash_fnv("Hello", 5)); + printf("hash_jdk6 0x%X\r\n", hash_jdk6("Hello", 5)); +} +``` + +**Result** + +``` +hash_bkdr 0x7D80646E +hash_ap 0x646C7322 +hash_djb 0xD4F2079 +hash_js 0x6060CD85 +hash_rs 0x15794872 +hash_sdbm 0x2B45B912 +hash_pjw 0x4EC32F +hash_elf 0x4EC32F +hash_dek 0xEB33DEF +hash_bp 0xCBB366F +hash_fnv 0x2EA7AFEE +hash_jdk6 0x42628B2 +``` diff --git a/doc/heap.en.md b/doc/heap.en.md new file mode 100644 index 0000000..2426fc3 --- /dev/null +++ b/doc/heap.en.md @@ -0,0 +1,136 @@ +### Introduction + +A heap is a special kind of complete binary tree with specific heap properties. There are mainly two types of heaps: max heap and min heap. + +1. **Concept**: + - A heap is a complete binary tree where every level is full except for the last level, and the nodes in the last level are arranged as far left as possible. + - Each node in the heap satisfies the heap property. That is, in a max heap, the value of each node is greater than or equal to the values of its child nodes; in a min heap, the value of each node is less than or equal to the values of its child nodes[^1^][^2^][^3^][^4^][^5^]. + +2. **Structure**: + - A heap is usually implemented using an array because a heap, being a complete binary tree, can be efficiently mapped onto an array. + - Assuming the root node of the heap is stored at the first position (index 0) in the array, the positions of the child nodes and parent node of any node can be calculated using the following formulas: + - Parent node index: `parent(i) = (i - 1) / 2` + - Left child node index: `left(i) = 2i + 1` + - Right child node index: `right(i) = 2i + 2`[^2^][^4^]. + +3. **Complete Binary Tree**: A heap is always a complete binary tree, meaning that all levels except the last one are full, and the nodes in the last level are arranged as far left as possible[^1^][^2^][^3^][^4^][^5^]. + +4. **Heap Order Property**: + - Max Heap: The value of each node is greater than or equal to the values of its child nodes, which ensures that the top of the heap (the root node) is the maximum value in the entire heap[^1^][^2^][^3^][^4^][^5^]. + - Min Heap: The value of each node is less than or equal to the values of its child nodes, which ensures that the top of the heap (the root node) is the minimum value in the entire heap[^1^][^2^][^3^][^4^][^5^]. + +5. **Height**: For a heap containing n nodes, its height is O(log n), which makes many operations on the heap have a logarithmic time complexity[^4^]. + +The `heap` is a general-purpose heap container module for the C language. It defines heap-related data types and a series of function interfaces for operating on heaps, covering functions such as heap creation, deletion, element insertion into the heap, element removal from the heap, modification, getting the top element of the heap, and getting the size of the heap. Meanwhile, through macro definitions, it provides convenient ways to obtain the indices of the parent node, left child node, and right child node of a node in the heap, facilitating developers to use the heap data structure for data management and operations in C language projects, such as implementing application scenarios like priority queues. + +### Interfaces + +#### Creation and Deletion of `heap` Objects +```c +heap_t heap_create(int dsize, int capacity, heap_root_t root); +void heap_delete(heap_t heap); +``` +Here, `heap_t` is the structure of `heap`. + +**Creation Method**: +The creation method will return a `heap` object. If the creation fails, it will return `NULL`. +- `dsize`: Represents the data size of each element in the heap, in bytes. It is used to determine the amount of space allocated in memory for each element. For example, when storing a structure element with multiple members, this parameter should be set to the size of the structure. +- `capacity`: The initial capacity of the heap, that is, the maximum number of elements that the heap can hold when it is created. When the number of inserted elements exceeds this capacity later, expansion and other related processing may be required (depending on the internal implementation logic). Developers can reasonably set this parameter according to the estimated number of elements. +- `root`: A pointer to a function that defines the rule for the root node type of the heap, that is, determines the nature of the heap (max heap or min heap) by comparing parent nodes and child nodes. The passed-in function needs to implement according to the specified return value logic so that subsequent heap operations can adjust elements based on the correct heap properties. + +**Deletion Method**: +Deletes the passed-in `heap` object. The creation method and deletion should be used in pairs. Once created and when the usage is finished, it should be deleted. + +```c +static int heap_root_min(void *parent, void *child) +{ + if (*(int *)parent < *(int *)child) return 1; + return 0; +} +static int heap_root_max(void *parent, void *child) +{ + if (*(int *)parent > *(int *)child) return 1; + return 0; +} + +static void test_create(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + + if (heap) + { + printf("heap create success!!!\r\n"); + } + else + { + printf("[ERROR] heap create fail!!!\r\n"); + } + + heap_delete(heap); +} +``` + +#### Insertion and Removal of Elements in `heap` +```c +int heap_push(heap_t heap, void *data); +int heap_pop(heap_t heap, void *data); +``` +These two methods can conveniently add data to the heap and remove data from the heap. For the `push` method, the `data` parameter passes in the address of the data to be inserted into the heap. For the `pop` method, the `data` parameter passes in the address of the memory where the removed data will be received. For both methods, `data` can be passed as `NULL`, which just serves as a placeholder. The methods return 1 if the operation is successful and 0 if it fails. + +```c +static void test_push(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + int push = 0, top = 0; + + push = 100; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 1; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 2; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 200; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = -10; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + + heap_delete(heap); +} +``` + +#### Modification of `heap` +```c +int heap_modify(heap_t heap, int index, void *data); +``` +This is used to modify the element data at the specified index position in the heap. After the modification, the structure of the heap will be readjusted according to the properties of the heap to ensure that the heap still maintains the correct heap property state. If the modification operation is successful, the function returns 1; if the index is out of range, the heap does not exist, or other situations prevent the modification from being carried out, it returns 0. +- `heap`: The handle of the heap. Through it, the specific heap where the element to be modified is located can be located. Only with the correct corresponding heap can the target element be accurately found and modified. +- `index`: The index position of the element to be modified in the heap. The index starts from 0 and represents the sequence number of the element in the heap. Through this index, the target element that needs to be modified can be accurately found. However, it is necessary to ensure that the passed-in index value is within the valid range of the heap (less than the current number of elements in the heap). +- `data`: A pointer to the new data. The data content pointed to by this pointer will replace the original element data at the specified index position in the heap. The data type and size of the new data should be consistent with the element data size specified when creating the heap. + +```c +static void test_base(void) +{ + heap_t h = heap_create(sizeof(int), 11, heap_root_max); + int i = 0; + + for (i = 0; i < 11; i++) + { + heap_push(h, &i); + } + + i = -9; + heap_modify(h, 6, &i); + + heap_delete(h); +} +``` + +#### Top of `heap` +```c +int heap_top(heap_t heap, void *data); +``` +This is used to obtain the element data at the top of the heap. For a max heap, the maximum element is obtained, and for a min heap, the minimum element is obtained. If the retrieval operation is successful, the function returns 1, and if the `data` parameter is not `NULL`, the element data at the top of the heap will be copied to the memory space pointed to by this pointer; if the heap is empty or other situations cause the retrieval to fail, it returns 0. +- `heap`: The handle of the heap. Through this handle, the target heap from which the top element is to be obtained is determined. Different heaps have their own independent top elements, and the correct corresponding heap needs to be accurately specified to obtain the correct top data. +- `data`: A pointer to the memory space used to store the element data at the top of the heap. If you want to obtain the content of the top element and store it for subsequent use, you can pass in a valid pointer address, and the function will perform a data copying operation; if you don't need to obtain the specific data, you can pass in `NULL` as the parameter. + +#### Size of `heap` +```c +int heap_size(heap_t heap); +``` +This is used to obtain the number of elements in the current heap. The returned integer value represents the actual number of elements stored in the heap, facilitating developers to understand the current storage situation of the heap, such as determining whether the heap is empty or full. +- `heap`: The handle of the heap. Based on this handle, the corresponding heap data structure can be found, and then the information about the number of elements contained in the heap can be obtained. Each heap has its own independent element quantity statistics, and through the handle, the situations of different heaps can be accurately distinguished. diff --git a/doc/heap.md b/doc/heap.md new file mode 100644 index 0000000..88821f9 --- /dev/null +++ b/doc/heap.md @@ -0,0 +1,135 @@ +## 介绍 + +堆(Heap)是一种特殊的完全二叉树,具有特定的堆性质。堆主要分为两种类型:最大堆和最小堆。 + +1. **概念**: + - 堆是一种完全二叉树,除了最后一层外,每一层都是满的,并且最后一层的节点尽可能地向左排列。 + - 堆中每个节点的值都满足堆性质,即在最大堆中,每个节点的值大于或等于其子节点的值;在最小堆中,每个节点的值小于或等于其子节点的值[^1^][^2^][^3^][^4^][^5^]。 + +2. **结构**: + - 堆通常通过数组来实现,因为堆是一棵完全二叉树,可以高效地映射到数组中。 + - 假设堆的根节点存储在数组的第一个位置(索引为0),那么对于任意一个节点,其子节点和父节点的位置可以通过以下公式计算: + - 父节点索引:`parent(i) = (i - 1) / 2` + - 左子节点索引:`left(i) = 2i + 1` + - 右子节点索引:`right(i) = 2i + 2`[^2^][^4^] + +3. **完全二叉树**:堆总是一棵完全二叉树,即除了最后一层外,所有层都是满的,且最后一层的节点尽可能地向左排列[^1^][^2^][^3^][^4^][^5^]。 + +4. **堆序性质**: + - 最大堆:每个节点的值大于或等于其子节点的值,这保证了堆顶(根节点)是整个堆中的最大值[^1^][^2^][^3^][^4^][^5^]。 + - 最小堆:每个节点的值小于或等于其子节点的值,这保证了堆顶(根节点)是整个堆中的最小值[^1^][^2^][^3^][^4^][^5^]。 + +5. **高度**:对于包含n个节点的堆,其高度为O(log n),这使得堆的许多操作具有对数时间复杂度[^4^]。 + +varch `heap`是一个用于C语言的通用堆容器模块。它定义了堆相关的数据类型以及一系列操作堆的函数接口,涵盖了堆的创建、删除、元素入堆、出堆、修改、获取堆顶元素、获取堆大小等功能,同时通过宏定义提供了获取堆中节点父节点、左右子节点索引的便捷方式,方便开发者在C语言项目中运用堆这种数据结构来进行数据管理和操作,例如实现优先级队列等应用场景。 + +## 接口 + +### 创建和删除heap对象 +```c +heap_t heap_create(int dsize, int capacity, heap_root_t root); +void heap_delete(heap_t heap); +``` +其中**heap_t**为heap的结构体。 + +创建方法: +创建方法则会返回一个heap对象,创建失败则返回NULL。 +- `dsize`:表示堆中每个元素的数据大小,以字节为单位,用于确定在内存中为每个元素分配的空间大小,比如存储一个包含多个成员的结构体元素时,该参数就应该设置为结构体的大小。 +- `capacity`:堆的初始容量,也就是堆在创建时最多能容纳的元素个数,当后续插入元素超过这个容量时,可能需要进行扩容等相关处理(具体取决于内部实现逻辑),开发者可以根据预估的元素数量合理设置该参数。 +- `root`:指向一个函数的指针,该函数用于定义堆的根节点类型规则,即通过比较父节点和子节点来确定堆的性质(大根堆或小根堆),传入的函数需要按照规定的返回值逻辑来实现,以便后续堆操作能依据正确的堆性质进行元素调整等操作。 + +删除方法: +删除传入的heap对象。创建方法和删除应该成对使用,创建出来在结束使用应该删除掉。 + +```c +static int heap_root_min(void *parent, void *child) +{ + if (*(int *)parent < *(int *)child) return 1; + return 0; +} +static int heap_root_max(void *parent, void *child) +{ + if (*(int *)parent > *(int *)child) return 1; + return 0; +} + +static void test_create(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + + if (heap) + { + printf("heap create success!!!\r\n"); + } + else + { + printf("[ERROR] heap create fail!!!\r\n"); + } + + heap_delete(heap); +} +``` + +### heap的入堆和出堆 +```c +int heap_push(heap_t heap, void *data); +int heap_pop(heap_t heap, void *data); +``` +这两个方法可以很方便的把数据添加到堆和从堆弹出,`push`方法`data`传入需要入队数据的地址,`pop`方法`data`传入需要接收出队数据的地址,这两个方法`data`都可以传入NULL,那只是一个占位。操作成功返回1,失败返回0。 +```c +static void test_push(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + int push = 0, top = 0; + + push = 100; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 1; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 2; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 200; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = -10; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + + heap_delete(heap); +} +``` + +### heap的修改 +```c +int heap_modify(heap_t heap, int index, void *data); +``` +用于修改堆中指定索引位置的元素数据,修改后会根据堆的性质重新调整堆的结构,确保堆依然保持正确的堆性质状态。若修改操作成功,函数返回1;若索引超出范围、堆不存在等情况导致修改无法进行,则返回0。 +- `heap`:堆的句柄,通过它来定位要修改元素所在的具体堆,只有对应正确的堆才能准确找到并修改目标元素。 +- `index`:要修改的元素在堆中的索引位置,索引从0开始计数,表示堆中元素的序号,通过该索引能准确找到需要修改的目标元素,不过要确保传入的索引值在堆的有效范围内(小于堆的当前元素个数)。 +- `data`:指向新数据的指针,该指针指向的数据内容会替换掉堆中指定索引位置原有的元素数据,其数据类型和大小应与创建堆时规定的元素数据大小一致。 + +```c +static void test_base(void) +{ + heap_t h = heap_create(sizeof(int), 11, heap_root_max); + int i = 0; + + for (i = 0; i < 11; i++) + { + heap_push(h, &i); + } + + i = -9; + heap_modify(h, 6, &i); + + heap_delete(h); +} +``` + +### heap的顶端 +```c +int heap_top(heap_t heap, void *data); +``` +用于获取堆顶的元素数据,对于大根堆来说获取的是最大元素,对于小根堆则是最小元素。若获取操作成功,函数返回1,并且若`data`参数不为`NULL`,会将堆顶元素数据复制到该指针所指向的内存空间中;若堆为空或者出现其他导致获取失败的情况,则返回0。 +- `heap`:堆的句柄,通过该句柄确定要获取堆顶元素的目标堆,不同的堆有各自独立的堆顶元素,需要准确指定对应的堆才能获取正确的堆顶数据。 +- `data`:指向用于存储堆顶元素数据的内存空间的指针,若希望获取堆顶元素内容并存储起来便于后续使用,可传入有效的指针地址,函数会进行数据复制操作;若不需要获取具体数据,可传入`NULL`作为参数。 + +### heap的大小 +```c +int heap_size(heap_t heap); +``` +用于获取当前堆中元素的个数,返回的整数值表示堆中实际存储的元素数量,方便开发者了解堆的当前存储情况,比如判断堆是否为空、是否已满等。 +- `heap`:堆的句柄,依据该句柄找到对应的堆数据结构,进而获取该堆所包含的元素个数信息,每个堆都有其独立的元素数量统计,通过句柄能准确区分不同堆的情况。 diff --git a/doc/ini.en.md b/doc/ini.en.md new file mode 100644 index 0000000..29f8f67 --- /dev/null +++ b/doc/ini.en.md @@ -0,0 +1,650 @@ +### Introduction + +INI is a commonly used configuration file format in engineering with a simple syntax. The INI parser provided by varch offers simple and reliable APIs, enabling easy loading and generation of INI files. + +Here is a brief introduction to the INI specification. The components of an INI file include the following parts: + +**Comments** +``` +INI supports comments, and comments take up a single line. +Comments are marked with '#' or ';'. +The comment mark should be at the beginning of the line, and spaces are allowed before it, but no other characters are permitted. +``` +Example: +``` +########################### +# This is a comment +########################### + +aaa ; What follows this is not considered a comment +``` + +**Section** +``` +Each section occupies a single line. +The section name is enclosed in "[]" brackets, and the content within the outermost brackets is taken as the section name. Any characters including brackets can be included within the brackets. +Section names are case-sensitive. +Sections cannot be repeated. +``` +Example: +``` +[section] +``` + +**Key** +``` +Keys and values form key-value pairs. +The key-value pairs are separated by ':' or '='. Spaces on both sides of the separator are allowed but not involved in the parsing (though it's recommended not to leave spaces). +Keys are not case-sensitive. +Keys are not allowed to be repeated within the same section, but they can be repeated in different sections. +``` + +**Value** +``` +The content after the key-value separator is the value. +Values can contain any characters (including '=', ':', etc., which are included in the value). +Values can span multiple lines, provided that the indentation of the new line is greater than that of the value, and then the new line will be included in the value's parsing. +``` +Example: +``` +# The following example is allowed +value1 = =aaaa;#[] + +# Values are allowed to be split into lines. What's enclosed in [] here will not be parsed as a section. +value2 = 1 + 2 + 3 + 4 + [sss] +``` +An example of the **system.ini** file in the Windows system: +```ini +; for 16-bit app support +[386Enh] +woafont=dosapp.fon +EGA80WOA.FON=EGA80WOA.FON +EGA40WOA.FON=EGA40WOA.FON +CGA80WOA.FON=CGA80WOA.FON +CGA40WOA.FON=CGA40WOA.FON + +[drivers] +wave=mmdrv.dll +timer=timer.drv + +[mci] + +``` + +### Interface + +#### Creating and Deleting an INI Object +```c +ini_t ini_create(void); +void ini_delete(ini_t ini); +``` +Here, **ini_t** is the structure type for INI. The creation method returns an empty INI object, and the deletion method deletes the passed INI object. + +#### Adding and Removing a Section +```c +int ini_add_section(ini_t ini, const char* section); +int ini_remove_section(ini_t ini, const char* section); +``` +First, the method for adding a section will add a section with the specified name to the INI object. It returns 1 if the addition is successful and 0 if it fails (for example, if the name is repeated, the addition will fail). +The method for removing a section also takes the specified section name as a parameter and returns 1 if the removal is successful and 0 if it fails. When a section is removed, all the key-value pairs under that section will also be removed. + +#### Getting a Section +```c +int ini_section_index(ini_t ini, const char* section); +const char* ini_section_name(ini_t ini, int index); +``` +These two methods for getting a section are symmetrical. The first one gets the index of the section by its name (the index is the order of sections from the beginning to the end of the INI file, starting from 0). If the specified section is not found, it returns -1. The second one gets the section name by its index. If the section with the given index does not exist, it returns NULL. +The INI parser in varch stores sections using a unidirectional linked list with a built-in iterator. Each time it is called, it records the current position. When called again, it first checks whether the position is the same as the previous one. So, when the call is the same as the previous one, the time complexity is **O(1)**, and when traversing using the **ini_section_name** method, the time complexity is **O(n)**. + +#### Setting and Getting a Value +```c +int ini_set_value(ini_t ini, const char* section, const char* key, const char* value); +const char* ini_get_value(ini_t ini, const char* section, const char* key); +``` +The method for setting a value sets the value of the specified key in the specified section of the INI object to the given value. Besides modifying existing key-value pairs, this method can also be used to add new key-value pairs. First, it checks if the section exists. If not, it adds the section. Then it looks for the key. If the key is not found, it adds the key and sets the value to the specified value. It returns 1 if successful and 0 if it fails. +The method for getting a value checks if the key-value pair under the specified section exists. If it exists, it returns the value; otherwise, it returns NULL. + +#### Removing a Key-Value Pair +```c +int ini_remove_key(ini_t ini, const char* section, const char* key); +``` +This method removes the key-value pair with the specified key in the specified section. It returns 1 if the removal is successful and 0 if it fails. + +#### Getting a Key +```c +int ini_key_index(ini_t ini, const char* section, const char* key); +const char* ini_key_name(ini_t ini, const char* section, int index); +``` +The methods for getting a key are very similar to those for getting a section. They respectively find the index by the key name and find the key name by the index. When finding the index, it returns a negative number if it fails, and when finding the key name, it returns NULL if it fails. +Similarly, key-value pairs are also stored using a unidirectional linked list with a built-in iterator. When the key name or index is the same as the previous call, the time complexity is O(1). When traversing forward by index, the time complexity is O(n). + +#### Getting Counts +```c +int ini_section_count(ini_t ini); +int ini_pair_count(ini_t ini, const char* section); +``` +These two methods are used to get the count of sections in the INI object and the count of key-value pairs in the specified section, respectively. + +#### Dumping an INI Object +```c +char* ini_dumps(ini_t ini, int preset, int *len); +int ini_file_dump(ini_t ini, char* filename); +``` +First, the **ini_dumps** method dumps the INI object into a string according to the format. The preset parameter is used to predict the length of the resulting string. During the conversion of the INI object to a string, space is reallocated as the conversion proceeds. If the predicted length is accurate and the pre-allocated space is sufficient, the frequency of reallocating space can be reduced, improving the conversion efficiency. The *len parameter is the length of the resulting string. When NULL is passed, the length is not obtained. The return value is the converted string, which is allocated by the function and **needs to be freed after use**. +The **ini_file_dump** method dumps the INI object into a file based on the **ini_dumps** method. The filename parameter is used to pass in the file name, and the return value is the length of the dumped content. A negative value indicates that the dumping failed. + +#### Loading an INI Object +```c +ini_t ini_loads(const char* text); +ini_t ini_file_load(const char* filename); +``` +Similar to the dumping methods, an INI object can be loaded from a string text or from a file. If the loading is successful, it returns an INI object; otherwise, it returns NULL. + +#### INI Loading Errors +```c +int ini_error_info(int* line, int* type); +``` +The INI parser provided by varch offers accurate error identification. When the INI loading fails, this method can be called to locate the error position and the error type. A return value of 1 indicates that an error occurred during parsing, and 0 indicates no error. The line parameter outputs the line where the error occurred, and the type parameter outputs the error type. The error types are defined as follows: +```c +#define INI_E_OK (0) // ok +#define INI_E_BRACKETS (1) // missing brackets ']' +#define INI_E_DELIM (2) // missing delimiter +#define INI_E_KEY (3) // missing key +#define INI_E_SECTION (4) // missing section +#define INI_E_REKEY (5) // key repeat +#define INI_E_RESECTION (6) // section repeat +#define INI_E_MEMORY (7) // memory allocation failed +#define INI_E_OPEN (8) // fail to open file +``` + +### Reference Examples + +#### Generating an INI File +```c +static void test_dump(void) +{ + ini_t ini = NULL; // Define an INI object, usually initialized to NULL + + ini = ini_create(); // Create an empty INI object + if (ini == NULL) + { + printf("INI creation failed!\r\n"); + return; + } + + /* Add sections */ + ini_add_section(ini, "Zhang San"); + ini_add_section(ini, "Li Si"); + ini_add_section(ini, "Wang Wu"); + + /* Add key-value pairs */ + ini_set_value(ini, "Zhang San", "age", "18"); + ini_set_value(ini, "Zhang San", "height", "178"); + ini_set_value(ini, "Zhang San", "email", "123456@qq.com"); + + ini_set_value(ini, "Li Si", "age", "20"); + ini_set_value(ini, "Li Si", "gender", "man"); + ini_set_value(ini, "Li Si", "weight", "65"); + + ini_set_value(ini, "Wang Wu", "age", "22"); + + /* Dump the INI object to a file */ + ini_file_dump(ini, WRITE_FILE); + + ini_delete(ini); // Delete the object after use +} +``` +The dumped file: +```ini +[Zhang San] +age = 18 +height = 178 +email = 123456@qq.com + +[Li Si] +age = 20 +gender = man +weight = 65 + +[Wang Wu] +age = 22 + +``` +In the example, many function return values are not checked. In actual applications, it's necessary to check the return values. + +#### Loading an INI File +The test file is the **system.ini** file found in the Windows system as mentioned before. +```ini +; for 16-bit app support +[386Enh] +woafont=dosapp.fon +EGA80WOA.FON=EGA80WOA.FON +EGA40WOA.FON=EGA40WOA.FON +CGA80WOA.FON=CGA80WOA.FON +CGA40WOA.FON=CGA40WOA.FON + +[drivers] +wave=mmdrv.dll +timer=timer.drv + +[mci] + +``` +The loading test code: +```c +void test(void) +{ + ini_t ini = NULL; // Define an INI object, usually initialized to NULL + + /* Load the INI file */ + ini = ini_file_load("system.ini"); + if (ini == NULL) + { + int line, type; + ini_error_info(&line, &type); + printf("INI parsing error! Line %d, error %d.\r\n", line, type); + return; + } + + /* Traverse the INI object */ + int section_count = ini_section_count(ini); // Get the section count + for (int i = 0; i < section_count; i++) + { + char *section_name = ini_section_name(ini, i); // Get the section name + printf("section: [%s]\r\n", section_name); + int pair_count = ini_pair_count(ini, section_name); // Get the pair count + for (int j = 0; j < pair_count; j++) + { + char *key = ini_key_name(ini, section_name, j); // Get the key name + printf("key[%s], value[%s]\r\n", key, ini_get_value(ini, section_name, key)); // Print the key-value pair + } + } + + ini_delete(ini); // Delete the object after use +} +``` +The running result: +``` +section: [386Enh] +key[woafont], value[dosapp.fon] +key[EGA80WOA.FON], value[EGA80WOA.FON] +key[EGA40WOA.FON], value[EGA40WOA.FON] +key[CGA80WOA.FON], value[CGA80WOA.FON] +key[CGA40WOA.FON], value[CGA40WOA.FON] +section: [drivers] +key[wave], value[mmdrv.dll] +key[timer], value[timer.drv] +section: [mci] +``` + +#### Loading Error +Based on the above example, modify the **system.ini** file by deleting the right bracket ']' of the section **[mci]**. Then load it again. +```ini +; for 16-bit app support +[386Enh] +woafont=dosapp.fon +EGA80WOA.FON=EGA80WOA.FON +EGA40WOA.FON=EGA40WOA.FON +CGA80WOA.FON=CGA80WOA.FON +CGA40WOA.FON=CGA40WOA.FON + +[drivers] +wave=mmdrv.dll +timer=timer.drv + +[mci + +``` +The running result: +``` +INI parsing error! Line 13, error 1. +``` +In this way, it can be located that an error of type 1 occurred on line 13, which corresponds to +```c +#define INI_E_BRACKETS (1) // missing brackets ']' +``` + +### Source Code Analysis + +#### INI Parser Structures + +All the structures of the INI parser are implicit, meaning that the members of the structures cannot be accessed directly. This approach ensures the independence and security of the module and prevents external calls from modifying the members of the structures, which could otherwise damage the storage structure of INI. Therefore, the INI parser only exposes the declaration of INI in the header file, while the definitions of the structures are placed in the source file. Only the methods provided by the INI parser can be used to operate on INI objects. + +The declaration of the INI type is as follows: +```c +typedef struct INI* ini_t; +``` +When using it, just use `ini_t`. + +```c +typedef struct INI +{ + SECTION* sections; /* sections base */ + ITERATOR iterator; /* section iterator */ + int count; /* section count */ +} INI; +``` +The INI structure contains 5 members: `sections` (a linked list of sections), `count` (the count of sections), and `iterator` (the section iterator). The `iterator` is worth special mention as it records the position when accessing sections. When accessing again and it's found to be at the same position, it can quickly return without having to traverse from the beginning. The iterator will be further explained later. + +```c +typedef struct SECTION +{ + struct SECTION *next; /* link */ + char* name; /* section name */ + PAIR* pairs; /* pairs base */ + ITERATOR iterator; /* pair iterator */ + int count; /* pair count */ +} SECTION; +``` +The `SECTION` structure contains 5 members: `next` (which points to the next `SECTION`, forming a unidirectional linked list), `name` (the section name), `pairs` (a linked list of key-value pairs), `iterator` (the pair iterator), and `count` (the count of pairs). + +```c +typedef struct PAIR +{ + struct PAIR *next; /* link */ + char* key; /* key */ + char* value; /* value */ +} PAIR; +``` +The `PAIR` structure contains 3 members: `next` (which points to the next `PAIR`, forming a unidirectional linked list), `key` (the key), and `value` (the value). + +```c +typedef struct +{ + void *p; /* iteration pointer */ + int i; /* iteration index */ +} ITERATOR; +``` +Finally, let's look at this iterator. In fact, it's quite simple. It records the current node pointed to in the unidirectional linked list (the `p` member) and the index of the current position in the unidirectional linked list. How it records specifically will be discussed later. + +The storage structure of the INI class is clear as follows: +``` +INI + + section[0] --> pair[0] --> pair[1] --> pair[2] + | k v k v k v + | + v + section[1] --> pair[0] --> pair[1] --> pair[2] + | k v k v k v + | + v + section[2] --> pair[0] --> pair[1] + | k v k v + | + v + section[3] --> pair[0] + . k v + . + . +``` + +### Iteration of Unidirectional Linked Lists + +The operation of unidirectional linked lists is not the focus here. The built-in iterator is mainly used to improve the access efficiency of unidirectional linked lists, so the iteration process will be emphasized. Taking the iteration of sections as an example, let's explain the process of how this iterator obtains the nodes of the linked list: +```c +static SECTION* ini_section(ini_t ini, int index) // Pass in the index +{ + if (index >= ini->count) return NULL; // Check whether the index is out of bounds + + /* + This step is to reset the iteration, that is, to position the iterator back to the beginning of the linked list. + The iterator will be reset if any of the following 3 conditions is met: + 1. Since it's a unidirectional linked list and cannot be reversed, when the target index is less than the index of the iterator, it needs to be reset and then iterated from the beginning of the linked list to the specified index. + 2. When the pointer of the iterator (the `p` member) is NULL, which means it doesn't point to a specific node, it must be reset. So, if you want to reset the iterator externally, you just need to set the `p` member to NULL. + 3. When the target index `index` is 0, which means actively obtaining the 0th position, that is, the first position. + */ + if (index < ini->iterator.i ||!ini->iterator.p || index == 0) + { + ini->iterator.i = 0; + ini->iterator.p = ini->sections; + } + + /* + Loop to iterate the iterator to the specified index position. + The index of the unidirectional linked list increases positively, so the time complexity is O(n) when traversing forward, and it's still O(n^2) when traversing backward. + */ + while (ini->iterator.p && ini->iterator.i < index) + { + ini->iterator.p = ((SECTION *)(ini->iterator.p))->next; + ini->iterator.i++; + } + + /* Return the node pointed to by the iterator */ + return ini->iterator.p; +} +``` +When adjusting the linked list, the iterator needs to be adjusted accordingly. The simplest way is to set the `p` member to NULL to reset the iterator. + +This iterator improves the efficiency when accessing by index. What about accessing by section name? When accessing randomly by section name, first check whether the section name pointed to by the current iterator matches. If it matches, return it directly; if not, it still needs to traverse the linked list from the beginning to find the matching one. + +### Explanation of INI Dumping + +Dumping means "printing" the INI according to a format, but instead of printing it on the console, it's printed in the specified memory space. Where does this space come from? It comes from dynamic memory allocation. How much space should be allocated? This goes back to the `preset` parameter mentioned earlier. If the `preset` is set properly, the appropriate space can be allocated at one time. Otherwise, the space needs to be dynamically adjusted during the dumping process to store these "printed" characters. + +Now, let's first look at the structure that maintains this printing space: +```c +typedef struct +{ + char* address; /**< buffer base address */ + unsigned int size; /**< size of buffer */ + unsigned int end; /**< end of buffer used */ +} BUFFER; +``` +It has 3 members in total: `address` (the base address of the space), `size` (the size of the space), and `end` (the index of the end of the used buffer). + +The process of dynamically adjusting the space is as follows: +```c +static int expansion(BUFFER* buf, unsigned int needed) // `needed` is the additional capacity required +{ + char* address; + int size; + + if (!buf ||!buf->address) return 0; + + /* Calculate the total space required by adding the currently used space and the required space */ + needed += buf->end; + + /* Check if the current space can still meet the need */ + if (needed <= buf->size) return 1; /* There is still enough space in the current buf */ + + /* + If the current space size cannot meet the requirement, recalculate the space that can meet the requirement. + The new space should not only meet the current need but also leave some margin for the next append operation. + Otherwise, it would be a waste of efficiency to reallocate space every time a little capacity is added. + And what's the best size for the newly allocated space? It's the smallest power of 2 that is larger than the required size. + Why choose the power of 2? + 1. The algorithm is easy to implement. It's easy to calculate the smallest power of 2 that is larger than the specified value. + 2. The space utilization is good, which is (1 + 2) / 2 = 75%. + 3. The number of space reallocations is small. For example, when 128 is finally needed, if the increment is fixed at 10 each time, it needs 13 reallocations, while only 7 reallocations are needed if using the power of 2. + */ + size = pow2gt(needed); + address = (char*)realloc(buf->address, size); + if (!address) return 0; + + buf->size = size; + buf->address = address; + + return 1; +} +``` +Let's take a piece of dumping code as an example to see how it's used: +```c +olen = strlen(sect->name); // First calculate the length of the section name +if (expansion(&p, olen + 3) == 0) goto FAIL; // Append the corresponding space. +3 is for other characters +/* Copy the characters to the printing space */ +p.address[p.end++] = '['; +for (k = 0; k < olen; k++) +{ + p.address[p.end++] = sect->name[k]; +} +p.address[p.end++] = ']'; +p.address[p.end++] = '\n'; +``` + +There's a point to note when dumping values. Values are allowed to span multiple lines, but the indentation of the new line should be greater than that of the key. Since there's no indentation added when printing the key, when the value has a new line, an indentation needs to be inserted after the new line to indicate that the following line belongs to the previous key-value pair. +```c +/* Get the length of the value */ +olen = 0; +k = 0; +while (1) +{ + if (!pair->value[olen]) break; + if (pair->value[olen] == '\n') k++; // Record the number of lines of the value for inserting indentations + olen++; // Record the actual length of the value +} +if (expansion(&p, olen + k + 1) == 0) goto FAIL; + +/* Dump the value */ +for (k = 0; k < olen; k++) +{ + p.address[p.end++] = pair->value[k]; + if (pair->value[k] == '\n') p.address[p.end++] = '\t'; // Insert an indentation +} +p.address[p.end++] = '\n'; +``` + +Finally, during the printing process, the INI is traversed, and the space is dynamically adjusted to print each section and key-value pair in order to the specified space. + +### Explanation of INI Loading + +The most important and complex part of the INI parser is the loading and parsing part. + +1. First, an empty INI object is created, and then the sections, keys, and values parsed are added to the INI object one by one as the parsing progresses. After the parsing is completed, a complete INI object can be obtained. + +2. Before parsing, the line number and errors for parsing need to be reset first. Then, whenever a line break is encountered during the parsing, the line number is incremented accordingly. When a parsing error occurs, the type of the error is recorded. + +3. Then comes the actual parsing process. The parsing process involves traversing and judging each character one by one to determine which range it belongs to, such as whether it's a section, a key, a value, or a comment. Once the corresponding range is determined, the corresponding parsing action is executed. + +The entire parsing is in a large loop: +```c +while (*text) +{ + ... + ... + ... +} +``` +Since the content specified by the INI syntax is basically divided by lines (values can span lines in special cases), basically, in this loop, information of each line is processed. + +In the first step of the `while` loop: +```c +s = skip(text); +depth = s - text; +text = s; +``` +Skip the useless characters, such as spaces or tabs that have no practical meaning. While skipping the characters, record the indentation, which will be used as the basis for judging whether the next line belongs to a new line or is within the range of the value of the previous line. + +Immediately after that, check whether it's a comment. Since comments take up a single line, once a comment mark is encountered, the content after it will be ignored and skipped. +```c +/* skip comments */ +if (iscomment(*text)) +{ + text = lend(text); // Directly position to the end of the line + if (*text == '\n') + { + text++; + eline++; + continue; + } +} +``` + +The rest is to define the ranges of sections, keys, and values and perform the corresponding parsing. The general structure is as follows: +```c +/* +`scope` represents the boundary range of the previous value. +If the text being parsed is greater than `scope`, that is, it exceeds the range of the previous value and no longer belongs to the previous value, then new sections and keys can be parsed. +*/ +if (text >= scope) +{ + /* A section is indicated by starting with [ */ + if (*text == '[') + { + /* + A section occupies a single line, and the section name is enclosed by the outermost []. + So here, we need to find the outermost []. Since the left one has been found, we need to find the right one. + To find the right one, first directly position to the end of the line, and then search backward from the end of the line. + Skip the useless characters and find the right ]. + */ + ... + ... + ... + } + else + { + /* + Keys must be at the beginning of a line. If it's not a section part, then it's a key part. + The range of a key is determined by finding the first separator, which is either = or :. + Once the separator is found, the part before the separator is the range of the key, and the part after it is the range of the value. + Since the range of the value can span multiple lines, the range of the value needs to be calculated separately. + The premise for a value to span lines is that the indentation of the new line should be greater than that of the key (a blank line is considered separately). + Then, a sentinel is used to explore forward to find the position that doesn't belong to the range of the value. + */ + ... + ... + ... + } +} +else +{ + /* + When it still belongs to the range of the previous value, + define which character blocks in each subsequent line are useful, that is, remove the useless characters at the beginning and end of the line, and then append the truly meaningful string to the value. + */ + ... + ... + ... +} +``` +Here, a special note on obtaining the range of the value: +```c +static const char* value_scope(const char* text, int depth) +{ + const char *s = text; + const char *sentinel = NULL; + + s = lend(s); // The range directly includes the line after the separator : = + while (*s) + { + sentinel = skip(s + 1); // Let the sentinel move to the meaningful character of the new line + /* Skip comments */ + if (iscomment(*sentinel)) + { + s = lend(sentinel); + continue; + } + /* If the indentation of the current line is greater than that of the key, this line is included in the range */ + if (sentinel - s - 1 > depth) + { + s = sentinel; + s = lend(s); + } + /* If the indentation becomes smaller, it doesn't necessarily mean it's out of the range */ + else + { + /* If a meaningful character is encountered, it's out of the range of the value */ + if (*sentinel!= 0 && *sentinel!= '\n') break; + /* Then directly go to the end of the line. If it's a blank line, it still belongs to the range of the value */ + else s = sentinel; + } + } + /* Go back to remove the blank lines at the end that don't belong to the range of the value */ + while (*s == 0 || *s == '\n') + { + sentinel = rskip(s - 1, text); + if (*sentinel!= 0 && *sentinel!= '\n') break; + s = sentinel; + } + + return s; +} +``` + +### Explanation of INI Addition, Deletion, Modification, and Query + +The rest of the operations for adding, deleting, modifying, and querying INI are actually basic operations on the linked list data structure, so there's no need to emphasize them here. \ No newline at end of file diff --git a/doc/init.en.md b/doc/init.en.md new file mode 100644 index 0000000..172188d --- /dev/null +++ b/doc/init.en.md @@ -0,0 +1,67 @@ +### Introduction + +Initialization and export modules play a crucial role in program development as they enable code reuse and modular organization. Through initialization and export modules, developers can divide different functions into separate files, reducing code redundancy and improving maintainability and readability. The following will discuss the functions of initialization and export modules and their implementation methods in different programming languages. + +**Basic Functions of Modules** + +**Encapsulation**: Variables and functions inside a module are private by default and won't pollute the global scope. This avoids naming conflicts between different code blocks. + +**Reusability**: Modules can be reused in multiple places, thus reducing code redundancy. For example, in Python, you can create a `math_tools` module that contains multiple mathematical operation functions and then import and use these functions in different projects. + +**Maintainability**: By separating the code logic into different modules, the readability and maintainability of the code can be improved. Each module has a clear responsibility, making it more convenient to modify and expand. + +### Interface + +#### Initialization and Export + +```c +#define init_export_hardware(func) __initialize(func, "1") +#define init_export_driver(func) __initialize(func, "2") +#define init_export_system(func) __initialize(func, "3") +#define init_export_module(func) __initialize(func, "4") +#define init_export_app(func) __initialize(func, "5") +``` + +The init module supports five different levels of initialization and export, and the only difference lies in the execution order of the initialization functions. + +**Example of Use**: +```c +void test_init_hardware(void) +{ + printf("hardware init!\r\n"); +} +init_export_hardware(test_init_hardware); + +void test_init_driver(void) +{ + printf("driver init!\r\n"); +} +init_export_driver(test_init_driver); + +void test_init_system(void) +{ + printf("system init!\r\n"); +} +init_export_system(test_init_system); + +void test_init_module(void) +{ + printf("module init!\r\n"); +} +init_export_module(test_init_module); + +void test_init_app(void) +{ + printf("app init!\r\n"); +} +init_export_app(test_init_app); + +void test_init_system1(void) +{ + printf("system1 init!\r\n"); +} +init_export_system(test_init_system1); +``` + +Generally, the export is done on the next line after the function ends. Then, call `init_execute()` at a specific position in the main function. When the program reaches `init_execute()`, the initialization functions will be called in sequence according to the initialization level. + diff --git a/doc/intl.en.md b/doc/intl.en.md new file mode 100644 index 0000000..8f2d1f7 --- /dev/null +++ b/doc/intl.en.md @@ -0,0 +1,226 @@ +### Introduction + +The `intl` is a large integer module that enables operations on large integers beyond the limits of the standard data types in the C language. It provides a flexible representation of long integers, which are stored using 16-bit and 32-bit segments. + +In mainstream computer systems nowadays, the maximum integer generally supported is usually 32 bits or 64 bits, which can meet the calculation and storage requirements in ordinary situations. However, when dealing with even larger numbers, they may fall short. The `intl` module, as an additional integer extension, adopts a consistent storage mechanism and is quite convenient to use. It offers the following calculations: + +- **Supports basic arithmetic operations**: addition, subtraction, multiplication, division, and modulo operations. +- **Bitwise operations**: AND, OR, XOR, and NOT. +- **Shift operations**: left shift and right shift. +- **Functions for converting strings and standard integers into large integers**. +- **Provides functions to output large integers in decimal, hexadecimal, and binary formats**. + +The `intl` can be configured as needed to be 128 bits, 256 bits, 512 bits, 1024 bits, 2048 bits, 4096 bits, or 8192 bits. If these are still not sufficient, it also provides functions to generate even larger configurations with more bits. + +### Interface + +#### Structure Definition + +##### `intl` + +It is a structure used to represent long integer values. It uses a union to store integers in two different ways: + +- An array of the `uint16_t` type (16-bit segments). +- An array of the `uint32_t` type (32-bit segments). + +```c +typedef struct { + union { + uint16_t u16[INTL_U16_PARTS]; + uint32_t u32[INTL_U32_PARTS]; + }; +} intl; +``` + +#### Definition Functions + +```c +intl intl_from(const char *str); // +intl intl_from2(int value); // +``` + +The `intl` provides two definition functions. + +The `intl_from` function parses and generates an `intl` from a numeric string. It supports parsing strings in decimal, binary, octal, and hexadecimal formats. +The `intl_from2` function creates an `intl` from an `int` type. This function is encapsulated into a shorter macro definition method `intl()`, which is closer to the usage habits and has higher execution efficiency. + +Regarding the usage of the two functions, when the value is within the range of `int`, it is recommended to use the `intl()` method. When the value exceeds the range of `int` or when it is necessary to define it in different number systems, the `intl_from()` method can be used. + +Meanwhile, macro definitions for commonly used values such as `INTL_MAX`, `INTL_MIN`, and `INTL_ZERO` are also available. + +**Example**: +```c +static void test_define(void) +{ + intl a = intl(0); + intl b = intl(10); + intl c = intl(-10); + intl d = intl(0xFF); + + intl e = intl_from("0"); + intl f = intl_from("100"); + intl g = intl_from("-100"); + intl h = intl_from("123456789123456789123456789"); + intl i = intl_from("0b1110000001111100101010100"); + intl j = intl_from("0o1236541236763210233642166"); + intl k = intl_from("0xF125E3F6D743648EEFFF12356"); + + intl max = INTL_MAX; + intl min = INTL_MIN; + intl n0 = INTL_ZERO; +} +``` + +#### Conversion Functions + +```c +const char* intl_sdec(intl a, char buffer[INTL_MAX_DEC]); +const char* intl_shex(intl a, char buffer[INTL_MAX_HEX]); +const char* intl_sbin(intl a, char buffer[INTL_MAX_BIN]); +``` + +These three functions are used in the same way. They convert the value of `a` into decimal, hexadecimal, and binary formats respectively and store the results in the passed `buffer`, and then return the address of the `buffer` with valid digits. + +```c +#define INTL_P_DEC 0x01 +#define INTL_P_BIN 0x02 +#define INTL_P_HEX 0x04 + +static void print_intl(intl a, int type) +{ + char buffer[INTL_MAX_BIN]; + if (type & INTL_P_DEC) printf("intl (decimal): %s\n", intl_sdec(a, buffer)); + if (type & INTL_P_HEX) printf("intl (hex): %s\n", intl_shex(a, buffer)); + if (type & INTL_P_BIN) printf("intl (bin): %s\n", intl_sbin(a, buffer)); +} + +static void test_print(void) +{ + intl a = intl_from("123456789123456789123456789"); + + print_intl(a, INTL_P_DEC); + print_intl(a, INTL_P_BIN); + print_intl(a, INTL_P_HEX); +} +``` + +**Result**: +``` +intl (decimal): 123456789123456789123456789 +intl (bin): 110011000011110111111011111001011100011101100011001111101111100000001000101111100010101 +intl (hex): 661EFDF2E3B19F7C045F15 +``` + +#### Operation Functions + +```c +intl intl_add(intl a, intl b); // a+b +intl intl_sub(intl a, intl b); // a-b +intl intl_mul(intl a, intl b); // a*b +intl intl_div(intl a, intl b); // a/b +intl intl_mod(intl a, intl b); // a%b +intl intl_and(intl a, intl b); // a&b +intl intl_xor(intl a, intl b); // a^b +intl intl_or(intl a, intl b); // a|b +intl intl_shl(intl a, uint32_t amount); // a<>amount +intl intl_not(intl a); // ~a +intl intl_abs(intl a); // |a| +intl intl_inc(intl a); // a++ +intl intl_dec(intl a); // a-- +intl intl_neg(intl a); // -a +int intl_cmp(intl a, intl b); // a>b a=b a<=b +int intl_sign(intl a); // a>0 a<0 a==0 +``` + +The operation functions of `intl` are consistent with the similar symbolic operations. + +**Example of Use**: +```c +static void test_calculate(void) +{ + intl a = intl_from("123456789123456789123456789"); + intl b = intl_from("987654321987654321987654321"); + + printf("a: \r\n"); + print_intl(a, INTL_P_DEC); + printf("b: \r\n"); + print_intl(b, INTL_P_DEC); + + printf("a + b: \r\n"); + print_intl(intl_add(a, b), INTL_P_DEC); + + printf("a - b: \r\n"); + print_intl(intl_sub(a, b), INTL_P_DEC); + + printf("a * b: \r\n"); + print_intl(intl_mul(a, b), INTL_P_DEC); + + printf("b / a: \r\n"); + print_intl(intl_div(b, a), INTL_P_DEC); + + printf("b %% a: \r\n"); + print_intl(intl_mod(b, a), INTL_P_DEC); + + printf("a & b: \r\n"); + print_intl(intl_and(a, b), INTL_P_DEC); + + printf("a | b: \r\n"); + print_intl(intl_or(a, b), INTL_P_DEC); + + printf("a ^ b: \r\n"); + print_intl(intl_xor(a, b), INTL_P_DEC); + + printf("~a: \r\n"); + print_intl(intl_not(a), INTL_P_DEC); + + printf("a << 1: \r\n"); + print_intl(intl_shl(a, 1), INTL_P_DEC); + + printf("b >> 1: \r\n"); + print_intl(intl_shr(b, 1), INTL_P_DEC); + + printf("compare: %d\r\n", intl_cmp(a, b)); + + printf("a incremented: \r\n"); + print_intl(intl_inc(a), INTL_P_DEC); + + printf("b decremented: \r\n"); + print_intl(intl_dec(b), INTL_P_DEC); +} +``` + +**Result**: +``` +a: +intl (decimal): 123456789123456789123456789 +b: +intl (decimal): 987654321987654321987654321 +a + b: +intl (decimal): 1111111111111111111111111110 +a - b: +intl (decimal): -864197532864197532864197532 +a * b: +intl (decimal): 121932631356500531591068431581771069347203169112635269 +b / a: +intl (decimal): 8 +b % a: +intl (decimal): 9000000009000000009 +a & b: +intl (decimal): 38793946662829560778723857 +a | b: +intl (decimal): 1072317164448281550332387253 +a ^ b: +intl (decimal): 1033523217785451989553663396 +~a: +intl (decimal): -123456789123456789123456790 +a << 1: +intl (decimal): 246913578246913578246913578 +b >> 1: +intl (decimal): 493827160993827160993827160 +compare: -1 +a incremented: +intl (decimal): 123456789123456789123456790 +b decremented: +intl (decimal): 987654321987654321987654320 +``` diff --git a/doc/json.en.md b/doc/json.en.md new file mode 100644 index 0000000..ee6d22c --- /dev/null +++ b/doc/json.en.md @@ -0,0 +1,377 @@ +# JSON + +## Introduction +It's a JSON interpreter in the C language. It includes the parsing and generation of JSON text files, occupies a small amount of space, is secure, efficient, concise, and flexible. It can be transplanted to most C language platforms with no or only minor modifications. + +## Usage Examples + +### Generation + +**Test Code** +```c +void test_dump(void) +{ + json_t json, t; + + /* create root node */ + json = json_create_object(NULL); + + /* Add to root node */ + json_add_string_to_object(json, "name", "json parser"); + json_add_string_to_object(json, "version", "1.6.0"); + json_add_string_to_object(json, "description", "This is a C language version of json streamlined parser."); + json_add_string_to_object(json, "repository", "https://gitee.com/Lamdonn/json"); + + /* Add an empty array to the root node */ + t = json_add_array_to_object(json, "keywords"); /* t receive added array */ + json_add_string_to_array(t, "json"); + json_add_string_to_array(t, "streamlined"); + json_add_string_to_array(t, "parser"); + + /* Add an empty object to the root node */ + t = json_add_object_to_object(json, "others"); /* t receive added object */ + json_add_bool_to_object(t, "open", JSON_TRUE); + json_add_string_to_object(t, "license", "GPL3.0"); + + /* Dump JSON objects to a file */ + json_file_dump(json, "test.json"); + + /* Delete after end of use */ + json_delete(json); +} +``` + +**Generated File Name**: **test.json** +```json +{ + "name": "json parser", + "version": "1.6.0", + "description": "This is a C language version of json streamlined parser.", + "repository": "https://gitee.com/Lamdonn/json", + "keywords": ["json", "streamlined", "parser"], + "others": { + "open": true, + "license": "GPL3.0" + } +} +``` + +### Parsing + +Parse the previously generated file: **test.json** + +**Test Code** +```c +void test_load(void) +{ + json_t json, t; + + /* Load json file */ + json = json_file_load("test.json"); + if (!json) return; + + t = json_to_key(json, "name"); + if (json_isstring(t)) printf("module name: %s\r\n", json_value_string(t)); + + t = json_to_key(json, "others", "open"); + if (json_isbool(t)) printf("open: %s\r\n", json_value_bool(t)? "yes" : "no"); + + t = json_to_index(json, 4, 1); + if (json_isstring(t)) printf("keywords[1]: %s\r\n", json_value_string(t)); + + /* Delete after end of use */ + json_delete(json); +} +``` + +**Printed Results** +``` +module name: json parser +open: yes +keywords[1]: streamlined +``` + +## JSON Syntax + +JSON syntax is a subset of the JavaScript object representation syntax. + +1. Data is in `key-value pairs`, and the key-value is indicated by `:`. +2. Data is separated by commas `,`. +3. Use the backslash `\` to escape characters. +4. Square brackets `[]` hold arrays, and arrays can contain multiple values of different types. +5. Curly braces `{}` hold objects, and objects can contain multiple key-value pairs. + +### JSON Data Types +```c +#define JSON_TYPE_NULL (1) /* base type, null */ +#define JSON_TYPE_BOOL (2) /* base type, bool */ +#define JSON_TYPE_NUMBER (3) /* base type, number */ +#define JSON_TYPE_STRING (4) /* base type, string */ +#define JSON_TYPE_ARRAY (5) /* extension type, array */ +#define JSON_TYPE_OBJECT (6) /* extension type, object */ +``` + +### JSON Key-Value Pairs + +The way to write key-value pairs: +```json +"key" : "value" +``` + +The key is of the string type and needs to be enclosed in double quotes `""`. +The value can be any of the following basic types: + +* Null type (`null`) +* Boolean type (`true`, `false`) +* Numeric type (integer or floating-point number) +* String type (in double quotes `""`) +* Array type (in square brackets `[]`) +* Object type (in curly braces `{}`) + +### Syntax Example +```json +{ + "information": { + "module": "json", + "history": [1.0, 1.1, 1.2, 1.21, 1.3, 1.31, 1.32, 1.42], + "paser": true, + "print": false + }, + "other": null +} +``` + +## Operation Methods + +### Common Methods + +#### JSON Parsing + +**Method Prototypes** +```c +json_t json_loads(const char* text); // Load text +json_t json_file_load(char* filename); // Load file +``` + +The `json_loads` function takes in JSON text information and can return the handle of the parsed JSON object. The `json_file_load` function can load a file and return a JSON object just by passing in the file name. Inside the function, it reads the file through the standard file operation function set in the C language and then applies the `json_loads` function for parsing. It supports UTF8 encoded files. + +#### JSON Generation + +**Method Prototypes** +```c +char* json_dumps(json_t json, int preset, int unformat, int* len); // Generate text +int json_file_dump(json_t json, char* filename); // Generate file +``` + +The `json_dumps` function converts a JSON object into text information. The `preset` is the preset text length. If the preset length is close to the final output text length, the number of memory reallocations can be reduced and the conversion efficiency can be improved. `unformat` indicates whether to use formatted output or not. If not using formatted output, the text will be squeezed into one line. `len` is the output length of the conversion. +The `json_file_dump` function applies the `json_dumps` function to store the text information into a file with the specified name. + +#### JSON Getting Sub-objects + +**Method Prototypes** +```c +json_t json_get_child(json_t json, const char* key, int index); +#define json_to_index(json, i,...) +#define json_to_key(json, key,...) +``` + +In a JSON object, keys are not checked for duplication, that is, in the same level of JSON, there may be multiple keys with the same name. The `json_get_child` method can be used to match specific keys. For this function, when `key` is passed as NULL, only the index takes effect and it matches the sub-object according to the index. When `key` is not NULL, it will only match the sub-object of the corresponding key and use the index to indicate which object named `key` to match. +```c +t = json_get_child(json, NULL, 3); // Find the sub-object with index 3 +t = json_get_child(json, "a", 3); // Find the sub-object with key "a" and index 3 +``` + +The `json_to_index` and `json_to_key` methods can both conveniently get sub-objects and can also be used as search methods to check whether corresponding sub-objects exist. +The `json_to_index` method gets sub-objects by index. This method can be used regardless of whether the object type is an array or an object. +The `json_to_key` method gets sub-objects by key, but this method is only applicable to object-type objects because arrays don't have keys. +The parameters of these two methods have variable arguments, which can input several indexes or keys to continuously get sub-objects of the next level. For example: +```c +t = json_to_key(json, "information", "module", "name"); +``` +is equivalent to +```c +t = json_to_key(json, "information"); +t = json_to_key(t, "module"); +t = json_to_key(t, "name"); +``` + +#### JSON Getting Object's Value Type + +**Method Prototypes** +```c +#define json_type(json) // Get type +#define json_isnull(json) // Check if it's null type +#define json_isbool(json) // Check if it's boolean type +#define json_isnumber(json) // Check if it's numeric type +#define json_isint(json) // Check if it's integer numeric type +#define json_isfloat(json) // Check if it's floating-point numeric type +#define json_isstring(json) // Check if it's string type +#define json_isarray(json) // Check if it's array type +#define json_isobject(json) // Check if it's object type +``` + +#### JSON Getting Object's Key and Value + +**Method Prototypes** +```c +const char* json_key(json_t json); +int json_value_bool(json_t json); +int json_value_int(json_t json); +double json_value_float(json_t json); +const char* json_value_string(json_t json); +json_t json_value_array(json_t json); +json_t json_value_object(json_t json); +``` + +Before performing operations to get values, it is recommended to first check whether it is the expected type. If operations are performed on incorrect types, it may damage the storage structure of JSON or even cause the program to crash. + +#### JSON Creating and Deleting Objects + +**Method Prototypes** +```c +json_t json_create(void); +void json_delete(json_t json); +#define json_create_null_for_object(key) (json_set_key(json_set_null(json_create()),(key))) +#define json_create_true_for_object(key) (json_set_key(json_set_bool(json_create(),JSON_TRUE),(key))) +#define json_create_false_for_object(key) (json_set_key(json_set_bool(json_create(),JSON_FALSE),(key))) +#define json_create_bool_for_object(key, b) (json_set_key(json_set_bool(json_create(),(b)),(key))) +#define json_create_int_for_object(key, n) (json_set_key(json_set_int(json_create(),(n)),(key))) +#define json_create_float_for_object(key, n) (json_set_key(json_set_float(json_create(),(n)),(key))) +#define json_create_string_for_object(key, s) (json_set_key(json_set_string(json_create(),(s)),(key))) +#define json_create_array_for_object(key) (json_set_key(json_set_array(json_create(),NULL),(key))) +#define json_create_object_for_object(key) (json_set_key(json_set_object(json_create(),NULL),(key))) +#define json_create_null_for_array() (json_set_null(json_create())) +#define json_create_true_for_array() (json_set_bool(json_create(),JSON_TRUE)) +#define json_create_false_for_array() (json_set_bool(json_create(),JSON_FALSE)) +#define json_create_bool_for_array(b) (json_set_bool(json_create(),(b))) +#define json_create_int_for_array(n) (json_set_int(json_create(),(n))) +#define json_create_float_for_array(n) (json_set_float(json_create(),(n))) +#define json_create_string_for_array(s) (json_set_string(json_create(),(s))) +#define json_create_array_for_array() (json_set_array(json_create(),NULL)) +#define json_create_object_for_array() (json_set_object(json_create(),NULL)) +``` + +These methods can create the basic types of JSON. Arrays and objects can store JSON objects, so by default, empty arrays and empty objects are created. Except for these two, other methods can specify initialization values when creating. +When creating an object and specifying a `key`, if a key is passed in, the created object can be added to an object type. If NULL is passed in, the created object can be added to an array type. +Arrays can store data of any type, but generally, they store data of the same type. Therefore, additional initialization methods are provided for creating arrays. +```c +json_t json_create_array_int(char* key, const int* numbers, int count); +json_t json_create_array_float(char* key, const float* numbers, int count); +json_t json_create_array_double(char* key, const double* numbers, int count); +json_t json_create_array_string(char* key, const char** strings, int count); +``` +Initialize data according to C language arrays into JSON arrays. + +#### JSON Linking and Unlinking Objects + +**Method Prototypes** +```c +json_t json_attach(json_t json, int index, json_t ins); +json_t json_detach(json_t json, const char* key, int index); +``` + +The `json_attach` method links a created object to another object according to the index. The value type of the object to be linked must be of array type or object type. It returns the item itself on success and NULL on failure. +The `json_detach` method, in an array or object, matches the specified sub-object according to the same `key` and `index` matching logic as `json_get_child` and unlinks it, returning the sub-object on success and NULL on failure. +Both of these methods do not involve the creation or deletion of objects but only the adjustment of the storage structure. They achieve addition or removal operations by cooperating with other methods. + +#### JSON Adding Objects +```c +#define json_add_null_to_array(json) +#define json_add_bool_to_array(json, b) +#define json_add_int_to_array(json, n) +#define json_add_float_to_array(json, n) +#define json_add_string_to_array(json, s) +#define json_add_array_to_array(json) +#define json_add_object_to_array(json) + +#define json_add_null_to_object(json, key) +#define json_add_bool_to_object(json, key, b) +#define json_add_int_to_object(json, key, n) +#define json_add_float_to_object(json, key, n) +#define json_add_string_to_object(json, key, s) +#define json_add_array_to_object(json, key) +#define json_add_object_to_object(json, key) +``` + +These methods are formed by combining creation methods and linking methods to add specific types of data to JSON objects of array or object type. + +#### JSON Removing Sub-objects +```c +#define json_erase(json, key, index) +#define json_erase_by_index(json, index) +#define json_erase_by_key(json, key) +``` + +These methods are formed by combining deletion methods and unlinking methods to remove specific sub-objects in arrays or objects. + +#### JSON Object Modification + +**Method Prototypes** +```c +json_t json_set_key(json_t json, const char* key); +json_t json_set_null(json_t json); +json_t json_set_bool(json_t json, int b); +json_t json_set_int(json_t json, int num); +json_t json_set_float(json_t json, double num); +json_t json_set_string(json_t json, const char* string); +json_t json_set_object(json_t json, json_t object); +json_t json_set_array(json_t json, json_t array); +json_t json_set_array_int(json_t json, const int* numbers, int count); +json_t json_set_array_float(json_t json, const float* numbers, int count); +json_t json_set_array_double(json_t json, const double* numbers, int count); +json_t json_set_array_string(json_t json, const char** strings, int count); +``` + +Modifying an object will overwrite the original content. + +#### JSON Object Copying + +**Method Prototypes** +```c +json_t json_copy(json_t json); +``` + +Deep copy a JSON object based on the source JSON object. + +#### JSON Parsing Error Reporting + +**Method Prototypes** +```c +int json_error_info(int* line, int* column); +``` + +This JSON parser has a relatively accurate error reporting mechanism. When executing `json_loads`-type loading functions and a null value is returned indicating a parsing error, the `json_error_info` method can be called to view specific error information. The `json_file_load` function already outputs error information internally. +In the parameters, `*line` is the output error line, `*column` is the output error column, and the return value is the error type. + +For example, in the following error where there is an extra minus sign in front of `true`: +```json +{ + "name": "json parser", + "version": "1.6.0", + "description": "This is a C language version of json streamlined parser.", + "repository": "https://gitee.com/Lamdonn/json", + "keywords": ["json", "streamlined", "parser"], + "others": { + "open": -true, + "license": "GPL3.0" + } +} +``` + +When loading this file, an error occurs, indicating that an error with code 7 occurs near `"true"` at line 8, column 12. +``` +Parsing error, code 7 line 8 column 12, near [true]. +``` + +The error types include the following: +```c +#define JSON_E_OK (0) // No error +#define JSON_E_INVALID (1) // Invalid +#define JSON_E_GRAMMAR (2) // Common syntax error +#define JSON_E_END (3) // Extra invalid text at the end of the text +#define JSON_E_KEY (4) // Error occurred when parsing the key +#define JSON_E_VALUE (5) // Error occurred when parsing the value +#define JSON_E_MEMORY (6) // Memory allocation failure +#define JSON_E_NUMBER (7) // Invalid number +#define JSON_E_INDICATOR (8) // Missing indicator ':' +``` diff --git a/doc/kern.en.md b/doc/kern.en.md new file mode 100644 index 0000000..3dca1cd --- /dev/null +++ b/doc/kern.en.md @@ -0,0 +1,97 @@ +### Introduction + +In embedded development, many devices nowadays run specific operating systems like Linux, RT-Thread, FreeRTOS, uC/OS, etc. However, there are still quite a number of devices that don't use an operating system and instead operate in a "bare-metal" mode (also known as a foreground-background system). In software development for such systems without an operating system, a common approach is to put all the functions to be executed in a loop and execute each function sequentially in the loop. And if there are requirements for the execution period of functions, timers can be used to determine how often a certain function should be executed. But when it comes to managing multiple such functions, this programming method becomes rather redundant and cumbersome, and it's not convenient for us to manage. + +Here, a kernel for managing timed tasks has been developed. The only hardware resource it requires is a timer (used to obtain time slices). Of course, it can also run on a computer as long as it can obtain time slices. The main function of this kernel is to achieve the periodic invocation of functions. In other words, this kernel realizes the function of a software timer. + +### Interface + +#### Kernel Initialization +```c +int kern_init(kern_tick_t tick_func, unsigned short time_slice); +``` +Before task scheduling, the kernel must be initialized first. The initialization is quite simple. Pass in the clock function `tick_func` that can obtain time slices and the specific clock period `time_slice` of the clock function (usually in milliseconds). If the initialization is successful, it will return `KE_OK`; otherwise, it will return other values indicating failure. + +#### Task Creation and Deletion +```c +task_t task_create(unsigned short period, task_handler_t handler); +int task_delete(task_t task); +``` +Task creation is straightforward. One needs to pass in the period `period` of the task (the unit is the time slice passed in during kernel initialization) and the execution function `handler` of the task. If the creation is successful, it will return the unique ID of the task; otherwise, it will return 0. For deletion, just pass in the task ID. If the deletion is successful, it will return `KE_OK`; otherwise, it will return other values indicating failure. + +#### Task Scheduling +```c +void kern_schedule(void); +``` +After initializing the kernel, the kernel scheduling function can be called. However, there will be no tasks at this time. Generally, tasks are created first and then scheduled. +This scheduling function has no exit and will keep running until the program ends. Therefore, the code after the scheduling function won't be executed. So don't write code after the scheduling function. + +#### Running Task +```c +task_t task_running(void); +``` +Different tasks can have the same execution function. This function is used to distinguish which task is calling the execution function. + +### Example +```c +static unsigned int get_msec(void) +{ + struct timeval mstime; + unsigned int ms = 0; + gettimeofday(&mstime, NULL); + ms = mstime.tv_sec * 1000 + mstime.tv_usec / 1000; + return ms; +} + +void task1(void) +{ + static int count = 0; + printf("task1 running! %d\r\n", ++count); +} + +void task2(void) +{ + static int count = 0; + printf("task2 running! %d\r\n", ++count); +} + +int main(int argc, char *argv[]) +{ + if (kern_init(get_msec, 1) == KE_OK) + { + printf("kern init success!\r\n"); + } + else + { + printf("*** kern init fail!\r\n"); + return; + } + + printf("create task %d\r\n", task_create(1000, task1)); + printf("create task %d\r\n", task_create(500, task2)); + + kern_schedule(); + + return 0; +} +``` + +**Running Results**: +``` +kern init success! +create task 1 +create task 2 +task1 running! 1 +task2 running! 1 +task2 running! 2 +task1 running! 2 +task2 running! 3 +task2 running! 4 +task1 running! 3 +task2 running! 5 +task2 running! 6 +task1 running! 4 +task2 running! 7 +task2 running! 8 +``` +Task 2 has run 2 times while the corresponding Task 1 has run 1 time. diff --git a/doc/list.en.md b/doc/list.en.md new file mode 100644 index 0000000..bb97127 --- /dev/null +++ b/doc/list.en.md @@ -0,0 +1,252 @@ +### Introduction + +The list container is a generalized encapsulation of the linked list in the C language. It supports arbitrary data types (such as int, char, struct, etc.). It encapsulates the commonly used methods for adding, deleting, modifying, and querying (where the query here refers to random access) of linked lists. It can be directly used as an ordinary container or can be further encapsulated on this basis. + +### Interface + +#### Creation and Deletion of list Objects +```c +list_t list_create(int dsize); +void list_delete(list_t list); +#define list(type) // For more convenient use, a macro definition is wrapped around list_create +#define _list(list) // A macro definition is wrapped around list_delete, and the list is set to NULL after deletion +``` +Here, **list_t** is the structure of the list. The creation method will return an empty list object, and it will return NULL if the creation fails. The `dsize` parameter is used to pass in the size of the data. The deletion method is used to delete the passed-in list object. The creation method and the deletion method should be used in pairs. Once created, the list object should be deleted when it's no longer in use. +```c +void test(void) +{ + list_t list = list(int); // Define and create a list of the int type + _list(list); // Use them in pairs and delete it after use +} +``` + +#### Insertion and Removal of list +```c +void* list_insert(list_t list, int index, void* data); +int list_erase(list_t list, int index, int num); +``` +The great advantage of the list is its efficiency in insertion and removal. There's no need to shift data; only the pointers in the linked list need to be modified. +The insertion method inserts the data at the specified address into the position specified by the index (when `data` is passed as NULL, it only allocates space without assigning a value). If the insertion is successful, it returns the address of the inserted data; otherwise, it returns NULL. The removal method removes `num` pieces of data starting from the specified index and returns the actual number of removed data. +```c +void test(void) +{ + list_t list = list(int); // Define and create a list of the int type + int i = 0; + void *data; + + /* Insert data */ + for (i = 0; i < 6; i++) + { + data = list_push_back(list, &i); // Insert at the end, inserting values from 0 to 5 + if (data) printf("insert %d\r\n", *(int*)data); // Print after insertion + } + + _list(list); // Use them in pairs and delete it after use +} +``` +**Results**: +``` +insert 0 +insert 1 +insert 2 +insert 3 +insert 4 +insert 5 +``` +Based on the insertion and removal methods, the following macro definition methods are extended: +```c +#define list_push_front(list, data) +#define list_push_back(list, data) +#define list_pop_front(list) +#define list_pop_back(list) +#define list_clear(list) +``` + +#### Reading and Writing of list Data +```c +void* list_data(list_t list, int index); +#define list_at(list, type, i) +``` +The `list_data` method is used to obtain the address of the data according to the index and returns the address of the specified data. NULL indicates failure. The `list_at` method adds the data type on the basis of `list_data`. +The random access of the list is different from that of arrays or vectors with continuous addresses. Arrays can directly locate the address of the specified index, while for linked lists to perform random access, they need to start from the head of the list and use the link pointers to point to the specified position step by step, which takes more time in this process. The list in varch has an added built-in iterator that can record the currently accessed position. When accessing subsequent positions next time, there's no need to start from the head of the linked list but can start from the current position and point to the specified position, thus having high forward traversal efficiency. +```c +void test(void) +{ + list_t list = list(int); + int i = 0; + + for (i = 0; i < 6; i++) + { + list_push_back(list, &i); + } + + for (i = 0; i < 6; i++) // Forward traversal + { + printf("list[%d] = %d\r\n", i, list_at(list, int, i)); + } + + _list(list); +} +``` +**Results**: +``` +list[0] = 0 +list[1] = 1 +list[2] = 2 +list[3] = 3 +list[4] = 4 +list[5] = 5 +``` + +#### Size of list and Data Size +```c +int list_size(list_t list); +int list_dsize(list_t list); +``` +The `size` of the list is easy to understand. It's similar to the size of an array. The `dsize` is the size of the data passed in during creation. +```c +void test(void) +{ + list_t list = list(int); + int i = 5; + while (i--) list_push_back(list, NULL); // Insert 5 empty data at the end, that is, only allocate space without assigning values + printf("size = %d, data size = %d\r\n", list_size(list), list_dsize(list)); + _list(list); +} +``` +**Results**: +``` +size = 5, data size = 4 +``` + +### Reference Example +```c +typedef struct { + char *name; + int age; +} STUDENT; + +void test(void) +{ + list_t list_int = list(int); // Define and create a list of the int type + list_t list_student = list(STUDENT); // Define and create a list of the struct STUDENT type + char *name[3] = { // Define three names + "ZhangSan", + "LiSi", + "WangWu", + }; + int i = 0; + + for (i = 0; i < 3; i++) + { + STUDENT s = {name[i], 18 + i}; + list_push_back(list_student, &s); // Insert three STUDENT objects + list_push_back(list_int, &i); // Insert 0, 1, 2 in sequence + } + + i = 1024; list_insert(list_int, 1, &i); // Insert 1024 at the position with index 1 + for (i = 0; i < list_size(list_int); i++) + { + printf("list_int[%d] = %d\r\n", i, list_at(list_int, int, i)); // Forward traversal + } + + for (i = 0; i < list_size(list_student); i++) + { + printf("list_student[%d]: name=%s, age=%d\r\n", i, list_at(list_student, STUDENT, i).name, list_at(list_student, STUDENT, i).age); + } + + // Delete the lists after using them + _list(list_int); + _list(list_student); +} +``` +**Results**: +``` +list_int[0] = 0 +list_int[1] = 1024 +list_int[2] = 1 +list_int[3] = 2 +list_student[0]: name=ZhangSan, age=18 +list_student[1]: name=LiSi, age=19 +list_student[2]: name=WangWu, age=20 +``` +In the example, many of the used functions don't check the return values. In practical applications, it's necessary to check the return values. + +### Source Code Analysis + +#### list Structure + +All the structures of the list container are implicit, which means that the members of the structures can't be accessed directly. This way ensures the independence and security of the module and prevents external calls from modifying the members of the structures, which could otherwise damage the storage structure of the list. So the list parser only leaves the single declaration of the list in the header file, and the definitions of the structures are placed in the source file. Only the methods provided by the list container can be used to operate on list objects. +The declaration of the list type: +```c +typedef struct LIST *list_t; +``` +When using it, just use `list_t`. +```c +/* type of list */ +typedef struct LIST +{ + NODE* base; /* address of base node */ + NODE* iterator; /* iterator of list */ + int size; /* size of list */ + int dsize; /* data size */ + int index; /* index of iterator */ +} LIST; +``` +The `LIST` structure contains 5 members: `base` (the base node of the linked structure, that is, the head of the list), `iterator` (the currently pointed node), `size` (the size of the list, that is, the length of the list), `dsize` (the size of each data), and `index` (the index where the `iterator` is located). +```c +/* type of list node */ +typedef struct _NODE_ +{ + struct _NODE_ *next; /* next node */ +} NODE; +#define data(node) ((node)+1) /* data of node */ +``` +In the `NODE` structure, the only explicitly defined member is `next` (which points to the next node, forming a linked structure). So where is the data stored? This is a characteristic of the list in varch that makes it compatible with all data structures. Since the sizes of different data types vary, if a fixed length is specified to follow the structure, it won't be able to be compatible with different length data types. However, in this `NODE` structure, the actual data is allocated in the space at the end of the structure, and the specific length is determined by the `dsize` of the `LIST`. Then this `data` member is called an implicit member (not directly shown in the structure). To obtain the address of the `data` member is quite simple. Just add an offset of the size of `NODE` to the node address (i.e., `+1`), and in this way, the pointer space for another level of pointer pointing to the `data` can be reduced. +When creating a list of the `int` type, the data of the `NODE` can be understood as follows: +```c +typedef struct _NODE_ +{ + struct _NODE_ *next; /* next node */ + char data[sizeof[int]]; +} NODE; +``` + +#### Random Access of the Iterator + +As mentioned before, the list has a built-in iterator. So how does this iterator work? +Let's look at the source code: +```c +static NODE* list_node(list_t list, int index) // Pass in the list and the index +{ + if (!list) return NULL; + if (index < 0 || index >= list->size) return NULL; // Check if the index is out of bounds + + /* + This step is to reset the iterator, that is, to position the iterator back to the head of the linked list. + The iterator will be reset if any of the following 3 conditions is met: + 1. Because it's a unidirectional linked list and can't be reversed, when the target index is less than the index of the iterator, it needs to be reset and then iterated from the head of the linked list to the specified index. + 2. When the pointer of the iterator is NULL, which means it doesn't point to a specific node, it must be reset. So if you want to reset the iterator externally, just set the pointer of the iterator to NULL. + 3. When the target index `index` is 0, which means actively obtaining the 0th position, that is, the first position. + */ + if (index < list->index ||!list->iterator || index == 0) + { + list->index = 0; + list->iterator = list->base; + } + + /* + Loop to iterate the iterator to the specified index position. + The index of the unidirectional linked list increases positively, so the time complexity is O(n) when traversing forward, and it's still O(n^2) when traversing backward. + */ + while (list->iterator && list->index < index) + { + list->iterator = list->iterator->next; + list->index++; + } + + /* Return the node pointed to by the iterator */ + return list->iterator; +} +``` +In all the APIs provided by the list in varch, as long as an index is passed in, the above access method will be called to locate the linked list. So when operating on the same index, there's no need to re-perform the pointing and positioning, and it can return quickly. diff --git a/doc/map.en.md b/doc/map.en.md new file mode 100644 index 0000000..608fd1b --- /dev/null +++ b/doc/map.en.md @@ -0,0 +1,225 @@ +### Introduction + +The map mapping is quite similar to the set collection. They are both logically discrete containers. The difference is that the data in the set is stored in the form of `index-data`, while the map mapping stores data in the form of `key-value` pairs. In a set, the `index` is of an integer type and the `data` can be of any type. In a map, the `key` can be of any type and the `value` can also be of any type. +The map container in varch uses a "red-black tree" in its underlying implementation, which is the same as that of the set. It has high efficiency in addition and deletion operations and also supports random access. The map also supports iterators. + +### Interface + +#### Creation and Deletion of map Objects +```c +map_t map_create(int dsize, int ksize, void *trans); +void map_delete(map_t map); +#define map(ktype, dtype) // For more convenient use, a macro definition is wrapped around map_create +#define _map(map) // A macro definition is wrapped around map_delete, and the map is set to NULL after deletion +``` +Here, **map_t** is the structure of the map. The creation method will return an empty map object, and it will return NULL if the creation fails. Among the parameters, `dsize` is used to pass in the size of the data, `ksize` is used to pass in the size of the key, and `trans` is the handle function for key conversion. +The `ksize` and `trans` are defined in the `map_cfg` configuration file. It's more convenient to directly use `map(ktype, dtype)`. +The map supports some default key types by default, including `char`, `int`, `float`, `double`, and `string`. Other types can also be added in the `cfg` file. +The deletion method is used to delete the passed-in map object. The creation method and the deletion method should be used in pairs. Once created, the map object should be deleted when it's no longer in use. +```c +void test(void) +{ + map_t map_char = map(char, int); + map_t map_int = map(int, int); + map_t map_float = map(float, int); + map_t map_double = map(double, int); + map_t map_string = map(string, int); + + _map(map_char); + _map(map_int); + _map(map_float); + _map(map_double); + _map(map_string); +} +``` +In this example, the type of the `value` is set to `int`, but it can actually be any other type. + +#### Insertion and Removal of map +```c +#define map_insert(map, key, value) +#define map_erase(map, key) +``` +The map has good efficiency in insertion and removal. There's no need to shift data; only the pointers in the linked list need to be modified. +The insertion method adds a specified key and copies the data to this key (when `value` is passed as NULL, it only allocates space without assigning a value). During the process of inserting the key, duplicate checking will be performed to ensure the uniqueness of the key. If the insertion is successful, it returns the address of the inserted data; otherwise, it returns NULL. The removal method removes the data with the specified key. It returns 1 if successful and 0 if failed. +Since different data structures can be passed in as the key to ensure flexibility, macro definitions are used for passing in parameters. + +#### Reading and Writing of map Data +```c +#define map_data(map, key) +#define map_at(map, vtype, key) +void* map_error(map_t map); +``` +The `map_data` method is used to obtain the address of the data according to the key and returns the address of the specified data. `map_error()` is used to indicate failure. The `map_at` method adds the data type on the basis of `map_data`. The `map_data` has a read-write protection mechanism. Since `map_error()` is returned instead of NULL, when using the `map_at` method and the `key` is entered incorrectly, the content pointed to by `map_error()` will be modified instead of causing the program to crash. +```c +void test(void) +{ + int value; + map_t map = map(string, int); + if (!map) return; + + value = 100; map_insert(map, "hello", &value); + value = 110; map_insert(map, "Zhang", &value); + printf("map[hello] = %d\r\n", map_at(map, int, "hello")); + printf("map[Zhang] = %d\r\n", map_at(map, int, "Zhang")); + map_erase(map, "hello"); + + _map(map); +} +``` +**Results**: +``` +map[hello] = 100 +map[Zhang] = 110 +``` + +#### Size of map and Data Size +```c +int map_size(map_t map); +int map_ksize(map_t map); +int map_vsize(map_t map); +``` +The `size` of the map is easy to understand. It's similar to the size of an array. The `ksize` is the size of the key passed in during creation (for a `string` type key, since it's a pointer and not an entity, the `ksize` is 0). The `vsize` is the size of the value. +```c +void test(void) +{ + int value; + map_t map = map(string, int); + if (!map) return; + + value = 100; map_insert(map, "hello", &value); + value = 110; map_insert(map, "Zhang", &value); + + printf("size = %d, key size = %d, value size = %d\r\n", map_size(map), map_ksize(map), map_vsize(map)); + + _map(map); +} +``` +**Results**: +``` +size = 2, key size = 0, value size = 4 +``` + +#### Map Finding +```c +#define map_find(map, key) +``` +This method is actually implemented by wrapping `map_data`. It returns 1 if the find is successful and 0 if it fails. + +#### Map Iterator +```c +void map_it_init(map_t map, int orgin); +void* map_it_get(map_t map, void **kaddress, int *ksize); +``` +The map also supports a built-in iterator, but mainly the iterator of the map is used for traversal. Since the map has discrete keys and can't be traversed in a sequential incrementing way, two iterator functions are provided here for traversing the map. +The `map_it_init` function initializes the iterator. When `orgin` is specified as `MAP_HEAD` or `MAP_TAIL`, it represents forward iteration and reverse iteration respectively. +The `map_it_get` function obtains the iteration and updates the iteration position. `**kaddress` is used to output the key (the current key, and it can also be passed as NULL if not needed to receive), and `*ksize` is used to output the size of the key (the current key, and it can also be passed as NULL if not needed to receive). It returns the data at the iteration position. +The number of iterations is controlled by `map_size`. +```c +void test(void) +{ + map_t map = map(string, int); + int value; + char *key; + void *data; + int i; + + value = 100; map_insert(map, "hello", &value); + value = 1; map_insert(map, "ZhangSan", &value); + value = 2; map_insert(map, "LiSi", &value); + value = 3; map_insert(map, "WangWu", &value); + value = 4; map_insert(map, "SunLiu", &value); + value = 5; map_insert(map, "QianQi", &value); + + map_it_init(map, MAP_HEAD); + i = map_size(map); + while (i--) + { + data = map_it_get(map, &key, NULL); + printf("map[%s] = %d\r\n", key, *(int *)data); + } + + _map(map); +} +``` +**Results**: +``` +map[LiSi] = 2 +map[QianQi] = 5 +map[SunLiu] = 4 +map[WangWu] = 3 +map[ZhangSan] = 1 +map[hello] = 100 +``` + +### Source Code Analysis + +The storage part using the red-black tree is the same as that of the set. The difference lies in supporting multiple types of keys. +For example, in places where the `index` was passed in the original set, it's now replaced with the following key type: +```c +typedef struct +{ + void* address; + int size; +} MKEY; +``` +And in the binary search of the red-black tree, instead of comparing the size of the `index`, it's changed to the following: +```c +static int key_compare(MKEY key0, MKEY key1) +{ + unsigned char *add0 = (unsigned char *)key0.address; + unsigned char *add1 = (unsigned char *)key1.address; + while (key0.size && key1.size) + { + key0.size--; + key1.size--; + if (*add0 < *add1) return -1; + else if (*add0 > *add1) return 1; + add0++; + add1++; + } + if (key0.size < key1.size) return -1; + else if (key0.size > key1.size) return 1; + return 0; +} +``` +This function actually compares the content stored in the storage space of the key, comparing byte by byte. + +So how is the formal parameter `key` converted into the address and size of the key? +This is related to the `trans` parameter in the `map_create` method. This is the handle of the conversion function that converts the formal parameter `key` into the address and size of the key. These functions are all defined in the `map_cfg.c` file. So if you want to add supported key types, you need to add them in the `map_cfg` file. + +Let's look at the specific process of the finding method. +```c +#define map_find(map, key) map_find((map), (key)) +int map_find(map_t map,...); +``` +Here, the second parameter of `map_find` is defined as a variable parameter to support passing in different data types, such as `int`, `float`, `string`, etc. When receiving the parameters, different types are received in the `trans` function. +```c +int map_find(map_t map,...) +{ + va_list args; + MKEY key; + if (!map) return 0; + va_start(args, map); + key = *(MKEY *)(map->trans(map, args)); + va_end(args); + return map_find_node(map, key)==map->nil?0:1; +} +``` +Inside the `map_find` function, the variable parameters are passed to the `trans` function, and then the corresponding key can be returned. +The rest is left to the `key_compare` function for binary search and comparison. + +### Adding Supported Key Types + +1. Add the `trans` function in the `map_cfg.c` file. The naming should follow the fixed format `void* map_key_trans__xxx(void* map, va_list args)`, where `xxx` is the desired naming type. +2. Implement the `trans` function internally: +```c +int key; // Define the key according to the type +key = va_arg(args, int); // The key receives the variable parameter of the corresponding type +return map_trans_key(map, &key, sizeof(int)); // Uniformly return the address and length of the key. For strings, return the length of the string. +``` +3. Declare `void* map_key_trans__xxx(void* map, va_list args);` in the `map_cfg.h` file. +4. Define the `type` of the key in the `map_cfg.h` file. `xxx` is the same as the key type above. It can be defined as `MAP_KEY_TYPE_ENTITY` or `MAP_KEY_TYPE_POINTER` later. For strings which are pointers themselves, they are defined as pointers, and for `int`, it's defined as an entity. +```c +#define MAP_KEY_TYPE__xxx MAP_KEY_TYPE_ENTITY // MAP_KEY_TYPE_POINTER // +``` +5. After adding these, you can use `map(ktype, vtype)` to create a map. diff --git a/doc/oscp.en.md b/doc/oscp.en.md new file mode 100644 index 0000000..bc800a8 --- /dev/null +++ b/doc/oscp.en.md @@ -0,0 +1,77 @@ +### Introduction +`oscp` is related to a simple analog oscilloscope module in the C language. It defines some constants related to the functions of the oscilloscope and provides several key function interfaces. These interfaces can be used to set the address of the value to be monitored, set the time scale, and there is also a display processing task function that needs to be called regularly. It's convenient for developers to implement functions related to the analog oscilloscope in C language projects, such as monitoring specific data and presenting it according to the set time scale. + +### Interfaces + +#### Functions +```c +void oscp_handle(void); +int oscp_set_monitor(int *m); +int oscp_set_scale(int s); +``` + +**`oscp_handle` Function**: +This function is responsible for handling the display processing tasks related to the analog oscilloscope. + +**`oscp_set_monitor` Function**: +This function is used to set the address of the value to be monitored. + +**`oscp_set_scale` Function**: +This function is used to set the time scale. + +### Usage Example +```c +int level = 0; + +static unsigned int get_msec(void) +{ + struct timeval mstime; + unsigned int ms = 0; + gettimeofday(&mstime, NULL); + ms = mstime.tv_sec * 1000 + mstime.tv_usec / 1000; + return ms; +} + +static void task1(void) +{ + oscp_handle(); +} + +static void task2(void) +{ + static int count = 0; + + count++; + if (count > 628) count = 0; + + level = RESOLUTION / 2 * sin((double)count / 10) + RESOLUTION / 2; +} + +static void test_base(void) +{ +#if defined(TEST_TARGET_oscp) + uint32_t count = 0; + + oscp_set_scale(O_SCALE_50MS); + oscp_set_monitor(&level); + + while (1) + { + count++; + if (count >= 25200000) count = 0; + + + if (count % 5 == 0) task1(); + if (count % 50 == 0) task2(); + + usleep(1000); + } +#else + printf("create task %d\r\n", task_create(5, task1)); + printf("create task %d\r\n", task_create(50, task2)); + + oscp_set_scale(O_SCALE_50MS); + oscp_set_monitor(&level); +#endif +} +``` diff --git a/doc/oscp.md b/doc/oscp.md new file mode 100644 index 0000000..c97b06f --- /dev/null +++ b/doc/oscp.md @@ -0,0 +1,71 @@ +## 介绍 + +`oscp`是一个用于C语言的简单模拟示波器模块相关,它定义了一些与示波器功能相关的常量以及提供了几个关键的函数接口,可用于设置要监测的值的地址、设置时间尺度,还有一个需定期调用的显示处理任务函数,方便开发者在C语言项目中实现模拟示波器相关的功能,比如对特定数据的监测及按设定时间尺度展示等操作。 + +## 接口 + +### 函数 + +```c +void oscp_handle(void); +int oscp_set_monitor(int *m); +int oscp_set_scale(int s); +``` + +使用例子: + +```c +int level = 0; + +static unsigned int get_msec(void) +{ + struct timeval mstime; + unsigned int ms = 0; + gettimeofday(&mstime, NULL); + ms = mstime.tv_sec * 1000 + mstime.tv_usec / 1000; + return ms; +} + +static void task1(void) +{ + oscp_handle(); +} + +static void task2(void) +{ + static int count = 0; + + count++; + if (count > 628) count = 0; + + level = RESOLUTION / 2 * sin((double)count / 10) + RESOLUTION / 2; +} + +static void test_base(void) +{ +#if defined(TEST_TARGET_oscp) + uint32_t count = 0; + + oscp_set_scale(O_SCALE_50MS); + oscp_set_monitor(&level); + + while (1) + { + count++; + if (count >= 25200000) count = 0; + + + if (count % 5 == 0) task1(); + if (count % 50 == 0) task2(); + + usleep(1000); + } +#else + printf("create task %d\r\n", task_create(5, task1)); + printf("create task %d\r\n", task_create(50, task2)); + + oscp_set_scale(O_SCALE_50MS); + oscp_set_monitor(&level); +#endif +} +``` diff --git a/doc/pid.en.md b/doc/pid.en.md new file mode 100644 index 0000000..ef044ec --- /dev/null +++ b/doc/pid.en.md @@ -0,0 +1,79 @@ +### Introduction +The PID algorithm, which stands for Proportional-Integral-Derivative algorithm, is a feedback control algorithm widely used in industrial control systems. It compares the error between the expected value and the actual value, and then adjusts the output of the controller based on the proportion (P), integral (I), and derivative (D) of the error, so as to make the output of the system closer to the expected value. + +The basic form of the PID algorithm is as follows: + +\[u(t) = K_p \times e(t) + K_i \times \int e(t)dt + K_d \times \frac{de(t)}{dt}\] + +Here, \(u(t)\) is the output of the controller, \(e(t)\) is the error between the expected value and the actual value, and \(K_p\), \(K_i\), and \(K_d\) are the proportional, integral, and derivative coefficients respectively. These coefficients need to be adjusted according to the specific system to achieve the best control effect. + +The advantages of the PID algorithm lie in its simplicity and ease of use, wide range of applicability, and the ability to adapt to different system characteristics by adjusting parameters. However, for some complex nonlinear systems, the PID algorithm may not be able to achieve the ideal control effect. + +### Interface + +```c +void pid_init(PIDC *pid); +``` +Before performing the PID algorithm calculation, it is necessary to initialize the PID algorithm control structure. + +```c +double pid_compute(PIDC *pid); +``` +This function is used to perform the actual PID algorithm calculation after the relevant structure has been initialized. + +### Test +```c +static void test_sim(void) +{ + PIDC pid; + + pid_init(&pid); + + pid.kp = 2; + pid.ki = 0.1; + pid.kd = 0.05; + + pid.point = 100.0; + + /* Simulate the process, assuming that the value of each cycle process increases by 5 */ + for (int i = 0; i < 20; i++) + { + pid.process += 5; + pid_compute(&pid); + + printf("Process Value: %.2f, PID Output: %.2f\n", pid.process, pid.output); + } +} +``` + +In this test function: +1. First, a `PIDC` structure variable `pid` is defined. +2. Then, the `pid` structure is initialized using the `pid_init` function. +3. After that, specific values are assigned to the coefficients `kp`, `ki`, `kd` and the target `point` of the `PID` algorithm within the `pid` structure. +4. In the loop, it simulates a process where the `process` value increases by 5 in each cycle. After each update of the `process` value, the `pid_compute` function is called to calculate the `PID` output based on the current state, and then the current `Process Value` and `PID Output` are printed out. + +**Results**: +``` +Process Value: 5.00, PID Output: 204.25 +Process Value: 10.00, PID Output: 198.25 +Process Value: 15.00, PID Output: 196.75 +Process Value: 20.00, PID Output: 194.75 +Process Value: 25.00, PID Output: 192.25 +Process Value: 30.00, PID Output: 189.25 +Process Value: 35.00, PID Output: 185.75 +Process Value: 40.00, PID Output: 181.75 +Process Value: 45.00, PID Output: 177.25 +Process Value: 50.00, PID Output: 172.25 +Process Value: 55.00, PID Output: 166.75 +Process Value: 60.00, PID Output: 160.75 +Process Value: 65.00, PID Output: 154.25 +Process Value: 70.00, PID Output: 147.25 +Process Value: 75.00, PID Output: 139.75 +Process Value: 80.00, PID Output: 131.75 +Process Value: 85.00, PID Output: 123.25 +Process Value: 90.00, PID Output: 114.25 +Process Value: 95.00, PID Output: 104.75 +Process Value: 100.00, PID Output: 94.75 +``` + +From the results, we can observe how the `PID` output changes as the `Process Value` gradually approaches the target `point` value of 100.0 under the influence of the set `PID` coefficients. diff --git a/doc/queue.en.md b/doc/queue.en.md new file mode 100644 index 0000000..c221901 --- /dev/null +++ b/doc/queue.en.md @@ -0,0 +1,179 @@ +### Introduction + +A queue is a special data structure with the First In First Out (FIFO) characteristic. Generally, it has only one entrance and one exit. Data enters from the rear of the queue and exits from the front. The operations of adding data to the queue is called "push", and removing data from the queue is called "pop". + +- **Capacity**: Capacity refers to the maximum number of queue items that can be stored during use. For example, for a queue with a capacity of 10, it can store at most 10 queue items. Once it's full, no more data can be pushed into it. The queue storage in varch has continuous addresses and is a queue with a limited capacity. + +- **Access Mechanism**: Generally, a queue only has two ways of operation, which are enqueueing (push) and dequeueing (pop). It can be quickly traversed and accessed based on continuous addresses. + +### Interface + +#### Creation and Deletion of queue Objects +```c +queue_t queue_create(int dsize, int capacity, void *base); +void queue_delete(queue_t queue); +#define queue(type, capacity) // For more convenient use, a macro definition is wrapped around queue_create +#define _queue(queue) // A macro definition is wrapped around queue_delete, and the queue is set to NULL after deletion +``` +Here, **queue_t** is the structure of the queue. The creation method will return a queue object, and it will return NULL if the creation fails. Among the parameters, `dsize` is used to pass in the size of the data, `capacity` is used to pass in the queue capacity, and `*base` is used to pass in the address of the buffer (it can be omitted, and if omitted, space with the size of `capacity` will be automatically allocated to store queue data). The deletion method is used to delete the passed-in queue object. The creation method and the deletion method should be used in pairs. Once created, the queue object should be deleted when it's no longer in use. +```c +void test(void) +{ + queue_t queue = queue(int, 10); // Define and create a queue of the int type with a capacity of 10 + _queue(queue); // Use them in pairs and delete it after use +} +``` + +#### Enqueueing and Dequeueing of queue +```c +int queue_push(queue_t queue, void* data); +int queue_pop(queue_t queue, void* data); +``` +These two methods can conveniently add data to the queue and remove data from the queue. For the `push` method, the `data` parameter should pass in the address of the data to be enqueued. For the `pop` method, the `data` parameter should pass in the address of the data to receive the dequeued data. In both methods, `data` can also be passed as NULL, which just serves as a placeholder. They return 1 if the operation is successful and 0 if it fails. +```c +void test(void) +{ + queue_t queue = queue(int, 10); + int i = 0; + + for (i = 0; i < queue_capacity(queue); i++) + { + queue_push(queue, &i); + } + queue_pop(queue, NULL); + queue_pop(queue, NULL); + + _queue(queue); // Use them in pairs and delete it after use +} +``` + +#### Size, Capacity, and Data Size of queue +```c +int queue_size(queue_t queue); +int queue_capacity(queue_t queue); +int queue_dsize(queue_t queue); +``` +The `capacity` of the queue is the capacity specified during creation, which indicates how many queue elements it can store. The `size` is the number of elements currently in the queue. The `dsize` is the size of the data passed in during creation. For example, for `int` data, the `dsize` is `sizeof(int)`. +```c +void test(void) +{ + queue_t queue = queue(int, 10); + int i = 0; + + for (i = 0; i < queue_capacity(queue); i++) + { + queue_push(queue, &i); + } + queue_pop(queue, NULL); + queue_pop(queue, NULL); + printf("queue capacity=%d, size=%d, dsize=%d\r\n", queue_capacity(queue), queue_size(queue), queue_dsize(queue)); + + _queue(queue); +} +``` +**Results**: +``` +queue capacity=10, size=8, dsize=4 +``` + +#### Reading and Writing of queue Data +```c +void* queue_data(queue_t queue, int index); +#define queue_at(queue, type, i) +``` +The `queue_data` method is used to obtain the address of the data according to the index and returns the address of the specified data. NULL indicates failure. The `queue_at` method adds the data type on the basis of `queue_data`. +```c +void test(void) +{ + queue_t queue = queue(int, 10); + int i = 0; + + for (i = 0; i < queue_capacity(queue); i++) + { + queue_push(queue, &i); + } + queue_pop(queue, NULL); + queue_pop(queue, NULL); + for (i = 0; i < queue_size(queue); i++) + { + printf("queue[%d] = %d\r\n", i, queue_at(queue, int, i)); + } + + _queue(queue); +} +``` +**Results**: +``` +queue[0] = 2 +queue[1] = 3 +queue[2] = 4 +queue[3] = 5 +queue[4] = 6 +queue[5] = 7 +queue[6] = 8 +queue[7] = 9 +``` + +#### Queue Data Storage Index +```c +int queue_index(queue_t queue, int index); +``` +The queue storage structure is a circular queue, which means that the continuous address storage space is connected end to end to form a circle, and the queue data enters and exits within this circle. Therefore, the index of the queue is not directly the index of the buffer. The `queue_index` method is used to map the index of the queue to the index of the buffer. It returns -1 if the mapping fails. Generally, this method is not used frequently. It's mainly used when the `base` address is passed in during the `queue_create` method, and then the `queue_index` method is used on the `base` address to obtain queue data. + +#### Empty Queue and Full Queue +```c +int queue_empty(queue_t queue); +int queue_full(queue_t queue); +``` +These two methods are actually related to the size of the queue. If the size is equal to 0, the queue is empty. If the size is equal to the capacity, the queue is full. + +### Source Code Analysis + +#### queue Structure + +All the structures of the queue container are implicit, which means that the members of the structures can't be accessed directly. This way ensures the independence and security of the module and prevents external calls from modifying the members of the structures, which could otherwise damage the storage structure of the queue. So the queue parser only leaves the single declaration of the queue in the header file, and the definitions of the structures are placed in the source file. Only the methods provided by the queue container can be used to operate on queue objects. +The declaration of the queue type: +```c +typedef struct QUEUE *queue_t; +``` +When using it, just use `queue_t`. +```c +typedef struct QUEUE +{ + void* base; /* base address of data */ + int cst; /* base const */ + int dsize; /* size of queue data */ + int capacity; /* capacity of queue */ + int size; /* size of queue */ + int head; /* index of queue head */ + int tail; /* index of queue tail */ +} QUEUE; +``` +The `QUEUE` structure contains 7 members: `base` (the base address of the data buffer of the queue structure), `cst` (indicating whether the space of `base` is passed in during the `create` method), `size` (the size of the queue, that is, the length of the queue), `dsize` (the size of each data), `capacity` (the capacity of the queue), and `head` and `tail` which are the indexes pointed to by the head and tail of the circular buffer respectively. + +The main problem that the queue container needs to solve is the issue of the circular queue and the First In First Out characteristic of data. Other operations like creation and deletion are mainly for basic initialization such as space initialization. +```c +#define at(i) (((unsigned char *)(queue->base))+(i)*(queue->dsize)) /* address of void array */ +int queue_push(queue_t queue, void* data) +{ + if (!queue) return 0; + if (queue_full(queue)) return 0; // Check if the queue is full before enqueueing + if (data) memcpy(at(queue->tail), data, queue->dsize); // If the data address is specified, copy the data at the address to the address pointed to by the tail + queue->tail++; // Increment the tail by 1, indicating that data is pushed at the tail + queue->tail %= queue->capacity; // Take the remainder of tail divided by capacity to ensure that tail doesn't exceed capacity and form a circular structure + queue->size++; // Increase the size by 1 + return 1; +} +int queue_pop(queue_t queue, void* data) +{ + if (!queue) return 0; + if (queue_empty(queue)) return 0; // Check if the queue is empty before dequeueing + if (data) memcpy(data, at(queue->head), queue->dsize); // If the data address is specified, copy the data at the head to the address pointed to by data + queue->head++; // Increment the head by 1, indicating that data is popped from the head + queue->head %= queue->capacity; // Take the remainder of head divided by capacity to ensure that head doesn't exceed capacity and form a circular structure + queue->size--; // Decrease the size by 1 + return 1; +} +``` + +These two functions `queue_push` and `queue_pop` implement the key operations of enqueueing and dequeueing in the circular queue, ensuring the correct operation of the FIFO mechanism within the limited capacity and circular structure. diff --git a/doc/ramt.en.md b/doc/ramt.en.md new file mode 100644 index 0000000..f2b6171 --- /dev/null +++ b/doc/ramt.en.md @@ -0,0 +1,56 @@ +### Introduction +`ramt` is a simple memory (RAM) testing module for the C language. It defines multiple memory testing modes and structures representing memory. Meanwhile, it provides a series of function interfaces such as initializing the memory testing structure, starting the test, stopping the test, obtaining the historical test results and the latest test results, etc. It's convenient for developers to conduct tests on simulated memory in different modes and for different durations in C language projects and view the corresponding test situations. + +### Interfaces + +#### Functions + +##### `ramt_init` Function +```c +int ramt_init(RAMT *ramt); +``` +**Function Description**: It is used to initialize the `RAMT` structure, preparing for the subsequent memory testing-related processes. For example, it may involve basic setup work such as memory space allocation and initialization of related variables. +**Parameter Introduction**: +- `ramt`: A pointer to the `RAMT` structure. This structure is used to describe the information related to the memory to be tested. By passing its pointer, the function can perform initialization operations on the members inside the structure. + +##### `ramt_start` Function +```c +int ramt_start(RAMT *ramt, uint32_t mode, uint32_t duration); +``` +**Function Description**: It starts the memory testing process and executes the corresponding memory testing tasks according to the specified testing mode and duration. Different testing modes correspond to different testing strategies and operations, such as read/write verification, boundary checking, etc. The duration determines the time range for the test to run. +**Parameter Introduction**: +- `ramt`: A pointer to the `RAMT` structure, which provides the basic information of the memory to be tested. The function determines key information such as the target memory area for testing based on this structure. +- `mode`: Represents the memory testing mode, and its value is taken from predefined testing mode macros (such as `RAMT_MODE_NORMAL`, `RAMT_MODE_BOUNDARY`, etc.). Different macros correspond to different types of testing logic. For example, `RAMT_MODE_NORMAL` represents the normal read/write test, which verifies whether the memory functions properly by performing read and write operations on it. +- `duration`: Specifies the duration of the memory test. The specific meaning of the unit, etc. depends on the actual setting. When the value is `0xFFFFFFFF`, it means continuous and uninterrupted operation. Developers can set an appropriate duration according to their needs to control the test cycle. + +##### `ramt_stop` Function +```c +int ramt_stop(RAMT *ramt); +``` +**Function Description**: It is used to stop the ongoing memory testing operation, ensuring that relevant test resources can be released reasonably and the test state ends correctly, avoiding abnormal situations such as resource occupation. +**Parameter Introduction**: +- `ramt`: A pointer to the `RAMT` structure. Based on the structure pointed to by this pointer, it determines which memory testing task to stop, finds the corresponding test resources and states, and performs the corresponding stop processing. + +##### `ramt_result` Function +```c +uint32_t ramt_result(RAMT *ramt); +``` +**Function Description**: It is used to obtain the historical result information of the memory test. By querying relevant records or status data, it returns the overall test situation accumulated during the previous memory testing process, facilitating developers to understand the performance of the memory in past tests. +**Parameter Introduction**: +- `ramt`: A pointer to the `RAMT` structure. Based on the memory testing task associated with this structure, it searches for and extracts the corresponding historical test result information. Different memory testing tasks have their own independent result records, and this pointer is used to locate the corresponding record position. + +##### `ramt_result_latest` Function +```c +uint32_t ramt_result_latest(RAMT *ramt); +``` +**Function Description**: It obtains the latest result of the memory test, that is, it obtains the result information generated by the most recent memory test closest to the current time. It helps developers quickly know the state of the memory in the most recent test. +**Parameter Introduction**: +- `ramt`: A pointer to the `RAMT` structure. With the help of the structure pointed to by this pointer, it locates and obtains the latest result data of the corresponding memory test, ensuring that the latest situation of the correct target memory testing task is returned. + +##### `ramt_task` Function +```c +void ramt_task(RAMT *ramt); +``` +**Function Description**: It periodically executes the operations related to the memory testing task according to the set testing mode and related configurations. In scenarios where continuous memory testing monitoring is required or tests are conducted at certain intervals, this function can be called to achieve the corresponding functions. +**Parameter Introduction**: +- `ramt`: A pointer to the `RAMT` structure. The function determines which memory to test and in which mode to execute the testing task through this pointer. It is associated with various key configuration information of the memory test and is the basis for executing specific testing operations. diff --git a/doc/ramt.md b/doc/ramt.md new file mode 100644 index 0000000..612f21f --- /dev/null +++ b/doc/ramt.md @@ -0,0 +1,56 @@ +## 介绍 +`ramt`是一个用于C语言的简单内存(RAM)测试模块。它定义了多种内存测试模式以及代表内存的结构体,同时提供了诸如初始化内存测试结构、启动测试、停止测试、获取测试历史结果与最新结果等一系列函数接口,方便开发者在C语言项目中对模拟内存进行不同模式、不同时长的测试操作,并能查看相应的测试情况。 + +## 接口 + +### 函数 + +#### `ramt_init`函数 +```c +int ramt_init(RAMT *ramt); +``` +**函数说明**:用于对`RAMT`结构体进行初始化操作,为后续的内存测试相关流程做准备,比如可能涉及到内存空间的分配、相关变量的初始化等基础设置工作。 +**参数介绍**: +- `ramt`:指向`RAMT`结构体的指针,该结构体用于描述要进行测试的内存相关信息,通过传入其指针,函数可以对该结构体内部成员进行初始化相关的操作。 + +#### `ramt_start`函数 +```c +int ramt_start(RAMT *ramt, uint32_t mode, uint32_t duration); +``` +**函数说明**:启动内存测试流程,按照指定的测试模式以及持续时间来执行相应的内存测试任务。不同的测试模式会对应不同的测试策略和操作,比如读/写验证、边界检查等,持续时间则决定了测试运行的时长范围。 +**参数介绍**: +- `ramt`:指向`RAMT`结构体的指针,提供要进行测试的内存的基础信息,函数依据此结构体来确定测试的目标内存区域等关键信息。 +- `mode`:表示内存测试的模式,其取值为预定义的测试模式宏(如`RAMT_MODE_NORMAL`、`RAMT_MODE_BOUNDARY`等),不同的宏对应不同类型的测试逻辑,例如`RAMT_MODE_NORMAL`代表正常读/写测试,通过对内存进行读写操作来验证其功能是否正常。 +- `duration`:指定内存测试持续的时长,单位等具体含义依实际设定而定,取值为`0xFFFFFFFF`时表示持续不间断运行,开发者可以根据需求设定合适的时长来控制测试周期。 + +#### `ramt_stop`函数 +```c +int ramt_stop(RAMT *ramt); +``` +**函数说明**:用于停止正在进行的内存测试操作,确保相关测试资源能够合理释放,测试状态正确结束,避免出现资源占用等异常情况。 +**参数介绍**: +- `ramt`:指向`RAMT`结构体的指针,根据此指针所指向的结构体来确定要停止的是哪个内存测试任务,找到对应的测试资源和状态进行相应的停止处理。 + +#### `ramt_result`函数 +```c +uint32_t ramt_result(RAMT *ramt); +``` +**函数说明**:用于获取内存测试的历史结果信息,通过查询相关的记录或者状态数据,返回之前进行的内存测试过程中积累的整体测试情况,方便开发者了解内存在过往测试中的表现。 +**参数介绍**: +- `ramt`:指向`RAMT`结构体的指针,基于该结构体所关联的内存测试任务来查找和提取对应的历史测试结果信息,不同的内存测试任务有其各自独立的结果记录,通过此指针定位到相应的记录位置。 + +#### `ramt_result_latest`函数 +```c +uint32_t ramt_result_latest(RAMT *ramt); +``` +**函数说明**:获取内存测试的最新结果,也就是获取距离当前最近一次进行内存测试所产生的结果信息,有助于开发者快速知晓最近一次测试时内存的状态情况。 +**参数介绍**: +- `ramt`:指向`RAMT`结构体的指针,借助该指针指向的结构体去定位和获取与之对应的内存测试最新结果数据,确保返回的是正确目标内存测试任务的最新情况。 + +#### `ramt_task`函数 +```c +void ramt_task(RAMT *ramt); +``` +**函数说明**:依据设定好的测试模式以及相关配置,周期性地执行内存测试任务相关操作,在需要持续进行内存测试监控或者按一定间隔进行测试的场景下,可以通过调用此函数来实现相应功能。 +**参数介绍**: +- `ramt`:指向`RAMT`结构体的指针,函数通过该指针确定具体要对哪块内存按照何种模式等设定来执行测试任务,其关联着内存测试的各项关键配置信息,是执行具体测试操作的依据。 diff --git a/doc/romt.en.md b/doc/romt.en.md new file mode 100644 index 0000000..5982692 --- /dev/null +++ b/doc/romt.en.md @@ -0,0 +1,56 @@ +### Introduction +`romt` is a simple read-only memory (ROM) testing module for the C language. It defines multiple ROM testing modes, related callback function types, and structures representing ROM. Moreover, it provides a series of function interfaces such as initializing the ROM testing structure, starting the test, stopping the test, obtaining the historical test results and the latest test results, and executing the test task. It's convenient for developers to conduct tests on simulated ROM in different modes and for different durations in C language projects and view the corresponding test situations. + +### Interfaces + +#### Functions + +##### `romt_init` Function +```c +int romt_init(ROMT *romt); +``` +**Function Description**: It is mainly used to initialize the `ROMT` structure. It checks the validity of the passed-in `ROMT` structure and its parameters to ensure that key elements in the structure, such as the size of the ROM, pointers to read and write functions, etc., are legal and valid. Meanwhile, it initializes the private data fields in the structure to zero, preparing for subsequent ROM testing-related operations. +**Parameter Introduction**: +- `romt`: A pointer to the `ROMT` structure. This structure carries key information related to the ROM to be tested, such as the starting address of the ROM (reflected by the `base` member), the size (`size` member), and corresponding pointers to read and write functions. The function uses this pointer to perform initialization-related processing on each member inside the structure. + +##### `romt_start` Function +```c +int romt_start(ROMT *romt, uint32_t mode, uint32_t duration); +``` +**Function Description**: It is responsible for starting the ROM testing operations. It configures and initiates the corresponding test process according to the passed-in specific testing mode and duration. Before starting, it first checks the validity of the input parameters and confirms that the ROM has been correctly initialized to ensure that the test operations can be carried out normally. +**Parameter Introduction**: +- `romt`: A pointer to the `ROMT` structure, which is used to locate the information related to the ROM to be tested, including its read and write functions, etc., providing a basic basis for conducting tests in accordance with the specified mode and duration later. +- `mode`: Represents the operation mode of the ROM test. Its value is taken from predefined testing mode macros (such as `ROMT_MODE_READ`, `ROMT_MODE_WRITE`, etc.). Different macros correspond to different types of testing logic. For example, `ROMT_MODE_READ` represents the test mode of reading data from the ROM, and `ROMT_MODE_WRITE` corresponds to the test mode of writing data to the ROM (it should be noted that the actual ROM is usually read-only, and the write test may be conducted for functional verification in a simulated scenario). +- `duration`: Specifies the duration of the ROM test. Based on this parameter, the time range for the test to run is controlled. The specific meaning depends on the actual setting. Different duration settings can meet different testing requirements, such as short-term quick tests or long-term comprehensive tests. + +##### `romt_stop` Function +```c +int romt_stop(ROMT *romt); +``` +**Function Description**: It is used to stop the ongoing ROM testing operation. During the stopping process, relevant configurations such as the test mode and duration will be reset to zero. Meanwhile, it ensures that the ROM has been correctly initialized before to ensure that the entire stopping operation can be completed smoothly, avoiding problems such as abnormal test states remaining and resource occupation. +**Parameter Introduction**: +- `romt`: A pointer to the `ROMT` structure. Based on the structure pointed to by this pointer, it determines which ROM testing task to stop, and then performs reasonable stop and reset processing on the corresponding test resources, states, etc. + +##### `romt_result` Function +```c +uint32_t romt_result(ROMT *romt); +``` +**Function Description**: It aims to obtain the historical result information of the ROM test. It first checks the validity of the passed-in `ROMT` structure. If the check passes, it extracts and returns the overall test situation accumulated during the previous ROM testing process from the private data of the structure, facilitating developers to comprehensively understand the comprehensive performance of the ROM in past multiple tests. If the check fails, it returns `0xFFFFFFFF` to indicate an error and the inability to obtain valid historical results. +**Parameter Introduction**: +- `romt`: A pointer to the `ROMT` structure. Based on the structure pointed to by this pointer, it searches for and obtains the corresponding historical test result data. Since different ROM testing tasks have their own independent result record storage locations, this pointer can accurately locate where the corresponding records are. + +##### `romt_result_latest` Function +```c +uint32_t romt_result_latest(ROMT *romt); +``` +**Function Description**: It is used to obtain the latest result of the ROM test, that is, the result information generated by the most recent ROM test closest to the current time. Similarly, it first verifies the validity of the `ROMT` structure. If it is valid, it extracts and returns the latest result from its private data, enabling developers to quickly know the state of the ROM in the most recent test. If the structure validity check fails, it returns `0xFFFFFFFF` to indicate an error in obtaining the result. +**Parameter Introduction**: +- `romt`: A pointer to the `ROMT` structure. With the help of the structure pointed to by this pointer, it locates and obtains the latest result data of the corresponding ROM test, ensuring that the latest situation of the correct target ROM testing task is returned. + +##### `romt_task` Function +```c +void romt_task(ROMT *romt); +``` +**Function Description**: It executes the operations related to the ROM testing task according to the configured testing mode. First, it checks the current testing mode. Then, within the allowed duration, it performs corresponding tests (such as specific test contents corresponding to different modes like read, write, normal, boundary, random, pattern generation, etc.) on the ROM represented by the `ROMT` structure. During the execution process, it updates the latest result and accumulates the relevant test situations into the historical result (stored in the private data of the structure) for viewing when obtaining results later. +**Parameter Introduction**: +- `romt`: A pointer to the `ROMT` structure. The function determines which ROM to test and in which mode to execute the testing task through this pointer. It is associated with various key configuration information of the ROM test and is the core basis for executing specific testing operations. Different ROMs correspond to structures containing different configuration situations, and through this pointer, they can be accurately distinguished and targeted tests can be conducted. diff --git a/doc/romt.md b/doc/romt.md new file mode 100644 index 0000000..c72b607 --- /dev/null +++ b/doc/romt.md @@ -0,0 +1,56 @@ +## 介绍 +`romt`是一个用于C语言的简单只读存储器(ROM)测试模块。它定义了多种ROM测试模式、相关的回调函数类型以及代表ROM的结构体,并且提供了诸如初始化ROM测试结构、启动测试、停止测试、获取测试历史结果与最新结果以及执行测试任务等一系列函数接口,方便开发者在C语言项目中对模拟ROM进行不同模式、不同时长的测试操作,并能查看相应的测试情况。 + +## 接口 + +### 函数 + +#### `romt_init`函数 +```c +int romt_init(ROMT *romt); +``` +**函数说明**:主要用于对`ROMT`结构体进行初始化操作。会检查传入的`ROMT`结构体及其参数的有效性,确保结构体中的ROM大小、读函数指针、写函数指针等关键元素合法有效,同时会将结构体中的私有数据字段初始化为零,为后续的ROM测试相关操作做好准备。 +**参数介绍**: +- `romt`:指向`ROMT`结构体的指针,该结构体承载了要进行测试的ROM的相关关键信息,像ROM的起始地址(通过`base`成员体现)、大小(`size`成员)以及对应的读写函数指针等,函数通过这个指针来对结构体内部各成员进行初始化相关的处理。 + +#### `romt_start`函数 +```c +int romt_start(ROMT *romt, uint32_t mode, uint32_t duration); +``` +**函数说明**:负责启动ROM测试相关操作,依据传入的特定测试模式和持续时间来配置并开启相应的测试流程。在启动前会先对输入的参数进行有效性检查,并且确认ROM已经过正确初始化,以此保障测试操作能正常开展。 +**参数介绍**: +- `romt`:指向`ROMT`结构体的指针,用于定位具体要进行测试操作的ROM相关信息,包括其读写函数等,为后续按照指定模式和时长开展测试提供基础依据。 +- `mode`:表示ROM测试的操作模式,其取值为预定义的测试模式宏(如`ROMT_MODE_READ`、`ROMT_MODE_WRITE`等),不同的宏对应不同类型的测试逻辑,例如`ROMT_MODE_READ`代表从ROM读取数据的测试模式,`ROMT_MODE_WRITE`则对应向ROM写入数据的测试模式(需注意实际ROM通常为只读,写入测试可能是模拟场景下进行功能性验证等)。 +- `duration`:指定ROM测试持续的时长,依据此参数来控制测试运行的时间范围,具体含义依实际设定而定,不同的时长设定可以满足不同的测试需求,比如短时间的快速测试或者长时间的全面测试等。 + +#### `romt_stop`函数 +```c +int romt_stop(ROMT *romt); +``` +**函数说明**:用于停止正在进行的ROM测试操作,在停止过程中会将测试模式和持续时间等相关配置重置为零,同时确保ROM之前是经过正确初始化的,保证整个停止操作能顺利完成,避免出现异常的测试状态遗留以及资源占用等问题。 +**参数介绍**: +- `romt`:指向`ROMT`结构体的指针,根据该指针所指向的结构体来确定要停止的是哪一个ROM测试任务,进而对相应的测试资源、状态等进行合理的停止和重置处理。 + +#### `romt_result`函数 +```c +uint32_t romt_result(ROMT *romt); +``` +**函数说明**:旨在获取ROM测试的历史结果信息,会先对传入的`ROMT`结构体的有效性进行检查,若检查通过,则从结构体的私有数据中提取并返回之前进行的ROM测试过程中积累的整体测试情况,方便开发者全面了解ROM在过往多次测试中的综合表现;若检查不通过,则返回`0xFFFFFFFF`来表示出现错误,无法获取有效历史结果。 +**参数介绍**: +- `romt`:指向`ROMT`结构体的指针,基于这个指针所指向的结构体去查找和获取对应的历史测试结果数据,由于不同的ROM测试任务有其各自独立的结果记录存储位置,通过此指针能准确地定位到相应记录所在之处。 + +#### `romt_result_latest`函数 +```c +uint32_t romt_result_latest(ROMT *romt); +``` +**函数说明**:用于获取ROM测试的最新结果,也就是获取距离当前最近一次进行ROM测试所产生的结果信息。同样会先验证`ROMT`结构体的有效性,若有效则从其私有数据中提取并返回最新结果,便于开发者快速知晓ROM最近一次测试时的状态情况;若结构体有效性检查失败,则返回`0xFFFFFFFF`来表示获取结果出现错误。 +**参数介绍**: +- `romt`:指向`ROMT`结构体的指针,借助该指针指向的结构体去定位和获取与之对应的ROM测试最新结果数据,以此确保返回的是正确目标ROM测试任务的最近一次测试情况。 + +#### `romt_task`函数 +```c +void romt_task(ROMT *romt); +``` +**函数说明**:会依据已经配置好的测试模式来执行ROM测试任务相关操作。首先会检查当前的测试模式,然后在持续时间允许的情况下,针对`ROMT`结构体所代表的ROM执行相应的测试(如读、写、正常、边界、随机、模式生成等不同模式对应的具体测试内容),并且在执行过程中会更新最新结果以及将相关测试情况累加到历史结果中(存储在结构体的私有数据里),用于后续获取结果时查看。 +**参数介绍**: +- `romt`:指向`ROMT`结构体的指针,函数通过该指针确定具体要对哪块ROM按照何种模式等设定来执行测试任务,其关联着ROM测试的各项关键配置信息,是执行具体测试操作的核心依据,不同的ROM对应的结构体包含不同的配置情况,通过此指针可准确区分并进行针对性测试。 diff --git a/doc/sList.en.md b/doc/sList.en.md new file mode 100644 index 0000000..dc48786 --- /dev/null +++ b/doc/sList.en.md @@ -0,0 +1,486 @@ +### Introduction +A linked list is a data structure where data elements are logically consecutive but can be physically stored in a dispersed manner. It can link multiple data blocks of the same type into a complete sequence through pointers and plays an important role in the implementation of data structures. +The sList module is a general-purpose single-linked list module. It stores a linear data collection through a series of interconnected nodes. Each node contains a data field and a pointer field that points to the next node. + +### Interface + +#### Creating sList +```c +sList *sList_create(void); +``` +In sList, sList is both a linked list and a node (because a node can be regarded as a linked list with a length of 1). So this method creates an empty linked list with a length of 1 (that is, creates an empty node). + +#### Deleting sList +```c +void sList_delete(sList *list); +``` +This method will delete the linked list (including all its nodes). + +#### Setting and Getting sList Node Contents +```c +int sList_set(sList *list, void* data, int size); +int sList_get(sList *list, void* data, int size); +``` +After a node is created, its data field is initially empty. The `sList_set` method is needed to set the content of its data field, and the `sList_get` method can be used to obtain the content of the node's data field. The `sList_set` method will overwrite the original data, and it can also be used to delete the data content of the sList node by specifying the `size` as **0**. +```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); +} +``` +**Results**: +``` +sList_create Success! +list->data 3 +list->data Hello sList +get data Hello sList +``` + +The `sList_ref` in the example is for data reference, and its specific usage will be introduced later. + +#### sList Inserting Data +```c +sList *sList_insert(sList **listRef, int index, void *data, int size); +``` +The data insertion method is more convenient to use as it saves the steps of creating nodes and setting data (even for the head of the linked list, the creation can be omitted and completed internally by this method), and it can flexibly insert specified data into specified positions. +```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); +} +``` +**Results**: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +``` + +The `sList_forEach` in the example is for traversing the linked list, and its specific usage will be introduced later. When the passed-in sList reference is NULL, the method will create a node and generate the head of the list for the first time. Passing in a negative `index` means inserting at the tail. The predefined `sList_front` and `sList_back` macro definitions can be used to represent the head and tail respectively. + +#### sList Erasing Data +```c +int sList_erase(sList **listRef, int index, sList **outPrev); +``` +This method is the counterpart of the `sList_insert` method. It erases the data at the specified position (removes the node from the linked list). Meanwhile, for more flexible use, it also supports obtaining the previous node of the erased one (which can make consecutive erasing more convenient and efficient). +```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); +} +``` +**Results**: +``` +data 1 +data 2 +data 3 +data 4 +``` + +Compared with the previous insertion example, the head of the list is erased here. + +#### sList Pushing and Popping +```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); +``` +These are respectively the methods for inserting at the head, inserting at the tail, deleting from the head, and deleting from the tail. In fact, they are encapsulations based on the `sList_insert` and `sList_erase` methods for common usage scenarios, making them easier to use. +```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); +} +``` +**Results**: +``` +data 3 +data 2 +data 1 +data 0 +data 0 +data 1 +data 2 +``` + +#### sList Appending +```c +int sList_append(sList *list, sList **append); +``` +This method can concatenate two linked lists into one. The `append` linked list will become invalid after the concatenation is successful. +**Note**: The `append` should be the head of the list. Although it can still be concatenated successfully even if it's not the head, it still belongs to the original linked list, and some unexpected situations may occur during operations. +```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); +} +``` +**Results**: +``` +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 Linking Nodes +```c +sList *sList_attach(sList **listRef, int index, sList *attach); +``` +This method links a node (or a linked list) to an existing linked list, and the specific position to link can be specified by the `index`. This method is very flexible and directly operates on the linked list structure. **Generally, this method is not commonly used**, and instead, the encapsulated methods like `sList_insert` are usually used. This method can be combined with other methods to be flexibly re-encapsulated into other methods. +```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); +} +``` +**Results**: +``` +data 0 +data 1 +data 2 +data 3 +data 4 +data 0 +data 1 +data 2 +``` + +#### sList Detaching Nodes +```c +sList *sList_detach(sList **listRef, int index, int count, sList **outPrev); +``` +This method is the counterpart of the `sList_attach` method. It can detach several nodes (a sub-linked list) from the linked list. The specific position and the number of nodes to detach can be specified by the `index` and `count` respectively. This method is very flexible and directly operates on the linked list structure. **Generally, this method is not commonly used**, and instead, the encapsulated methods like `sList_erase` are usually used. This method can be combined with other methods to be flexibly re-encapsulated into other methods. +```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); +} +``` +**Results**: +``` +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 Copying +```c +sList *sList_copy(sList *list, int begin, int end); +``` +This method will make a deep copy of a new linked list according to the specified interval of the source linked list. +```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); +} +``` +**Results**: +``` +data 2 +data 3 +data 4 +data 5 +data 6 +data 7 +data 8 +data 9 +``` + +#### sList Reversing an Interval +```c +int sList_reverse(sList *list, int begin, int end); +``` +This method will reverse the specified interval of the source linked list. +```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); +} +``` +**Results**: +``` +data 9 +data 8 +data 7 +data 6 +data 5 +data 4 +data 3 +data 2 +data 1 +data 0 +``` + +#### sList Getting a Specified Node +```c +sList *sList_to(sList *list, int index); +``` +This method can obtain the node with a specified offset from the current position of the head node. +```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); +} +``` +**Results**: +``` +data 6 +``` + +#### sList Size +```c +int sList_size(sList *list); +``` +This method is used to obtain the number of data elements in the linked 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); +} +``` +**Results**: +``` +size 10 +size 7 +``` + +#### sList Traversing +```c +#define sList_forEach(list, node) +``` +This method is for traversing the linked list. Specific examples can be referred to in the above usage examples. + +#### sList Node Data Reference +```c +#define sList_ref(node, type) +``` +This method is similar to the reference in C++. In fact, it operates on pointers (but hides them), making reading and writing more convenient. However, it should be noted that data operations should not exceed the bounds. For example, if a node originally stores `char`-type data (and only 1 byte of space is allocated), using it as `int`-type data would cause an out-of-bounds situation. Specific examples can be referred to in the above usage examples. diff --git a/doc/search.en.md b/doc/search.en.md new file mode 100644 index 0000000..aae71b9 --- /dev/null +++ b/doc/search.en.md @@ -0,0 +1,97 @@ +### Introduction + +The search algorithm is designed to find specific elements within a data structure. Common search algorithms include linear search, binary search, and hash search. Here is a brief introduction to the principles and implementations of these search algorithms: + +- **Linear Search**: Linear search is the simplest search algorithm. It starts from the beginning of the data structure and compares each element one by one until the target element is found or all elements have been traversed. The time complexity of linear search is O(n), where n represents the size of the data structure. + +- **Binary Search**: Binary search is applicable to ordered data structures. Each time, it divides the data structure into two halves and then determines the sub-interval to continue the search based on the comparison result between the target element and the middle element. The time complexity of binary search is O(log n). + +- **Hash Search**: Hash search constructs a hash function to map data into a hash table and then directly accesses the target element according to the hash value. The average time complexity of hash search is O(1). ***It is applied in the dict module using hash search***. + +### Interface + +```c +int search_linear(void *array, int left, int right, void *target, SearchOPS *ops); +int search_binary(void *array, int left, int right, void *target, SearchOPS *ops); +``` +Both of these search algorithms are used in the same way. They require passing in the address of the data, the search interval of the array, the search target, and the operation set that depends on the data container. They return the index of the found element in the array (a negative value will be returned if the search fails). + +### Testing + +- **Linear Search Test**: +```c +static void test_linear(void) +{ + int array[] = {6, 3, 1, 0, -2, 9}; + SearchOPS ops; + int target = 1; + + ops.addr = int_array_addr; + ops.cmp = int_cmp; + + target = 9, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = -2, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 0, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 1, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 3, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 6, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + + target = 5, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = -9, printf("search %d, result %d\r\n", target, search_linear(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); +} +``` + +**Results**: +``` +search 9, result 5 +search -2, result 4 +search 0, result 3 +search 1, result 2 +search 3, result 1 +search 6, result 0 +search 5, result -1 +search -9, result -1 +``` +In this test for linear search: +1. First, an integer array `array` is defined with several elements. +2. Then, a `SearchOPS` structure `ops` is created, and its members `addr` and `cmp` are initialized with appropriate functions related to the array address and comparison operations for integers. +3. Subsequently, multiple searches are performed with different target values. For each target value, the `search_linear` function is called, and the result (the index if found or a negative value if not found) is printed out. + +- **Binary Search Test**: +```c +static void test_binary(void) +{ + int array[] = {0, 3, 6, 10, 62, 99}; + SearchOPS ops; + int target = 10; + + ops.addr = int_array_addr; + ops.cmp = int_cmp; + + target = 0, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 3, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 6, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 10, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 62, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = 99, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + + target = 5, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); + target = -9, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); +} +``` + +**Results**: +``` +search 0, result 0 +search 3, result 1 +search 6, result 2 +search 10, result 3 +search 62, result 4 +search 99, result 5 +search 5, result -1 +search -9, result -1 +``` +For the binary search test: +1. An ordered integer array `array` is defined. +2. Similar to the linear search test, the `SearchOPS` structure `ops` is initialized. +3. Different target values are used to call the `search_binary` function one by one, and the corresponding search results are printed. The results show that when the target exists in the ordered array, the correct index is returned, and when the target doesn't exist, a negative value is returned, which verifies the functionality of the binary search algorithm. diff --git a/doc/set.en.md b/doc/set.en.md new file mode 100644 index 0000000..2d6cbb7 --- /dev/null +++ b/doc/set.en.md @@ -0,0 +1,250 @@ +### Introduction + +The set collection is a logically discrete container, which is different from vector and list containers. The access index of the set is discrete and not necessarily continuous. When elements are inserted into the set, sorting and index deduplication are performed. +The set container in varch also has the basic properties of a set. In its underlying implementation, it uses a **red-black tree**. The efficiency of addition and deletion operations is higher than that of the vector, and it also supports random access. The time complexity of random access is much better than that of the list. As a chained structure, the set collection also supports iterators. + +### Interface + +#### Creation and Deletion of set Objects +```c +set_t set_create(int dsize); +void set_delete(set_t set); +#define set(type) // For more convenient use, a macro definition is wrapped around set_create +#define _set(set) // A macro definition is wrapped around set_delete, and the set is set to NULL after deletion +``` +Here, **set_t** is the structure of the set. The creation method will return an empty set object, and it will return NULL if the creation fails. The `dsize` parameter is used to pass in the size of the data. The deletion method is used to delete the passed-in set object. The creation method and the deletion method should be used in pairs. Once created, the set object should be deleted when it's no longer in use. +```c +void test(void) +{ + set_t set = set(int); // Define and create a set of the int type + _set(set); // Use them in pairs and delete it after use +} +``` + +#### Insertion and Removal of set +```c +void* set_insert(set_t set, int index, void* data); +int set_erase(set_t set, int index); +``` +The set has good efficiency in insertion and removal. There's no need to shift data; only the pointers in the linked list need to be modified. +The insertion method adds a specified index and copies the data to this index (when `data` is passed as NULL, it only allocates space without assigning a value). During the process of inserting the index, duplicate checking will be performed to ensure the uniqueness of the index. If the insertion is successful, it returns the address of the inserted data; otherwise, it returns NULL. The removal method removes the data at the specified index. It returns 1 if successful and 0 if failed. + +#### Reading and Writing of set Data +```c +void* set_data(set_t set, int index); +void* set_error(set_t set); +#define set_at(set, type, i) +``` +The `set_data` method is used to obtain the address of the data according to the index and returns the address of the specified data. `set_error()` is used to indicate failure. The `set_at` method adds the data type on the basis of `set_data`. The `set_data` has a read-write protection mechanism. Since `set_error()` is returned instead of NULL, when using the `set_at` method and the `i` value is entered incorrectly, the content pointed to by `set_error()` will be modified instead of causing the program to crash. +The random access of the set is different from that of arrays with continuous addresses or lists with non-continuous addresses. Arrays can directly locate the address of the specified index, while lists need to use links to point step by step for random access. The set uses a **red-black tree** and can quickly access the specified index through binary search. +```c +void test(void) +{ + set_t set = set(int); + int i; + + for (i = 0; i < 100; i++) + { + set_insert(set, i, &i); + } + i = -100; set_insert(set, i, &i); + i = 1024; set_insert(set, i, &i); + + printf("set[6] = %d\r\n", set_at(set, int, 6)); + printf("set[-100] = %d\r\n", set_at(set, int, -100)); + printf("set[1024] = %d\r\n", set_at(set, int, 1024)); + + set_at(set, int, 6) = 11111; + printf("set[6] = %d\r\n", set_at(set, int, 6)); + + _set(set); +} +``` +**Results**: +``` +set[6] = 6 +set[-100] = -100 +set[1024] = 1024 +set[6] = 11111 +``` + +#### Size of set and Data Size +```c +int set_size(set_t set); +int set_dsize(set_t set); +``` +The `size` of the set is easy to understand. It's similar to the size of an array. The `dsize` is the size of the data passed in during creation. +```c +void test(void) +{ + set_t set = set(int); + int i; + + for (i = 0; i < 100; i++) + { + set_insert(set, i, &i); + } + printf("size = %d, data size = %d\r\n", set_size(set), set_dsize(set)); + + _set(set); +} +``` +**Results**: +``` +size = 100, data size = 4 +``` + +#### Set Finding +```c +int set_find(set_t set, int index); +``` +This method is actually implemented by wrapping `set_data`. It returns 1 if the find is successful and 0 if it fails. + +#### Set Iterator +```c +void set_it_init(set_t set, int orgin); +void* set_it_get(set_t set, int *out_index); +``` +The set also supports a built-in iterator, but mainly the iterator of the set is used for traversal. Since the list can be traversed by incrementing the index starting from 0, but the set has discrete indexes and can't be traversed in this way, two iterator functions are provided here for traversing the set. +The `set_it_init` function initializes the iterator. When `orgin` is specified as `SET_HEAD` or `SET_TAIL`, it represents forward iteration and reverse iteration respectively. +The `set_it_get` function obtains the iteration and updates the iteration position. `*out_index` is used to output the index (the current index, and it can also be passed as NULL if not needed to receive), and it returns the data at the iteration position. +The number of iterations is controlled by `set_size`. +```c +void test(void) +{ + set_t set = set(int); + int i, index; + void *data; + + i = -100; set_insert(set, i, &i); + i = 1024; set_insert(set, i, &i); + i = 0; set_insert(set, i, &i); + i = 7; set_insert(set, i, &i); + i = -2; set_insert(set, i, &i); + i = -2; set_insert(set, i, &i); + + set_at(set, int, 3) = 100; + + set_it_init(set, SET_HEAD); + i = set_size(set); + while (i--) + { + data = set_it_get(set, &index); + printf("set[%d] = %d\r\n", index, *(int *)data); + } + + _set(set); +} +``` +**Results**: +``` +set[-100] = -100 +set[-2] = -2 +set[0] = 0 +set[7] = 7 +set[1024] = 1024 +``` + +### Source Code Analysis + +#### set Structure + +All the structures of the set container are implicit, which means that the members of the structures can't be accessed directly. This way ensures the independence and security of the module and prevents external calls from modifying the members of the structures, which could otherwise damage the storage structure of the set. So the set parser only leaves the single declaration of the set in the head file, and the definitions of the structures are placed in the source file. Only the methods provided by the set container can be used to operate on set objects. +The declaration of the set type: +```c +typedef struct SET *set_t; +``` +When using it, just use `set_t`. +```c +/* type of set */ +typedef struct SET +{ + NODE* root; /* root node */ + NODE* nil; /* nil node */ + NODE* iterator; /* iterator of set */ + int orgin; /* iterator orgin */ + int size; /* set size */ + int dsize; /* data size */ +} SET; +``` +The `SET` structure contains 6 members: `root` (the root node of the red-black tree), `nil` (the nil node of the red-black tree), `iterator` (the node currently pointed to by the iterator), `orgin` (the starting position of the iterator), `size` (the size of the set, that is, the total number of nodes in the red-black tree of the set), and `dsize` (the size of each data). +```c +/* set node type define */ +typedef struct NODE +{ + struct NODE *parent; + struct NODE *left; + struct NODE *right; + int color; + int index; +} NODE; +#define data(node) ((node)+1) /* data of node */ +``` +In the `NODE` structure, the explicit members include `parent`, `left`, and `right` which are pointers to form the tree structure, `color` which represents the color of the red-black tree node, and `index`. The data field is the same as that of the `list`, which follows the node space and is dynamically allocated in size. + +#### Red-Black Tree + +The red-black tree is a binary search tree, and an additional storage bit is added to each node to represent the color of the node, which can be Red or Black. By restricting the coloring method of each node on any path from the root to the leaves, the red-black tree ensures that no path is twice as long as other paths, so it is nearly balanced. +The storage structure of the set is completely based on the red-black tree. Here, we won't go into details about the red-black tree. + +#### Iterator + +As mentioned before, the set has a built-in iterator. So how does this iterator work? +Let's look at the source code: +```c +void set_it_init(set_t set, int orgin) +{ + if (!set) return; + set->orgin = (orgin==SET_HEAD)?SET_HEAD:SET_TAIL; + set->iterator = (set->orgin==SET_HEAD)?(NODE*)node_min(set, set->root):(NODE*)node_max(set, set->root); // According to the starting point, iterate the iterator to the minimum or maximum node of the root node +} +void* set_it_get(set_t set, int *out_index) +{ + NODE *node; + if (!set) return NULL; + node = set->iterator; + set->iterator = (set->orgin==SET_HEAD)?node_next(set, set->iterator):node_prev(set, set->iterator); // According to the iteration direction, choose to iterate to the next or previous node + if (out_index) *out_index = node->index; // Output the index of the iteration + return data(node); +} +``` +So how are `node.prev` and `node.next` implemented in the red-black tree? +In the red-black tree, the current node is larger than all the nodes in its left subtree and smaller than any node in its right subtree. +So when getting the next node, we need to find the smallest node that is larger than the current node. The priority for searching is as follows: +1. Nodes in the right subtree. When looking at the nodes in the right subtree, the smallest node in the right subtree is on the leftmost side of the right subtree. +2. Parent node. When looking at the parent node, we also need to distinguish whether the current node is the left or right child of the parent node. If it's the left child of the parent node, then the parent node is larger than the current node, and we can directly return the parent node. If it's the right child of the parent node, then the parent node is smaller than the current node, so we need to return the "grandparent node" which is larger than the current node and is the smallest one. +Similarly, the logic for `prev` is just the opposite. +How to end the iteration? We can control the number of iterations through the size of the set. If we keep iterating, the iterator will finally stay at the `nil` position. +```c +static NODE* node_next(set_t set, NODE* node) +{ + if (node->right!= set->nil) // There is a right subtree + { + node = node->right; // First, go to the right child node + node = node_min(set, node); // Then, go to the leftmost node (the smallest node) of the right subtree + } + else // There is no right subtree + { + if (node == node->parent->left) node = node->parent; // If the current node is the left child of the parent node, return the parent node + else node = node->parent->parent; // If the current node is the right child of the parent node, return the "grandparent node" + } + return node; +} + +static NODE* node_prev(set_t set, NODE* node) +{ + if (node->left!= set->nil) + { + node = node->left; + node = node_max(set, node); + } + else + { + if (node == node->parent->right) node = node->parent; + else node = node->parent->parent; + } + return node; +} +``` + +These functions related to the iterator implement the traversal operation in the set based on the characteristics of the red-black tree, enabling us to access each element in the set in an orderly manner. diff --git a/doc/sort.en.md b/doc/sort.en.md new file mode 100644 index 0000000..a2000c6 --- /dev/null +++ b/doc/sort.en.md @@ -0,0 +1,363 @@ +### Introduction + +Sorting is an important operation in computer programming. Its function is to rearrange an arbitrary sequence of data elements (or records) into a sequence ordered by keys. Sorting means arranging the elements in a collection in a certain order. Generally, there are two types of sorting: ascending order and descending order. + +The logic of sorting algorithms is fixed. However, the structures of the data sets and data items to be sorted can be different. If a set of sorting algorithm functions is written for each data set structure or data item structure, it will undoubtedly increase redundant code. + +The sort module provided by varch aims to solve this problem. It abstracts the differences in data set and data item types required by the algorithm model. As long as the corresponding operation functions are provided for different data and sorting rules, the sorting algorithm can be uniformly called for sorting operations. + +### Interface + +```c +int sort_bubble(void *array, int begin, int end, SOPS* ops); +int sort_select(void *array, int begin, int end, SOPS* ops); +int sort_insert(void *array, int begin, int end, SOPS* ops); +int sort_shell(void *array, int begin, int end, SOPS* ops); +int sort_quick(void *array, int begin, int end, SOPS* ops); +int sort_heap(void *array, int begin, int end, SOPS* ops); +``` +Here, six commonly used sorting methods are provided, namely bubble sorting, selection sorting, insertion sorting, Shell sorting, quick sorting, and heap sorting. The usage of these sorting methods is consistent. The `array` parameter is used to pass in an array or any other data container, `begin` and `end` are used to specify the interval that needs to be sorted, and `ops` is used to specify a set of function operations required for sorting. + +```c +/* Sorting algorithm operation function set structure definition */ +typedef struct +{ + /** + * \brief ordering rules, e.g front < back ascending and front > back descending + * \param[in] front: address of front data + * \param[in] back: address of back data + * \return positive: following sorting rules + * negative: violating sorting rules + * 0: does't affect sorting rules + */ + int (*order)(void *front, void *back); + + /** + * \brief get the address of the space where the element is located + * \param[in] array: data handle + * \param[in] index: item data index + * \return address of item data + */ + void* (*addr)(void *array, int index); + + /** + * \brief Swap data between two spaces + * \param[in] array: data handle + * \param[in] index0: item data index0 + * \param[in] index1: item data index1 + * \return none + */ + void (*swap)(void *array, int index0, index1); + +} SOPS; +``` +Firstly, in `ops`, the `order` member needs to specify the sorting rule (ascending or descending order). If the item of `front` is greater than the item of `back`, it is a descending order rule, and vice versa. +Since `array` supports data structures of any type, the `addr` member in `ops` needs to specify the method to obtain the address of a specific member of a specific data structure item. For example, the addresses returned for `char` arrays and `int` arrays are different. +Sorting involves exchanging the positions of data, and the `swap` member of `ops` specifies the method for exchanging data (because not all data structures exchange data by directly swapping the contents of the data. For example, for a linked list, it is sufficient to exchange the pointers to the data). + +The `ops` for commonly used data structure sorting has been encapsulated. Just select the actual `ops` according to the type. +```c +extern SOPS sops_char_ascend; +extern SOPS sops_char_descend; +extern SOPS sops_uchar_ascend; +extern SOPS sops_uchar_descend; +extern SOPS sops_short_ascend; +extern SOPS sops_short_descend; +extern SOPS sops_ushort_ascend; +extern SOPS sops_ushort_descend; +extern SOPS sops_int_ascend; +extern SOPS sops_int_descend; +extern SOPS sops_uint_ascend; +extern SOPS sops_uint_descend; +extern SOPS sops_float_ascend; +extern SOPS sops_float_descend; +extern SOPS sops_double_ascend; +extern SOPS sops_double_descend; +``` + +### Actual Test Examples + +#### Basic Usage of Ascending and Descending Order Sorting + +For the ascending and descending order sorting of basic data types, take the bubble sorting algorithm as an example. + +```c +static void test_basic_usage(void) +{ + /* For an int-type array, perform ascending order sorting */ + int array_int[] = {1, 3, 6, 5, 0, 2, 9, 8, 7, -4}; // Arbitrary disordered when defined + + // Pass in the base address of the array, the sorting interval, and specify the ascending order ops for the int type + sort_bubble(array_int, 0, sizeof(array_int)/sizeof(array_int[0]) - 1, &sops_int_ascend); + + // Output the sorted array + for (int i = 0; i < sizeof(array_int)/sizeof(array_int[0]); i++) + { + printf("%d, ", array_int[i]); + } + printf("\r\n"); + + /* ---------------------------------------- Code divider ---------------------------------------- */ + + /* For a float-type array, perform descending order sorting */ + float array_float[] = {-1.1, 2.1, 6.6, 5.0, 0.1, 2.8, 9.9, 9.8, 7.0, -4.5}; // Arbitrary disordered when defined + + // Pass in the base address of the array, the sorting interval, and specify the descending order ops for the float type + sort_bubble(array_float, 0, sizeof(array_float)/sizeof(array_float[0]) - 1, &sops_float_descend); + + // Output the sorted array + for (int i = 0; i < sizeof(array_float)/sizeof(array_float[0]); i++) + { + printf("%.2f, ", array_float[i]); + } + printf("\r\n"); +} +``` + +**Test Results**: +``` +-4, 0, 1, 2, 3, 5, 6, 7, 8, 9, +9.90, 9.80, 7.00, 6.60, 5.00, 2.80, 2.10, 0.10, -1.10, -4.50, +``` + +#### Sorting for a Specified Interval + +Besides the common sorting for the entire interval, sorting within a certain specified interval can also be performed. + +```c +static void test_interval(void) +{ + int array_int[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // First define an ascending order array + + // Pass in the base address of the array, specify the sorting interval as [3, 7], and specify the descending order ops for the int type + sort_bubble(array_int, 3, 7, &sops_int_descend); + + // Output the sorted array + for (int i = 0, i < sizeof(array_int)/sizeof(array_int[0]); i++) + { + printf("%d, ", array_int[i]); + } + printf("\r\n"); +} +``` + +**Test Results**: +``` +0, 1, 2, 7, 6, 5, 4, 3, 8, 9, +``` + +#### Sorting for Containers Other Than Arrays + +The following takes the list container as an example to test the ascending order sorting of an int-type list. This test code depends on the `list` container. + +```c +// Get the address of the list data item +static void* list_int_addr(void *array, int index) +{ + return list_data((list_t)array, index); +} + +// Swap the data of two members in the list +static void list_int_swap(void *array, int index0, int index1) +{ + int temp = list_at((list_t)array, int, index0); + list_at((list_t)array, int, index0) = list_at((list_t)array, int, index1); + list_at((list_t)array, int, index1) = temp; +} + +// Display the data stored in the list +static void list_int_show(list_t list) +{ + for (int i = 0; i < list_size(list); i++) + { + printf("%d, ", list_at(list, int, i)); + } + printf("\r\n"); +} + +static void test_list_sort(void) +{ + list_t list = list(int); // Define an int-type list + SOPS ops; // Define a new ops + int array[] = {1, 3, 6, 5, 0, 2, 9, 8, 7, -4}; // Define the data to be loaded into the list + + // Load the data into the list + for (int i = 0; i < sizeof(array)/sizeof(array[0]); i++) + { + list_push_back(list, &array[i]); + } + + ops.order = sops_int_ascend.order; // Reuse the ascending order rule for int + ops.addr = list_int_addr; // Use the method to obtain the element address for the list + ops.swap = list_int_swap; // Use the method to swap data in the list + + // Output the list before sorting + list_int_show(list); + + // Perform sorting + sort_bubble(list, 0, list_size(list) - 1, &ops); + + // Output the list after sorting + list_int_show(list); + + // Release the list + _list(list); +} +``` + +**Test Results**: +``` +1, 3, 6, 5, 0, 2, 9, 8, 7, -4, +-4, 0, 1, 2, 3, 5, 6, 7, 8, 9, +``` + +#### Sorting for Custom Data Structures + +The previous examples provided are for basic data types. The following example is for custom data structures, such as structures, and sorting is performed according to certain member items of the structure. + +```c +typedef struct +{ + char *name; + int age; + char gender; /* 1 represents male, 0 represents female */ + float weight; +} STUDENT; + +static void student_print(STUDENT *s, int size) +{ + for (int i = 0; i < size; i++) + { + printf("name: %s, \t\t age = %d, \t\t gender = %d, \t\t weight = %.2f\r\n", s[i].name, s[i].age, s[i].gender, s[i].weight); + } +} + +static void* student_addr(void *array, int index) +{ + return ((STUDENT *)array) + index; +} + +static void student_swap(void *array, int index0, int index1) +{ + STUDENT temp = ((STUDENT *)array)[index0]; + ((STUDENT *)array)[index0] = ((STUDENT *)array)[index1]; + ((STUDENT *)array)[index1] = temp; +} + +static int student_name_ascend(void *front, void *back) +{ + return -strcmp(((STUDENT *)front)->name, ((STUDENT *)back)->name); +} + +static int student_gender_descend(void *front, void *back) +{ + if (((STUDENT *)front)->gender > ((STUDENT *)back)->gender) return 1; + else if (((STUDENT *)front)->gender < ((STUDENT *)back)->gender) return -1; + else return 0; +} + +static int student_age_descend(void *front, void *back) +{ + if (((STUDENT *)front)->age > ((STUDENT *)back)->age) return 1; + else if (((STUDENT *)front)->age < ((STUDENT *)back)->age) return -1; + else return 0; +} + +static int student_weight_ascend(void *front, void *back) +{ + if (((STUDENT *)front)->weight < ((STUDENT *)back)->weight) return 1; + else if (((STUDENT *)front)->weight > ((STUDENT *)back)->weight) return -1; + else return 0; +} + +static void test_struct(void) +{ + SOPS ops; + STUDENT table[] = { + {"ZhanSan", 18, 1, 56.3}, + {"LiSi", 17, 1, 62.2}, + {"WangWu", 21, 0, 58.9}, + {"ZhaoLiu", 22, 1, 65.5}, + {"SunQi", 18, 1, 71.7}, + {"ZhouBa", 30, 0, 48.8}, + {"WuJiu", 16, 1, 56.3}, + {"ZhenShi", 19, 0, 52.1}, + }; + + ops.addr = student_addr; + ops.swap = student_swap; + + // Specify sorting in ascending order by name + ops.order = student_name_ascend; + sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); + printf("sorting by name in ascending order\r\n"); + student_print(table, sizeof(table)/sizeof(table[0])); + printf("---------------------------------\r\n"); + + // Specify sorting in descending order by age + ops.order = student_age_descend; + sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); + printf("sorting by age in descending order\r\n"); + student_print(table, sizeof(table)/sizeof(table[0])); + printf("---------------------------------\r\n"); + + // Specify sorting in descending order by gender + ops.order = student_gender_descend; + sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); + printf("sorting by gender in descending order\r\n"); + student_print(table, sizeof(table)/sizeof(table[0])); + printf("---------------------------------\r\n"); + + // Specify sorting in ascending order by weight + ops.order = student_weight_ascend; + sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); + printf("sorting by weight in ascending order\r\n"); + student_print(table, sizeof(table)/sizeof(table[0])); + printf("---------------------------------\r\n"); +} +``` + +**Test Results**: + +``` +sorting by name in ascending order +name: LiSi, age = 17, gender = 1, weight = 62.20 +name: SunQi, age = 18, gender = 1, weight = 71.70 +name: WangWu, age = 21, gender = 0, weight = 58.90 +name: WuJiu, age = 16, gender = 1, weight = 56.30 +name: ZhanSan, age = 18, gender = 1, weight = 56.30 +name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 +name: ZhenShi, age = 19, gender = 0, weight = 52.10 +name: ZhouBa, age = 30, gender = 0, weight = 48.80 +--------------------------------- +sorting by age in descending order +name: ZhouBa, age = 30, gender = 0, weight = 48.80 +name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 +name: WangWu, age = 21, gender = 0, weight = 58.90 +name: ZhenShi, age = 19, gender = 0, weight = 52.10 +name: ZhanSan, age = 18, gender = 1, weight = 56.30 +name: SunQi, age = 18, gender = 1, weight = 71.70 +name: LiSi, age = 17, gender = 1, weight = 62.20 +name: WuJiu, age = 16, gender = 1, weight = 56.30 +--------------------------------- +sorting by gender in descending order +name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 +name: ZhanSan, age = 18, gender = 1, weight = 56.30 +name: SunQi, age = 18, gender = 1, weight = 71.70 +name: LiSi, age = 17, gender = 1, weight = 62.20 +name: WuJiu, age = 16, gender = 1, weight = 56.30 +name: WangWu, age = 21, gender = 0, weight = 58.90 +name: ZhenShi, age = 19, gender = 0, weight = 52.10 +name: ZhouBa, age = 30, gender = 0, weight = 48.80 +--------------------------------- +sorting by weight in ascending order +name: ZhouBa, age = 30, gender = 0, weight = 48.80 +name: ZhenShi, age = 19, gender = 0, weight = 52.10 +name: ZhanSan, age = 18, gender = 1, weight = 56.30 +name: WuJiu, age = 16, gender = 1, weight = 56.30 +name: WangWu, age = 21, gender = 0, weight = 58.90 +name: LiSi, age = 17, gender = 1, weight = 62.20 +name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 +name: SunQi, age = 18, gender = 1, weight = 71.70 +--------------------------------- +``` diff --git a/doc/stack.en.md b/doc/stack.en.md new file mode 100644 index 0000000..c8cf945 --- /dev/null +++ b/doc/stack.en.md @@ -0,0 +1,172 @@ +### Introduction + +A stack is a data structure with a First In Last Out (FILO) characteristic. Generally, it has only one entrance and exit, where data is pushed onto the stack from the top and popped from the top as well. The operations of adding data to the stack is called "push", and removing data from the stack is called "pop". +The implementation logic of stacks and queues in varch is quite similar. A queue has two ports and stores data cyclically within a specified space, while a stack has only one port. Usually, data enters and exits the stack from the top. The stack bottom data is located in the low-address section of the specified space, and the stack top data is in the high-address section. + +- **Capacity**: Capacity refers to the maximum number of stack data that can be stored during usage. For example, a stack with a capacity of 10 can store at most 10 stack data items. Once it's full, no more data can be pushed onto it. The stack storage in varch uses continuous addresses and is a stack with a limited capacity. + +### Interface + +#### Creation and Deletion of stack Objects +```c +stack_t stack_create(int dsize, int capacity, void *base); +void stack_delete(stack_t stack); +#define stack(type, capacity) // For more convenient use, a macro definition is wrapped around stack_create +#define _stack(stack) // A macro definition is wrapped around stack_delete, and the stack is set to NULL after deletion +``` +Here, **stack_t** is the structure of the stack. The creation method will return a stack object, and it will return NULL if the creation fails. The `dsize` parameter is used to pass in the size of the data, `capacity` is used to pass in the stack capacity, and `*base` is used to pass in the address of the buffer (it can be omitted. If omitted, space with a size of `capacity` will be automatically allocated to store stack data). The deletion method is used to delete the passed-in stack object. The creation method and the deletion method should be used in pairs. Once created, the stack object should be deleted when it's no longer in use. +```c +void test(void) +{ + stack_t stack = stack(int, 10); // Define and create an int-type stack with a capacity of 10 + _stack(stack); // Use them in pairs and delete it after use +} +``` + +#### Stack's Pushing and Popping +```c +int stack_push(stack_t stack, void* data); +int stack_pop(stack_t stack, void* data); +``` +These two methods can conveniently add data to the stack and pop data from the stack. For the `push` method, the `data` parameter is used to pass in the address of the data to be pushed onto the stack. For the `pop` method, the `data` parameter is used to pass in the address that will receive the popped data. For both methods, `data` can be passed as NULL, which just serves as a placeholder. If the operation is successful, 1 is returned; otherwise, 0 is returned. +```c +void test(void) +{ + stack_t stack = stack(int, 10); + int i = 0; + + for (i = 0; i < stack_capacity(stack); i++) + { + stack_push(stack, &i); + } + stack_pop(stack, NULL); + stack_pop(stack, NULL); + + _stack(stack); // Use them in pairs and delete it after use +} +``` + +#### Size, Capacity, and Data Size of the Stack +```c +int stack_size(stack_t stack); +int stack_capacity(stack_t stack); +int stack_dsize(stack_t stack); +``` +The `capacity` of the stack is the capacity specified during creation, which indicates how many stack elements can be stored. The `size` represents the number of elements currently in the stack. The `dsize` is the size of the data passed in during creation, for example, for `int`, the `dsize` is `sizeof(int)`. +```c +void test(void) +{ + stack_t stack = stack(int, 10); + int i = 0; + + for (i = 0; i < stack_capacity(stack); i++) + { + stack_push(stack, &i); + } + stack_pop(stack, NULL); + stack_pop(stack, NULL); + printf("stack capacity=%d, size=%d, dsize=%d\r\n", stack_capacity(stack), stack_size(stack), stack_dsize(stack)); + + _stack(stack); +} +``` +**Results**: +``` +stack capacity=10, size=8, dsize=4 +``` + +#### Reading and Writing of Stack Data +```c +void* stack_data(stack_t stack, int index); +#define stack_at(stack, type, i) +``` +The `stack_data` method is used to obtain the address of the data according to the index and returns the address of the specified data. NULL indicates failure. The `stack_at` method adds the data type on the basis of `stack_data`. +```c +void test(void) +{ + stack_t stack = stack(int, 10); + int i = 0; + + for (i = 0; i < stack_capacity(stack); i++) + { + stack_push(stack, &i); + } + stack_pop(stack, NULL); + stack_pop(stack, NULL); + for (i = 0; i < stack_size(stack); i++) + { + printf("stack[%d] = %d\r\n", i, stack_at(stack, int, i)); + } + + _stack(stack); +} +``` +**Results**: +``` +stack[0] = 0 +stack[1] = 1 +stack[2] = 2 +stack[3] = 3 +stack[4] = 4 +stack[5] = 5 +stack[6] = 6 +stack[7] = 7 +``` + +#### Stack Data Storage Index +```c +#define stack_index(stack, index) +``` +In fact, `stack_index` corresponds to the `index`. If it exceeds the range, -1 is returned to indicate failure. Generally, this method is not widely used. It is mainly used when the `base` address is passed in during the `stack_create` method to obtain stack data based on the `base` address. + +#### Empty Stack and Full Stack +```c +int stack_empty(stack_t stack); +int stack_full(stack_t stack); +``` +These two methods are actually related to the `size` of the stack. If the `size` is equal to 0, the stack is empty; if the `size` is equal to the capacity, the stack is full. + +### Source Code Analysis + +#### stack Structure + +All the structures of the stack container are implicit, which means that the members of the structures can't be accessed directly. This way ensures the independence and security of the module and prevents external calls from modifying the members of the structures, which could otherwise damage the storage structure of the stack. So the stack parser only leaves the single declaration of the stack in the head file, and the definitions of the structures are placed in the source file. Only the methods provided by the stack container can be used to operate on stack objects. +The declaration of the stack type: +```c +typedef struct STACK *stack_t; +``` +When using it, just use `stack_t`. +```c +typedef struct STACK +{ + void* base; /* base address of data */ + int cst; /* base const */ + int dsize; /* size of stack data */ + int capacity; /* capacity of stack */ + int top; /* index of stack top */ +} STACK; +``` +The `STACK` structure contains 5 members: `base` (the base address of the stack structure's data buffer), `cst` (indicating whether the `base` space is passed in during the `create` method), `dsize` (the size of each data), `capacity` (the capacity of the stack), and `top` (indicating the index of the next stack data, that is, `top` represents the `size`). + +The main problem that the stack container needs to solve is to implement the First In Last Out characteristic. Other operations like creation and deletion are used to complete basic initialization operations such as space initialization. +```c +#define at(i) (((unsigned char *)(stack->base))+(i)*(stack->dsize)) /* address of void array */ +int stack_push(stack_t stack, void* data) +{ + if (!stack) return 0; + if (stack_full(stack)) return 0; // Check if the stack is full before pushing data + if (data) memcpy(at(stack->top), data, stack->dsize); // Copy the data to the stack top + stack->top++; // Increase the stack top index + return 1; +} +int stack_pop(stack_t stack, void* data) +{ + if (!stack) return 0; + if (stack_empty(stack)) return 0; // Check if the stack is empty before popping data + stack->top--; // Since top points to the index of the next data, it needs to be decreased by 1 first + if (data) memcpy(data, at(stack->top), stack->dsize); // Copy the data out + return 1; +} +``` + +These functions related to pushing and popping data on the stack implement the core operations of adding and removing elements while maintaining the FILO property of the stack. diff --git a/doc/str.en.md b/doc/str.en.md new file mode 100644 index 0000000..4fcc546 --- /dev/null +++ b/doc/str.en.md @@ -0,0 +1,798 @@ +### Introduction + +A string is actually a collection of characters. In the C language, a string is an array of the `char` type with a terminating null character `\0` at the end. Without the terminating character, it's just a character array. In C, there isn't a dedicated string variable. However, the `str` in varch encapsulates array strings, making it a string variable where you don't need to worry about the underlying storage structure. Although `str` still stores character sets in contiguous address spaces, the work related to storage is handled by the methods provided by `str`, and it also offers methods that are in line with string operations, facilitating flexible manipulation of strings. For convenience, traditional C language strings will be referred to as array strings later, and the ones provided by varch will be called `str` strings. + +### Interface + +#### Creation and Deletion of str String Objects +```c +str_t str_create(void *string); +void str_delete(str_t str); +#define str(str) // For more convenient use, a macro definition is wrapped around str_create +#define _str(str) // A macro definition is wrapped around str_delete, and the str is set to NULL after deletion +``` +Here, **str_t** is the structure of `str`. The creation method will return an `str` object, and it will return `NULL` if the creation fails. The `string` parameter is used to pass in the initializing string. This `string` is defined as the `void *` type, so it can support passing both array strings and `str` strings for initialization (all subsequent mentions of `void *string` can support these two types of strings for initialization). The deletion method is used to delete the passed-in `str` object. The creation method and the deletion method should be used in pairs. Once created, the `str` object should be deleted when it's no longer in use. +```c +void test(void) +{ + str_t ss = str("Hello str!"); // Use an array string for assignment construction + str_t copy = str(ss); // Use an str string for copy construction + _str(ss); // Delete str + _str(copy); // Delete str +} +``` + +#### str String's Array String +```c +#define str_c_str(str) +#define _S(str) +``` +`str` stores strings in contiguous address spaces, and by obtaining this address, you can access the actually stored string. However, it's not recommended to modify the string as an array string in this way during use. There's also a method `char* str_data(str_t str, int pos);` that has a similar function. `str_c_str()` is implemented by using `str_data`, and it's not recommended to replace `str_c_str()` with this method. And `_S()` is a shorter version. +```c +void test(void) +{ + str_t ss = str("Hello str!"); // Use an array string for assignment construction + str_t copy = str(ss); // Use an str string for copy construction + printf("ss: %s\r\n", _S(ss)); + printf("copy: %s\r\n", _S(copy)); + _str(ss); // Delete str + _str(copy); // Delete str +} +``` +**Results**: +``` +ss: Hello str! +copy: Hello str! +``` + +#### str Assignment +```c +str_t str_assign(str_t str, void *string); +``` +`str` abstracts the string as a class, and the `str_assign` method performs an assignment operation similar to the `=` operator. As mentioned before, `void *string` can support both array strings and `str` strings. +```c +void test(void) +{ + str_t ss = str(""); // Initialize as an empty string + printf("ss: %s\r\n", _S(ss)); + str_assign(ss, "Hello str!"); // Assign the value "Hello str!" + printf("ss: %s\r\n", _S(ss)); + str_assign(ss, "This is the assignment method!"); // Reassign the value + printf("ss: %s\r\n", _S(ss)); + _str(ss); +} +``` +**Results**: +``` +ss: +ss: Hello str! +ss: This is the assignment method! +``` +From this, we can see that `str` doesn't require you to worry about the size of the defined space like array strings do. Instead, `str` internally adapts to the data. + +#### str Concatenation +```c +#define str_append(str,...) +``` +Besides having an assignment operation similar to the `=` operator, `str` also has a concatenation operation similar to the `+=` operator. The method has variable arguments after the formal parameter, which means it can concatenate at least one string of any quantity. Again, these can be either array strings or `str` strings. The prototype of the `str_append` method is `str_t str_append_series(str_t str,...);`. The prototype function with variable arguments requires ending with `NULL`, and it's not recommended to use the prototype method. +```c +void test(void) +{ + str_t name = str("123456789"); + str_t ident = str("qq"); + str_t email = str(""); + + str_append(email, name, "@", ident, ".com"); // Concatenate several strings + printf("%s\r\n", str_c_str(email)); + + _str(name); + _str(ident); + _str(email); +} +``` +**Results**: +``` +123456789@qq.com +``` + +#### str Insertion +```c +str_t str_insert(str_t str, int pos, void *string); +``` +This method inserts a string into `str` at the specified position and returns `str` itself. `pos` is the insertion position, and `string` is the source string to be inserted. +```c +void test(void) +{ + str_t ss = str("0123456"); + str_insert(ss, 3, "|insert|"); // Insert the string at position 3 + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); +} +``` +**Results**: +``` +ss: 012|insert|3456 +``` + +#### str Removal +```c +str_t str_erase(str_t str, int pos, int len); +``` +This method removes a segment of characters from `str` and returns `str` itself. `pos` is the removal position, and `len` is the length of the segment to be removed. +```c +void test(void) +{ + str_t ss = str("0123456789"); + str_erase(ss, 3, 3); // Remove three characters starting from position 3, that is, remove "345" + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); +} +``` +**Results**: +``` +ss: 0126789 +``` + +#### str Push Back and Pop Back +```c +int str_push_back(str_t str, char c); +char str_pop_back(str_t str); +``` +These two methods are used to push and pop a character from the end of the `str` string respectively. The `push_back` method returns 1 for success and 0 for failure, and the `pop_back` method returns the popped character. The storage structure of `str` is similar to that of `vector`, and operations at the end are relatively efficient. +```c +void test(void) +{ + str_t ss = str("0123456789"); + str_push_back(ss, 'A'); + printf("ss: %s\r\n", str_c_str(ss)); + printf("pop %c\r\n", str_pop_back(ss)); + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); +} +``` +**Results**: +``` +ss: 0123456789A +pop A +ss: 0123456789 +``` + +#### str String Comparison +```c +int str_compare(str_t str, void *string); +``` +This method compares `str` with another string. A return value of 0 means they are equal, -1 means `str` is less than the other string, and 1 means `str` is greater than the other string. +```c +void test(void) +{ + str_t ss = str("01234"); + str_t copy = str(ss); + printf("compare: %d\r\n", str_compare(ss, copy)); + printf("compare: %d\r\n", str_compare(ss, "01233")); + printf("compare: %d\r\n", str_compare(ss, "012345")); + _str(ss); + _str(copy); +} +``` +**Results**: +``` +compare: 0 +compare: 1 +compare: -1 +``` + +#### str Creating a Substring +```c +str_t str_substr(str_t str, int pos, int len); +``` +This method creates a new substring from `str`. Remember to call the `_str(str)` method to delete it after you finish using it. `pos` is used to pass in the starting position, and `len` is used to pass in the length of the substring to be created. +```c +void test(void) +{ + str_t ss = str("0123456789"); + str_t sub = str_substr(ss, 4, 2); + printf("ss: %s\r\n", str_c_str(ss)); + printf("sub: %s\r\n", str_c_str(sub)); + _str(ss); + _str(sub); +} +``` +**Results**: +``` +ss: 0123456789 +sub: 45 +``` + +#### str String Length +```c +int str_length(str_t str); +``` +This is the standard method for obtaining the length of an `str` string. Of course, you can also use the `strlen` method, but for `str` objects, it's not necessary because this standard method doesn't take time to measure the length as it already has the recorded length available. +```c +void test(void) +{ + str_t ss = str("0123456789"); + printf("length: %d\r\n", str_length(ss)); + _str(ss); +} +``` +**Results**: +``` +length: 10 +``` + +#### str Character Reading and Writing +```c +#define str_at(str, i) +``` +`str` provides operations for reading and writing characters within it, and you can use them just like elements of an array. However, remember not to modify a character to `\0`. +```c +void test(void) +{ + str_t ss = str("0123456789"); + printf("ss[3] = %c\r\n", str_at(ss, 3)); + str_at(ss, 3) = 'A'; + printf("ss: %s\r\n", _S(ss)); + _str(ss); +} +``` +**Results**: +``` +ss[3] = 3 +ss: 012A456789 +``` + +#### str String Searching +```c +int str_find(str_t str, void *string, int pos); +int str_rfind(str_t str, void *string, int pos); +``` +These two methods are used to search for a character substring in the forward and reverse directions respectively. `string` is used to pass in the string to be searched for, and `pos` is used to pass in the starting index for the search. The return value is the position index of the substring if found, and `str_npos` is returned if not found. +```c +void test(void) +{ + str_t ss = str("0123456789"); + printf("find = %d\r\n", str_find(ss, "3456", 0)); // Start searching from position 0 + printf("rfind = %d\r\n", str_rfind(ss, "23", str_length(ss) - 1)); // Start searching backward from the end + printf("find = %d\r\n", str_find(ss, "0000", 0)); // Search for a non-existent string + _str(ss); +} +``` +**Results**: +``` +find = 3 +rfind = 2 +find = 2147483647 +``` + +#### str Searching for Matching Character Sets +```c +int str_find_first_of(str_t str, void *string, int pos); +int str_find_first_not_of(str_t str, void *string, int pos); +int str_find_last_of(str_t str, void *string, int pos); +int str_find_last_not_of(str_t str, void *string, int pos); +``` +These four methods are different from `str_find` and `str_rfind`. The `find` and `rfind` methods require a complete match of all characters, while these four methods only need to match any one of the characters in the character set to be considered a match. Similarly, `string` is used to pass in the string (the matching character set), `pos` is used to pass in the starting index for the search, and the return value is the position index of the substring if found, and `str_npos` is returned if not found. These four functions are used to find the **"first occurrence of any character in the set"**, **"first occurrence of any character not in the set"**, **"last occurrence of any character in the set"**, and **"last occurrence of any character not in the set"** respectively. +Example: To obtain the file name and the drive letter from a file path in a practical application. +```c +void test(void) +{ + str_t ss = str("C:/workspace/project/C/00 - vlib/Project/main.c"); // Given a file path first + str_t pan = NULL; // Initialize to NULL, not constructed + str_t filename = NULL; // Initialize to NULL, not constructed + pan = str_substr(ss, 0, str_find_first_of(ss, ":", 0)); // First find the first occurrence of ":" + filename = str_substr(ss, str_find_last_of(ss, "\\/", str_length(ss)-1) + 1, str_length(ss)); // Find the last occurrence of "\\" or "/" as the path separator + printf("pan: %s\r\n", str_c_str(pan)); + printf("filename: %s\r\n", str_c_str(filename)); + _str(ss); + _str(pan); + _str(filename); +} +``` +**Results**: +``` +pan: C +filename: main.c +``` + +#### str String Reversal +```c +str_t str_reverse(str_t str, int begin, int end); +``` +This method reverses the entire string within the specified interval of `str`. `begin` is the starting position of the interval, and `end` is the ending position of the interval. +```c +void test(void) +{ + str_t ss = str("0123456789"); + str_reverse(ss, 2, 8); + printf("ss = %s\r\n", str_c_str(ss)); + _str(ss); +} +``` +**Results**: +``` +ss = 0187654329 +``` + +#### str Character Replacement +```c +str_t str_replace(str_t str, int pos, int len, void *string); +``` +This method replaces the characters at the specified position with a specified length by a given string. +```c +void test(void) +{ + str_t ss = str("My name is ZhangSan!"); + printf("ss = %s\r\n", str_c_str(ss)); + str_replace(ss, 11, 8, "Lisi"); // Replace "ZhangSan" with "Lisi" + printf("ss = %s\r\n", str_c_str(ss)); + _str(ss); +} +``` +**Results**: +``` +ss = My name is ZhangSan! +ss = My name is Lisi! +``` + +#### str String Swap +```c +void str_swap(str_t str, str_t swap); +``` +This method swaps two strings. +```c +void test(void) +{ + str_t s1 = str("This s1!"); + str_t s2 = str("This s2!"); + str_swap(s1, s2); + printf("s1 = %s\r\n", str_c_str(s1)); + printf("s2 = %s\r\n", str_c_str(s2)); + _str(s1); + _str(s2); +} +``` +**Results**: +``` +s1 = This s2! +s2 = This s1! +``` + +### Copying str String to an Array +- **Function Functionality and Parameter Introduction**: + - The function `int str_copy(str_t str, int pos, int len, char *buf);` is used to copy the characters at the specified position in the `str` string to the specified array `buf`. Among them, the parameter `pos` indicates the starting position of the copy, `len` represents the maximum length of the copy, and `buf` is the array used to receive the copied data. The function will finally return the actual length of the copied content. + - For example, in the following test code: +```c +void test(void) +{ + str_t ss = str("0123456789"); + char array[32] = { 0 }; + int length = 0; + length = str_copy(ss, 5, sizeof(array), array); // Start copying from the position, and the maximum length of the copy is the length of the array + array[length] = '\0'; // Add a terminator at the end for easy printing + printf("length = %d, copy: %s\r\n", length, array); + _str(ss); +} +``` +The running result is: +``` +length = 5, copy: 56789 +``` +From the code execution process, it starts copying from the 5th position of the `str` object `ss`. Since the set maximum copy length is the length of the `array` array (which is long enough here), 5 characters "56789" are actually copied to the `array` array. Then, a terminator `\0` is added and the result is printed. Finally, the `str` object is correctly released. + +### String Formatting of str +- **Function Functionality and Features**: + - The function `str_t str_format(str_t str, char *format,...);` realizes the string formatting operation of `str`. It borrows from and rewrites the `sprintf` function. Its usage is basically the same as that of `sprintf`, but it also has unique features. It adds the function that the format specifier `%s` can support the `str_t` type. This means that when formatting a string, `%s` can not only support the `char*` type string as traditionally, but also support the `str_t` object, achieving compatibility between the two string types. In addition, the `str_format` function will perform segmented dynamic expansion during the formatting process. Users don't need to worry about whether the length of the `buf` exceeds the limit as when using the `sprintf` function, making the operation more convenient. + - Here is a test example: +```c +void test(void) +{ + str_t ss = str(""); + str_t pp = str("format"); + str_format(ss, "Hello str! %s %s! int:%d, float:%.2f, char:%c", pp, "function", 18, 175.5, 'A'); + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); + _str(pp); +} +``` +The output result is: +``` +ss: Hello str! format function! int:18, float:175.50, char:A +``` +From this example, it can be clearly seen that when using the `str_format` function, there is no need to consider the space size required for the content to be formatted in advance. As long as the parameters are passed in according to the format requirements, the formatting operation can be successfully completed, which is very convenient. + +## Reference Examples +### Example 1: Judging Palindrome Strings +- **Code Logic and Implementation Steps**: + - The following is the code for judging whether the input string is a palindrome: +```c +void test(void) +{ + str_t s = str(""); + str_t r = str(""); + char buf[64] = {0}; + while (1) + { + scanf("%s", buf); // Input a string + str_assign(s, buf); // Assign the input string to s + str_assign(r, s); // Assign s to r + str_reverse(r, 0, str_length(r) - 1); // Reverse r + if (str_compare(s, r) == 0) // Compare whether s and r are the same + { + printf("This is a palindrome string!\r\n"); + } + else + { + printf("This is not a palindrome string!\r\n"); + } + } + _str(s); + _str(r); +} +``` + - This code continuously receives the strings input by the user in a loop. First, it assigns the input string to `s` through the `str_assign` function, then assigns `s` to `r`. Next, it uses the `str_reverse` function to reverse the `r` string. Finally, it compares whether `s` and the reversed `r` are the same through the `str_compare` function. If they are the same, it outputs that it is a palindrome string; if not, it outputs that it is not a palindrome string. For example, when inputting "qwewq", after the above operations, the comparison result is equal, and it will output "This is a palindrome string!". When inputting "zxcvfd", the comparison result is not equal, and it will output "This is not a palindrome string!". + +### Example 2: Deleting Repeated Characters in a String +- **Code Logic and Implementation Idea**: + - The following is the code for deleting repeated characters in a string: +```c +void test(void) +{ + str_t s = str(""); + str_t r = str(""); + char buf[64] = {0}; + + while (1) + { + scanf("%s", buf); // Input a string + str_assign(s, buf); // Assign the input string to s + str_assign(r, ""); // Set r to an empty string + for (int i = 0, n = str_length(s); i < n; i++) // Traverse s + { + char t[2] = {str_at(s, i),0}; // Take out the character of s and form a short array string + if (str_find(r, t, 0) == str_npos) // Judge whether this array string is in r, that is, judge whether the character of s is in r + { + str_append(r, t); // If it is not in r, append it to r + } + } + printf("%s\r\n", _S(r)); + } + + _str(s); + _str(r); +} +``` + - The code first receives the string input by the user and assigns it to `s`, and initializes `r` as an empty string. Then, it traverses each character of the `s` string. Each time, it takes out a character to form a short array string `t`, and then uses the `str_find` function to judge whether `t` has appeared in `r`. If it has not appeared (that is, returns `str_npos`), it uses the `str_append` function to append the character to `r`. Finally, it outputs the processed `r` string, thus realizing the function of removing repeated characters. For example, when inputting "16783679816488742135468794", after processing, it will output "167839425", and the repeated characters are removed. + +## Source Code Analysis +### str Structure +- **Purpose of Structure Design and Meaning of Members**: + - The structures of the `str` container are implicit. Such a design ensures the independence and security of the module, avoids external arbitrary access and modification of structure members, and thus prevents the destruction of the `str` storage structure. Its type declaration is `typedef struct STR *str_t;`. When using it, `str_t` can be used to represent this structure type. + - The specific definition of the `STR` structure is as follows: +```c +typedef struct STR +{ + char ident; /* ident of str */ + char* base; /* base address of string */ + int length; /* length of str */ + int capacity; /* capacity of str */ +} STR; +``` + - The `ident` member in the structure is the basis for distinguishing between array strings and `str` strings. The `base` member points to the base address where the string is actually stored. The `length` represents the size of `str`, that is, the length of the string. The `capacity` is the space capacity allocated for actually storing the string. However, in general usage scenarios, there is no need to pay special attention to capacity-related matters. + +### Distinguishing between Array Strings and str Strings +- **Distinguishing Mechanism and Code Implementation**: + - Array strings and `str` strings are distinguished by the `ident` member in the `STR` structure. The distinguishing code is as follows: +```c +typedef struct +{ + char *base; + int length; +} str_info_t; + +static str_info_t string_info(void *string) +{ + str_info_t info; + + if (((char *)string)[0] == 0) /* empty array string */ + { + info.base = (char *)string; + info.length = 0; + } + else if (((str_t)string)->ident == ident()) /* str type */ + { + info.base = ((str_t)string)->base; + info.length = ((str_t)string)->length; + } + else /* array string type */ + { + info.base = (char *)string; + info.length = strlen((char *)string); + } + + return info; +} +``` + - Because `ident` is in the first position of the structure, when `void *string` is passed in, the content of the first byte of its address is first judged. If it is `\0`, it is an empty array string. If it is `ident()` (which is defined as `-1`), it represents an `str` string. Other values represent array strings. The reason for choosing `-1` to distinguish is that in common ASCII encoding (the character range is `0 ~ 127`), Unicode encoding (the range is `0x0000 ~ 0x10FFFF`), and UTF-8 encoding, there will be no situation where it starts with `-1` (the corresponding unsigned value is `0xFF`). So it is feasible to use it as a distinguishing mark in these common encoding scenarios. In the APIs related to `str`, whenever it is necessary to distinguish the string type, the `string_info` function will be called to obtain the `str_info_t` type information containing the base address and length of the string. + +### str Self-Adapting Length +- **Implementation Principle Taking the Assignment Function as an Example**: + - Looking at the self-adapting length mechanism of `str` from the `str_assign` assignment function, the code is as follows: +```c +str_t str_assign(str_t str, void *string) +{ + str_info_t info; + if (!str) return NULL; + if (!string) return NULL;; + info = string_info(string); // Obtain the information (base address and length) of the string + if (!str_alter_capacity(str, info.length)) return NULL; // Adjust the capacity according to the length + strcpy(str->base, info.base); // Copy the string to the str space + str->length = info.length; // Update the length + return str; +} +``` + - First, it will obtain the relevant information (base address and length) of the string to be assigned through the `string_info` function. Then, it calls the `str_alter_capacity` function to adjust the capacity of the target `str` object according to the obtained length. If the capacity adjustment is successful, it copies the source string to the space of the target `str` object and updates its length. Finally, it returns the adjusted `str` object. If the capacity adjustment fails, it returns `NULL`. Inside the `str_alter_capacity` function, it will first judge whether the capacity needs to be changed. If the `str` object has not been allocated space originally, it will allocate new space through `malloc`. If there is already space, it will reallocate space through `realloc` and finally update the capacity value and return the corresponding result. + +### str Obtaining the Actual Stored Data +- **Function Implementation and Error Prevention Mechanism**: + - The function `char* str_data(str_t str, int pos)` is used to obtain the address of the character at the specified index position in the `str` object. Functions such as `str_at`, `str_c_str`, and `_S` are all implemented based on it. The code is as follows: +```c +char* str_data(str_t str, int pos) +{ + if (!str || pos < 0 || pos >= str->length) + { + error = 0; /* reset error area */ + return &error; + } + return &str->base[pos]; +} +``` + - The function will first perform parameter validity judgment. If the passed-in `str` object is empty or the specified position `pos` is illegal (less than 0 or exceeding the string length range), it will reset a static variable `error` defined in the module and return the address of `error`. This is to prevent the situation that returning `NULL` causes the program to crash due to subsequent operations on the null address. If the parameters are legal, it will return the address after offsetting the address pointed to by `base` in the `str` structure by `pos` positions, that is, the address of the character at the corresponding index position. + +### str String Replacement Principle +- **Key Steps and Logical Processing in the Replacement Operation**: + - The function `str_t str_replace(str_t str`, which involves the following key aspects in the replacement operation: +```c +str_t str_replace(str_t str, int pos, int len, void *string) +{ + str_info_t info; + char *overlap = NULL; + if (!str) return NULL; + if (!string) return NULL; + if (pos < 0 || pos > str->length) return NULL; + if (len > str->length - pos) len = str->length - pos; + info = string_info(string); + + /* Check if addresses overlap + This step is to check whether the addresses overlap, that is, whether the address of the string information overlaps with the original address of str. + The processing mechanism for overlap is to allocate a piece of space, copy the content to be replaced, + and then replace the copied content later. + */ + if (str->base <= info.base && info.base <= str->base + str->length && pos < str->length) + { + overlap = (char *)malloc(info.length + 1); + if (!overlap) return NULL; + strcpy(overlap, info.base); + info.base = overlap; + } + + /* + This step judges the length relationship between the string to be replaced and the replacing string, that is, judges whether the capacity needs to be adjusted. + */ + if (info.length > len) // The replacing string is longer and needs to expand the capacity + { + /* First expand the capacity, and the expanded capacity is the length difference */ + if (str_alter_capacity(str, str->length + (info.length - len)) == 0) + { + if (overlap) free(overlap); + return NULL; + } + /* Move the latter part of the data backward to make room for storing the longer replacing string */ + memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len)); + /* Copy the replacing string into the vacated space */ + memcpy(&str->base[pos], info.base, info.length); + } + else if (info.length < len) /* The length becomes smaller */ + { + /* Move the latter data forward, leaving the length of info for the replacing string */ + memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len)); + /* Copy the replacing string into the vacated space */ + memcpy(&str->base[pos], info.base, info.length); + /* For shrinking, adjust the capacity later */ + str_alter_capacity(str, str->length + (info.length - len)); + } + else /* The length has not changed */ + { + /* Directly overwrite */ + memcpy(&str->base[pos], info.base, info.length); + } + str->length += (info.length - len); + str->base[str->length] = 0; + if (overlap) free(overlap); + + return str; +} +``` +The function first performs some basic parameter validity checks and obtains information about the string to be inserted through the `string_info` function. Then, it checks whether the addresses of the original string and the string to be inserted overlap. If there is an overlap, it will allocate additional space to handle it properly. Next, it compares the lengths of the two strings to determine whether to expand, shrink, or directly overwrite the capacity of the original string. Finally, it updates the length of the string in the `str` object, adds a terminator, and releases the temporarily allocated space if necessary, and then returns the modified `str` object. And the functions `str_insert` and `str_erase` are implemented based on the `str_replace` function. For example, `str_insert` is implemented by calling `str_replace` with a length of 0 for the replacement part, and `str_erase` is implemented by calling `str_replace` with an empty string for the replacement part. + +### Principle of str Concatenation +The actual operation is to call the `str_append_series` function for parameter passing, and append a `NULL` at the end of the variable arguments. + +Inside the `str_append_series` function, it processes the variable arguments. It inserts each segment of strings to the end of the `str` one by one through the `str_insert` function to complete the concatenation. The specific code is as follows: +```c +#define str_append(str,...) str_append_series((str), ##__VA_ARGS__, NULL) +str_t str_append_series(str_t str,...) +{ + va_list args; + void* s = NULL; + if (!str) return NULL; + va_start(args, str); + s = va_arg(args, void*); + while (s) + { + /* Insert at the end */ + if (!str_insert(str, str->length, s)) + { + va_end(args); + return NULL; + } + s = va_arg(args, void*); + } + va_end(args); + return str; +} +``` + +### Source Code of str Formatting +The source code of the `str_format` function is relatively long. The general process is to traverse the `format` string. When encountering the `%` symbol, specific formatting operations will be carried out. Taking the `%` symbol as the delimiter, the entire `format` string is divided into segments for dynamic expansion and assignment. The detailed code is shown below: +```c +str_t str_format(str_t str, char *format,...) +{ + va_list args; + int i = 0; + int len = 0; /* length of sub string */ + char *s = NULL; /* temp string */ + str_info_t info; /* str info */ + double dbl = 0.0; /* double precision floating point */ + char *begin = NULL; /* begin of format */ + char qualifier = 0; /* conversion qualifier for integer as 'h','l','L' */ + int width = 0; /* transition field width */ + int precision = 0; /* minimum digits of integers and maximum digits of strings */ + char tfmt[16] = "%"; /* temporary format */ + + if (!str) return NULL; + str_assign(str, ""); + va_start(args, format); + for (; *format; format++) + { + /* When it is an ordinary character */ + if (*format!= '%') + { + if (!begin) begin = format; // Record the starting position of the ordinary character segment + continue; // Skip this loop iteration and move to the next one + } + + /* If the starting position of the ordinary character has been recorded, it means the previous part of the current position is ordinary characters */ + if (begin) + { + len = format - begin; // Record the length for later expansion + if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY; + while (len--) str->base[str->length++] = *begin++; // Copy the ordinary characters in + begin = NULL; + } + + /* Start processing the formatted data segment from here */ + begin = format; + while (1) // Record the prefix symbol flag + { + /* Also skips the first '%' */ + format++; + if ((*format!= '0') && + (*format!= ' ') && + (*format!= '+') && + (*format!= '-') && + (*format!= '#')) break; + } + + /* Get field width */ + width = -1; + if (is_digit(*format)) + { + format += get_digit(format, &width); + } + else if (*format == '*') + { + format++; + width = va_arg(args, int); + if (width < 0) width = -width; + } + + /* Get the precision */ + precision = -1; + if (*format == '.') + { + format++; + if (is_digit(*format)) format += get_digit(format, &precision); + else if (*format == '*') + { + format++; + precision = va_arg(args, int); + } + if (precision < 0) precision = 0; + } + + /* Get the conversion qualifier */ + qualifier = 0; + if (*format == 'h' || *format == 'l' || *format == 'L') + { + qualifier = *format; + format++; + if (qualifier == 'l' && *format == 'l') + { + qualifier = 'L'; + format++; + } + } + + /* Format distribution */ + switch (*format) + { + case 'c': + // Estimate how much space is needed + // Call sprintf to append a character at the end + ... + case 's': + // Estimate how much space is needed + // Call sprintf to append a segment of string at the end + ... + case 'p': + // Estimate how much space is needed + // Call sprintf to append a pointer at the end + ... + case 'a': + case 'A': + case 'e': + case 'E': + case 'g': + case 'G': + case 'f': + // Estimate how much space is needed + // Call sprintf to append a floating-point number at the end + ... + case 'o': + case 'X': + case 'x': + case 'd': + case 'i': + case 'u': + // Estimate how much space is needed + // Call sprintf to append an integer at the end + ... + case '%': + // Push in % + ... + default: + // Push in % + ... + } + } + + /* Copy the tail string to str */ + if (begin) + { + len = format - begin; + if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY; + while (len--) str->base[str->length++] = *begin++; + } + + if (!str_alter_capacity(str, str->length)) goto FAIL_CAPACITY; + str->base[str->length] = '\0'; + va_end(args); + return str; + +FAIL_CAPACITY: + str->base[str->length] = '\0'; + va_end(args); + return NULL; +} +``` diff --git a/doc/tool.en.md b/doc/tool.en.md new file mode 100644 index 0000000..6f9751b --- /dev/null +++ b/doc/tool.en.md @@ -0,0 +1,4 @@ +### Introduction + +This tool module encompasses several commonly used code tools, such as frequently utilized macro definitions and so on. + diff --git a/doc/tree.en.md b/doc/tree.en.md new file mode 100644 index 0000000..46817e8 --- /dev/null +++ b/doc/tree.en.md @@ -0,0 +1,402 @@ +### Introduction + +#### 1. Basic Concepts + +- **Definition**: A tree is a non-linear data structure composed of n (n ≥ 0) nodes. Each node can have zero or more child nodes, but only one parent node (except for the root node). This structure is similar to a tree in nature and has obvious hierarchy[^1^][^2^][^3^]. + +- **Characteristics**: + - A tree has a root node and leaf nodes. The root node is the node without a parent node, while leaf nodes are nodes without child nodes[^1^][^2^][^3^]. + - Except for the root node, each node has exactly one parent node[^1^][^2^][^3^]. + - The degree of a tree refers to the maximum degree of all nodes in the tree, that is, the maximum number of child nodes a node has[^2^][^3^]. + - The height or depth of a tree refers to the number of edges on the longest path from the root node to the farthest leaf node[^2^][^3^]. + +#### 2. Basic Terminology + +- The degree of a node: The number of child nodes a node has is called the degree of the node[^2^][^3^]. + +- Leaf node: A node with a degree of 0 is called a leaf node[^2^][^3^]. + +- Non-terminal node or branch node: A node with a degree not equal to 0 is called a branch node[^2^][^3^]. + +- Parent node: If a node contains child nodes, then this node is called the parent node of its child nodes[^2^][^3^]. + +- Child node: The root node of the subtree contained by a node is called the child node of this node[^2^][^3^]. + +- Sibling nodes: Nodes that have the same parent node are called sibling nodes[^2^][^3^]. + +- Ancestor nodes: All nodes on the branches from the root to a certain node are the ancestor nodes of this node[^2^][^3^]. + +- Descendants: Any node in the subtree with a certain node as the root is called a descendant of this node[^2^][^3^]. + +- Forest: A collection of m (m > 0) mutually disjoint trees is called a forest[^2^][^3^]. + +#### 3. Representation Methods + +There are multiple ways to represent a tree, including the parent representation method, the child representation method, and the child-sibling representation method. Among them, the child-sibling representation method is the most commonly used one. It stores the whole tree using a binary linked list, where the left pointer points to its first child and the right pointer points to its adjacent sibling[^1^][^2^][^3^]. + +#### 4. Special Types + +- Binary tree: A binary tree is a special type of tree. Each node of it has at most two child nodes, usually called the left child node and the right child node[^5^]. + +- Balanced tree: A balanced tree is a special binary tree whose height difference between the left and right subtrees does not exceed 1. Common balanced trees include AVL trees, red-black trees, etc.[^5^]. + +#### 5. Traversal Methods + +The main traversal methods of a tree include preorder traversal, inorder traversal, postorder traversal, and level-order traversal. These traversal methods can help us visit each node in the tree and perform processing as needed[^5^]. + +#### 6. Practical Applications + +Trees have a wide range of applications in practice, such as in file systems, database indexes, and compiler design. In a file system, the tree structure is used to organize and manage files. In a database, the tree structure is used to build efficient indexes. In compiler design, the tree structure is used to represent the syntax structure of a program[^5^]. + +### Interfaces + +#### 1. Creation and Deletion of `tree` Objects + +```c +tree_t tree_create(void); +void tree_delete(tree_t tree, void (*func)(tree_t tree)); +``` + +**Creation Method**: +It is used to create an empty tree, that is, to initialize an instance of the tree structure, preparing for subsequent operations such as adding nodes to the tree and setting data. If the creation is successful, it will return the handle of the tree (of type `tree_t`). Subsequent operations on this tree can be performed through this handle. If problems such as memory allocation failure occur during the creation process and lead to creation failure, it will return `NULL`. This function has no input parameters and can be directly called to attempt to create a new empty tree structure. + +**Deletion Method**: +It is responsible for deleting the specified tree structure, releasing the memory resources occupied by the tree and all its nodes. Meanwhile, an additional execution function can be passed in to perform some custom cleanup operations (such as releasing specific resources in nodes) for each node during the tree deletion process, ensuring that all resources related to the tree can be properly handled and avoiding problems like memory leaks. This function has no return value and finishes its task after performing the corresponding deletion and resource release operations. +- `tree`: The handle of the tree to be deleted. The specific tree to be deleted is specified through the tree handle obtained by the `tree_create` function or other means, ensuring that the corresponding tree and all its associated resources are accurately cleaned up. +- `func`: A pointer to a function. This function takes the tree handle as a parameter and is used to perform additional custom operations when deleting each node of the tree. If no additional operations are needed, `NULL` can be passed as this parameter. + +```c +static void test_create(void) +{ + tree_t tree = tree_create(); + + if (tree) + { + printf("tree create success!!!\r\n"); + } + else + { + printf("[ERROR] tree create fail!!!\r\n"); + } + + tree_delete(tree, NULL); +} +``` + +#### 2. Setting and Getting Data of `tree` + +```c +int tree_set_data(tree_t tree, void* data, int size); +int tree_get_data(tree_t tree, void* data, int size); +const void* tree_data(tree_t tree); +``` + +**Setting**: +It is used to set the given data into the specified tree node, overwriting the original data content. If the size of the passed-in data is 0, the original data will be deleted. If the data setting operation is successful, the function returns 1. If the tree node does not exist or the passed-in parameters are illegal and other situations lead to setting failure, it returns 0. +- `tree`: The handle of the tree. Through this handle, the target tree node where the data is to be set is located, ensuring that the data can be accurately set into the corresponding node. +- `data`: A pointer to the data to be set. The data content pointed to by this pointer will be copied into the tree node, replacing the original data. Its data type and size should meet the requirements of the tree node for data. +- `size`: The size of the data to be set, in bytes. If this value is 0, the operation of deleting the original data will be executed; otherwise, the new data will be copied into the tree node according to this size. + +**Getting**: +It retrieves data from the specified tree node and copies the retrieved data into the memory space pointed to by the passed-in pointer. If the retrieval operation is successful, the function returns 1. If the tree node does not have data or the passed-in parameters are illegal (such as the passed-in memory space being insufficient to store the data) and other situations lead to retrieval failure, it returns 0. +- `tree`: The handle of the tree. Based on this handle, the corresponding tree node is found, and data is retrieved from this node, ensuring that the data of the target node is accurately retrieved. +- `data`: A pointer to the memory space used to store the retrieved data. The memory space pointed to by this pointer needs to have sufficient capacity to store the data retrieved from the tree node, and its data type should also match that of the tree node's data. +- `size`: The expected size of the data to be retrieved, in bytes. It needs to match the actual size of the data stored in the tree node, otherwise retrieval failure may occur. + +```c +static void test_set(void) +{ + tree_t tree = tree_create(); + int set = 100; + int get = 0; + + tree_set_data(tree, &set, sizeof(set)); + + tree_get_data(tree, &get, sizeof(get)); + + printf("get %d\r\n", get); + printf("data %d\r\n", *(int *)tree_data(tree)); + + tree_delete(tree, NULL); +} +``` + +#### 3. Insertion and Erasure of Child Nodes of `tree` + +```c +int tree_insert(tree_t tree, int index); +int tree_erase(tree_t tree, int index); +``` + +**`tree_insert`**: +It inserts an empty child space at the specified index position of the specified tree for subsequent operations such as adding a subtree to this position. If the insertion operation is successful, the function returns 1. If the index is out of range, the tree does not exist, or other situations lead to insertion failure, it returns 0. +- `tree`: The handle of the tree. It is used to locate the target tree where the insertion operation is to be performed. Only with the correct corresponding tree can the insertion position be accurately found and the insertion operation be executed. +- `index`: The index position where the empty child space is to be inserted. This index represents the position number of the node in the tree, starting from 0. Through this index, the position in the tree where the new child space is inserted is determined. However, it is necessary to ensure that the passed-in index value is within the valid range of the tree (less than the current number of nodes and within other reasonable ranges). + +**`tree_erase`**: +It is used to erase the empty child space at the specified index position of the specified tree. If the erasure operation is successful, the function returns 1. If the index is illegal, there is no empty child space at the corresponding position, or the tree itself has problems and other situations lead to erasure failure, it returns 0. +- `tree`: The handle of the tree. Through this handle, the target tree where the erasure operation is to be performed is determined. Based on the tree structure, the corresponding index position is found to execute the operation of erasing the empty child space. +- `index`: The index position where the empty child space to be erased is located. Similar to the index in `tree_insert`, it needs to be within the valid index range of the tree to accurately locate the target position to be processed. + +```c +static void test_insert(void) +{ + tree_t tree = tree_create(); + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + printf("tree child size %d\r\n", tree_csize(tree)); + + tree_erase(tree, 2); + + printf("tree child size %d\r\n", tree_csize(tree)); + + tree_delete(tree, NULL); +} +``` + +#### 4. Attachment and Detachment of Child Nodes of `tree` + +```c +int tree_attach(tree_t tree, int index, tree_t attach); +tree_t tree_detach(tree_t tree, int index); +``` + +**`tree_attach`**: +It attaches an independent tree to the specified index position of another tree, realizing the combination and expansion of the tree structure and building a more complex tree-like hierarchical structure. If the attachment operation is successful, the function returns 1. If the index is illegal, there are problems with the target tree or the tree to be attached (such as an invalid handle), and other situations lead to attachment failure, it returns 0. +- `tree`: The handle of the target tree, that is, the handle of the tree to which other trees are to be attached. Through it, the main tree structure where the attachment target position is located is determined. +- `index`: The index position in the target tree. It is used to specify at which position node of the target tree the independent tree is to be attached. It is also necessary to ensure that the index value is within the valid range of the target tree. +- `attach`: The handle of the independent tree to be attached. It points to another already created tree structure, and this tree will be added to the specified position of the target tree and become a part of the target tree. + +**`tree_detach`**: +It detaches a subtree from the specified index position of the specified tree and returns the handle of the detached subtree. If the detachment operation is successful, subsequent operations can be performed on the detached subtree through the returned handle. If the index is illegal, there is no subtree at the corresponding position, or the tree itself has problems and other situations lead to detachment failure, it returns `NULL`. +- `tree`: The handle of the tree. It is used to locate the original tree where the detachment operation is to be performed. A subtree is attempted to be detached from the specified index position of this tree. +- `index`: The index position where the subtree to be detached is located. Through this index, the target subtree's position in the original tree is accurately found for the detachment operation. It is necessary to ensure that the index is valid. + +```c +static void test_attach(void) +{ + tree_t tree = tree_create(); + tree_t node = NULL; + int data = 0; + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + for (int i = 0; i < tree_csize(tree); i++) + { + node = tree_create(); + data = 1000 + i; + tree_attach(tree, i, node); + tree_set_data(node, &data, sizeof(data)); + } + + tree_print(tree, 0, print_int); + + node = tree_detach(tree, 1); + tree_delete(node, NULL); + + tree_print(tree, 0, print_int); + + tree_delete(tree, NULL); +} +``` + +#### 5. Getting Related Nodes of `tree` + +```c +tree_t tree_parent(tree_t tree); +tree_t tree_child(tree_t tree, int index); +``` + +**`tree_parent`**: +It is used to get the handle of the parent tree of the specified tree. If the tree has a parent tree, it returns the handle of the parent tree. Through this handle, further operations on the parent tree can be performed. If the tree has no parent tree (such as when it is the tree where the root node is located or it is an independent tree), it returns `NULL`. +- `tree`: The handle of the tree. Through it, the target tree whose parent tree is to be found is located, and the corresponding parent tree is found according to the hierarchical structure of the tree and its handle is returned. + +**`tree_child`**: +It gets the handle of the subtree at the specified index position of the specified tree. If there is a subtree at the index position, it returns the corresponding subtree handle, facilitating subsequent operations on the subtree. If the index is illegal or there is no subtree at the corresponding position, it returns `NULL`. +- `tree`: The handle of the tree. It is used to determine the target tree where the subtree is to be found. In the structure of this tree, the corresponding subtree handle is located and obtained according to the index. +- `index`: The index position of the subtree in the target tree, starting from 0. Through this index, the corresponding subtree is searched for in the target tree. It is necessary to ensure that the index is within the valid range. + +```c +static void test_parent(void) +{ + tree_t tree = tree_create(); + tree_t node = NULL; + tree_t parent, child; + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + tree_set_data(tree, "Parent", 7); + + node = tree_create(); tree_set_data(node, "Child0", 7); tree_attach(tree, 0, node); + node = tree_create(); tree_set_data(node, "Child1", 7); tree_attach(tree, 1, node); + node = tree_create(); tree_set_data(node, "Child2", 7); tree_attach(tree, 2, node); + node = tree_create(); tree_set_data(node, "Child3", 7); tree_attach(tree, 3, node); + + tree_print(tree, 0, print_string); + + parent = tree_parent(node); print_string(parent); printf("\r\n"); + + child = tree_child(tree, 0); print_string(child); printf("\r\n"); + child = tree_child(tree, 1); print_string(child); printf("\r\n"); + child = tree_child(tree, 2); print_string(child); printf("\r\n"); + child = tree_child(tree, 3); print_string(child); printf("\r\n"); + + tree_delete(tree, NULL); +} +``` +### Setting and Retrieving Attributes of `tree` +```c +int tree_set_attribute(tree_t tree, void* attribute, int size); +int tree_get_attribute(tree_t tree, void* attribute, int size); +const void* tree_attribute(tree_t tree); +``` + +#### `tree_set_attribute` +This function is used to set the given attribute data into the specified tree node. It will overwrite the original attribute content. If the size of the passed-in attribute data is 0, the original attribute data will be deleted. If the attribute setting operation is successful, the function returns 1. If the tree node does not exist or the parameters are illegal and other situations lead to setting failure, it returns 0. +- **`tree`**: The handle of the tree. Through this handle, the target tree node where the attribute is to be set can be located to ensure that the attribute can be accurately set into the corresponding node. +- **`attribute`**: A pointer to the attribute data to be set. The data content pointed to by this pointer will be copied into the tree node, replacing the original attribute data. Its data type and size should meet the requirements of the tree node for attributes. +- **`size`**: The size of the attribute data to be set, in bytes. If this value is 0, the operation of deleting the original attribute data will be executed; otherwise, the new attribute data will be copied into the tree node according to this size. + +#### `tree_get_attribute` +It retrieves the attribute data from the specified tree node and copies the retrieved data into the memory space pointed to by the passed-in pointer. If the retrieval operation is successful, the function returns 1. If the tree node does not have attribute data or the passed-in parameters are illegal (such as the passed-in memory space being insufficient to store the attribute data) and other situations lead to retrieval failure, it returns 0. +- **`tree`**: The handle of the tree. Based on this handle, the corresponding tree node is found to retrieve the attribute data content from this node, ensuring the accurate retrieval of the target node's attribute data. +- **`attribute`**: A pointer to the memory space used to store the retrieved attribute data. The memory space pointed to by this pointer needs to have sufficient capacity to store the attribute data retrieved from the tree node, and its data type should also match that of the tree node's attribute data. +- **`size`**: The expected size of the attribute data to be retrieved, in bytes. It needs to match the actual size of the attribute data stored in the tree node, otherwise retrieval failure may occur. + +#### `tree_attribute` +It gets the address of the attribute information of the specified tree node. If the node has valid attribute data, it returns its address pointer, which is convenient for subsequent access and operation on the attribute data. If the node has no attribute or does not exist and other situations, it returns `NULL`. +- **`tree`**: The handle of the tree. By using this handle, the specific tree node is located to obtain its associated attribute information address. Different tree nodes may have different attribute situations, and through the handle, the attribute data address of the corresponding node can be accurately obtained. + +```c +static void test_attr(void) +{ + tree_t tree = tree_create(); + char attr[20]; + + tree_set_attribute(tree, "This attribute!!!", 18); + tree_get_attribute(tree, attr, sizeof(attr)); + + printf("attr %s\r\n", attr); + + tree_delete(tree, NULL); +} +``` + +### Depth of `tree` +```c +int tree_depth(tree_t tree); +``` +This function is used to obtain the depth of the specified tree. The depth calculation includes the level where the tree itself is located. The returned integer value represents the number of levels from the root node to the farthest leaf node, which can be used to understand the complexity of the tree's hierarchical structure and other situations. +- **`tree`**: The handle of the tree. Through this handle, the corresponding tree structure is located, and then its depth information is calculated and returned. Different trees have different depths due to their different structures, and through the handle, the depth situation of the corresponding tree can be accurately obtained. + +```c +static void test_depth(void) +{ + tree_t root = tree_create(); + tree_t node, temp; + + tree_insert(root, 0); + tree_insert(root, 1); + tree_insert(root, 2); + + tree_attach(root, 0, tree_create()); + tree_attach(root, 1, tree_create()); + + node = tree_child(root, 0); + tree_insert(node, 0); + tree_insert(node, 1); + + node = tree_child(root, 1); + tree_insert(node, 0); + tree_insert(node, 1); + tree_attach(node, 0, tree_create()); + tree_attach(node, 1, tree_create()); + + tree_print(root, 0, NULL); + + printf("depth %d\r\n", tree_depth(root)); + + tree_delete(root, NULL); +} +``` + +### Continuously Retrieving Subtrees of `tree` +```c +tree_t tree_to_valist(tree_t tree, int index,...); +#define tree_to(tree, i,...) +``` +It retrieves subtrees in the tree through continuous index values. Starting from the specified tree, it searches for subtrees in the order of the passed-in indexes one by one until a negative index is encountered to stop the search. If the corresponding subtrees can be successfully found, it returns the handle of the final subtree. If during the search process, situations such as illegal indexes or no subtrees at the corresponding positions lead to the failure to find the target subtrees, it returns `NULL`. The `tree_to` is a simpler macro definition version, with a negative number added by default at the end of the parameters. +- **`tree`**: The handle of the tree. It is used to determine the starting tree structure for the search. Subsequent index searches are all based on this tree, which is the basic starting point of the entire search operation. +- **`index`**: The first index value. Starting from this index position, the search for subtrees in the tree begins. More index values can be passed in later (implemented through variable parameters). However, it is necessary to ensure that each index is legal and valid and within the range of the corresponding tree structure. +- **`...`**: The variable parameter part, representing the subsequent other index values. These index values are used continuously to continue searching for the next level of subtrees in the found subtrees until a negative index is encountered to stop the search process. + +```c +static void test_to(void) +{ + tree_t root = tree_create(); + tree_t node, temp; + + tree_insert(root, 0); + tree_insert(root, 1); + tree_insert(root, 2); + + tree_attach(root, 0, tree_create()); + tree_attach(root, 1, tree_create()); + + node = tree_child(root, 0); + tree_insert(node, 0); + tree_insert(node, 1); + + node = tree_child(root, 1); + tree_insert(node, 0); + tree_insert(node, 1); + tree_attach(node, 0, tree_create()); + tree_attach(node, 1, tree_create()); + + temp = tree_to(root, 1, 1); + tree_set_data(temp, "To function!!!", 15); + + tree_print(root, 0, print_string); + + tree_delete(root, NULL); +} +``` + +### Size of `tree` +```c +int tree_csize(tree_t tree); +int tree_dsize(tree_t tree); +int tree_asize(tree_t tree); +int tree_size(tree_t tree); +``` + +#### `tree_csize` +It is used to obtain the number of subtrees of the specified tree, that is, the number of child nodes the tree has. The returned integer value can help developers understand the branching situation of the tree, for example, to judge whether a certain node of the tree has child nodes. + +#### `tree_dsize` +It gets the size of the data stored in the specified tree node. The returned integer value represents the number of bytes occupied by the corresponding node's data, which is helpful for understanding the scale of the data and making judgments in some operations related to the data size. + +#### `tree_asize` +It obtains the size of the attribute data of the specified tree node. The returned integer value represents the number of bytes occupied by the corresponding node's attribute information, which can be used to understand the scale of the attribute data and as a judgment basis in related operations. + +#### `tree_size` +It gets the total number of nodes of the specified tree. The returned integer value represents the number of all nodes contained in the whole tree, which can intuitively reflect the size of the tree and is convenient for developers to refer to the overall scale of the tree when performing some traversal, statistics and other operations. + +### Printing `tree` +```c +void tree_print(tree_t tree, int depth, void (*print)(tree_t tree)); +``` +This function is used to perform an extended print display of the tree structure according to the specified depth. Through the passed-in print function (the developer customizes the specific print logic), relevant information of the tree, such as node data and hierarchical relationships, is output. If the `depth` parameter is 0, the entire information of the whole tree will be printed. If a specific depth value is passed in, the corresponding levels of the tree structure will be printed according to that depth. This function has no return value and is mainly used for visualizing the structure of the tree. +- **`tree`**: The handle of the tree. Through this handle, the target tree structure to be printed and displayed is determined. Different trees have their own independent structures and data, and through the handle, the corresponding tree information can be accurately distinguished and printed. +- **`depth`**: The depth to be printed, represented by the number of levels. A value of 0 means printing all the level information of the whole tree. If an integer greater than 0 is passed in, the corresponding layers of the tree structure will be printed starting from the root node according to that number of levels. +- **`print`**: A pointer to a custom print function. This function takes the tree handle as a parameter. The developer needs to implement the specific print logic inside this function, such as in what format to output node data and how to reflect hierarchical relationships, so as to achieve a personalized print display effect of the tree structure. diff --git a/doc/tree.md b/doc/tree.md new file mode 100644 index 0000000..199cedd --- /dev/null +++ b/doc/tree.md @@ -0,0 +1,405 @@ +## 介绍 + +### 一、基本概念 + +1. **定义**:树是一种非线性的数据结构,由n(n≥0)个节点组成,每个节点可以有零个或多个子节点,但只有一个父节点(根节点除外)。这种结构类似于自然界中的树,具有明显的层次性[^1^][^2^][^3^]。 + +2. **特点**: + - 树具有根节点和叶节点。根节点是没有父节点的节点,而叶节点是没有子节点的节点[^1^][^2^][^3^]。 + - 除了根节点外,每个节点有且仅有一个父节点[^1^][^2^][^3^]。 + - 树的度是指树中所有节点的最大度数,即节点拥有的最大子节点数[^2^][^3^]。 + - 树的高度或深度是指从根节点到最远叶节点的最长路径上的边数[^2^][^3^]。 + +### 二、基本术语 + +1. **节点的度**:节点拥有的子节点数称为节点的度[^2^][^3^]。 + +2. **叶节点**:度为0的节点称为叶节点[^2^][^3^]。 + +3. **非终端节点或分支节点**:度不为0的节点称为分支节点[^2^][^3^]。 + +4. **双亲节点或父节点**:若一个节点含有子节点,则这个节点称为其子节点的父节点[^2^][^3^]。 + +5. **孩子节点或子节点**:一个节点含有的子树的根节点称为该节点的子节点[^2^][^3^]。 + +6. **兄弟节点**:具有相同父节点的节点互称为兄弟节点[^2^][^3^]。 + +7. **祖先节点**:从根到该节点所经分支上的所有节点都是这个节点的祖先节点[^2^][^3^]。 + +8. **子孙**:以某节点为根的子树中任一节点都称为该节点的子孙[^2^][^3^]。 + +9. **森林**:由m(m>0)棵互不相交的树的集合称为森林[^2^][^3^]。 + +### 三、表示方法 + +树的表示方法有多种,包括双亲表示法、孩子表示法、孩子兄弟表示法等。其中,孩子兄弟表示法是最常用的一种表示方法,它将整棵树用二叉链表存储起来,左指针指向自己的第一个孩子,右指针指向与自己相邻的兄弟[^1^][^2^][^3^]。 + +### 四、特殊类型 + +1. **二叉树**:二叉树是一种特殊的树,它的每个节点最多有两个子节点,通常称为左子节点和右子节点[^5^]。 + +2. **平衡树**:平衡树是一种特殊的二叉树,它的左右子树的高度差不超过1。常见的平衡树包括AVL树、红黑树等[^5^]。 + +### 五、遍历方法 + +树的遍历方法主要有前序遍历、中序遍历、后序遍历和层序遍历。这些遍历方法可以帮助我们访问树中的每一个节点,并根据需要进行处理[^5^]。 + +### 六、实际应用 + +树在实际中有着广泛的应用,如文件系统、数据库索引、编译器设计等[^5^]。在文件系统中,树形结构用于组织和管理文件;在数据库中,树形结构用于构建高效的索引;在编译器设计中,树形结构用于表示程序的语法结构等。 + + +## 接口 + +### 创建和删除tree对象 + +```c +tree_t tree_create(void); +void tree_delete(tree_t tree, void (*func)(tree_t tree)); +``` + +创建方法: +用于创建一个空树,即初始化一个树结构的实例,为后续向树中添加节点、设置数据等操作做准备。若创建成功,会返回该树的句柄(`tree_t`类型),后续对这棵树的其他操作都可以通过该句柄来进行;若创建过程中出现内存分配失败等问题导致创建失败,则返回`NULL`。该函数无输入参数,直接调用即可尝试创建一个新的空树结构。 + +删除方法: +负责删除指定的树结构,释放树及其所有节点占用的内存资源,同时还可以传入一个额外的执行函数,在删除树的过程中针对每个节点执行一些自定义的清理操作(比如释放节点中特定资源等),以确保树相关的所有资源都能被妥善处理,避免出现内存泄漏等问题。此函数无返回值,执行完相应的删除和资源释放操作后即完成任务。 +- `tree`:要删除的树的句柄,通过之前`tree_create`函数创建或者其他途径获取的树句柄来指定具体要删除的是哪棵树,确保准确地对相应树及其关联的所有资源进行清理操作。 +- `func`:指向一个函数的指针,该函数接受树的句柄作为参数,用于在删除树的每个节点时执行额外的自定义操作,若不需要执行额外操作,可以传入`NULL`作为该参数。 + +```c +static void test_create(void) +{ + tree_t tree = tree_create(); + + if (tree) + { + printf("tree create success!!!\r\n"); + } + else + { + printf("[ERROR] tree create fail!!!\r\n"); + } + + tree_delete(tree, NULL); +} +``` + +### tree的设置和获取数据 + +```c +int tree_set_data(tree_t tree, void* data, int size); +int tree_get_data(tree_t tree, void* data, int size); +const void* tree_data(tree_t tree); +``` + +设置: +用于将给定的数据设置到指定树节点中,会覆盖原有的数据内容,如果传入的数据大小为0,则会删除原有的数据。若设置数据操作成功,函数返回1;若树节点不存在、传入的参数不合法等情况导致设置失败,则返回0。 +- `tree`:树的句柄,通过该句柄定位到要设置数据的目标树节点,确保数据能准确设置到对应的节点中。 +- `data`:指向要设置的数据的指针,该指针指向的数据内容会被复制到树节点中,替换原有的数据,其数据类型和大小应符合树节点对数据的要求。 +- `size`:要设置的数据的大小,以字节为单位,若该值为0,则执行删除原数据的操作;否则按照该大小将新数据复制到树节点中。 + +获取: +从指定树节点中获取数据,并将获取到的数据复制到传入的指针所指向的内存空间中,若获取操作成功,函数返回1;若树节点不存在数据、传入的参数不合法(如传入的内存空间大小不足以存储数据等)导致获取失败,则返回0。 +- `tree`:树的句柄,依据该句柄找到对应的树节点,从该节点中获取数据内容,确保准确获取目标节点的数据。 +- `data`:指向用于存储获取的数据的内存空间的指针,该指针指向的内存空间需要有足够的容量来存放从树节点获取的数据,其数据类型也应与树节点的数据类型匹配。 +- `size`:期望获取的数据大小,以字节为单位,需要与树节点实际存储的数据大小相匹配,否则可能导致获取失败。 + +```c +static void test_set(void) +{ + tree_t tree = tree_create(); + int set = 100; + int get = 0; + + tree_set_data(tree, &set, sizeof(set)); + + tree_get_data(tree, &get, sizeof(get)); + + printf("get %d\r\n", get); + printf("data %d\r\n", *(int *)tree_data(tree)); + + tree_delete(tree, NULL); +} +``` + +### tree插入和擦除子节点 +```c +int tree_insert(tree_t tree, int index); +int tree_erase(tree_t tree, int index); +``` + +tree_insert: +在指定树的指定索引位置插入一个空的子空间,用于后续向该位置添加子树等操作。若插入操作成功,函数返回1;若索引超出范围、树不存在或者出现其他导致插入失败的情况,则返回0。 +- `tree`:树的句柄,用于定位要进行插入操作的目标树,只有对应正确的树才能准确找到插入的位置并执行插入操作。 +- `index`:要插入空孩子空间的索引位置,该索引表示树中节点的位置序号,从0开始计数,通过该索引确定在树的哪个位置插入新的子空间,不过要确保传入的索引值在树的有效范围内(小于当前节点数等合理范围)。 + +tree_erase: +用于擦除指定树中指定索引位置的空孩子空间。若擦除操作成功,函数返回1;若索引不合法、对应位置不存在空孩子空间或者树本身存在问题等导致擦除失败,则返回0。 +- `tree`:树的句柄,通过该句柄确定要进行擦除操作的目标树,依据树的结构找到对应的索引位置来执行擦除空孩子空间的操作。 +- `index`:要擦除的空孩子空间所在的索引位置,与`tree_insert`中的索引类似,需在树的有效索引范围内,以此准确定位到要处理的目标位置。 + +```c +static void test_insert(void) +{ + tree_t tree = tree_create(); + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + printf("tree child size %d\r\n", tree_csize(tree)); + + tree_erase(tree, 2); + + printf("tree child size %d\r\n", tree_csize(tree)); + + tree_delete(tree, NULL); +} + +``` + +### tree附着和分离子节点 +```c +int tree_attach(tree_t tree, int index, tree_t attach); +tree_t tree_detach(tree_t tree, int index); +``` + +tree_attach: +将一个独立的树附着到另一个树的指定索引位置上,实现树结构的组合与扩展,构建更复杂的树状层次结构。若附着操作成功,函数返回1;若索引不合法、目标树或要附着的树存在问题(如句柄无效等)导致附着失败,则返回0。 +- `tree`:目标树的句柄,即要将其他树附着到的那个树的句柄,通过它确定附着的目标位置所在的树结构主体。 +- `index`:在目标树中的索引位置,用于指定将独立树附着到目标树的哪个位置节点下,同样要保证索引值在目标树的有效范围内。 +- `attach`:要附着的独立树的句柄,指向另一个已经创建好的树结构,该树会被添加到目标树的指定位置,成为目标树的一部分。 + +tree_detach: +从指定树的指定索引位置分离出子树,返回分离出来的子树句柄,若分离操作成功,可通过返回的句柄对分离出的子树进行后续操作;若索引不合法、对应位置没有子树或者树本身存在问题等导致分离失败,则返回`NULL`。 +- `tree`:树的句柄,用于定位要进行分离操作的原始树,从该树的指定索引位置尝试分离出子树。 +- `index`:要分离子树所在的索引位置,通过该索引准确找到目标子树在原始树中的位置,进行分离操作,需确保索引是有效的。 + + +```c +static void test_attach(void) +{ + tree_t tree = tree_create(); + tree_t node = NULL; + int data = 0; + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + for (int i = 0; i < tree_csize(tree); i++) + { + node = tree_create(); + data = 1000 + i; + tree_attach(tree, i, node); + tree_set_data(node, &data, sizeof(data)); + } + + tree_print(tree, 0, print_int); + + node = tree_detach(tree, 1); + tree_delete(node, NULL); + + tree_print(tree, 0, print_int); + + tree_delete(tree, NULL); +} +``` + +### tree获取相关节点 + +```c +tree_t tree_parent(tree_t tree); +tree_t tree_child(tree_t tree, int index); +``` + +tree_parent: +用于获取指定树的父树句柄,若该树存在父树,则返回父树的句柄,通过该句柄可以进一步操作父树;若该树没有父树(比如是根节点所在的树或者本身就是独立的树等情况),则返回`NULL`。 +- `tree`:树的句柄,通过它来定位要查找父树的目标树,依据树的层次结构去寻找对应的父树并返回其句柄。 + +tree_child: +获取指定树中指定索引位置的子树句柄,若索引位置存在子树,则返回对应的子树句柄,方便后续对子树进行相关操作;若索引不合法或者对应位置没有子树,则返回`NULL`。 +- `tree`:树的句柄,用于确定要查找子树的目标树,在该树的结构内依据索引去定位和获取相应的子树句柄。 +- `index`:子树在目标树中的索引位置,从0开始计数,通过该索引在目标树中查找对应的子树,需保证索引在有效范围内。 + +```c +static void test_parent(void) +{ + tree_t tree = tree_create(); + tree_t node = NULL; + tree_t parent, child; + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + tree_set_data(tree, "Parent", 7); + + node = tree_create(); tree_set_data(node, "Child0", 7); tree_attach(tree, 0, node); + node = tree_create(); tree_set_data(node, "Child1", 7); tree_attach(tree, 1, node); + node = tree_create(); tree_set_data(node, "Child2", 7); tree_attach(tree, 2, node); + node = tree_create(); tree_set_data(node, "Child3", 7); tree_attach(tree, 3, node); + + tree_print(tree, 0, print_string); + + parent = tree_parent(node); print_string(parent); printf("\r\n"); + + child = tree_child(tree, 0); print_string(child); printf("\r\n"); + child = tree_child(tree, 1); print_string(child); printf("\r\n"); + child = tree_child(tree, 2); print_string(child); printf("\r\n"); + child = tree_child(tree, 3); print_string(child); printf("\r\n"); + + tree_delete(tree, NULL); +} +``` + +### tree设置和获取属性 +```c +int tree_set_attribute(tree_t tree, void* attribute, int size); +int tree_get_attribute(tree_t tree, void* attribute, int size); +const void* tree_attribute(tree_t tree); +``` + +tree_set_attribute: +将给定的属性数据设置到指定树节点中,会覆盖原有的属性内容,若传入的属性数据大小为0,则会删除原有的属性数据。若设置属性操作成功,函数返回1;若树节点不存在、参数不合法等情况导致设置失败,则返回0。 +- `tree`:树的句柄,通过该句柄定位到要设置属性的目标树节点,保证属性能准确设置到对应的节点中。 +- `attribute`:指向要设置的属性数据的指针,该指针指向的数据内容会被复制到树节点中,替换原有的属性数据,其数据类型和大小应符合树节点对属性的要求。 +- `size`:要设置的属性数据的大小,以字节为单位,若该值为0,则执行删除原属性数据的操作;否则按照该大小将新属性数据复制到树节点中。 + +tree_get_attribute: +从指定树节点中获取属性数据,并将获取到的属性数据复制到传入的指针所指向的内存空间中,若获取操作成功,函数返回1;若树节点不存在属性数据、传入的参数不合法(如传入的内存空间大小不足以存储属性数据等)导致获取失败,则返回0。 +**参数介绍**: +- `tree`:树的句柄,根据该句柄找到对应的树节点,从该节点中获取属性数据内容,确保准确获取目标节点的属性数据。 +- `attribute`:指向用于存储获取的属性数据的内存空间的指针,该指针指向的内存空间需要有足够的容量来存放从树节点获取的属性数据,其数据类型也应与树节点的属性数据类型匹配。 +- `size`:期望获取的属性数据大小,以字节为单位,需要与树节点实际存储的属性数据大小相匹配,否则可能导致获取失败。 + +tree_attribute: +获取指定树节点的属性信息的地址,若节点存在有效的属性数据,则返回其地址指针,方便后续对属性数据进行访问和操作;若节点没有属性或者不存在等情况,则返回`NULL`。 +- `tree`:树的句柄,利用该句柄定位到具体的树节点,从而获取其关联的属性信息地址,不同树节点的属性情况可能不同,通过句柄能准确获取对应节点的属性数据地址。 + +```c +static void test_attr(void) +{ + tree_t tree = tree_create(); + char attr[20]; + + tree_set_attribute(tree, "This attribute!!!", 18); + tree_get_attribute(tree, attr, sizeof(attr)); + + printf("attr %s\r\n", attr); + + tree_delete(tree, NULL); +} +``` + +### tree的深度 +```c +int tree_depth(tree_t tree); +``` +获取指定树的深度,深度计算包含树本身所在的层级,返回的整数值表示从根节点到最远叶子节点的层级数,可用于了解树的层次结构复杂程度等情况。 +- `tree`:树的句柄,通过该句柄定位到对应的树结构,进而计算并返回其深度信息,不同树的深度因结构不同而有差异,通过句柄能准确获取对应树的深度情况。 + +```c +static void test_depth(void) +{ + tree_t root = tree_create(); + tree_t node, temp; + + tree_insert(root, 0); + tree_insert(root, 1); + tree_insert(root, 2); + + tree_attach(root, 0, tree_create()); + tree_attach(root, 1, tree_create()); + + node = tree_child(root, 0); + tree_insert(node, 0); + tree_insert(node, 1); + + node = tree_child(root, 1); + tree_insert(node, 0); + tree_insert(node, 1); + tree_attach(node, 0, tree_create()); + tree_attach(node, 1, tree_create()); + + tree_print(root, 0, NULL); + + printf("depth %d\r\n", tree_depth(root)); + + tree_delete(root, NULL); +} +``` + +### tree连续获取子节点 +```c +tree_t tree_to_valist(tree_t tree, int index,...); +#define tree_to(tree, i, ...) +``` +通过连续的索引值来获取树中的子树,从指定树开始,按照传入的索引顺序依次查找子树,直到遇到负数索引停止查找,若能成功找到对应的子树,则返回最终的子树句柄;若在查找过程中出现索引不合法、对应位置无子树等情况导致无法找到目标子树,则返回`NULL`。`tree_to`为更简单的宏定义版本,默认在参数末尾添加了负数。 +- `tree`:树的句柄,用于确定起始查找的树结构,后续的索引查找都是基于这个树开始进行的,是整个查找操作的基础起点。 +- `index`:第一个索引值,从该索引位置开始在树中查找子树,后续还可以传入更多的索引值(通过可变参数实现),不过要保证每个索引都是合法有效的,且在对应的树结构范围内。 +- `...`:可变参数部分,表示后续的其他索引值,这些索引值是连续使用的,用于在找到的子树中继续查找下一级子树,直到遇到负数索引停止查找过程。 + +```c +static void test_to(void) +{ + tree_t root = tree_create(); + tree_t node, temp; + + tree_insert(root, 0); + tree_insert(root, 1); + tree_insert(root, 2); + + tree_attach(root, 0, tree_create()); + tree_attach(root, 1, tree_create()); + + node = tree_child(root, 0); + tree_insert(node, 0); + tree_insert(node, 1); + + node = tree_child(root, 1); + tree_insert(node, 0); + tree_insert(node, 1); + tree_attach(node, 0, tree_create()); + tree_attach(node, 1, tree_create()); + + temp = tree_to(root, 1, 1); + tree_set_data(temp, "To function!!!", 15); + + tree_print(root, 0, print_string); + + tree_delete(root, NULL); +} +``` + +### tree大小 +```c +int tree_csize(tree_t tree); +int tree_dsize(tree_t tree); +int tree_asize(tree_t tree); +int tree_size(tree_t tree); +``` + +tree_csize: +用于获取指定树的子树数量,也就是该树拥有的孩子节点个数,返回的整数值可以帮助开发者了解树的分支情况,例如判断树的某个节点是否有子节点等。 + +tree_dsize: +获取指定树节点存储的数据大小,返回的整数值表示对应节点数据所占用的字节数,有助于了解数据的规模以及进行一些与数据大小相关的操作判断等。 + +tree_asize: +获取指定树节点的属性数据大小,返回的整数值表示对应节点属性信息所占用的字节数,可用于了解属性数据的规模以及相关操作时的判断依据等。 + +tree_size: +获取指定树的总节点数量,返回的整数值代表整棵树包含的所有节点个数,能直观体现树的规模大小,方便开发者在进行一些遍历、统计等操作时参考树的整体规模情况。 + +### tree打印 +```c +void tree_print(tree_t tree, int depth, void (*print)(tree_t tree)); +``` +用于按照指定的深度对树结构进行扩展打印展示,通过传入的打印函数(由开发者自定义具体的打印逻辑)来输出树的相关信息,比如节点数据、层次关系等内容,若`depth`参数为0,则会打印整棵树的全部信息;若传入具体的深度值,则按照该深度进行相应层级的打印。此函数无返回值,主要用于可视化展示树的结构情况。 +- `tree`:树的句柄,通过该句柄确定要进行打印展示的目标树结构,不同的树有各自独立的结构和数据,通过句柄能准确区分并打印对应的树信息。 +- `depth`:要打印的深度,以层级数表示,取值为0表示打印整棵树的所有层级信息,若传入大于0的整数,则按照该层级数从根节点开始向下打印相应层数的树结构信息。 +- `print`:指向一个自定义打印函数的指针,该函数接受树的句柄作为参数,开发者需要在该函数内部实现具体的打印逻辑,比如按照何种格式输出节点数据、如何体现层次关系等,以此来实现个性化的树结构打印展示效果。 diff --git a/doc/txls.en.md b/doc/txls.en.md new file mode 100644 index 0000000..ec0eb8d --- /dev/null +++ b/doc/txls.en.md @@ -0,0 +1,503 @@ +### Introduction + +`txls` is a text-based table format in `varch`, with its syntax referring to that of Markdown tables. For better visual clarity in text, the syntax rules of `txls` are a bit stricter than those of Markdown tables. Through `txls`, it's quite convenient to access the row and column contents of a `txls` file, as well as generate a neatly formatted, standardized, and highly readable text-based table file. + +Here's a brief introduction to the `txls` specification and a comparison with the Markdown table syntax. + +**Table Header** +``` +The table header is quite similar to that of a Markdown table, but a bit stricter. +Markdown tables don't require the use of '|' delimiters at both ends of the current row, but txls requires '|' delimiters at both ends of the row for a neater format. The same requirement applies to each subsequent row of content. +The table header is followed by a divider row, and the number of columns in the divider row must be consistent with that of the table header. +The content of the divider row must contain consecutive '-' characters. +``` +Example: +```md +| col 1 | col 2 | col 3 | col 4 | col 5 | +|-------|-------|--------------------|-------|-------| +``` + +**Rows** +``` +In a row, the '|' delimiter is used to distinguish columns. +Between the '|' delimiters lies the cell content, excluding the spaces at both ends of the cell content. +The cell content can contain escape characters like "\|" to represent '|', and "
" to represent '\n'. +``` +Example: +``` +| col 1 | Zhang san | col 3 | col 4 | col 5 | +|-------|----------:|----------------|-------|-------| +| 11 | 21 | 31 | 41 | 51 | +| 12 | 22 | 1234\|
5678 | 42 | 52 | +| 13 | 23 | 33 | 43 | 53 | +| 14 | 24 | 34 | 44 | 54 | +| 15 | 25 | 35 | 45 | 55 | +``` + +**Alignment** +``` +There are three alignment methods in txls: left alignment, right alignment, and center alignment. In the table, the alignment is indicated by ':' at the positions on both ends of the corresponding cell in the divider row. +':' on the left indicates left alignment, on the right indicates right alignment, and when it appears on both sides, it indicates center alignment. If there's no ':' present, it's the default alignment (currently, the default alignment in txls is left alignment). +There can be at most one ':' at each end. +``` +Example: +``` +| left align | right align | center align | default align | +|:-----------------|-----------------:|:----------------:|------------------| +| 0 | 0 | 0 | 0 | +| 0123456789abcdef | 0123456789abcdef | 0123456789abcdef | 0123456789abcdef | +``` + +## Interfaces + +### Creating and Deleting txls Objects +```c +txls_t txls_create(int col, int row); +void txls_delete(txls_t txls); +``` +Here, `txls_t` is the structure of `txls`. The creation method will create a table with `col` columns and `row` rows. If the creation is successful, it will return the handle of the `txls` object. The deletion method is used to delete a `txls` object. + +### Getting the Number of Columns and Rows +```c +int txls_col(txls_t txls); +int txls_row(txls_t txls); +``` +These two methods are used to return the number of columns and rows of the `txls` table respectively. + +### Inserting and Deleting a Column +```c +int txls_insert_col(txls_t txls, int col); +int txls_delete_col(txls_t txls, int col); +``` +These two methods are used to insert and delete a column in `txls` respectively. The column index starts from 1. If the operation is successful, it will return 1; otherwise, it will return 0. + +### Inserting and Deleting a Row +```c +int txls_insert_row(txls_t txls, int row); +int txls_delete_row(txls_t txls, int row); +``` +These two methods are used to insert and delete a row in `txls` respectively. The row index starts from 1. If the operation is successful, it will return 1; otherwise, it will return 0. + +### Setting and Getting Cell Content +```c +int txls_set_text(txls_t txls, int col, int row, const char* text); +const char* txls_get_text(txls_t txls, int col, int row); +``` +The setting method is used to set the content of the specified cell in `txls` to the specified `text`. It's not recommended to set spaces at both ends of the text content, as they will be ignored after being written into the table. Also, the text cannot contain invisible characters except for the newline character. If the operation is successful, it will return 1; otherwise, it will return 0. +The getting method is used to return the content of the specified cell. Returning `NULL` indicates that the retrieval has failed. +When `row` is set to 0, it's used for setting and getting the table header. +```c +#define txls_set_head(txls, col, head) +#define txls_get_head(txls, col) +``` + +### Setting the Alignment Method +```c +int txls_set_align(txls_t txls, int col, int align); +``` +This method is used to set the alignment method of the specified column. The alignment methods include `TXLS_ALIGN_LEFT`, `TXLS_ALIGN_RIGHT`, and `TXLS_ALIGN_CENTER`. Other values represent unknown alignment methods. + +### txls Object Dumping +```c +char* txls_dumps(txls_t txls, int neat, int* len); +int txls_file_dump(txls_t txls, const char* filename); +``` +Firstly, for the `txls_dumps` method, it dumps the `txls` object into a string according to the format. The `neat` parameter is a control variable for whether to dump neatly or not. A value of 0 means unneat output, and other values mean neat output. Neat output means that each column is aligned with the same width, while unneat output shows the actual length of each cell. Neat output maintains good appearance and readability but will occupy more space to store spaces. The `len` parameter is the length of the converted string. When `NULL` is passed in, the length will not be obtained. The return value is the converted string, which is allocated by the function. **It's necessary to free this string after finishing using it**. +The `txls_file_dump` method dumps the `txls` object into a file based on the `txls_dumps` method. The `filename` parameter is used to pass in the file name. The return value is the length of the dumped content, and a negative value indicates that the dumping has failed. + +### txls Object Loading +```c +txls_t txls_loads(const char* text); +txls_t txls_file_load(const char* filename); +``` +Similar to the dumping methods, the `txls` object can be loaded from a string text or a file. If the loading is successful, it will return a `txls` object; otherwise, it will return `NULL`. + +### txls Loading Errors +```c +int txls_error_info(int* line); +``` +The `txls` parser in `varch` provides accurate error recognition. When the loading of `txls` fails, this method can be called to locate the error position and the error type. A return value of 1 indicates that there's an error in the current parsing, and 0 indicates that there's no error. The `line` parameter is used to output the line where the error occurs, and the `type` parameter is used to output the error type. The error types are defined as follows: +```c +#define TXLS_E_OK (0) /* no error */ +#define TXLS_E_HEAD (1) /* irregular header format */ +#define TXLS_E_ALLOC (2) /* failed to allocate space */ +#define TXLS_E_BEGIN (3) /* missing "|" separator at the begin */ +#define TXLS_E_END (4) /* missing "|" separator at the end */ +#define TXLS_E_IDENT (5) /* missing "-" separator at split row */ +#define TXLS_E_BRANK (6) /* there are extra blank lines */ +#define TXLS_E_MEMORY (7) // memory allocation failed +#define TXLS_E_OPEN (8) // fail to open file +``` + +## Reference Examples + +### Generating a txls File +```c +void test(void) +{ + txls_t x = NULL; // Define a txls object, usually initialized as NULL + + x = txls_create(4, 5); // Create a 4x5 table + if (!x) + { + return; + } + + /* Set the table header, leaving the first column blank */ + txls_set_head(x, 2, "Zhang San"); + txls_set_head(x, 3, "Li Si"); + txls_set_head(x, 4, "Wang Wu"); + + /* Set the alignment method */ + txls_set_align(x, 2, TXLS_ALIGN_LEFT); + txls_set_align(x, 3, TXLS_ALIGN_CENTER); + txls_set_align(x, 4, TXLS_ALIGN_RIGHT); + + /* Use the first column as the information category */ + txls_set_text(x, 1, 1, "age"); + txls_set_text(x, 1, 2, "gender"); + txls_set_text(x, 1, 3, "height"); + txls_set_text(x, 1, 4, "weight"); + txls_set_text(x, 1, 5, "email"); + + /* Write each person's information */ + // Zhang San + txls_set_text(x, 2, 1, "18"); + txls_set_text(x, 2, 2, "man"); + txls_set_text(x, 2, 3, "178.5"); + txls_set_text(x, 2, 4, "65"); + txls_set_text(x, 2, 5, "123321@qq.com"); + // Li Si + txls_set_text(x, 3, 1, "24"); + txls_set_text(x, 3, 2, "woman"); + txls_set_text(x, 3, 3, "165"); + txls_set_text(x, 3, 4, "48"); + txls_set_text(x, 3, 5, "lisi@163.com"); + // Wang Wu + txls_set_text(x, 4, 1, "20"); + txls_set_text(x, 4, 2, "man"); + txls_set_text(x, 4, 3, "175"); + txls_set_text(x, 4, 4, "75"); + txls_set_text(x, 4, 5, "ww1234567890@qq.com"); + + txls_file_dump(x, "info.txls"); + + txls_delete(x); +} +``` +The dumped file `info.txls`: +```txls +| | Zhang San | Li Si | Wang Wu | +|--------|:--------------|:------------:|--------------------:| +| age | 18 | 24 | 20 | +| gender | man | woman | man | +| height | 178.5 | 165 | 175 | +| weight | 65 | 48 | 75 | +| email | 123321@qq.com | lisi@163.com | ww1234567890@qq.com | +``` +The display effect in a Markdown reader: + +| | Zhang San | Li Si | Wang Wu | +|--------|:--------------|:------------:|--------------------:| +| age | 18 | 24 | 20 | +| gender | man | woman | man | +| height | 178.5 | 165 | 175 | +| weight | 65 | 48 | 75 | +| email | 123321@qq.com | lisi@163.com | ww1234567890@qq.com | + +In the example, many functions don't have their return values judged. In practical applications, it's necessary to judge the return values. + +### Loading a txls File +Based on the file generated above, load the file. +The loading test code: +```c +void test(void) +{ + txls_t x = NULL; // Define a txls object, usually initialized as NULL + + /* Load the txls file */ + x = txls_file_load("info.txls"); + if (!x) // If the loading fails, locate the error + { + int line, type; + type = txls_error_info(&line); + printf("txls parse error! line %d, error %d.\r\n", line, type); + return; + } + + /* Traverse the table header to locate the column */ + int col = 0; + for (col = 1; col <= txls_col(x); col++) + { + if (strcmp("Li Si", txls_get_head(x, col)) == 0) + { + break; + } + } + if (col > txls_col(x)) // If not found + { + printf("Lookup failed\r\n"); + return; + } + + /* Print the information */ + printf("name: %s, age=%s, gender: %s, height=%s, weight=%s, email:%s\r\n", + txls_get_text(x, col, 0), + txls_get_text(x, col, 1), + txls_get_text(x, col, 2), + txls_get_text(x, col, 3), + txls_get_text(x, col, 4), + txls_get_text(x, col, 5)); + + txls_delete(x); // Delete the object after using it +} +``` +Running result: +``` +name: Li Si, age=24, gender: woman, height=165, weight=48, email:lisi@163.com +``` + +### Loading Errors +Based on the above example, modify the file by deleting the '|' delimiter at the end of the 4th row, and then load it again. +``` +| | Zhang San | Li Si | Wang Wu | +|--------|:--------------|:------------:|--------------------:| +| age | 18 | 24 | 20 | +| gender | man | woman | man +| height | 178.5 | 165 | 175 | +| weight | 65 | 48 | 75 | +| email | 123321@qq.com | lisi@163.com | ww1234567890@qq.com | +``` +Running result: +``` +txls parse error! line 4, error 4. +``` +In this way, it can be located that an error of type 4 occurs on line 4, which corresponds to: +``` +#define TXLS_E_END (4) /* missing "|" separator at the end */ +``` + +### Source Code Analysis + +#### txls Parser Structure + +The structures within the `txls` parser are all implicit, meaning that the members of these structures can't be accessed directly. This design guarantees the independence and security of the module. It prevents external calls from modifying the structure members, which could otherwise damage the storage structure of `txls`. As a result, only the declaration of `txls` is placed in the header file, while the actual definitions of the structures are in the source file. Operations on `txls` objects can only be carried out using the methods provided by the `txls` parser. + +The `txls` type is declared as follows: +```c +typedef struct TXLS *txls_t; +``` +When in use, simply use `txls_t`. + +The `TXLS` structure is defined like this: +```c +typedef struct TXLS +{ + COLUMN *columns; /* columns base */ + ITERATOR iterator; /* column iterator */ + int col; /* column count */ + int row; /* row count */ +} TXLS; +``` +It contains four members. `columns` is a linked list of columns. The `iterator` is used to keep track of the position during column access, enabling quick returns when accessing the same position again instead of having to traverse from the beginning to the end. `col` and `row` represent the number of columns and rows of the table respectively. + +The `COLUMN` structure is: +```c +typedef struct COLUMN +{ + struct COLUMN *next; /* next column */ + CELL *cells; /* the cell base address of this column */ + ITERATOR iterator; /* cell list iterator */ + int align; /* alignment */ + int width; /* the output width of this column when neatly outputting */ +} COLUMN; +``` +It has five members. `next` points to the next `COLUMN` to form a singly linked list. `cells` is a linked list of cells within this column. The `iterator` is for iterating over the cell list. `align` indicates the alignment method, and `width` represents the width of the column when outputting neatly. + +The `CELL` structure is: +```c +typedef struct CELL +{ + struct CELL *next; /* next cell */ + char* address; /* address of cell content */ +} CELL; +``` +It consists of two members. `next` points to the next `CELL` to form a singly linked list, and `address` stores the content of the cell as a string. + +The `ITERATOR` structure is: +```c +typedef struct +{ + void *p; /* iteration pointer */ + int i; /* iteration index */ +} ITERATOR; +``` +It simply records the node (through the `p` member) that the current pointer points to in the singly linked list and the index within that list. + +Overall, the storage structure of `TXLS` is clear: columns are linked together in a list, and each column has its own list of cells linked together as well, forming a hierarchical structure like this: +``` +INI + col[1] --> col[2] --> col[3] --> col[4] + | | | | + V V V V + cell[0] cell[0] cell[0] cell[0] + | | | | + V V V V + cell[1] cell[1] cell[1] cell[1] + | | | | + V V V V + cell[2] cell[2] cell[2] cell[2] +``` + +#### Iteration of Singly Linked Lists + +The operation of singly linked lists isn't the main focus here. The built-in iterator aims to improve the access efficiency of singly linked lists, and the iteration process is worth elaborating on. Taking the iteration of the `column` linked list as an example, here's how the iterator obtains the nodes of the linked list: + +The `txls_column` function is as follows: +```c +static COLUMN *txls_column(txls_t txls, int index, int col) // Pass in the index and the limited number of columns +{ + if (index >= col) return NULL; // Check if the index is out of bounds + + // This step resets the iteration, positioning the iterator back to the head of the linked list. + // The iterator will be reset if any of the following three conditions is met: + // 1. Since it's a singly linked list and can't be traversed backward, when the target index is less than the iterator's index, it needs to be reset and then iterated from the head of the list to the specified index. + // 2. When the iterator pointer (the p member) is NULL, which means it doesn't point to a specific node, it must be reset. So, externally, if you want to reset the iterator, you just need to set the p member to NULL. + // 3. When the target index index is 0, which means actively getting the 0th position, i.e., the first position. + if (index < txls->iterator.i ||!txls->iterator.p || index == 0) + { + txls->iterator.i = 0; + txls->iterator.p = txls->columns; + } + + // Loop to iterate the iterator to the specified index position. + // In a singly linked list, the index increases positively, so the time complexity for forward traversal is O(n), and for reverse traversal, it's O(n^2). + while (txls->iterator.p && txls->iterator.i < index) + { + txls->iterator.p = ((COLUMN *)(txls->iterator.p))->next; + txls->iterator.i++; + } + + // Return the node pointed to by the iterator. + return txls->iterator.p; +} +``` +When adjusting the linked list, the iterator also needs to be adjusted accordingly. The simplest way is to set the `p` member to `NULL` to reset the iterator. + +#### Explanation of txls Dumping + +Dumping means printing the `txls` in a specific format, but not to the console. Instead, it's printed into a specified memory space. Where does this space come from? It's allocated dynamically. How much space should be allocated? If it's a neat output (when `neat` is not 0), the required space can be predicted based on the column widths. For unneat output, the size needs to be adjusted dynamically during the dumping process. + +First, let's look at the structure that maintains the printing space: +```c +typedef struct +{ + char* address; /* buffer base address */ + int size; /* size of buffer */ + int end; /* end of buffer used */ +} BUFFER; +``` +It has three members. `address` is the base address of the space, `size` is the size of the space, and `end` is the index of the used end. + +The process of dynamically adjusting the space is as follows: +```c +static int buf_append(BUFFER *buf, int needed) // needed is the additional capacity required +{ + char* address; + int size; + + if (!buf ||!buf->address) return 0; + + // Calculate the total space required by adding the currently used space and the needed space. + needed += buf->end; + + // Check if the current space can still meet the requirement. + if (needed <= buf->size) return 1; /* there is still enough space in the current buf */ + + // If the current space size can't meet the requirement, recalculate the space that can meet the requirement. + // The new space should not only meet the current need but also leave some margin for the next append operation. + // Otherwise, reallocating space every time a small amount of capacity is added would be very inefficient. + // And what's the best size for the newly allocated space? It's a power of 2, which is the smallest power of 2 greater than the required amount. + // Why choose a power of 2? + // 1. The algorithm is easy to implement. Calculating the smallest power of 2 greater than a specified value is straightforward. + // 2. It has good space utilization, which is (1 + 2) / 2 = 75%. + // 3. The number of space reallocations is small. For example, when finally 128 units of space are needed, if adding 10 units of space each time, it would require 13 reallocations, while using powers of 2 only requires 7 reallocations. + size = pow2gt(needed); + address = (char*)realloc(buf->address, size); + if (!address) return 0; + + buf->size = size; + buf->address = address; + + return 1; +} +``` +Here's an example of how it's used in the dumping code: +```c +if (!buf_append(buf, 2)) return 0; // First, expand the space +buf_push(buf, '|'); // Call the function to push the character into the buf +buf_push(buf, '\n'); +``` + +When dumping cells, there's a point to note. Cells are allowed to have newline characters and the delimiter '|'. The newline character is replaced with `
`, and the delimiter '|' is escaped as `\|`. The code for handling this is like this: +```c +while (addr && *addr) +{ + if (*addr == '\n') + { + buf_push(buf, '<'); + buf_push(buf, 'b'); + buf_push(buf, 'r'); + buf_push(buf, '>'); + } + else if (*addr == '|') + { + buf_push(buf, '\\'); + buf_push(buf, '|'); + } + else + { + buf_push(buf, *addr); + } + addr++; +} +``` +Finally, when printing, the `txls` is traversed, and the space is dynamically adjusted to print each cell in each column in sequence into the specified space. + +#### Explanation of txls Loading + +The most important and complex part of the `txls` parser is the loading and parsing part. + +1. First, a `0x0` `txls` table object is created. Then, as the parsing progresses, the parsed table header, rows, and columns are added one by one to the `txls` object. Once the parsing is completed, a complete `txls` object is obtained. + +2. Before parsing, the line number and errors need to be reset. Then, as the parsing proceeds and a newline character is encountered, the line number is incremented accordingly. If an error occurs during parsing, the error type is recorded. + +3. Then comes the actual parsing process, which is divided into two steps. The first step is to parse the table header and the table divider row. This step is to determine the total number of columns in the table, the alignment methods, and whether the syntax conforms to the table header syntax. The second step, based on the determined columns, parses each row one by one, analyzes the cells distinguished in each row, and classifies the cell contents into the specified rows and columns. + +The entire parsing process is shown as follows: +```c +/* Create an empty table */ +txls = txls_create(0, 0); +... + +/* Reset error information */ +etype = TXLS_E_OK; +eline = 1; + +/* Parse the table header */ +s = parse_head(text, txls); +if (etype) goto FAIL; +while (1) +{ + /* Parse each row */ + s = parse_line(s, txls); + if (etype) goto FAIL; + if (!*s) break; +} +return txls; +``` +Since the content specified by the `txls` syntax is basically divided by rows (values can have special cases where they can span multiple lines), basically, the processing of row-by-row information is done within this loop. + +#### Explanation of txls Insertion, Deletion, Modification, and Query + +The remaining operations regarding insertion, deletion, modification, and query for `txls` are basically common operations on linked list data structures and don't require special emphasis here. diff --git a/doc/txls.md b/doc/txls.md index adf7900..a22253c 100644 --- a/doc/txls.md +++ b/doc/txls.md @@ -67,8 +67,8 @@ int txls_row(txls_t txls); ### 插入和删除一列 ```c -int txls_insert_column(txls_t txls, int col); -int txls_delete_column(txls_t txls, int col); +int txls_insert_col(txls_t txls, int col); +int txls_delete_col(txls_t txls, int col); ``` 这两个方法分别向txls插入和删除一列,列的索引从1开始,成功返回1失败返回0 diff --git a/doc/unitt.en.md b/doc/unitt.en.md new file mode 100644 index 0000000..823e7b6 --- /dev/null +++ b/doc/unitt.en.md @@ -0,0 +1,103 @@ +### Introduction +`unitt` is a simple unit testing module for the C language. It defines multiple function pointer types, covering types corresponding to functions related to clock, testing, pre-test setup, post-test cleanup, and random input generation. Meanwhile, it defines structures representing test cases and test suites. In addition, it provides a series of functions for asserting whether various conditions are met and a function for executing test suites. Moreover, through macro definitions, it simplifies the definition and operation of test cases and the test execution process, helping developers efficiently conduct unit testing work in C language projects and check whether the code functions meet expectations. + +### Interfaces + +#### Functions + +##### `unitt_det_true` Function +```c +int unitt_det_true(int condition, const char* message); +``` +**Function Description**: It is used to assert whether the given condition is true. During the testing process, by passing in the condition expression to be evaluated and the prompt message to be displayed when the condition is not met (i.e., the assertion fails), if the condition is true, it means the assertion passes and the function returns `UNITT_E_OK` (with a value of 0). If the condition is false, the assertion fails and the function returns `UNITT_E_FAIL` (with a value of -1). Meanwhile, corresponding error prompt messages may be output according to the specific implementation of the testing framework. +**Parameter Introduction**: +- `condition`: The condition expression to be evaluated. Usually, it is an expression that returns a Boolean type (represented by an integer in the C language, where non-zero means true and zero means false), such as comparisons between variables, results of logical operations, etc., which is used to judge whether specific testing expectations are met. +- `message`: The prompt message to be displayed when the assertion fails. It is a string constant, which is used to inform developers which testing condition has not passed and related error information, facilitating the location of problems. + +##### `unitt_det_false` Function +```c +int unitt_det_false(int condition, const char* message); +``` +**Function Description**: Opposite to `unitt_det_true`, it is used to assert whether the given condition is false. If the passed-in condition expression evaluates to false, the assertion passes and the function returns `UNITT_E_OK`. If the condition is true, the assertion fails and the function returns `UNITT_E_FAIL`. At the same time, corresponding error prompt messages will also be displayed according to the setting (if the testing framework supports message output). +**Parameter Introduction**: +- `condition`: The condition expression to be evaluated, similar to that in `unitt_det_true`, it is an expression that can yield a Boolean type result (represented by an integer), but here it is expected to be false for the assertion to pass. +- `message`: The prompt message to be displayed when the assertion fails. It is also a string constant, which is used to clearly indicate the error situation where the condition expected to be false in the test is actually true. + +##### `unitt_det_equal` Function +```c +int unitt_det_equal(int expected, int actual, const char* message); +``` +**Function Description**: It is used to assert whether two integers are equal. In unit testing, the expected integer value is compared with the actual integer value obtained. If the two values are equal, the assertion passes and the function returns `UNITT_E_OK`. If they are not equal, the assertion fails and the function returns `UNITT_E_FAIL`, and the specified error prompt message will be displayed to help developers know which integer comparison has a difference. +**Parameter Introduction**: +- `expected`: The expected integer value, that is, the integer result that developers expect to obtain in the test, which serves as the standard value for comparison in the assertion judgment. +- `actual`: The actual integer value, which is the integer value actually generated after the test execution and will be compared with the expected value to determine whether it meets the testing expectations. +- `message`: The prompt message to be displayed when the two integers are not equal and the assertion fails. Through detailed text descriptions, developers can quickly understand which two integers have a comparison problem and the corresponding test scenarios and other information. + +##### `unitt_det_nequal` Function +```c +int unitt_det_nequal(int expected, int actual, const char* message); +``` +**Function Description**: It is used to assert whether two integers are not equal. If the passed-in expected integer value and the actual integer value are indeed not equal, the assertion passes and the function returns `UNITT_E_OK`. If the two values are equal, the assertion fails and the function returns `UNITT_E_FAIL`, and the corresponding error prompt message will be displayed to inform developers that the two integers that should be unequal are actually equal, which does not meet expectations. +**Parameter Introduction**: +- `expected`: The expected integer value, which serves as a reference for comparison and is expected to be different from the actual value for the assertion to pass. +- `actual`: The actual integer value obtained, which is compared with the expected value to determine whether it meets the assertion requirement of being unequal. +- `message`: The prompt message to be displayed when the assertion fails (i.e., when the two integers are equal), helping developers understand which group of integer comparisons has violated expectations and related test background information. + +##### `unitt_det_null` Function +```c +int unitt_det_null(void* pointer, const char* message); +``` +**Function Description**: It is used to assert whether the given pointer is `NULL`. When testing code involving pointer operations, if the passed-in pointer value is `NULL`, it means the assertion passes and the function returns `UNITT_E_OK`. If the pointer is not `NULL`, the assertion fails and the function returns `UNITT_E_FAIL`, and the corresponding error prompt message will be output to help developers know which pointer's `NULL` check has a problem. +**Parameter Introduction**: +- `pointer`: The pointer variable to be evaluated. Its value will be checked to see if it is `NULL` to verify whether the handling of the pointer being null in the relevant code meets expectations, such as whether a function correctly handles the boundary case of an incoming null pointer. +- `message`: The prompt message to be displayed when the pointer is not `NULL` and the assertion fails. It details which pointer does not meet the expected `NULL` state and related test situations, facilitating the location of the problem source. + +##### `unitt_det_nnull` Function +```c +int unitt_det_nnull(void* pointer, const char* message); +``` +**Function Description**: Opposite to `unitt_det_null`, it is used to assert whether the given pointer is not `NULL`. If the passed-in pointer is not `NULL`, the assertion passes and the function returns `UNITT_E_OK`. If the pointer is `NULL`, the assertion fails and the function returns `UNITT_E_FAIL`, and the corresponding error prompt message will be displayed to prompt developers that the pointer being null does not meet expectations. +**Parameter Introduction**: +- `pointer`: The pointer variable to be evaluated. It is expected that its value is not `NULL` for the assertion to hold. It is often used in test scenarios such as checking whether the pointer returned by a function is valid and whether the pointer member in a structure is correctly assigned. +- `message`: The prompt message to be displayed when the pointer is `NULL` and the assertion fails. It informs developers which pointer has not reached the expected non-`NULL` state and the corresponding test background information, which helps in troubleshooting. + +##### `unitt_det_float` Function +```c +int unitt_det_float(float expected, float actual, float epsilon, const char* message); +``` +**Function Description**: It is used to assert whether two floating-point numbers are approximately equal. Due to the precision issues in the storage and calculation of floating-point numbers in computers, simple equality judgment cannot be used. Therefore, by passing in the acceptable difference (`epsilon`), the range of approximate equality is determined. If the difference between the two floating-point numbers is within the `epsilon` range, they are considered approximately equal and the assertion passes, and the function returns `UNITT_E_OK`. If the difference exceeds the range, the assertion fails and the function returns `UNITT_E_FAIL`, and the specified error prompt message will be output to inform developers that the comparison of floating-point numbers does not meet expectations. +**Parameter Introduction**: +- `expected`: The expected floating-point number, which is the floating-point number result that developers expect to obtain in the test and serves as a reference value for the approximate equality judgment. +- `actual`: The actual floating-point number obtained, which is compared with the expected floating-point number. Based on the comparison between the difference between the two and `epsilon`, it is determined whether the assertion requirement of approximate equality is met. +- `epsilon`: The acceptable difference, which is used to define the range of approximate equality of two floating-point numbers. That is, as long as `|expected - actual| <= epsilon`, the two floating-point numbers are considered approximately equal. This parameter needs to be reasonably set according to specific testing precision requirements. +- `message`: The prompt message to be displayed when the difference between the two floating-point numbers exceeds the `epsilon` range and the assertion fails. It helps developers understand which two floating-point numbers have a comparison that does not meet the expectation of approximate equality and the corresponding test scenarios and other information. + +##### `unitt_det_string` Function +```c +int unitt_det_string(const char* expected, const char* actual, const char* message); +``` +**Function Description**: It is used to assert whether two strings are equal. It will compare the passed-in expected string and the actual string character by character. If the two strings are exactly the same, the assertion passes and the function returns `UNITT_E_OK`. If there is any difference in characters, the assertion fails and the function returns `UNITT_E_FAIL`, and the corresponding error prompt message will be output to inform developers which two strings have a comparison inconsistency and related test background information. +**Parameter Introduction**: +- `expected`: The expected string, that is, the string content that developers expect to obtain in the test, which serves as the standard string for comparison in the assertion judgment. +- `actual`: The actual string, which is the string content actually generated after the test execution and will be compared with the expected string character by character to determine whether it meets the testing expectations. +- `message`: The prompt message to be displayed when the two strings are not equal and the assertion fails. Through detailed text descriptions, developers can quickly know which group of strings has a comparison problem and the corresponding test scenarios and other information. + +##### `unitt_fail_handle` Function +```c +int unitt_fail_handle(const char* name); +``` +**Function Description**: It is used to handle the situation of test failure. When a test case fails during execution, by passing in the name of the failed test case, the function can execute the corresponding failure handling logic, such as recording failure information, outputting specific error prompts, etc. And regardless of the specific handling process, this function will finally return `UNITT_E_OK` to indicate that the process of handling the failure situation has been completed. +**Parameter Introduction**: +- `name`: The name of the failed test case, which is a string constant. Based on this name, it can be located which test case has a problem, and then targeted failure handling operations can be executed, such as accurately recording which test link has failed in the test report. + +##### `unitt_execute` Function +```c +void unitt_execute(UNITT* suites, uint32_t count, unitt_failcb_t failcb, const char* specific, const char* filename); +``` +**Function Description**: It is responsible for executing a series of test suites. It will traverse the passed-in array of test suites and execute the testing operations one by one according to the test cases included in each test suite. During the testing process, it will call the corresponding setup function (if any), execute the testing function, make assertion judgments, call the cleanup function (if any), etc., and update the relevant statistical information (such as the number of test executions, the number of passed tests, and the total time consumption) according to the test results. If a test case fails, it will also call the passed-in failure callback function to handle the failure situation. Meanwhile, according to the passed-in specific test name parameter, it can choose to execute only a specific test case with a matching name, or it can also specify the file name of the output result (if it is `NULL`, the result will be output to the standard output). In this way, it can flexibly control the test execution and result output methods. +**Parameter Introduction**: +- `suites`: A pointer to an array of test suites of the `UNITT` structure type. The `UNITT` structure contains key information such as the name of the test suite, an array of test cases, the number of test cases, and a pointer to the clock function for timing. Through this pointer, each test suite to be executed and the situation of the test cases inside it can be located, which is the core data source for executing the test. +- `count`: The number of elements in the array of test suites, that is, the number of test suites to be executed, which is used to determine the number of times to loop through the test suites to ensure that each test suite can be correctly processed. +- `failcb`: A pointer to the failure callback function, whose type is `unitt_failcb_t`. When a test case fails, the function pointed to by this pointer will be called to handle the operations related to the failure, such as recording the failure log, outputting error prompts, etc. Developers can customize functions that meet the requirements of this type to implement personalized failure handling logic. +- `specific`: The specific test name, which is a string constant. If it is not `NULL`, it means only the specific test case with a matching name will be executed, achieving targeted test execution. If it is `NULL`, all the test cases in all the test suites will be executed in turn. +- `filename`: The file name of the output result. If it is `NULL`, the test result will be output to the standard output (usually the console). If a specific file name is specified, the test result will be written into the corresponding file in a certain format, facilitating the subsequent viewing and analysis of the test situation. diff --git a/doc/unitt.md b/doc/unitt.md new file mode 100644 index 0000000..76f67ac --- /dev/null +++ b/doc/unitt.md @@ -0,0 +1,104 @@ +## 介绍 + +`unitt`是一个用于C语言的简单单元测试模块。它定义了多种函数指针类型,涵盖了时钟、测试、测试前设置、测试后清理以及随机输入生成等相关功能对应的类型,同时定义了表示测试用例和测试套件的结构体。此外,还提供了一系列用于断言各种条件是否满足的函数以及执行测试套件的函数,并且通过宏定义方便地对测试用例和测试执行流程进行简化定义与操作,有助于开发者在C语言项目中高效地开展单元测试工作,检查代码功能是否符合预期。 + +## 接口 + +### 函数 + +#### `unitt_det_true`函数 +```c +int unitt_det_true(int condition, const char* message); +``` +**函数说明**:用于断言给定的条件是否为真。在测试过程中,通过传入要评估的条件表达式以及当条件不满足(即断言失败)时显示的提示消息,如果条件为真,则表示该断言通过,函数返回`UNITT_E_OK`(值为0);若条件为假,则断言失败,函数返回`UNITT_E_FAIL`(值为 -1),同时可能会依据具体的测试框架实现来输出相应的错误提示消息。 +**参数介绍**: +- `condition`:要评估的条件表达式,通常是一个返回值为布尔类型(在C语言中以整型表示,非0为真,0为假)的表达式,例如变量之间的比较、逻辑运算结果等,用于判断特定的测试预期是否满足。 +- `message`:当断言失败时需要显示的提示消息,是一个字符串常量,用于告知开发者具体哪个测试条件未通过以及相关的错误信息,方便定位问题所在。 + +#### `unitt_det_false`函数 +```c +int unitt_det_false(int condition, const char* message); +``` +**函数说明**:与`unitt_det_true`相反,用于断言给定的条件是否为假。若传入的条件表达式计算结果为假,则断言通过,函数返回`UNITT_E_OK`;若条件为真,则断言失败,函数返回`UNITT_E_FAIL`,同时也会按照设定显示相应的错误提示消息(若测试框架支持消息输出)。 +**参数介绍**: +- `condition`:待评估的条件表达式,和`unitt_det_true`中的类似,是一个能得出布尔类型结果(以整型体现)的表达式,不过这里期望其结果为假来使断言通过。 +- `message`:断言失败时展示的提示消息,同样为字符串常量,用于清晰指出测试中期望为假的条件却为真的这种错误情况相关信息。 + +#### `unitt_det_equal`函数 +```c +int unitt_det_equal(int expected, int actual, const char* message); +``` +**函数说明**:用于断言两个整数是否相等。在单元测试中对比预期的整数值和实际得到的整数值,如果二者相等,则该断言通过,函数返回`UNITT_E_OK`;若不相等,断言失败,返回`UNITT_E_FAIL`,并且会显示指定的错误提示消息来帮助开发者知晓是哪个整数比较出现了差异。 +**参数介绍**: +- `expected`:预期的整数值,即开发者期望在测试中得到的整数结果,作为对比的标准值参与断言判断。 +- `actual`:实际的整数值,是测试执行后实际产生的整数值,会和预期值进行比较,以此确定是否符合测试预期。 +- `message`:当两个整数不相等、断言失败时要显示的提示消息,通过详细的文字描述让开发者能快速明白是哪两个整数对比出现了问题以及对应的测试场景等信息。 + +#### `unitt_det_nequal`函数 +```c +int unitt_det_nequal(int expected, int actual, const char* message); +``` +**函数说明**:用于断言两个整数是否不相等。若传入的预期整数值和实际整数值确实不相等,则断言通过,函数返回`UNITT_E_OK`;若二者相等,断言失败,函数返回`UNITT_E_FAIL`,同时会展示对应的错误提示消息,告知开发者本应不相等的两个整数却相等了这一不符合预期的情况。 +**参数介绍**: +- `expected`:预期的整数值,作为对比参照,期望其和实际值不同来使断言通过。 +- `actual`:实际得到的整数值,与预期值进行对比,用于确定是否满足不相等的断言要求。 +- `message`:在断言失败(即两个整数相等)时显示的提示消息,帮助开发者了解具体是哪组整数比较出现了违背预期的情况以及相关测试背景信息。 + +#### `unitt_det_null`函数 +```c +int unitt_det_null(void* pointer, const char* message); +``` +**函数说明**:用于断言给定的指针是否为`NULL`。在测试涉及指针操作的代码时,若传入的指针值为`NULL`,则表示该断言通过,函数返回`UNITT_E_OK`;若指针不为`NULL`,则断言失败,函数返回`UNITT_E_FAIL`,同时会输出对应的错误提示消息,方便开发者知晓哪个指针的`NULL`性判断出现了问题。 +**参数介绍**: +- `pointer`:要评估的指针变量,其值会被检查是否为`NULL`,以验证相关代码对于指针为空情况的处理是否符合预期,比如函数是否正确处理了传入空指针的边界情况等。 +- `message`:当指针不为`NULL`、断言失败时展示的提示消息,详细说明是哪个指针未满足预期的`NULL`状态以及相关的测试情况,便于定位问题源头。 + +#### `unitt_det_nnull`函数 +```c +int unitt_det_nnull(void* pointer, const char* message); +``` +**函数说明**:与`unitt_det_null`相反,用于断言给定的指针是否不为`NULL`。若传入的指针不为`NULL`,则断言通过,函数返回`UNITT_E_OK`;若指针为`NULL`,断言失败,函数返回`UNITT_E_FAIL`,并且会显示相应的错误提示消息来提示开发者指针为空不符合预期的情况。 +**参数介绍**: +- `pointer`:待评估的指针变量,期望其值不为`NULL`来使断言成立,常用于检查函数返回的指针是否有效、结构体中的指针成员是否正确赋值等测试场景中。 +- `message`:在指针为`NULL`、断言失败时要显示的提示消息,告知开发者具体是哪个指针没有达到不为`NULL`的预期以及对应的测试背景等信息,有助于排查问题。 + +#### `unitt_det_float`函数 +```c +int unitt_det_float(float expected, float actual, float epsilon, const char* message); +``` +**函数说明**:用于断言两个浮点数是否近似相等。由于浮点数在计算机中的存储和运算存在精度问题,不能简单地用相等判断,所以通过传入可接受的差值(`epsilon`)来确定近似相等的范围。若两个浮点数的差值在`epsilon`范围内,则认为它们近似相等,断言通过,函数返回`UNITT_E_OK`;若差值超出范围,则断言失败,函数返回`UNITT_E_FAIL`,同时会输出指定的错误提示消息告知开发者浮点数比较不符合预期的情况。 +**参数介绍**: +- `expected`:预期的浮点数,是开发者期望在测试中得到的浮点数结果,作为近似相等判断的一个参照值。 +- `actual`:实际得到的浮点数,与预期浮点数进行对比,依据二者差值和`epsilon`的比较来确定是否满足近似相等的断言要求。 +- `epsilon`:可接受的差值,用于界定两个浮点数近似相等的范围,即只要`|expected - actual| <= epsilon`,就认为两个浮点数近似相等,该参数需根据具体的测试精度需求合理设置。 +- `message`:当两个浮点数差值超出`epsilon`范围、断言失败时显示的提示消息,帮助开发者明白是哪两个浮点数比较出现了不符合近似相等预期的情况以及对应的测试场景等信息。 + +#### `unitt_det_string`函数 +```c +int unitt_det_string(const char* expected, const char* actual, const char* message); +``` +**函数说明**:用于断言两个字符串是否相等。会逐个字符比较传入的预期字符串和实际字符串,如果二者完全一致,则断言通过,函数返回`UNITT_E_OK`;若存在任何字符不同,则断言失败,函数返回`UNITT_E_FAIL`,同时会输出对应的错误提示消息,告知开发者是哪两个字符串比较出现了不一致的情况以及相关测试背景信息。 +**参数介绍**: +- `expected`:预期的字符串,即开发者期望在测试中得到的字符串内容,作为对比的标准字符串参与断言判断。 +- `actual`:实际的字符串,是测试执行后实际产生的字符串内容,会和预期字符串进行逐一字符的比较,以此确定是否符合测试预期。 +- `message`:当两个字符串不相等、断言失败时要显示的提示消息,通过详细文字描述让开发者能快速知晓是哪组字符串对比出现了问题以及对应的测试场景等信息。 + +#### `unitt_fail_handle`函数 +```c +int unitt_fail_handle(const char* name); +``` +**函数说明**:用于处理测试失败的情况,当某个测试用例执行失败时,通过传入该失败测试用例的名称,函数可以执行相应的失败处理逻辑,比如记录失败信息、输出特定的错误提示等,并且无论具体处理过程如何,该函数最终都会返回`UNITT_E_OK`,表示对失败情况的处理流程已执行完毕。 +**参数介绍**: +- `name`:失败测试用例的名称,为字符串常量,依据这个名称可以定位到具体是哪个测试用例出现了问题,进而执行针对性的失败处理操作,例如在测试报告中准确记录是哪个测试环节失败等。 + +#### `unitt_execute`函数 +```c +void unitt_execute(UNITT* suites, uint32_t count, unitt_failcb_t failcb, const char* specific, const char* filename); +``` +**函数说明**:负责执行一系列的测试套件。它会遍历传入的测试套件数组,按照每个测试套件中包含的测试用例依次执行测试操作,在测试过程中会调用相应的设置函数(若有)、执行测试函数、进行断言判断、调用清理函数(若有)等,并依据测试结果更新相关的统计信息(如测试执行次数、通过次数、总耗时等)。如果有测试用例失败,还会调用传入的失败回调函数来处理失败情况。同时,可以根据传入的特定测试名称参数选择只执行某个特定的测试用例,也能指定输出结果的文件名(若为`NULL`则输出到标准输出),以此灵活地控制测试执行和结果输出方式。 +**参数介绍**: +- `suites`:指向`UNITT`结构体类型的测试套件数组的指针,`UNITT`结构体中包含了测试套件名称、测试用例数组、测试用例数量以及用于计时的时钟函数指针等关键信息,通过该指针能定位到要执行的各个测试套件及其内部的测试用例情况,是执行测试的核心数据来源。 +- `count`:测试套件数组中的元素个数,即要执行的测试套件的数量,用于确定循环遍历测试套件的次数,确保每个测试套件都能被正确处理。 +- `failcb`:指向失败回调函数的指针,其类型为`unitt_failcb_t`,当测试用例出现失败情况时,会调用该指针指向的函数来处理失败相关的操作,比如记录失败日志、输出错误提示等,开发者可以自定义符合该类型要求的函数来实现个性化的失败处理逻辑。 +- `specific`:特定的测试名称,为字符串常量,若不为`NULL`,则表示只执行名称与之匹配的那个特定测试用例,实现有针对性的测试执行;若为`NULL`,则会依次执行所有测试套件中的全部测试用例。 +- `filename`:输出结果的文件名,若为`NULL`,则测试结果会输出到标准输出(通常是控制台);若指定了具体的文件名,则测试结果会按照一定格式写入到对应的文件中,方便后续查看和分析测试情况。 diff --git a/doc/valloc.en.md b/doc/valloc.en.md new file mode 100644 index 0000000..0273277 --- /dev/null +++ b/doc/valloc.en.md @@ -0,0 +1,75 @@ +### Introduction + +Memory dynamic allocation refers to the process of dynamically allocating memory space during the execution of a program based on the program's requirements. This is done to store data or create objects. It is usually implemented using pointers. By calling memory allocation functions provided by the system (such as `malloc`, `calloc`, etc.), memory space is requested. Once the request is successful, a pointer pointing to the allocated memory space is returned, and then this pointer can be used in the program to access the allocated memory area. + +The advantages of memory dynamic allocation include the ability to allocate memory space dynamically, which helps avoid waste and improves the efficiency of memory usage. Moreover, the dynamically allocated memory space can also be released dynamically according to the program's needs, preventing memory leaks and errors. However, there are also some disadvantages when using memory dynamic allocation. For example, issues like memory leaks and memory fragmentation are prone to occur, and programmers need to have certain skills and pay attention to some details to use memory dynamic allocation correctly and efficiently. + +Here, a small tool named `valloc` that can check the usage of dynamic memory is introduced. It can be used to calculate the usage of dynamic memory and check whether there are any unreleased memory fragments. By using its methods to replace the regular memory allocation methods, the usage situation of dynamic memory can be counted. + +### Interfaces + +#### Checking Unreleased Memory +```c +void v_check_unfree(void); +``` +This function is used to check the memory that has not been released yet. + +#### Getting the Count of Allocated Memory Blocks +```c +int v_check_count(void); +``` +It is used to obtain the count of the memory blocks that have been allocated. + +#### Getting the Total Size of Used Memory +```c +int v_check_used(void); +``` +This function helps to get the total size of the memory that has been used. + +### Usage + +It's quite simple to use, and the steps are as follows: + +**1** Add the `valloc.h` header file to the source file that needs to perform statistics. Add it after `stdlib.h`. It doesn't matter which line it is as long as it's after `stdlib.h`. The memory functions (such as `malloc()`) in `valloc.h` will override those in `stdlib.h`, and the functions in `valloc.h` will be used instead. + +**2** In the places where output is needed, just call the APIs of `valloc`. + +### Example + +The following is an example code: +```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); +``` +The result is: +``` +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/vector.en.md b/doc/vector.en.md new file mode 100644 index 0000000..a1b45e5 --- /dev/null +++ b/doc/vector.en.md @@ -0,0 +1,360 @@ +### Introduction + +The `vector` container is quite similar to an array. It encapsulates the commonly used methods of arrays, and in most cases, it can be used to replace ordinary arrays, providing better security. Arrays have static space, meaning their size can't be changed once defined, while `vector` is dynamic and its size can be adjusted dynamically during usage. + +Moreover, the `vector` in `varch` can also be accessed randomly and directly like an array. + +### Interfaces + +#### Creating and Deleting vector Objects +```c +vector_t vector_create(int dsize, int size); +void vector_delete(vector_t vector); +#define vector(type, size) // For more convenient use, a macro definition is wrapped around vector_create +#define _vector(vector) // A macro definition is wrapped around vector_delete, and the vector is set to NULL after deletion +``` +Here, `vector_t` is the structure of `vector`. The creation method will return a `vector` object if successful, or `NULL` if the creation fails. The `dsize` parameter is used to pass in the size of the data, and `size` is used to pass in the size (the number of data elements) of the `vector` array. The deletion method is used to delete the passed-in `vector` object. The creation and deletion methods should be used in pairs. Once a `vector` object is created and no longer needed, it should be deleted. +For example: +```c +void test(void) +{ + vector_t vt = vector(int, 16); // Create an int-type vector with a length of 16 + int array[16]; + + _vector(vt); // Delete vt +} +``` + +#### Reading and Writing vector Data +```c +void* vector_data(vector_t vector, int index); +#define vector_at(vector, type, i) +#define v2a(vector, type) +``` +The `vector_data` method is used to obtain the address of the data according to the index, and it will return the address of the specified data. If it returns `NULL`, it means the operation has failed. The `vector_at` method adds a data type on top of `vector_data`, and `v2a` enables accessing the `vector` as an ordinary array using the `[]` subscript. +Here's an example: +```c +void test(void) +{ + vector_t vt = vector(int, 8);; // Define and create an int-type vector with a length of 8 + int i = 0; + + for (i = 0; i < 8; i++) + { + vector_at(vt, int, i) = i; // Use the at method to access + } + + for (i = 0; i < 8; i++) + { + printf("vt[%d] = %d\r\n", i, v2a(vt, int)[i]); // Use the subscript to access + } + + _vector(vt); // Delete the vector after using it +} +``` +The result is: +``` +vt[0] = 0 +vt[1] = 1 +vt[2] = 2 +vt[3] = 3 +vt[4] = 4 +vt[5] = 5 +vt[6] = 6 +vt[7] = 7 +``` + +#### Size and Capacity of vector +```c +int vector_size(vector_t vector); +int vector_capacity(vector_t vector); +``` +The size of `vector` is easy to understand, which is similar to the size of an array. But what about the capacity? The capacity refers to the space used to store the `vector` size. Since it's a dynamic array, in order to facilitate better expansion, some space is usually reserved for future expansion. Therefore, the capacity must be greater than or equal to the size. **In actual usage, it's usually sufficient to focus on the size rather than the capacity**. +For example: +```c +void test(void) +{ + vector_t vt = vector(int, 10); + printf("size=%d, capacity=%d\r\n", vector_size(vt), vector_capacity(vt)); + _vector(vt); +} +``` +The result is: +``` +size=10, capacity=12 +``` + +#### Adjusting the Size of vector +```c +int vector_resize(vector_t vector, int size); +#define vector_clear(vector) +``` +This method is used to readjust the `vector` container. It can expand or shrink the container. When expanding, additional space is appended at the end of the original space, and the appended space is not initialized (i.e., not necessarily set to 0). When shrinking, the extra part at the tail is discarded. The `vector_clear` macro uses `vector_resize` to adjust the size to 0. +Here's an example: +```c +void test(void) +{ + vector_t vt = vector(int, 10); + printf("size=%d\r\n", vector_size(vt)); + vector_resize(vt, 32); + printf("size=%d\r\n", vector_size(vt)); + vector_resize(vt, 14); + printf("size=%d\r\n", vector_size(vt)); + _vector(vt); +} +``` +The result is: +``` +size=10 +size=32 +size=14 +``` + +#### Inserting and Removing Elements in vector +```c +int vector_insert(vector_t vector, int index, void* data, int num); +int vector_erase(vector_t vector, int index, int num); +``` +The insertion method is used to insert `num` pieces of data at the specified address into the specified index position, while the removal method is used to remove `num` pieces of data starting from the specified index. +Here's an example: +```c +void test(void) +{ + vector_t vt = vector(int, 0); // Create a vector container with a length of 0 + int array[10] = {0,1,2,3,4,5,6,7,8,9}; + int i = 0; + + printf("insert:\r\n"); + vector_insert(vt, 0, array, 10); // Insert array into vt, which is equivalent to assigning the value of array to vt + for (i = 0; i < vector_size(vt); i++) + { + printf("vt[%d]=%d\r\n", i, v2a(vt, int)[i]); + } + + printf("erase:\r\n"); + vector_erase(vt, 5, 2); // Remove two data elements starting from index 5, that is, the data at index 5 and index 6 + for (i = 0; i < vector_size(vt); i++) + { + printf("vt[%d]=%d\r\n", i, v2a(vt, int)[i]); + } + + _vector(vt); +} +``` +The result is: +``` +insert: +vt[0]=0 +vt[1]=1 +vt[2]=2 +vt[3]=3 +vt[4]=4 +vt[5]=5 +vt[6]=6 +vt[7]=7 +vt[8]=8 +vt[9]=9 +erase: +vt[0]=0 +vt[1]=1 +vt[2]=2 +vt[3]=3 +vt[4]=4 +vt[5]=7 +vt[6]=8 +vt[7]=9 +``` +Based on the insertion and removal methods, the following macro definition methods are also derived: +```c +#define vector_push_front(vector, data) +#define vector_push_back(vector, data) +#define vector_pop_front(vector) +#define vector_pop_back(vector) +``` + +### Reference Examples +```c +void test(void) +{ + vector_t vt_vector = vector(vector_t, 3); // A vector of type vector_t + int i = 0; + char *name[3] = { // Use these three names as data sources to generate a new vector + "ZhangSan", + "LiSi", + "WangWu" + }; + + // Traverse vt_vector + for (i = 0; i < vector_size(vt_vector); i++) + { + v2a(vt_vector, vector_t)[i] = vector(char, 0); // Create a new char-type vector for each item in vt_vector + vector_insert(v2a(vt_vector, vector_t)[i], 0, name[i], strlen(name[i]) + 1); // Insert the names into the vector + } + + for (i = 0; i < vector_size(vt_vector); i++) + { + printf("vt_vector[%d]: %s\r\n", i, &vector_at(v2a(vt_vector, vector_t)[i], char, 0)); // Print the names recorded in vt_vector + _vector(v2a(vt_vector, vector_t)[i]); // Delete the vectors under vt_vector + } + + _vector(vt_vector); +} +``` +The result is: +``` +vt_vector[0]: ZhangSan +vt_vector[1]: LiSi +vt_vector[2]: WangWu +``` +In the example, many functions don't have their return values judged. In practical applications, it's necessary to judge the return values. + +### Source Code Analysis + +#### vector Structure +All the structures of the `vector` container are implicit, meaning that the structure members can't be accessed directly. This design ensures the independence and security of the module, preventing external calls from modifying the structure members and thus avoiding damage to the `vector` storage structure. Therefore, the `vector` parser only leaves the single declaration of `vector` in the header file, and the structure definitions are placed in the source file. Only the methods provided by the `vector` container can be used to operate on `vector` objects. +The `vector` type is declared as follows: +```c +typedef struct VECTOR *vector_t; +``` +When using it, just use `vector_t`. + +The `VECTOR` structure is defined as follows: +```c +/* type of vector */ +typedef struct VECTOR +{ + void* base; /* base address for storing data */ + int dsize; /* size of item */ + int size; /* size of vector */ + int capacity; /* capacity of vector */ +} VECTOR; +``` +The `VECTOR` structure contains four members. `base` is the base address where the data is actually stored, `dsize` represents the size of each data element, `size` is the size (length) of the `vector`, and `capacity` is the capacity of the `vector`. + +#### Dynamic Size of vector +Judging from the structure members, the logic implemented by `vector` to achieve dynamic size adjustment isn't too complicated. First, calculate the size of the data to be stored, and then call `malloc` to dynamically allocate the specified space. When it's time to adjust the size, use `realloc` to reallocate the space. +Let's look at the source code: +```c +int vector_resize(vector_t vector, int size) +{ + void* base = NULL; + int capacity; + if (!vector) return 0; + if (size < 0) return 0; + capacity = gradient_capacity(size); // Calculate how much capacity the new space needs + if (capacity!= vector->capacity) // If the calculated capacity is different from the current capacity, reallocate the space + { + base = realloc(vector->base, capacity * vector->dsize); + if (!base) return 0; + vector->base = base; + vector->capacity = capacity; + } + vector->size = size; // Update the new size + return 1; +} +``` +As shown in the above code, implementing dynamic size adjustment is that simple. + +But how is the capacity size calculated? +```c +#define up_multiple(x, mul) ((x)+((mul)-((x)-1)%(mul))-1) /* get the smallest'mul' multiple larger than 'x' */ +static int gradient_capacity(int size) +{ + int capacity = 1; + if (size <= 1) return 1; + while (capacity < size) capacity <<= 1; // Find the smallest power of 2 larger than size + capacity >>= 1; // Find the largest power of 2 smaller than size + if (capacity < 4) capacity = capacity << 1; + else if (capacity < 16) capacity = up_multiple(size, capacity >> 1); + else if (capacity < 256) capacity = up_multiple(size, capacity >> 2); + else capacity = up_multiple(size, 64); + return capacity; +} +``` +This function divides the `size` according to gradients. The `size` within a certain gradient range will get the corresponding `capacity` for that gradient. First, find the largest power of 2 smaller than `size`, and then increase it based on the gradient levels (4, 16, 256) until the maximum increase of 64 at the end. +The following code can be used to test the gradients distinguished by this gradient algorithm: +```c +int size = 0, capacity = 0; +for (size = 1; size < 1024; ) +{ + capacity = gradient_capacity(size); + printf("+%d\t%d\r\n", capacity - size + 1, capacity); + size = capacity + 1; +} +``` +The result is: +``` ++1 1 ++1 2 ++2 4 ++2 6 ++2 8 ++4 12 ++4 16 ++4 20 ++4 24 ++4 28 ++4 32 ++8 40 ++8 48 ++8 56 ++8 64 ++16 80 ++16 96 ++16 112 ++16 128 ++32 160 ++32 192 ++32 224 ++32 256 ++64 320 ++64 384 ++64 448 ++64 512 ++64 576 ++64 640 ++64 704 ++64 768 ++64 832 ++64 896 ++64 960 ++64 1024 +``` +At the end, the maximum increase is only 64. + +#### Insertion and Removal Principle of vector +The principle of insertion is to move the data after the specified position backward as a whole to make room for the inserted data, and then copy the data to be inserted in. Similarly, the principle of removing data is to move the data behind forward as a whole to overwrite the data segment to be removed. + +First, let's look at the source code for insertion: +```c +int vector_insert(vector_t vector, int index, void* data, int num) +{ + int i = 0; + int size; + if (!vector) return 0; + if (index < 0 || index > vector->size) return 0; + if (num == 0) return 0; + size = vector->size; // Record the original size first, which will be needed when moving the data later. After the vector is resized, vector->size will change, and the original tail index won't be accessible. + if (!vector_resize(vector, vector->size + num)) return 0; // Expand the vector to reserve space for the inserted data + if (index < size) memmove(at(index + num), at(index), vector->dsize * (size - index)); // Move the data backward as a whole + if (data) memcpy(at(index + i), data, vector->dsize * num); // Copy the new data in + return 1; +} +``` +It can be seen that the earlier the insertion position is, the more data needs to be moved later, and the lower the insertion efficiency will be. + +Now, let's look at the source code for removal: +```c +int vector_erase(vector_t vector, int index, int num) +{ + unsigned char *op_ptr = NULL; + if (!vector) return 0; + if (vector->size == 0) return 0; + if (index < 0 || index >= vector->size) return 0; + if (num <= 0) return 0; + if (num > vector->size - index) num = vector->size - index; // If num exceeds the number of data elements at the tail, just remove all the data at the tail. + memmove(at(index), at(index + num), vector->dsize * (vector->size - (index + num))); // Move the data forward + vector_resize(vector, vector->size - num); // Shrink the vector to remove the latter part + return 1; +} +``` diff --git a/doc/vlog.en.md b/doc/vlog.en.md new file mode 100644 index 0000000..ff9a446 --- /dev/null +++ b/doc/vlog.en.md @@ -0,0 +1,107 @@ +### Introduction + +The `vlog` is a simple log output module that adopts the channel filtering mode. In other words, it provides log output for multiple channels, and only when a channel is enabled will there be log output for that particular channel. + +### Interfaces + +#### vlog Log Output +```c +int vlog(vlogChnType channel, const char *format,...); +``` +The `channel` parameter provides 8 channels, which are defined as follows: +```c +#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 +``` +It's possible to perform multi-channel output. Multiple channels can be combined for output by using the `|` operator. By default, only `VLOG_CHANNEL_0` is opened. +For example: +```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"); +} +``` +The result is: +``` +[VLOG_CHANNEL_0] vlog! +``` + +#### vlog Setting and Getting Channel Filters +```c +void vlog_set_filter(vlogChnType mask); +vlogChnType vlog_get_filter(void); +``` +These two functions can be used to set and get the enabled channel filters respectively. For more convenience, the following macro definition methods are provided to open and close specific channels: +```c +#define VLOG_ENABALE(c) +#define VLOG_DISABALE(c) +``` +Here's an example: +```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"); +} +``` +The result is: +``` +[VLOG_CHANNEL_1] vlog! +``` + +#### vlog Console Output +```c +int vlog_set_console(vlogChnType channel, int console); +``` +This method is used to set whether to enable console output for the specified channel. By default, console output for each channel is already enabled. + +#### vlog Offline Saving +```c +int vlog_set_offline(vlogChnType channel, const char *filename); +``` +This method can start offline saving or stop it (by passing in a null file name). +For example: +```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); +} +``` +The result is saved in the file [link](/test/file/log.txt). + +#### vlog Function Callback +```c +int vlog_set_func(vlogChnType channel, vlogFuncType func); +``` +This method is used to set the `vlog` callback function, which provides greater flexibility. That means the content of the `vlog` function will be output to the callback function. +The callback function type is defined as follows: +```c +typedef void (*vlogFuncType)(char *buf, int len); +``` + +#### vlog Getting Channel Status +```c +vlogChnType vlog_get_console(void); +vlogChnType vlog_get_offline(void); +vlogChnType vlog_get_func(void); +``` +These functions can be used to get the status masks of each channel for the three output directions respectively. diff --git a/doc/xml.en.md b/doc/xml.en.md new file mode 100644 index 0000000..219ce22 --- /dev/null +++ b/doc/xml.en.md @@ -0,0 +1,243 @@ +### Introduction +This is a C language XML parser that can handle the parsing and generation of XML text files. It is suitable for use on most C language platforms. + +### Usage Examples + +#### Generation +**Test Code**: +```c +void test_write(void) +{ + xml_t root, x; + + root = xml_create("root"); + if (!root) return; + + x = xml_create("name"); + xml_set_text(x, "xml parser"); + xml_insert(root, 0, x); + + x = xml_create("description"); + xml_set_text(x, "This is a C language version of xml parser."); + xml_insert(root, 1, x); + + x = xml_create("license"); + xml_set_text(x, "GPL3.0"); + xml_insert(root, 2, x); + + xml_file_dump(root, "write.xml"); + + xml_delete(root); +} +``` +**Generated File Name**: **write.xml** +```xml + + xml parser + This is a C language version of xml parser. + GPL3.0 + +``` + +#### Parsing +**File Name**: **read.xml** +```xml + + + + Harry Potter + J K.Rowling + 2005 + 29.99 + + + Learning XML + Erik T.Ray + 2004 + 39.95 + + +``` +**Test Code**: +```c +void test_read(void) +{ + xml_t root, x; + + root = xml_file_load(READ_FILE); + if (!root) return; + + printf("load success!\r\n"); + + x = xml_to(root, "book", 1); + printf("x attr: %s\r\n", xml_get_attribute(x, NULL, 0)); + + x = xml_to(x, "author", 0); + printf("author: %s\r\n", xml_get_text(x)); + + xml_delete(root); +} +``` +**Printed Result**: +``` +load success! +x attr: WEB +author: Erik T.Ray +``` + +### XML Syntax + +#### XML Documents Must Have a Root Element +XML must contain a root element which is the parent of all other elements. For example, in the following instance, `root` is the root element: +```xml + + + ..... + + +``` + +#### XML Declaration +The XML declaration is an optional part of the XML file. If it exists, it should be placed on the first line of the document, like this: +```xml + +``` +* This XML parser only supports the parsing of this declaration and doesn't actually apply the parsed version and encoding in practice. + +#### All XML Elements Must Have a Closing Tag +In XML, omitting a closing tag is illegal. All elements must have closing tags: +```xml +

This is a paragraph.

+``` + +#### XML Tags Are Case-Sensitive +XML tags are case-sensitive. The tag `` is different from the tag ``. The opening and closing tags must be written in the same case: +```xml +这是错误的 +这是正确的 +``` + +#### XML Must Be Correctly Nested +In XML, all elements must be correctly nested within each other: +```xml +This text is bold and italic +``` + +#### XML Attribute Values Must Be in Quotes +In XML, the attribute values of XML elements must be enclosed in quotes. +```xml + +Tove +Jani + +``` +```xml + +Tove +Jani + +``` +The error in the first document is that the `date` attribute in the `note` element is not in quotes. + +#### Entity References +In XML, some characters have special meanings. If you put the character "<" inside an XML element, an error will occur because the parser will treat it as the start of a new element. To avoid this error, use entity references instead of the "<" character: +```xml +if salary < 1000 then +``` +There are five predefined entity references in XML: +| | | | +|:--------:|:-:|:--------------:| +| `<` | < | less than | +| `>` | > | greater than | +| `&` | & | ampersand | +| `'` | ' | apostrophe | +| `"` | " | quotation mark | + +Note: In XML, only the characters "<" and "&" are actually illegal. The greater than sign is legal, but it's a good practice to use entity references instead. + +### Operation Methods + +#### Common Methods + +##### XML Parsing +**Method Prototypes**: +```c +xml_t xml_loads(const char* text); +xml_t xml_file_load(const char* filename); +``` +The `xml_loads` function takes XML text information as input and returns the handle of the parsed XML object. The `xml_file_load` function takes a file name as input to load the file and return the XML object. Inside the function, it reads the file using the C language standard file operation function set and then applies the `xml_loads` function for parsing. It supports files encoded in UTF-8. + +##### XML Generation +**Method Prototypes**: +```c +char* xml_dumps(xml_t xml, int preset, int unformat, int* len); +int xml_file_dump(xml_t xml, char* filename); +``` +The `xml_dumps` function converts an XML object into text information. The `preset` parameter is the preset text length. If the preset length is close to the final output text length, it can reduce the number of memory reallocations and improve the conversion efficiency. The `unformat` parameter determines whether to use formatted output or not. If not using formatted output, the text will be squeezed into one line. The `len` parameter is the length of the converted output. The `xml_file_dump` function uses the `xml_dumps` function to store the text information into a file with the specified name. + +##### XML Object Creation and Deletion +**Method Prototypes**: +```c +xml_t xml_create(void); +void xml_delete(xml_t xml); +``` +The `xml_create` function creates and returns an empty XML object. If it returns `NULL`, it means the creation has failed. The `xml_delete` function is used to delete an XML object. + +##### XML Getting Child Objects +**Method Prototypes**: +```c +xml_t xml_to(xml_t xml, const char *name, int index); +``` +In an XML object, the `name` is not checked for duplication. That is, in the same level of XML, there may be multiple elements with the same `name`. The `xml_to` method can be used to match specific elements. When `name` is passed as `NULL`, only the `index` is used to match the child object according to the index. When `name` is not `NULL`, it will only match the child objects with the corresponding `name` and use the `index` to indicate which element with the `name` to match. +```c +t = xml_to(xml, NULL, 3); // Find the child object with index 3 +t = xml_to(xml, "a", 3); // Find the child object with key "a" and index 3 +``` + +##### XML Setting and Getting Text +**Method Prototypes**: +```c +int xml_set_text(xml_t xml, const char *text); +const char* xml_get_text(xml_t xml); +``` +These two methods are used to set and get the text of an XML element respectively. + +##### XML Adding and Removing Attributes +**Method Prototypes**: +```c +int xml_add_attribute(xml_t xml, const char *name, const char *value); +int xml_remove_attribute(xml_t xml, const char *name, int index); +``` +The `xml_add_attribute` function adds an attribute with the corresponding `name` and `value` to the beginning of the XML element. The `xml_remove_attribute` function has a matching logic similar to that of `xml_to` and is used to remove specific attributes. Both functions return 1 if the operation is successful and 0 if it fails. + +##### XML Getting Attributes +```c +const char* xml_get_attribute(xml_t xml, const char *name, int index); +``` +This method uses a matching logic similar to that of `xml_to` to get the corresponding attribute value. + +##### XML Inserting and Deleting Child Objects +**Method Prototypes**: +```c +int xml_insert(xml_t xml, int index, xml_t ins); +int xml_remove(xml_t xml, const char *name, int index); +``` +The `xml_insert` method inserts a created object into another object according to the index. The `xml_remove` method is similar to `xml_remove_attribute` and is used to remove specific child objects. Both methods return 1 if the operation is successful and 0 if it fails. + +##### XML Parsing Error Reporting +The error types include the following: +```c +#define XML_E_OK 0 // ok +#define XML_E_TEXT 1 // empty text +#define XML_E_MEMORY 2 // memory +#define XML_E_LABEL 3 // label +#define XML_E_VERSION 4 // version +#define XML_E_ENCODING 5 // encoding +#define XML_E_ILLEGAL 6 // illegal character +#define XML_E_END 7 // end +#define XML_E_VALUE 8 // missing value +#define XML_E_QUOTE 9 // missing quete +#define XML_E_COMMENT 10 // missing comment tail --> +#define XML_E_NOTES 11 // head notes error +#define XML_E_CDATA 12 // missing comment tail ]]> +``` diff --git a/release.txt b/release.txt new file mode 100644 index 0000000..d012e78 --- /dev/null +++ b/release.txt @@ -0,0 +1,17 @@ +version 0.2.0 +date 2024.08.11 +changes + 1. Add readme files for some modules(cQueue, dList, sList, tool, valloc, vlog) + 2. Add std related file description + 3. Fix the set method for sList and dList + 4. Add graph module + +--------------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------------------- + +version 0.1.0 +date 2024.07.21 +changes + 1. Initialization version + 2. Add algorithm classification, including PID, filter and search algorithms + 3. Update some readme files \ No newline at end of file diff --git a/source/01_general/cQueue.h b/source/01_general/cQueue.h index 7490342..75c6586 100644 --- a/source/01_general/cQueue.h +++ b/source/01_general/cQueue.h @@ -46,6 +46,18 @@ int cQueue_moveHead(cQueue *q); int cQueue_moveTail(cQueue *q); int cQueue_index(cQueue *q, unsigned int index); + +/** + * \brief Locally used anonymous cQueue type definition. + * \param[in] type: data type + * \param[in] cap: capacity of queue + * + * example: cQueue(int, 64) qi64; + * + * \return none + */ +#define cQueue(type, cap) struct { cQueue queue; type data[cap]; } + /** * \brief Initialize queue object structure. * \param[in] qObject: queue object @@ -53,6 +65,20 @@ int cQueue_index(cQueue *q, unsigned int index); */ #define cQueue_init(qObject) (cQueue_initCap(&((qObject).queue), sizeof((qObject).data)/sizeof((qObject).data[0]))) +/** + * \brief get the size of queue. + * \param[in] qObject: queue object + * \return size of queue + */ +#define cQueue_size(qObject) ((qObject).queue.size) + +/** + * \brief get the capacity of queue. + * \param[in] qObject: queue object + * \return capacity of queue + */ +#define cQueue_cap(qObject) ((qObject).queue.cap) + /** * \brief check if empty. * \param[in] qObject: queue object diff --git a/source/01_general/calculate.c b/source/01_general/calculate.c index e311b5a..a17f4dd 100644 --- a/source/01_general/calculate.c +++ b/source/01_general/calculate.c @@ -38,6 +38,14 @@ typedef struct int argc; /**< Number of function arguments, Maximum support 8 */ } function_t; +/* Calculation function structure definition */ +typedef struct +{ + char *name; /**< Function name */ + int len; /**< Length of function name */ + double value; /**< C function that performs calculations */ /**< Number of function arguments, Maximum support 8 */ +} constv_t; + /* The definition of the built-in function corresponding to the calculation function */ static double cot(double v1) { return 1 / tan(v1); } static double acot(double v1) { return atan(1 / v1); } @@ -73,9 +81,18 @@ static function_t in_function_table[] = /* External function table */ static function_t ex_function_table[CALCULATE_EXFUNC_MAX]; +static constv_t const_value_table[CALCULATE_CONSTV_MAX] = +{/* name len value */ + {"Pi", 2, PI}, + {"E", 1, E}, +}; + /* The current number of external functions */ static int ex_function_num = 0; +/* The current number of external functions */ +static int const_value_num = 2; + /* Function declaration */ static char *parse_value(char *p, double *n); @@ -365,10 +382,19 @@ static char *parse_value(char *p, double *n) } /* constant */ - if ((s[0] == 'p' || s[0] == 'P') && (s[1] == 'i' || s[1] == 'I')) value = PI; - else if (s[0] == 'e' || s[0] == 'E') value = E; - else if (s[0] >= '0' && s[0] <= '9') value = v_atof(s, p - s); - else return p; + for (i = 0; i < const_value_num; i++) + { + if (!strncmp(s, const_value_table[i].name, const_value_table[i].len)) + { + value = const_value_table[i].value; + break; + } + } + if (i >= const_value_num) + { + if (s[0] >= '0' && s[0] <= '9') value = v_atof(s, p - s); + else return p; + } if (isnan(value)) return p; @@ -377,7 +403,7 @@ static char *parse_value(char *p, double *n) return p; } -int calculate_export(const char *name, double (*func)(), int argc) +int calculate_function(const char *name, double (*func)(), int argc) { int i = 0, count, len; @@ -419,6 +445,36 @@ int calculate_export(const char *name, double (*func)(), int argc) return 1; } +int calculate_constant(const char *name, double value) +{ + int i = 0, count, len; + + if (const_value_num >= CALCULATE_CONSTV_MAX) return 0; + + /* check validity */ + if (!name) return 0; + + len = strlen(name); + if (len <= 0) return 0; + + for (i = 0; i < const_value_num; i++) + { + if (!strncmp(name, const_value_table[i].name, const_value_table[i].len)) + { + const_value_table[i].value = value; + return 1; + } + } + + /* Add a function to the extern function list */ + const_value_table[const_value_num].name = (char *)name; + const_value_table[const_value_num].len = len; + const_value_table[const_value_num].value = value; + const_value_num++; + + return 1; +} + double calculate(const char *expression) { double n; @@ -438,3 +494,61 @@ double calculate(const char *expression) return n; } +const char* calculate_ls_func(int *argc) +{ + static int it = 0; + function_t *ft = NULL; + int count = sizeof(in_function_table) / sizeof(in_function_table[0]); + + if (it > count + ex_function_num) + { + it = 0; + return NULL; + } + + if (it < count) // in_function_table + { + ft = &in_function_table[it]; + } + else // ex_function_table + { + if (it - count < ex_function_num) + { + ft = &ex_function_table[it - count]; + } + } + + if (!ft) + { + it = 0; + return NULL; + } + + // Update iterator to get next function + it++; + if (argc) *argc = ft->argc; + + return (const char *)ft->name; +} + +const char* calculate_ls_const(double *value) +{ + static int it = 0; + constv_t *ct = NULL; + + if (it < const_value_num) + { + ct = &const_value_table[it]; + } + else + { + it = 0; + return NULL; + } + + // Update iterator to get next function + it++; + if (value) *value = ct->value; + + return (const char *)ct->name; +} diff --git a/source/01_general/calculate.h b/source/01_general/calculate.h index e7db5ad..b076d78 100644 --- a/source/01_general/calculate.h +++ b/source/01_general/calculate.h @@ -20,6 +20,7 @@ /* Configuration information */ #define CALCULATE_EXFUNC_MAX 32 /**< The maximum extern function count supported */ +#define CALCULATE_CONSTV_MAX 16 /**< The maximum const value count supported, include Pi and E */ /** * \brief enter a calculation expression to calculate the result, support 'in_function_table[]' table operation function @@ -35,6 +36,28 @@ double calculate(const char *expression); * \param[in] argc: count of arguments * \return 1 success or 0 fail */ -int calculate_export(const char *name, double (*func)(), int argc); +int calculate_function(const char *name, double (*func)(), int argc); + +/** + * \brief Export const value + * \param[in] *name: function name + * \param[in] value: const value + * \return 1 success or 0 fail + */ +int calculate_constant(const char *name, double value); + +/** + * \brief The iteration outputs the currently supported calculateion functions + * \param[out] argc: address that output the function argc + * \return function name, or NULL iteration end + */ +const char* calculate_ls_func(int *argc); + +/** + * \brief The iteration outputs the currently supported constant + * \param[out] value: address that output the constant + * \return constant name, or NULL iteration end + */ +const char* calculate_ls_const(double *value); #endif diff --git a/source/01_general/command.c b/source/01_general/command.c index 7b6cf25..21d490d 100644 --- a/source/01_general/command.c +++ b/source/01_general/command.c @@ -44,7 +44,7 @@ static struct COMMAND base[COMMAND_COUNT_MAX] = static void usage(void) { printf( -"Usage: cmd [opt] [arg]\n" +"Usage: cmd [opt] [arg] ...\n" "Enter the command line to execute the corresponding command\n" "\n" "options:\n" diff --git a/source/01_general/date.c b/source/01_general/date.c index 31545df..fc7dd80 100644 --- a/source/01_general/date.c +++ b/source/01_general/date.c @@ -108,11 +108,11 @@ uint32_t date_current_days(DATE date) } /** - * \brief Get the week number of a given date based on the total days. + * \brief Get the day of week of a given date based on the total days. * \param[in] date: The date to evaluate. * \return The week number [1, 7] for the specified date (Mon | Tue | Wed | Thu | Fri | Sat | Sun), 0 if invalid. */ -uint32_t date_get_week(DATE date) +uint32_t date_get_dow(DATE date) { #define bias 0 // Adjust to week start uint32_t days = date_current_days(date); @@ -184,7 +184,7 @@ DATE date_offset(DATE date, int32_t days) * \param[in] year: The year of the calendar to show. * \param[in] month: The month of the calendar to show. */ -void date_show(uint16_t year, uint8_t month) +void date_calendar(uint16_t year, uint8_t month) { DATE date = {year, month, 1}; uint32_t days; @@ -199,7 +199,7 @@ void date_show(uint16_t year, uint8_t month) return; // Exit if the date is invalid } - week = date_get_week(date) % 7; // Get the starting day of the week + week = date_get_dow(date) % 7; // Get the starting day of the week // Print the calendar header printf("+-----------------------------------------+\r\n"); diff --git a/source/01_general/date.h b/source/01_general/date.h index 58c68c8..fe00d3c 100644 --- a/source/01_general/date.h +++ b/source/01_general/date.h @@ -74,11 +74,11 @@ uint32_t date_year_days(uint16_t year); uint32_t date_month_days(uint16_t year, uint8_t month); /** - * \brief Get the week number of a given date based on the total days. + * \brief Get the day of week of a given date based on the total days. * \param[in] date: The date to evaluate. * \return The week number [1, 7] for the specified date (Mon | Tue | Wed | Thu | Fri | Sat | Sun), 0 if invalid. */ -uint32_t date_get_week(DATE date); +uint32_t date_get_dow(DATE date); /** * \brief Calculate the total number of days from a given date to a base date. @@ -115,6 +115,6 @@ DATE date_offset(DATE date, int32_t days); * \param[in] year: The year of the calendar to show. * \param[in] month: The month of the calendar to show. */ -void date_show(uint16_t year, uint8_t month); +void date_calendar(uint16_t year, uint8_t month); #endif diff --git a/source/01_general/tool.c b/source/01_general/tool.c index 40bd183..3b65a25 100644 --- a/source/01_general/tool.c +++ b/source/01_general/tool.c @@ -136,3 +136,12 @@ int GetStringHex(char inHexString[], unsigned int maxInSize, char outArray[], un return len; // Return the length of the converted array } + +uint64_t unitt_clock(void) +{ + struct timeval mstime; + uint64_t us = 0; + gettimeofday(&mstime, NULL); + us = mstime.tv_sec * 1000000 + mstime.tv_usec; + return us; +} \ No newline at end of file diff --git a/source/01_general/tool.h b/source/01_general/tool.h index 9bc483e..a5d68ac 100644 --- a/source/01_general/tool.h +++ b/source/01_general/tool.h @@ -20,6 +20,8 @@ extern "C" #include #include +#include +#include /* Version infomation */ diff --git a/source/05_parser/csv.h b/source/05_parser/csv.h index 57e257e..d677ba3 100644 --- a/source/05_parser/csv.h +++ b/source/05_parser/csv.h @@ -38,6 +38,7 @@ typedef struct CSV* csv_t; /* find flag */ +#define CSV_F_FLAG_Default (0x00) /* default find rule */ #define CSV_F_FLAG_MatchCase (0x01) /* match case sensitive */ #define CSV_F_FLAG_MatchEntire (0x02) /* match the entire cell content */ #define CSV_F_FLAG_MatchByCol (0x04) /* match by column */ diff --git a/source/05_parser/txls.c b/source/05_parser/txls.c index 49de6da..81fd7b4 100644 --- a/source/05_parser/txls.c +++ b/source/05_parser/txls.c @@ -101,7 +101,7 @@ static CELL *column_cell(COLUMN *column, int index, int size) * \param[in] row: number of rows * \return txls handle or NULL FAIL */ -txls_t txls_create(unsigned int col, unsigned int row) +txls_t txls_create(unsigned int row, unsigned int col) { txls_t txls = NULL; unsigned int i; @@ -116,7 +116,7 @@ txls_t txls_create(unsigned int col, unsigned int row) /* Insert Column */ for (i = 0; i < col; i++) { - if (!txls_insert_column(txls, 1)) goto FAIL; + if (!txls_insert_col(txls, 1)) goto FAIL; } /* Insert Row */ @@ -228,7 +228,7 @@ void txls_delete(txls_t txls) while (txls->col) { /* Delete the first column of the txls_t structure */ - txls_delete_column(txls, 1); + txls_delete_col(txls, 1); } /* Free the memory allocated for the txls_t structure */ @@ -375,7 +375,7 @@ int txls_set_align(txls_t txls, unsigned int col, int align) * \param[in] row: the row, starting from 1 * \return conten address or NULL fail */ -const char* txls_get_text(txls_t txls, unsigned int col, unsigned int row) +const char* txls_get_text(txls_t txls, unsigned int row, unsigned int col) { COLUMN *column; CELL *cell; @@ -404,7 +404,7 @@ const char* txls_get_text(txls_t txls, unsigned int col, unsigned int row) * \param[in] *text: specified content * \return 1 success or 0 fail */ -int txls_set_text(txls_t txls, unsigned int col, unsigned int row, const char* text) +int txls_set_text(txls_t txls, unsigned int row, unsigned int col, const char* text) { COLUMN *column; CELL *cell; @@ -453,7 +453,7 @@ int txls_set_text(txls_t txls, unsigned int col, unsigned int row, const char* t * \param[in] *head: header * \return 1 success or 0 fail */ -int txls_insert_column(txls_t txls, unsigned int col) +int txls_insert_col(txls_t txls, unsigned int col) { COLUMN *column = NULL, *pcolumn = NULL; CELL *cell = NULL, *pcell = NULL; @@ -526,7 +526,7 @@ FAIL: * \param[in] col: the column, starting from 1 * \return 1 success or 0 fail */ -int txls_delete_column(txls_t txls, unsigned int col) +int txls_delete_col(txls_t txls, unsigned int col) { COLUMN *column = NULL, *pcolumn = NULL; @@ -1146,7 +1146,7 @@ static const char* parse_head(const char* text, txls_t txls) { s = skip(s + 1); if (*s == '\n') continue; - if (!txls_insert_column(txls, txls->col + 1)) + if (!txls_insert_col(txls, txls->col + 1)) { E(TXLS_E_MEMORY); return s; diff --git a/source/05_parser/txls.h b/source/05_parser/txls.h index ccb481e..fe58356 100644 --- a/source/05_parser/txls.h +++ b/source/05_parser/txls.h @@ -24,7 +24,7 @@ extern "C" #define TXLS_V_MAJOR 1 #define TXLS_V_MINOR 0 -#define TXLS_V_PATCH 0 +#define TXLS_V_PATCH 1 /* txls type definition, hiding structural members, not for external use */ @@ -65,7 +65,7 @@ int txls_file_dump(txls_t txls, const char* filename); /* Create and delete txls */ -txls_t txls_create(unsigned int col, unsigned int row); +txls_t txls_create(unsigned int row, unsigned int col); void txls_delete(txls_t txls); /* Get the number of txls rows and columns*/ @@ -75,13 +75,13 @@ unsigned int txls_col(txls_t txls); /* set and get txls cell text */ -const char* txls_get_text(txls_t txls, unsigned int col, unsigned int row); -int txls_set_text(txls_t txls, unsigned int col, unsigned int row, const char* text); +const char* txls_get_text(txls_t txls, unsigned int row, unsigned int col); +int txls_set_text(txls_t txls, unsigned int row, unsigned int col, const char* text); /* row and column operations */ -int txls_insert_column(txls_t txls, unsigned int col); -int txls_delete_column(txls_t txls, unsigned int col); +int txls_insert_col(txls_t txls, unsigned int col); +int txls_delete_col(txls_t txls, unsigned int col); int txls_insert_row(txls_t txls, unsigned int row); int txls_delete_row(txls_t txls, unsigned int row); diff --git a/source/06_performance/romt.c b/source/06_performance/romt.c index a1e0217..806d148 100644 --- a/source/06_performance/romt.c +++ b/source/06_performance/romt.c @@ -20,7 +20,7 @@ /** * \brief Test the read functionality of the ROMT structure. * - * This function reads data from the ROMT structure in units of UNIT_SIZE + * This function reads data from the ROMT structure in units of ROMT_UNIT_SIZE * until the entire ROM is read. It verifies that the number of bytes read * matches the expected number for each read operation. * @@ -31,16 +31,16 @@ static uint8_t test_read(ROMT *romt) { uint32_t base = romt->base; ///< Current address in the ROM uint32_t size = 0; ///< Total bytes read - uint8_t buffer[UNIT_SIZE]; ///< Buffer to hold read data + uint8_t buffer[ROMT_UNIT_SIZE]; ///< Buffer to hold read data uint16_t num; ///< Number of bytes to read in current iteration while (size < romt->size) ///< Loop until the entire ROM is read { - if (romt->size - size > UNIT_SIZE) num = UNIT_SIZE; ///< Determine number of bytes to read + if (romt->size - size > ROMT_UNIT_SIZE) num = ROMT_UNIT_SIZE; ///< Determine number of bytes to read else num = romt->size - size; // Perform the read operation and check if the number of bytes read is correct - if (num != (uint8_t)(romt->read)(base, buffer, num)) + if (num != (romt->read)(base, buffer, num)) { return 0; ///< Return 0 if read operation fails } @@ -55,7 +55,7 @@ static uint8_t test_read(ROMT *romt) /** * \brief Test the write functionality of the ROMT structure. * - * This function writes data to the ROMT structure in units of UNIT_SIZE + * This function writes data to the ROMT structure in units of ROMT_UNIT_SIZE * until the entire ROM is written. It verifies that the number of bytes * written matches the expected number for each write operation. * @@ -66,16 +66,16 @@ static uint8_t test_write(ROMT *romt) { uint32_t base = romt->base; ///< Current address in the ROM uint32_t size = 0; ///< Total bytes written - uint8_t buffer[UNIT_SIZE] = {0}; ///< Buffer initialized to zeros for writing + uint8_t buffer[ROMT_UNIT_SIZE] = {0}; ///< Buffer initialized to zeros for writing uint16_t num; ///< Number of bytes to write in current iteration while (size < romt->size) ///< Loop until the entire ROM is written { - if (romt->size - size > UNIT_SIZE) num = UNIT_SIZE; ///< Determine number of bytes to write + if (romt->size - size > ROMT_UNIT_SIZE) num = ROMT_UNIT_SIZE; ///< Determine number of bytes to write else num = romt->size - size; // Perform the write operation and check if the number of bytes written is correct - if (num != (uint8_t)(romt->write)(base, buffer, num)) + if (num != (romt->write)(base, buffer, num)) { return 0; ///< Return 0 if write operation fails } @@ -101,25 +101,25 @@ static uint8_t test_normal(ROMT *romt) { uint32_t base = romt->base; ///< Current address in the ROM uint32_t size = 0; ///< Total bytes processed - uint8_t buffer[UNIT_SIZE]; ///< Buffer for reading and writing data + uint8_t buffer[ROMT_UNIT_SIZE]; ///< Buffer for reading and writing data uint16_t i, num; ///< Loop index and number of bytes for current iteration while (size < romt->size) ///< Loop until the entire ROM is processed { // Determine number of bytes to write in the current iteration - if (romt->size - size > UNIT_SIZE) - num = UNIT_SIZE; + if (romt->size - size > ROMT_UNIT_SIZE) + num = ROMT_UNIT_SIZE; else num = romt->size - size; // Fill the buffer with a pattern (0, 1, 2, ..., 255) - for (i = 0; i < UNIT_SIZE; i++) + for (i = 0; i < ROMT_UNIT_SIZE; i++) { buffer[i] = (uint8_t)(i % 256); } // Perform the write operation and check if the number of bytes written is correct - if (num != (uint8_t)(romt->write)(base, buffer, num)) + if (num != (romt->write)(base, buffer, num)) { return 0; ///< Return 0 if write operation fails } @@ -128,13 +128,13 @@ static uint8_t test_normal(ROMT *romt) memset(buffer, 0, sizeof(buffer)); // Perform the read operation and check if the number of bytes read is correct - if (num != (uint8_t)(romt->read)(base, buffer, num)) + if (num != (romt->read)(base, buffer, num)) { return 0; ///< Return 0 if read operation fails } // Verify that the read data matches the expected pattern - for (i = 0; i < UNIT_SIZE; i++) + for (i = 0; i < ROMT_UNIT_SIZE; i++) { if (buffer[i] != (uint8_t)(i % 256)) { @@ -244,11 +244,11 @@ static uint8_t test_pattern(ROMT *romt, uint8_t pattern) { uint32_t base = romt->base; ///< Current base address in the ROM uint32_t size = 0; ///< Total bytes processed - uint8_t buffer[UNIT_SIZE], temp[UNIT_SIZE]; ///< Buffers for writing and reading data + uint8_t buffer[ROMT_UNIT_SIZE], temp[ROMT_UNIT_SIZE]; ///< Buffers for writing and reading data uint16_t i, num; ///< Loop index and number of bytes for current iteration // Fill the buffer with the specified pattern - for (i = 0; i < UNIT_SIZE; i++) + for (i = 0; i < ROMT_UNIT_SIZE; i++) { buffer[i] = pattern; } @@ -256,13 +256,13 @@ static uint8_t test_pattern(ROMT *romt, uint8_t pattern) while (size < romt->size) ///< Loop until the entire ROM is processed { // Determine number of bytes to write in the current iteration - if (romt->size - size > UNIT_SIZE) - num = UNIT_SIZE; + if (romt->size - size > ROMT_UNIT_SIZE) + num = ROMT_UNIT_SIZE; else num = romt->size - size; // Write the pattern to the ROM - if (num != (uint8_t)(romt->write)(base, buffer, num)) + if (num != (romt->write)(base, buffer, num)) { return 0; // Return 0 if the write operation fails } @@ -270,13 +270,13 @@ static uint8_t test_pattern(ROMT *romt, uint8_t pattern) memset(temp, 0, sizeof(temp)); // Clear the temporary buffer before reading // Read back the data from the ROM - if (num != (uint8_t)(romt->read)(base, temp, num)) + if (num != (romt->read)(base, temp, num)) { return 0; // Return 0 if the read operation fails } // Verify that the read data matches the expected pattern - for (i = 0; i < UNIT_SIZE; i++) + for (i = 0; i < ROMT_UNIT_SIZE; i++) { if (temp[i] != pattern) { diff --git a/source/06_performance/romt.h b/source/06_performance/romt.h index b8e900b..dcd8f45 100644 --- a/source/06_performance/romt.h +++ b/source/06_performance/romt.h @@ -29,7 +29,7 @@ #define ROMT_MODE_RANDOM ((uint32_t)0x00000010) ///< Random access mode #define ROMT_MODE_PATTERN ((uint32_t)0x00000020) ///< Pattern generation mode -#define UNIT_SIZE ((uint16_t)256) ///< Size of a unit data block +#define ROMT_UNIT_SIZE ((uint16_t)256) ///< Size of a unit data block /** * \brief Type definition for read callback function. @@ -38,7 +38,7 @@ * \param[in] length: Number of bytes to read. * \return Returns the number of bytes read on success; otherwise, an error code. */ -typedef int (*romt_read_t)(uint32_t address, const uint8_t *data, uint32_t length); +typedef int (*romt_read_t)(uint32_t address, uint8_t *data, uint32_t length); /** * \brief Type definition for write callback function. @@ -47,7 +47,7 @@ typedef int (*romt_read_t)(uint32_t address, const uint8_t *data, uint32_t lengt * \param[in] length: Number of bytes to write. * \return Returns the number of bytes written on success; otherwise, an error code. */ -typedef int (*romt_write_t)(uint32_t address, const uint8_t *data, uint32_t length); +typedef int (*romt_write_t)(uint32_t address, uint8_t *data, uint32_t length); // ROM structure definition typedef struct diff --git a/test/test.mk b/test/test.mk index 5608c16..504a42a 100644 --- a/test/test.mk +++ b/test/test.mk @@ -2,7 +2,7 @@ TEST_LIST += init TEST_LIST += kern TEST_LIST += valloc TEST_LIST += arg -TEST_LIST += vstd +# TEST_LIST += vstd TEST_LIST += vlog TEST_LIST += ini TEST_LIST += txls @@ -38,6 +38,7 @@ TEST_LIST += dList TEST_LIST += cQueue TEST_LIST += intl TEST_LIST += ramt +TEST_LIST += romt TEST_LIST += cpul TEST_LIST += date TEST_LIST += unitt diff --git a/test/test_arg.c b/test/test_arg.c index 0280e80..688fe61 100644 --- a/test/test_arg.c +++ b/test/test_arg.c @@ -3,53 +3,26 @@ #include #if defined(TEST_TARGET_arg) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "arg.h" #endif -static void usage(void) +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_base(void) { - printf( -"Usage: arg [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("arg version %d.%d.%d\r\n", ARG_V_MAJOR, ARG_V_MINOR, ARG_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - printf("ARG_MAX %d\r\n", ARG_MAX); printf("ARGC %d\r\n", ARGC()); @@ -65,10 +38,74 @@ static int test(int argc, char *argv[]) printf("ARGS %s\r\n", ARGS(0, "arg0", "arg1", "arg2")); printf("ARGS %s\r\n", ARGS(1, "arg0", "arg1", "arg2")); printf("ARGS %s\r\n", ARGS(2, "arg0", "arg1", "arg2")); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: arg [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" -h Print help\n" +" -v Print version\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hv"); + if (opt == -1) break; + + switch (opt) + { + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("arg version %d.%d.%d\r\n", ARG_V_MAJOR, ARG_V_MINOR, ARG_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_arg) int main(int argc, char *argv[]) { @@ -78,6 +115,8 @@ int main(int argc, char *argv[]) void test_arg(void) { command_export("arg", test); + + // command("arg"); } init_export_app(test_arg); #endif diff --git a/test/test_cQueue.c b/test/test_cQueue.c index 3172a21..0905a23 100644 --- a/test/test_cQueue.c +++ b/test/test_cQueue.c @@ -3,32 +3,107 @@ #include #if defined(TEST_TARGET_cQueue) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "cQueue.h" #endif -void test_int(void) +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) { + static cQueue(unsigned int, 10) queue; + static int initFlag = 0; + static unsigned int start = 0, end = 0; + + unsigned int data = 0; + unsigned int size = end - start; + + if (!initFlag) + { + cQueue_init(queue); + initFlag = 1; + } + + if (size != cQueue_size(queue)) return UNITT_E_FAIL; + if (size > 0) + { + data = rand() % cQueue_size(queue); + if (cQueue_at(queue, data) != (start + data)) return UNITT_E_FAIL; + } + + printf("size %u\r\n", size); + + if (rand() % 2) + { + if (cQueue_push(queue, end)) + { + end++; + } + } + else + { + if (cQueue_pop(queue, data)) + { + start++; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "cQueue suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_int(void) +{ +#if 0 typedef struct { cQueue queue; int data[10]; } intQueueType; intQueueType intQueue; +#else + cQueue(int, 10) intQueue; +#endif cQueue_init(intQueue); - for (int i = 0; i < intQueue.queue.cap; i++) + for (int i = 0; i < cQueue_cap(intQueue); i++) { cQueue_push(intQueue, i); } printf("cQueue[5] = %d\r\n", cQueue_at(intQueue, 5)); - while (intQueue.queue.size > 0) + while (cQueue_size(intQueue) > 0) { int data; cQueue_pop(intQueue, data); @@ -36,64 +111,85 @@ void test_int(void) } } -void test_struct(void) +static void test_struct(void) { typedef struct { char *name; int age; } Stu; - typedef struct - { - cQueue queue; - Stu data[10]; - } StuQueueType; - StuQueueType StuQueue; + // typedef struct + // { + // cQueue queue; + // Stu data[10]; + // } StuQueueType; + // StuQueueType StuQueue; + cQueue(Stu, 10) StuQueue; Stu s = {"Zhang", 18}; cQueue_init(StuQueue); - for (int i = 0; i < StuQueue.queue.cap; i++) + for (int i = 0; i < cQueue_cap(StuQueue); i++) { s.age = 18 + i; cQueue_push(StuQueue, s); } - while (StuQueue.queue.size > 0) + while (cQueue_size(StuQueue) > 0) { cQueue_pop(StuQueue, s); printf("cQueue_pop name: %s age %d\r\n", s.name, s.age); } } +static void test_base(void) +{ + // test_int(); + test_struct(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: cQueue [opt] [arg]\n" +"Usage: cQueue [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("cQueue version %d.%d.%d\r\n", CQUEUE_V_MAJOR, CQUEUE_V_MINOR, CQUEUE_V_PATCH); return 0; @@ -107,12 +203,38 @@ static int test(int argc, char *argv[]) } } - // test_int(); - test_struct(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_cQueue) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_cQueue) int main(int argc, char *argv[]) { diff --git a/test/test_calculate.c b/test/test_calculate.c index 1d719f0..8534d38 100644 --- a/test/test_calculate.c +++ b/test/test_calculate.c @@ -7,18 +7,227 @@ #include #if defined(TEST_TARGET_calculate) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" #include "calculate.h" +#include "kern.h" #endif -static int command_calculate(int argc, char *argv[]) +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +typedef union { + uint64_t int_; + double double_; +} doubel_ut; + +extern uint64_t unitt_clock(void); + +// #define EXIT_TEST + +static char expbuffer[1000]; + +static double random_double() +{ + doubel_ut random; + + for (int i = 0; i < 8; i++) + { + random.int_ <<= 8; + random.int_ |= rand() & 0xFF; + } + +#if 0 + if (isnan(random.double_)) random.double_ = 1.0; + else if (isinf(random.double_)) + { + if (random.double_ > 0) random.double_ = 2.0; + else random.double_ = -2.0; + } + else if (random.double_ == 0.0) random.double_ = 1.0; +#else + uint64_t temp = random.int_; + random.double_ = (double)temp / 100; +#endif + + return random.double_; +} + +static int double_eq(double a, double b) +{ + double epsilon = 1e-6; + doubel_ut a1 = {.double_ = a}, b1 = {.double_ = b}; + + if (a1.int_ == b1.int_) return 1; + if (isnan(a) && isnan(b)) return 1; + if (isinf(a) && isinf(b)) return 1; + if (fabs(a - b) < epsilon) return 1; + + return 0; +} + +static int test_add(void) +{ + for (int i = 0; i < 100; i++) + { + double a, b; + doubel_ut r1 = {.int_ = 0}, r2 = {.int_ = 0}; + + a = random_double(); + b = random_double(); + + snprintf(expbuffer, sizeof(expbuffer), "%lf + %lf", a, b); + + r1.double_ = calculate(expbuffer); + r2.double_ = a + b; + + if (!double_eq(r1.double_, r2.double_)) + { + printf("add fail: \r\nexpert: \r\n%llu | %lf \r\nactual: \r\n%llu | %lf \r\n", r2.int_, r2.double_, r1.int_, r1.double_); + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static int test_sub(void) +{ + for (int i = 0; i < 100; i++) + { + double a, b; + doubel_ut r1 = {.int_ = 0}, r2 = {.int_ = 0}; + + a = random_double(); + b = random_double(); + + snprintf(expbuffer, sizeof(expbuffer), "%lf - %lf", a, b); + + r1.double_ = calculate(expbuffer); + r2.double_ = a - b; + + if (!double_eq(r1.double_, r2.double_)) + { + printf("sub fail: \r\nexpert: \r\n%llu | %lf \r\nactual: \r\n%llu | %lf \r\n", r2.int_, r2.double_, r1.int_, r1.double_); + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static int test_mul(void) +{ + for (int i = 0; i < 100; i++) + { + double a, b; + doubel_ut r1 = {.int_ = 0}, r2 = {.int_ = 0}; + + a = random_double(); + b = random_double(); + + snprintf(expbuffer, sizeof(expbuffer), "%lf * %lf", a, b); + + r1.double_ = calculate(expbuffer); + r2.double_ = a * b; + + if (!double_eq(r1.double_, r2.double_)) + { + printf("mul fail: \r\nexpert: \r\n%llu | %lf \r\nactual: \r\n%llu | %lf \r\n", r2.int_, r2.double_, r1.int_, r1.double_); + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static int test_div(void) +{ + for (int i = 0; i < 100; i++) + { + double a, b; + doubel_ut r1 = {.int_ = 0}, r2 = {.int_ = 0}; + + a = random_double(); + b = random_double(); + + snprintf(expbuffer, sizeof(expbuffer), "%lf / %lf", a, b); + + r1.double_ = calculate(expbuffer); + r2.double_ = a / b; + + if (!double_eq(r1.double_, r2.double_)) + { + printf("div fail: \r\nexpert: \r\n%llu | %lf \r\nactual: \r\n%llu | %lf \r\n", r2.int_, r2.double_, r1.int_, r1.double_); + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_add), + UNITT_TCASE(test_sub), + UNITT_TCASE(test_mul), + UNITT_TCASE(test_div), + }; + + static UNITT suites[] = { + { "calculate suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void ls(void) +{ + const char *name = NULL; + int fargc = 0; + double value = 0; + + while (name = calculate_ls_const(&value)) + { + printf("- %s<%lf>\r\n", name, value); + } + + while (name = calculate_ls_func(&fargc)) + { + printf("- %s(%d)\r\n", name, fargc); + } +} + +static int command_calculate(const char *expression) { double r = NAN; - if (argc < 2) return 0; - r = calculate(argv[1]); + if (!expression) return 0; + r = calculate(expression); if (fabs(floor(r) - r) <= DBL_EPSILON && fabs(r) < 1.0e60) printf("%.0lf\r\n", r); else if (fabs(r) < 1.0e-6 || fabs(r) > 1.0e9) printf("%e\r\n", r); else @@ -38,38 +247,82 @@ static double factorial(double n) return n * factorial(n - 1); } -static double K() +static void test_base(void) { - return 1024.0; + const char *expression[] = { + " ( 99 * 3 ) ", + " min (12, 3)", + "sin ( 11 / 2 * Pi ) + 100 ", + }; + + for (int i = 0; i < sizeof(expression) / sizeof(expression[0]); i++) + { + printf("cal: %s = %lf\r\n", expression[i], calculate(expression[i])); + } } +static void test_export(void) +{ + int ret = 0; + ret = calculate_function("fac", factorial, 1); + printf("export func '%s' ret<%d>\r\n", "fac", ret); + ret = calculate_constant("K", 1024); + printf("export const '%s' ret<%d>\r\n", "K", ret); + ls(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: calculate [opt] [arg]\n" +"Usage: calculate [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Calculate string math expression\n" +" Test base export function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +" -c Calculation expression\n" +" -l Lists the currently supported calculation functions and constants\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + char *expression = NULL; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::lc:"); if (opt == -1) break; switch (opt) { + case 'c' : + expression = command_optarg; + break; + case 'l' : + ls(); + return 0; + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("calculate version %d.%d.%d\r\n", CALCULATE_V_MAJOR, CALCULATE_V_MINOR, CALCULATE_V_PATCH); return 0; @@ -83,18 +336,53 @@ static int test(int argc, char *argv[]) } } - printf("mul %lf\r\n", calculate(" ( 99 * 3 ) ")); - printf("min %lf\r\n", calculate(" min (12, 3)")); - printf("sin %lf\r\n", calculate("sin ( 11 / 2 * pi ) + 100 ")); - - calculate_export("fac", factorial, 1); - calculate_export("K", K, 0); - - command_export("cal", command_calculate); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_calculate) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "cal")) + { + if (expression) + { + command_calculate(expression); + } + else + { + printf("Use the -c option to specify the evaluated expression!\r\n"); + } + } + else if (!strcmp(execute, "export")) + { + test_export(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_calculate) int main(int argc, char *argv[]) { @@ -104,6 +392,11 @@ int main(int argc, char *argv[]) void test_calculate(void) { command_export("calculate", test); + + // command("calculate"); + // command("calculate -e ut"); + // command("calculate -e cal -c 1+2"); + // command("calculate -e export"); } init_export_app(test_calculate); #endif diff --git a/test/test_check.c b/test/test_check.c index a30faee..24f2d8f 100644 --- a/test/test_check.c +++ b/test/test_check.c @@ -5,13 +5,23 @@ #include #if defined(TEST_TARGET_check) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" #include "check.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + typedef uint8_t (*checkType)(uint8_t* data, uint32_t len); static checkType checkFunTable[] = { @@ -21,46 +31,8 @@ static checkType checkFunTable[] = { check_xor, }; -static void usage(void) +static void test_base(void) { - printf( -"Usage: check [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("check version %d.%d.%d\r\n", CHECK_V_MAJOR, CHECK_V_MINOR, CHECK_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - char *testSample[] = { "Hello", "check algorithms", @@ -76,10 +48,108 @@ static int test(int argc, char *argv[]) printf("Func %d, check %02X\r\n", j, check); } } +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: check [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Calculate the check value\n" +" -h Print help\n" +" -v Print version\n" +" -c Check algorithm, default `check_sum`\n" +" 0: check_sum\n" +" 1: check_parity\n" +" 2: check_lrc\n" +" 3: check_xor\n" +" -s Check string\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + checkType check = checkFunTable[0]; + char *s = NULL; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvc:s:"); + if (opt == -1) break; + + switch (opt) + { + case 'c' : + { + int index = 0; + index = atoi(command_optarg); + if (index >= (sizeof(checkFunTable) / sizeof(checkFunTable[0]))) + { + printf("No such check functiion!\r\n"); + return -1; + } + check = checkFunTable[index]; + } + break; + case 's' : + s = command_optarg; + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("check version %d.%d.%d\r\n", CHECK_V_MAJOR, CHECK_V_MINOR, CHECK_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "cal")) + { + if (!s) + { + printf("Use the -s option to specify the check string!\r\n"); + return -1; + } + printf("check value: 0x%X\r\n", check(s, strlen(s))); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_check) int main(int argc, char *argv[]) { @@ -89,6 +159,12 @@ int main(int argc, char *argv[]) void test_check(void) { command_export("check", test); + + // command("check"); + // command("check -e cal -c 0 -s 123456789"); + // command("check -e cal -c 1 -s 123456789"); + // command("check -e cal -c 2 -s 123456789"); + // command("check -e cal -c 3 -s 123456789"); } init_export_app(test_check); #endif diff --git a/test/test_command.c b/test/test_command.c index 8640e11..974b665 100644 --- a/test/test_command.c +++ b/test/test_command.c @@ -3,12 +3,72 @@ #include #if defined(TEST_TARGET_command) #include +#include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #endif -int func1(int argc, char *argv[]) +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int cb2(int argc, char *argv[]) +{ + return 2; +} + +static int cb3(int argc, char *argv[]) +{ + return 3; +} + +static int cb4(int argc, char *argv[]) +{ + return 4; +} + +static int test_0(void) +{ + static char cbuffer[] = "cb0"; + for (int i = 2; i <= 4; i++) + { + cbuffer[2] = '0' + i; + if (i != command(cbuffer)) + { + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + }; + + static UNITT suites[] = { + { "command suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static int func1(int argc, char *argv[]) { printf("I am func1!\r\n"); printf("argc = %d\r\n", argc); @@ -19,58 +79,20 @@ int func1(int argc, char *argv[]) return 1; } -int func2(int argc, char *argv[]) +static int func2(int argc, char *argv[]) { printf("I am func2!\r\n"); return 1; } -int func3(int argc, char *argv[]) +static int func3(int argc, char *argv[]) { printf("I am func3!\r\n"); return 1; } -static void usage(void) +static void test_base(void) { - printf( -"Usage: command [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("command version %d.%d.%d\r\n", COMMAND_V_MAJOR, COMMAND_V_MINOR, COMMAND_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - command_export("func1", func1); command_export("func2", func2); command_export("func3", func3); @@ -88,10 +110,96 @@ static int test(int argc, char *argv[]) // command("func1 1\\ 2 3"); // printf("--------------------------\r\n"); // command("func1 \"1 2 3\""); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: command [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("command version %d.%d.%d\r\n", COMMAND_V_MAJOR, COMMAND_V_MINOR, COMMAND_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + command_export("cb2", cb2); + command_export("cb3", cb3); + command_export("cb4", cb4); + #if defined(TEST_TARGET_command) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_command) int main(int argc, char *argv[]) { diff --git a/test/test_cpul.c b/test/test_cpul.c index 099c70f..d547c75 100644 --- a/test/test_cpul.c +++ b/test/test_cpul.c @@ -10,15 +10,94 @@ #include #include #include +// #ifdef _WIN32 +// #include +// #include +// #endif #if defined(TEST_TARGET_cpul) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "cpul.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +#ifdef _WIN32 +uint16_t cpul_raw(uint8_t coreid) +{ + FILETIME idleTime, kernelTime, userTime; + if (GetSystemTimes(&idleTime, &kernelTime, &userTime) == 0) { + printf("Failed to get system times\n"); + return 1; + } + + ULARGE_INTEGER idle, kernel, user; + idle.LowPart = idleTime.dwLowDateTime; + idle.HighPart = idleTime.dwHighDateTime; + + kernel.LowPart = kernelTime.dwLowDateTime; + kernel.HighPart = kernelTime.dwHighDateTime; + + user.LowPart = userTime.dwLowDateTime; + user.HighPart = userTime.dwHighDateTime; + + ULONGLONG totalSystemTime = kernel.QuadPart + user.QuadPart; + ULONGLONG totalIdleTime = idle.QuadPart; + + // Calculate CPU usage percentage + double cpuUsage = (double)(totalSystemTime - totalIdleTime) * 10000.0 / totalSystemTime; + + return (uint16_t)cpuUsage; +} + +#else #define CORE_NUM_MAX 32 #define LINE_BUFFER_SIZE 1024 @@ -169,47 +248,13 @@ void *loadgen_entry(void *ptr) usleep(1000 * PERIOD); } } +#endif -static void usage(void) +static void test_base(void) { - printf( -"Usage: cpul [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("cpul version %d.%d.%d\r\n", CPUL_V_MAJOR, CPUL_V_MINOR, CPUL_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - +#ifdef _WIN32 + printf("cpul %u\r\n", cpul_raw(0)); +#else int num_cpus = sysconf(_SC_NPROCESSORS_CONF); pthread_t thread_update, thread_gen[num_cpus]; int coreid[num_cpus]; @@ -239,11 +284,95 @@ static int test(int argc, char *argv[]) for (int i = 0; i < num_cpus; i++) { pthread_join(thread_gen[i], NULL); + } +#endif +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: cpul [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("cpul version %d.%d.%d\r\n", CPUL_V_MAJOR, CPUL_V_MINOR, CPUL_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } } - + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_cpul) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } + return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_cpul) int main(int argc, char *argv[]) { diff --git a/test/test_crc.c b/test/test_crc.c index 5fe04e6..f8128f8 100644 --- a/test/test_crc.c +++ b/test/test_crc.c @@ -5,13 +5,24 @@ #include #if defined(TEST_TARGET_crc) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "crc.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + uint32_t std_crc(uint8_t* data, uint32_t len, uint32_t index) { uint32_t crc = 0; @@ -46,46 +57,8 @@ uint32_t std_crc(uint8_t* data, uint32_t len, uint32_t index) return crc; } -static void usage(void) +static void test_base(void) { - printf( -"Usage: crc [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("crc version %d.%d.%d\r\n", CRC_V_MAJOR, CRC_V_MINOR, CRC_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - char *testSample[] = { "Hello", "crc algorithms", @@ -103,10 +76,119 @@ static int test(int argc, char *argv[]) printf("crc %02d, crc0 %08X, crc1 %08X, same %d\r\n", j, crc0, crc1, (crc0 == crc1) ? 1 : 0); } } +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: crc [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Calculate the check value\n" +" -h Print help\n" +" -v Print version\n" +" -c [] CRC algorithm, default `CRC-4/ITU`\n" +" 0 : CRC-4/ITU \n" +" 1 : CRC-5/EPC \n" +" 2 : CRC-5/ITU \n" +" 3 : CRC-5/USB \n" +" 4 : CRC-6/ITU \n" +" 5 : CRC-7/MMC \n" +" 6 : CRC-8 \n" +" 7 : CRC-8/ITU \n" +" 8 : CRC-8/ROHC \n" +" 9 : CRC-8/MAXIM \n" +" 10 : CRC-16/IBM \n" +" 11 : CRC-16/MAXIM \n" +" 12 : CRC-16/USB \n" +" 13 : CRC-16/MODBUS \n" +" 14 : CRC-16/CCITT \n" +" 15 : CRC-16/CCITT-FALSE \n" +" 16 : CRC-16/X25 \n" +" 17 : CRC-16/XMODEM \n" +" 18 : CRC-16/DNP \n" +" 19 : CRC-32 \n" +" 20 : CRC-32/MPEG-2 \n" +" -s Check string\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int index = 0; + char *s = NULL; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::c:s:"); + if (opt == -1) break; + + switch (opt) + { + case 'c' : + if (command_optarg) + { + index = atoi(command_optarg); + if (index >= (sizeof(crcParaModelTable) / sizeof(crcParaModelTable[0]))) + { + printf("No such crc functiion!\r\n"); + return -1; + } + } + break; + case 's' : + s = command_optarg; + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("crc version %d.%d.%d\r\n", CRC_V_MAJOR, CRC_V_MINOR, CRC_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "cal")) + { + printf("check value: 0x%X, 0x%X\r\n", std_crc(s, strlen(s), index), crc(s, strlen(s), &crcParaModelTable[index])); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_crc) int main(int argc, char *argv[]) { diff --git a/test/test_csv.c b/test/test_csv.c index 89039a0..37b8ab4 100644 --- a/test/test_csv.c +++ b/test/test_csv.c @@ -2,16 +2,417 @@ #include #if defined(TEST_TARGET_csv) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "csv.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define READ_FILE "source/application/test/file/read.csv" #define WRITE_FILE "source/application/test/file/write.csv" +static void test_create(void) +{ + csv_t csv = NULL; + csv = csv_create(3, 5, NULL); + if (csv) + { + printf("csv_create success!!! %p\r\n", csv); + } + csv_delete(csv); +} + +static void test_cell(void) +{ + csv_t csv = NULL; + + csv = csv_create(3, 5, NULL); + + printf("csv_row %d\r\n", csv_row(csv)); + printf("csv_col %d\r\n", csv_col(csv)); + printf("csv_cell %d\r\n", csv_cell(csv)); + + csv_delete(csv); +} + +static void test_set(void) +{ + csv_t csv = NULL; + unsigned int row = 0, col = 0; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + csv = csv_create(3, 5, array); + + row = csv_row(csv); + col = csv_col(csv); + + printf("--- Before set:\r\n"); + for (int i = 1; i <= row; i++) + { + for (int j = 1; j <= col; j++) + { + printf("%s,", csv_get_text(csv, i, j)); + } + printf("\r\n"); + } + + csv_set_text(csv, 2, 4, "21"); + csv_clean_text(csv, 3, 5); + + printf("--- After set:\r\n"); + for (int i = 1; i <= row; i++) + { + for (int j = 1; j <= col; j++) + { + printf("%s,", csv_get_text(csv, i, j)); + } + printf("\r\n"); + } + + csv_delete(csv); +} + +static void test_for_each(void) +{ + csv_t csv = NULL; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + csv = csv_create(3, 5, array); + + csv_for_each(csv, row, col, text) + { + printf("[%u,%u]: %s\r\n", row, col, text); + } + + csv_delete(csv); +} + +static void test_op_ranks(void) +{ + csv_t csv = NULL; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + for (int i = 0; i < 9; i++) + { + printf("-------------------------------------\r\n"); + csv = csv_create(3, 5, array); + + switch (i) + { + case 0: + { + printf("Before OP:\r\n"); + } break; + case 1: + { + printf("csv_insert_row:\r\n"); + csv_insert_row(csv, 2, NULL, 0); + } break; + case 2: + { + printf("csv_insert_col:\r\n"); + csv_insert_col(csv, 2, NULL, 0); + } break; + case 3: + { + printf("csv_delete_row:\r\n"); + csv_delete_row(csv, 2); + } break; + case 4: + { + printf("csv_delete_col:\r\n"); + csv_delete_col(csv, 2); + } break; + case 5: + { + printf("csv_move_row_to:\r\n"); + csv_move_row_to(csv, 2, 3); + } break; + case 6: + { + printf("csv_move_col_to:\r\n"); + csv_move_col_to(csv, 2, 3); + } break; + case 7: + { + printf("csv_copy_row_to:\r\n"); + csv_copy_row_to(csv, 2, 3); + } break; + case 8: + { + printf("csv_copy_col_to:\r\n"); + csv_copy_col_to(csv, 2, 3); + } break; + default: + break; + } + + text = csv_dumps(csv, NULL); + if (text) + { + printf("%s\r\n", text); + free(text); + } + else + { + printf("[ERROR] dumps fail!!!\r\n"); + } + csv_delete(csv); + } +} + +static void test_op_cells(void) +{ + csv_t csv = NULL; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"11", "12", "13", "14", "15"}, + {"21", "22", "23", "24", "25"}, + {"31", "32", "33", "34", "35"}, + }; + + for (int i = 0; i < 7; i++) + { + printf("-------------------------------------\r\n"); + csv = csv_create(3, 5, array); + + switch (i) + { + case 0: + { + printf("Before OP:\r\n"); + } break; + case 1: + { + printf("csv_insert_cell[move down]:\r\n"); + csv_insert_cell(csv, 2, 1, 1); + } break; + case 2: + { + printf("csv_insert_cell[move right]:\r\n"); + csv_insert_cell(csv, 2, 1, 0); + } break; + case 3: + { + printf("csv_delete_cell[move up]:\r\n"); + csv_delete_cell(csv, 2, 1, 1); + } break; + case 4: + { + printf("csv_delete_cell[move left]:\r\n"); + csv_delete_cell(csv, 2, 1, 0); + } break; + case 5: + { + printf("csv_copy_cell_to:\r\n"); + csv_copy_cell_to(csv, 2, 1, 3, 2); + } break; + case 6: + { + printf("csv_cut_cell_to:\r\n"); + csv_cut_cell_to(csv, 2, 1, 3, 2); + } break; + default: + break; + } + + text = csv_dumps(csv, NULL); + if (text) + { + printf("%s\r\n", text); + free(text); + } + else + { + printf("[ERROR] dumps fail!!!\r\n"); + } + csv_delete(csv); + } +} + +static void test_find(void) +{ + csv_t csv; + unsigned int row = 0, col = 0; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + csv = csv_create(3, 5, array); + if (!csv) + { + printf("create csv fail!\r\n"); + return; + } + + // while (csv_find(csv, "2", CSV_F_FLAG_MatchForward | CSV_F_FLAG_MatchByCol, &row, &col) == 1) + while (csv_find(csv, "2", CSV_F_FLAG_Default, &row, &col) == 1) + { + printf("[%u, %u]: %s\r\n", row, col, csv_get_text(csv, row, col)); + } + + csv_delete(csv); +} + +static void test_duplicate(void) +{ + csv_t csv; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + + csv = csv_create(3, 5, array); + if (!csv) + { + printf("create csv fail!\r\n"); + return; + } + + csv_t dup = csv_duplicate(csv); + csv_for_each(dup, row, col, text) + { + printf("[%u,%u]: %s\r\n", row, col, text); + } + csv_delete(dup); + + csv_delete(csv); +} + +static void test_minify(void) +{ + csv_t csv; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + + csv = csv_create(3, 5, array); + if (!csv) + { + printf("create csv fail!\r\n"); + return; + } + + csv_clean_text(csv, 2, 2); + csv_clean_text(csv, 2, 3); + csv_clean_text(csv, 2, 4); + csv_clean_text(csv, 2, 5); + + csv_minify(csv); + text = csv_dumps(csv, NULL); + if (text) + { + printf("%s\r\n", text); + free(text); + } + else + { + printf("[ERROR] dumps fail!!!\r\n"); + } + + csv_delete(csv); +} + +static void test_to_array(void) +{ + csv_t csv; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + csv = csv_create(3, 5, array); + if (!csv) + { + printf("create csv fail!\r\n"); + return; + } + + char *out[2][3]; + csv_to_array(csv, 2, 2, out, 2, 3); + printf("out: %s\r\n", out[1][2]); + + csv_delete(csv); +} + static void test_dump(void) { csv_t csv; @@ -29,62 +430,6 @@ static void test_dump(void) return; } - int row = csv_row(csv); - int col = csv_col(csv); - printf("row %d, col %d\r\n", row, col); - - csv_to_array(csv, 2, 4, out, 2, 3); - - printf("out: %s\r\n", out[1][2]); - -#if 0 - csv_set_text(csv, 1, 1, "DID"); - csv_set_text(csv, 1, 2, "Function"); - csv_set_text(csv, 1, 3, "Comment"); - - csv_set_text(csv, 2, 1, "$D131"); - csv_set_text(csv, 2, 2, "Sample ID"); - csv_set_text(csv, 2, 3, "Yes/No"); - - csv_set_text(csv, 3, 3, "中国"); - - csv_copy_cell_to(csv, 3,3, 3, 1); - - csv_cut_cell_to(csv, 3, 1, 7, 10); -#endif - // csv_insert_col(csv, 2); - char *rows[5] = {"20240107005", "Wangwu", "Woman", "21", "160"}; - csv_insert_row(csv, 0, rows, 5); - char *cols[3] = {"Weight", "56", "62"}; - csv_insert_col(csv, 0, cols, 3); - - // csv_move_row_to(csv, 6, 1); - // csv_move_col_to(csv, 6, 3); - // csv_copy_row_to(csv, 4, 2); - // csv_copy_col_to(csv, 4, 2); - // csv_insert_cell(csv, 2, 4, 1); - // csv_delete_cell(csv, 2, 4, 1); - - // csv_insert_col(csv, 0, NULL, 0); - // csv_insert_col(csv, 0, NULL, 0); - - csv_minify(csv); - -#if 0 - const char *text = NULL; - csv_for_each(csv, row, col, text) - { - printf("[%d, %d]: %s\r\n", row, col, text); - } -#endif - - // printf("cell count %d.\r\n", csv_cell(csv)); - - while (csv_find(csv, "6", CSV_F_FLAG_MatchForward | CSV_F_FLAG_MatchByCol, &row, &col) == 1) - { - printf("[%d, %d]: %s\r\n", row, col, csv_get_text(csv, row, col)); - } - printf("File length %d.\r\n", csv_file_dump(csv, WRITE_FILE)); csv_delete(csv); @@ -181,33 +526,65 @@ static void load_demo(void) csv_delete(csv); } +static void test_base(void) +{ + dump_demo(); + load_demo(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: csv [opt] [arg]\n" +"Usage: csv [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test cell information functions\n" +" Test set, get and clean functions\n" +" Test `for each` method\n" +" Test operate rows and cols functions\n" +" Test operate cells functions\n" +" Test find function\n" +" Test duplicate function\n" +" Test minify function\n" +" Test to array function\n" +" Test dump functions\n" +" Test load functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("csv version %d.%d.%d\r\n", CSV_V_MAJOR, CSV_V_MINOR, CSV_V_PATCH); return 0; @@ -221,15 +598,85 @@ static int test(int argc, char *argv[]) } } - // test_dump(); - // test_load(); - - // dump_demo(); - load_demo(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_csv) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "cell")) + { + test_cell(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "each")) + { + test_for_each(); + } + else if (!strcmp(execute, "opranks")) + { + test_op_ranks(); + } + else if (!strcmp(execute, "opcells")) + { + test_op_cells(); + } + else if (!strcmp(execute, "find")) + { + test_find(); + } + else if (!strcmp(execute, "dup")) + { + test_duplicate(); + } + else if (!strcmp(execute, "minify")) + { + test_minify(); + } + else if (!strcmp(execute, "array")) + { + test_to_array(); + } + else if (!strcmp(execute, "dump")) + { + test_dump(); + } + else if (!strcmp(execute, "load")) + { + test_load(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_csv) int main(int argc, char *argv[]) { diff --git a/test/test_dList.c b/test/test_dList.c index 952c0eb..9265e86 100644 --- a/test/test_dList.c +++ b/test/test_dList.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_dList) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "dList.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_create(void) { dList *list = NULL; @@ -318,46 +364,8 @@ FAIL: dList_delete(list); } -static void usage(void) +static void test_base(void) { - printf( -"Usage: dList [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("dList version %d.%d.%d\r\n", DLIST_V_MAJOR, DLIST_V_MINOR, DLIST_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - // test_create(); // test_set(); // test_insert(); @@ -371,10 +379,158 @@ static int test(int argc, char *argv[]) // test_append(); // test_copy(); // test_reverse(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: dList [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test set function\n" +" Test insert function\n" +" Test erase function\n" +" Test attach function\n" +" Test detach function\n" +" Test push function\n" +" Test pop function\n" +" Test locate to specify index function\n" +" Test size function\n" +" Test append function\n" +" Test copy function\n" +" Test reverse function\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("dList version %d.%d.%d\r\n", DLIST_V_MAJOR, DLIST_V_MINOR, DLIST_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_dList) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "erase")) + { + test_erase(); + } + else if (!strcmp(execute, "attach")) + { + test_attach(); + } + else if (!strcmp(execute, "detach")) + { + test_detach(); + } + else if (!strcmp(execute, "push")) + { + test_push(); + } + else if (!strcmp(execute, "pop")) + { + test_pop(); + } + else if (!strcmp(execute, "to")) + { + test_to(); + } + else if (!strcmp(execute, "size")) + { + test_size(); + } + else if (!strcmp(execute, "append")) + { + test_append(); + } + else if (!strcmp(execute, "copy")) + { + test_copy(); + } + else if (!strcmp(execute, "reverse")) + { + test_reverse(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_dList) int main(int argc, char *argv[]) { diff --git a/test/test_date.c b/test/test_date.c index 662e677..2c6a30c 100644 --- a/test/test_date.c +++ b/test/test_date.c @@ -3,81 +3,100 @@ #include #if defined(TEST_TARGET_date) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "date.h" #endif -#if 0 -static int century_days(int century) +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_base(void) { - int days = 0; - int year = (century - 1) * 100 + 1; + printf("month days %d\r\n", date_month_days(2024, 11)); + printf("date_isleap %d\r\n", date_isleap(1582)); + // printf("date_current_days %d\r\n", date_current_days(DATE(1,1,1))); + // printf("date_current_days %d\r\n", date_current_days(DATE(2000,12,31))); + // printf("date_current_days %d\r\n", date_current_days(DATE(2001,1,1))); + // printf("date_current_days %d\r\n", date_current_days(DATE(2024,11,9))); + printf("date_get_dow %d\r\n", date_get_dow(DATE(2024,11,11))); - for (int i = 0; i < 100; i++) - { - days += date_year_days(year + i); - } + printf("date_diff_days %d\r\n", date_diff_days(DATE(2018,3,14), DATE(2024,11,10))); - return days; + // DATE date = date_from_days(date_current_days(DATE(2024,11,30))); + DATE date = date_offset(DATE(2024,11,30), -1); + + printf("%d.%d.%d\r\n", date.year,date.month,date.day); + + date_calendar(1998,7); + date_calendar(2024,11); + date_calendar(2224,11); } -static int thousand2_days(int base) -{ - int days = 0; - - for (int i = 0; i < 2000; i++) - { - days += date_year_days(base + i); - } - - return days; -} - -static int thousand3_days(int base) -{ - int days = 0; - int century = (base / 100) + 1; - - for (int i = 0; i < 20; i++) - { - // days += century_days(century + i); - days += date_century_days(century + i); - } - - return days; -} -#endif +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ static void usage(void) { printf( -"Usage: date [opt] [arg]\n" +"Usage: date [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the base test\n" +" Test base function\n" +" Show the calendar for a specific month and year\n" +" Check if a year is a leap year\n" +" Validate a date\n" +" Get the day of week of a given date based on the total days\n" +" Calculate the difference in days between two dates\n" " -h Print help\n" " -v Print version\n" +" -y Year\n" +" -m Month\n" +" -d Day\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + DATE date[2] = {0}; + int iy = 0, im = 0, id = 0; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::y:m:d:"); if (opt == -1) break; switch (opt) { + case 'y' : + if (iy < 2) date[iy++].year = atoi(command_optarg); + break; + case 'm' : + if (im < 2) date[im++].month = atoi(command_optarg); + break; + case 'd' : + if (id < 2) date[id++].day = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("date version %d.%d.%d\r\n", DATE_V_MAJOR, DATE_V_MINOR, DATE_V_PATCH); return 0; @@ -91,28 +110,59 @@ static int test(int argc, char *argv[]) } } - printf("month days %d\r\n", date_month_days(2024, 11)); - printf("date_isleap %d\r\n", date_isleap(1582)); - // printf("date_current_days %d\r\n", date_current_days(DATE(1,1,1))); - // printf("date_current_days %d\r\n", date_current_days(DATE(2000,12,31))); - // printf("date_current_days %d\r\n", date_current_days(DATE(2001,1,1))); - // printf("date_current_days %d\r\n", date_current_days(DATE(2024,11,9))); - printf("date_get_week %d\r\n", date_get_week(DATE(2024,11,11))); - - printf("date_diff_days %d\r\n", date_diff_days(DATE(2018,3,14), DATE(2024,11,10))); - - // DATE date = date_from_days(date_current_days(DATE(2024,11,30))); - DATE date = date_offset(DATE(2024,11,30), -1); - - printf("%d.%d.%d\r\n", date.year,date.month,date.day); - - date_show(1998,7); - date_show(2024,11); - date_show(2224,11); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "calendar")) + { + date_calendar(date[0].year, date[0].month); + } + else if (!strcmp(execute, "leap")) + { + printf("%d %s leap year!\r\n", date[0].year, date_isleap(date[0].year) ? "is" : "isn't"); + } + else if (!strcmp(execute, "valid")) + { + printf("%04u.%02u.%02u %s valid!\r\n", date[0].year, date[0].month, date[0].day, date_isleap(date[0].year) ? "is" : "isn't"); + } + else if (!strcmp(execute, "dow")) + { + const char *dow[] = { + "Invalid", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + }; + printf("%04u.%02u.%02u is %s!\r\n", date[0].year, date[0].month, date[0].day, + dow[date_get_dow(date[0])]); + } + else if (!strcmp(execute, "diff")) + { + printf("%04u.%02u.%02u is %d days away from %04u.%02u.%02u!\r\n", + date[0].year, date[0].month, date[0].day, + date_diff_days(date[0], date[1]), + date[1].year, date[1].month, date[1].day); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_date) int main(int argc, char *argv[]) { diff --git a/test/test_deque.c b/test/test_deque.c index 7baa218..4add8d2 100644 --- a/test/test_deque.c +++ b/test/test_deque.c @@ -3,13 +3,154 @@ #include #if defined(TEST_TARGET_deque) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "deque.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + static deque_t deque = NULL; + static int initFlag = 0; + static unsigned int start = 0x7FFFFFFF, end = 0x7FFFFFFF; + + unsigned int data = 0; + unsigned int size = end - start; + int op = rand() % 4; + + if (!initFlag) + { + deque = deque(unsigned int, 10); + initFlag = 1; + } + + if (size != deque_size(deque)) return UNITT_E_FAIL; + if (size > 0) + { + data = rand() % deque_size(deque); + if (deque_at(deque, unsigned int,data) != (start + data)) return UNITT_E_FAIL; + } + + printf("size %u\r\n", size); + + switch (op) + { + case 0: + if (deque_push_front(deque, ((unsigned int[1]){start - 1}))) start--; + break; + case 1: + if (deque_push_back(deque, &end)) end++; + break; + case 2: + if (deque_pop_front(deque, NULL)) start++; + break; + case 3: + if (deque_pop_back(deque, NULL)) end--; + break; + default: + break; + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "deque suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + deque_t deque = deque(int, 10); + + if (deque) + { + printf("deque create success!!!\r\n"); + } + else + { + printf("[ERROR] deque create fail!!!\r\n"); + } + + _deque(deque); +} + +static void test_push(void) +{ + deque_t deque = deque(int, 10); + int data = 0; + + if (!deque) + { + printf("[ERROR] deque create fail!!!\r\n"); + return; + } + + data = 1; deque_push_back(deque, &data); + data = 2; deque_push_back(deque, &data); + data = -100; deque_push_front(deque, &data); + + for (int i = 0; i < deque_size(deque); i++) + { + printf("deque[%d] = %d\r\n", i, deque_at(deque, int, i)); + } + + _deque(deque); +} + +static void test_pop(void) +{ + deque_t deque = deque(int, 10); + + if (!deque) + { + printf("[ERROR] deque create fail!!!\r\n"); + return; + } + + for (int i = 0; i < deque_capacity(deque); i++) + { + deque_push_back(deque, &i); + } + + deque_pop_back(deque, NULL); + deque_pop_back(deque, NULL); + deque_pop_front(deque, NULL); + + for (int i = 0; i < deque_size(deque); i++) + { + printf("deque[%d] = %d\r\n", i, deque_at(deque, int, i)); + } + + _deque(deque); +} + static void test_base(void) { deque_t deque = deque(int, 10); @@ -29,33 +170,50 @@ static void test_base(void) _deque(deque); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: deque [opt] [arg]\n" +"Usage: deque [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test push functions\n" +" Test pop functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("deque version %d.%d.%d\r\n", DEQUE_V_MAJOR, DEQUE_V_MINOR, DEQUE_V_PATCH); return 0; @@ -69,11 +227,50 @@ static int test(int argc, char *argv[]) } } - test_base(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_deque) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "push")) + { + test_push(); + } + else if (!strcmp(execute, "pop")) + { + test_pop(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_deque) int main(int argc, char *argv[]) { diff --git a/test/test_dict.c b/test/test_dict.c index 446b523..d08ee8d 100644 --- a/test/test_dict.c +++ b/test/test_dict.c @@ -3,14 +3,222 @@ #include #if defined(TEST_TARGET_dict) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "dict.h" #endif -#if 0 +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + dict_t dict = dict(int); + + if (dict) + { + printf("dict create success!!!\r\n"); + } + else + { + printf("[ERROR] dict create fail!!!\r\n"); + } + + _dict(dict); +} + +static void test_insert(void) +{ + dict_t dict = dict(int); + int value = 0; + + if (!dict) + { + printf("[ERROR] dict create fail!!!\r\n"); + return; + } + + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + + printf("dict[ZhangSan] = %d\r\n", dict_at(dict, int, "ZhangSan")); + printf("dict[LiSi] = %d\r\n", dict_at(dict, int, "LiSi")); + printf("dict[WangWu] = %d\r\n", dict_at(dict, int, "WangWu")); + + _dict(dict); +} + +static void test_erase(void) +{ + dict_t dict = dict(int); + int value = 0; + + if (!dict) + { + printf("[ERROR] dict create fail!!!\r\n"); + return; + } + + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + + dict_erase(dict, "LiSi"); + + printf("dict[ZhangSan] = %d\r\n", dict_at(dict, int, "ZhangSan")); + printf("dict[LiSi] = %d\r\n", dict_at(dict, int, "LiSi")); + printf("dict[WangWu] = %d\r\n", dict_at(dict, int, "WangWu")); + + _dict(dict); +} + +static void test_clear(void) +{ + dict_t dict = dict(int); + int value = 0; + + if (!dict) + { + printf("[ERROR] dict create fail!!!\r\n"); + return; + } + + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + + printf("size %d\r\n", dict_size(dict)); + dict_clear(dict); + printf("size %d\r\n", dict_size(dict)); + + printf("dict[ZhangSan] = %d\r\n", dict_at(dict, int, "ZhangSan")); + printf("dict[LiSi] = %d\r\n", dict_at(dict, int, "LiSi")); + printf("dict[WangWu] = %d\r\n", dict_at(dict, int, "WangWu")); + + _dict(dict); +} + +static void test_find(void) +{ + dict_t dict = dict(int); + int value = 0; + + if (!dict) + { + printf("[ERROR] dict create fail!!!\r\n"); + return; + } + + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + + printf("dict_find [ZhangSan], result = %d\r\n", dict_find(dict, "ZhangSan")); + printf("dict_find [Hello], result = %d\r\n", dict_find(dict, "Hello")); + + _dict(dict); +} + +static void test_ktype(void) +{ + dict_t dict = dict(int); + dict_set_klength(dict, sizeof(int), 0); + + if (!dict) + { + printf("[ERROR] dict create fail!!!\r\n"); + return; + } + + dict_insert(dict, literal(int, 1024), literal(int, 1)); + dict_insert(dict, literal(int, 512), literal(int, 2)); + dict_insert(dict, literal(int, 128), literal(int, 3)); + + printf("dict[1024] = %d\r\n", dict_at(dict, int, literal(int, 1024))); + printf("dict[512] = %d\r\n", dict_at(dict, int, literal(int, 512))); + printf("dict[128] = %d\r\n", dict_at(dict, int, literal(int, 128))); + + _dict(dict); +} + +static void test_it(void) +{ + dict_t dict = dict(int); + int value = 0; + + if (!dict) + { + printf("[ERROR] dict create fail!!!\r\n"); + return; + } + + value = 1; dict_insert(dict, "ZhangSan", &value); + value = 2; dict_insert(dict, "LiSi", &value); + value = 3; dict_insert(dict, "WangWu", &value); + + char *key; + void *vaddr = NULL; + void *error; + if (!dict) return; + dict_it_init(dict); + error = dict_error(dict); + while (1) + { + vaddr = dict_it_get(dict, &key); + if (vaddr == error) break; + printf("key: %s,\tvalue: %d\r\n", key, *(int *)vaddr); + } + + _dict(dict); +} + +#if 1 static void dict_print(dict_t dict) { char *key; @@ -124,33 +332,55 @@ static void test_base(void) _dict(dict); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: dict [opt] [arg]\n" +"Usage: dict [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Performance test\n" +" Test create and delete functions\n" +" Test insert function\n" +" Test erase function\n" +" Test clear function\n" +" Test find function\n" +" Test different types of key\n" +" Test traversed through iterator\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("dict version %d.%d.%d\r\n", DICT_V_MAJOR, DICT_V_MINOR, DICT_V_PATCH); return 0; @@ -164,13 +394,69 @@ static int test(int argc, char *argv[]) } } - test_base(); - // test_it(); - // test_performance(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_dict) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "perf")) + { + test_performance(); + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "erase")) + { + test_erase(); + } + else if (!strcmp(execute, "clear")) + { + test_clear(); + } + else if (!strcmp(execute, "find")) + { + test_find(); + } + else if (!strcmp(execute, "ktype")) + { + test_ktype(); + } + else if (!strcmp(execute, "it")) + { + test_it(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_dict) int main(int argc, char *argv[]) { diff --git a/test/test_encrypt.c b/test/test_encrypt.c index 9b5d1af..df8621b 100644 --- a/test/test_encrypt.c +++ b/test/test_encrypt.c @@ -4,13 +4,58 @@ #include #if defined(TEST_TARGET_encrypt) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "encrypt.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ static void test_des(void) @@ -99,33 +144,55 @@ static void test_des3(void) printf("\r\n"); } +static void test_base(void) +{ + // test_des(); + test_des3(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: encrypt [opt] [arg]\n" +"Usage: encrypt [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test des function\n" +" Test des3 function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("encrypt version %d.%d.%d\r\n", ENCRYPT_V_MAJOR, ENCRYPT_V_MINOR, ENCRYPT_V_PATCH); return 0; @@ -139,12 +206,45 @@ static int test(int argc, char *argv[]) } } - // test_des(); - test_des3(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_encrypt) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "des")) + { + test_des(); + } + else if (!strcmp(execute, "des3")) + { + test_des3(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_encrypt) int main(int argc, char *argv[]) { diff --git a/test/test_filter.c b/test/test_filter.c index 128b214..b471559 100644 --- a/test/test_filter.c +++ b/test/test_filter.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_filter) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "filter.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_median(void) { double data[] = {1, 2, 3, 2.5, 3.5, 2, 3, 4, 5}; @@ -55,33 +101,58 @@ static void test_kalman(void) printf("Estimated value: %f\n", estimates[0]); } +static void test_base(void) +{ + test_median(); + // test_average(); + // test_kalman(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: filter [opt] [arg]\n" +"Usage: filter [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test median function\n" +" Test average function\n" +" Test kalman function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("filter version %d.%d.%d\r\n", FILTER_V_MAJOR, FILTER_V_MINOR, FILTER_V_PATCH); return 0; @@ -95,13 +166,50 @@ static int test(int argc, char *argv[]) } } - test_median(); - // test_average(); - // test_kalman(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_filter) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "median")) + { + test_median(); + } + else if (!strcmp(execute, "average")) + { + test_average(); + } + else if (!strcmp(execute, "kalman")) + { + test_kalman(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_filter) int main(int argc, char *argv[]) { diff --git a/test/test_graph.c b/test/test_graph.c index 63d38d8..b023acf 100644 --- a/test/test_graph.c +++ b/test/test_graph.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_graph) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "graph.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define la(type, value) ((type[1]){value}) void graph_traverse_int(int index, void *data, int size) @@ -367,33 +413,72 @@ static void test_others(void) graph_delete(graph); } +static void test_base(void) +{ + // test_create(); + // test_add_vertex(); + // test_add_edge(); + // test_remove(); + // test_search(); + // test_data(); + // test_degree(); + // test_weight(); + // test_shortest_path(); + test_min_cover(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: graph [opt] [arg]\n" +"Usage: graph [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test add_vertex function\n" +" Test add_edge function\n" +" Test remove function\n" +" Test search function\n" +" Test data function\n" +" Test degree function\n" +" Test weight function\n" +" Test shortest_path function\n" +" Test min_cover function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("graph version %d.%d.%d\r\n", GRAPH_V_MAJOR, GRAPH_V_MINOR, GRAPH_V_PATCH); return 0; @@ -407,20 +492,78 @@ static int test(int argc, char *argv[]) } } - // test_create(); - // test_add_vertex(); - // test_add_edge(); - // test_remove(); - // test_search(); - // test_data(); - // test_degree(); - // test_weight(); - // test_shortest_path(); - test_min_cover(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_graph) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "addv")) + { + test_add_vertex(); + } + else if (!strcmp(execute, "adde")) + { + test_add_edge(); + } + else if (!strcmp(execute, "remove")) + { + test_remove(); + } + else if (!strcmp(execute, "search")) + { + test_search(); + } + else if (!strcmp(execute, "data")) + { + test_data(); + } + else if (!strcmp(execute, "degree")) + { + test_degree(); + } + else if (!strcmp(execute, "weight")) + { + test_weight(); + } + else if (!strcmp(execute, "shortp")) + { + test_shortest_path(); + } + else if (!strcmp(execute, "minc")) + { + test_min_cover(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_graph) int main(int argc, char *argv[]) { diff --git a/test/test_hash.c b/test/test_hash.c index 9f1a92a..876a4e2 100644 --- a/test/test_hash.c +++ b/test/test_hash.c @@ -5,40 +5,117 @@ #include #if defined(TEST_TARGET_hash) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "hash.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_base(void) +{ + printf("hash_bkdr 0x%X\r\n", hash_bkdr("Hello", 5)); + printf("hash_ap 0x%X\r\n", hash_ap("Hello", 5)); + printf("hash_djb 0x%X\r\n", hash_djb("Hello", 5)); + printf("hash_js 0x%X\r\n", hash_js("Hello", 5)); + printf("hash_rs 0x%X\r\n", hash_rs("Hello", 5)); + printf("hash_sdbm 0x%X\r\n", hash_sdbm("Hello", 5)); + printf("hash_pjw 0x%X\r\n", hash_pjw("Hello", 5)); + printf("hash_elf 0x%X\r\n", hash_elf("Hello", 5)); + printf("hash_dek 0x%X\r\n", hash_dek("Hello", 5)); + printf("hash_bp 0x%X\r\n", hash_bp("Hello", 5)); + printf("hash_fnv 0x%X\r\n", hash_fnv("Hello", 5)); + printf("hash_jdk6 0x%X\r\n", hash_jdk6("Hello", 5)); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: hash [opt] [arg]\n" +"Usage: hash [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("hash version %d.%d.%d\r\n", HASH_V_MAJOR, HASH_V_MINOR, HASH_V_PATCH); return 0; @@ -52,22 +129,38 @@ static int test(int argc, char *argv[]) } } - printf("hash_bkdr 0x%X\r\n", hash_bkdr("Hello", 5)); - printf("hash_ap 0x%X\r\n", hash_ap("Hello", 5)); - printf("hash_djb 0x%X\r\n", hash_djb("Hello", 5)); - printf("hash_js 0x%X\r\n", hash_js("Hello", 5)); - printf("hash_rs 0x%X\r\n", hash_rs("Hello", 5)); - printf("hash_sdbm 0x%X\r\n", hash_sdbm("Hello", 5)); - printf("hash_pjw 0x%X\r\n", hash_pjw("Hello", 5)); - printf("hash_elf 0x%X\r\n", hash_elf("Hello", 5)); - printf("hash_dek 0x%X\r\n", hash_dek("Hello", 5)); - printf("hash_bp 0x%X\r\n", hash_bp("Hello", 5)); - printf("hash_fnv 0x%X\r\n", hash_fnv("Hello", 5)); - printf("hash_jdk6 0x%X\r\n", hash_jdk6("Hello", 5)); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_hash) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_hash) int main(int argc, char *argv[]) { diff --git a/test/test_heap.c b/test/test_heap.c index 394a133..b06bcd5 100644 --- a/test/test_heap.c +++ b/test/test_heap.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_heap) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "heap.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static int heap_root_min(void *parent, void *child) { if (*(int *)parent < *(int *)child) return 1; @@ -20,6 +66,56 @@ static int heap_root_max(void *parent, void *child) if (*(int *)parent > *(int *)child) return 1; return 0; } + +static void test_create(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + + if (heap) + { + printf("heap create success!!!\r\n"); + } + else + { + printf("[ERROR] heap create fail!!!\r\n"); + } + + heap_delete(heap); +} + +static void test_push(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + int push = 0, top = 0; + + push = 100; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 1; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 2; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = 200; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + push = -10; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top); + + heap_delete(heap); +} + +static void test_pop(void) +{ + heap_t heap = heap_create(sizeof(int), 11, heap_root_max); + int push = 0, pop = 0; + + push = 100; heap_push(heap, &push); + push = 1; heap_push(heap, &push); + push = 2; heap_push(heap, &push); + push = 200; heap_push(heap, &push); + push = -10; heap_push(heap, &push); + + while (heap_pop(heap, &pop)) + { + printf("pop %d\r\n", pop); + } + + heap_delete(heap); +} + static void test_base(void) { heap_t h = heap_create(sizeof(int), 11, heap_root_max); @@ -45,33 +141,51 @@ static void test_base(void) heap_delete(h); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: heap [opt] [arg]\n" +"Usage: heap [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test push functions\n" +" Test pop functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("heap version %d.%d.%d\r\n", HEAP_V_MAJOR, HEAP_V_MINOR, HEAP_V_PATCH); return 0; @@ -85,11 +199,50 @@ static int test(int argc, char *argv[]) } } - test_base(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_heap) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "push")) + { + test_push(); + } + else if (!strcmp(execute, "pop")) + { + test_pop(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_heap) int main(int argc, char *argv[]) { diff --git a/test/test_ini.c b/test/test_ini.c index 4030ba1..402fb10 100644 --- a/test/test_ini.c +++ b/test/test_ini.c @@ -1,16 +1,210 @@ #include #if defined(TEST_TARGET_ini) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "ini.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define READ_FILE "test/file/read.ini" #define WRITE_FILE "test/file/write.ini" +static void ini_preview(ini_t ini) +{ + char *text = NULL; + text = ini_dumps(ini, 0, NULL); + if (text) + { + printf("%s\r\n", text); + free(text); + } + else + { + printf("[ERROR] dumps fail!!!\r\n"); + } +} + +static void test_create(void) +{ + ini_t ini = NULL; + ini = ini_create(); + if (ini) + { + printf("ini_create success!!! %p\r\n", ini); + } + ini_delete(ini); +} + +static void test_op_sections(void) +{ + ini_t ini = NULL; + + ini = ini_create(); + if (ini) + { + printf("ini_create success!!! %p\r\n", ini); + } + + ini_add_section(ini, "Zhang San"); + ini_add_section(ini, "Li Si"); + ini_add_section(ini, "Wang Wu"); + + ini_preview(ini); + + printf("=================================\r\n"); + + ini_remove_section(ini, "Li Si"); + + ini_preview(ini); + + ini_delete(ini); +} + +static void test_get_sections(void) +{ + ini_t ini = NULL; + int count = 0; + + ini = ini_create(); + if (ini) + { + printf("ini_create success!!! %p\r\n", ini); + } + + ini_add_section(ini, "Zhang San"); + ini_add_section(ini, "Li Si"); + ini_add_section(ini, "Wang Wu"); + + count = ini_section_count(ini); + + for (int i = 0; i < count; i++) + { + printf("ini[%d] = %s\r\n", i, ini_section_name(ini, i)); + } + + printf("section index = %d\r\n", ini_section_index(ini, "Zhang San")); + printf("section index = %d\r\n", ini_section_index(ini, "Li Si")); + printf("section index = %d\r\n", ini_section_index(ini, "Wang Wu")); + + ini_delete(ini); +} + +static void test_op_pairs(void) +{ + ini_t ini = NULL; + + ini = ini_create(); + if (ini) + { + printf("ini_create success!!! %p\r\n", ini); + } + + ini_set_value(ini, "Zhang San", "age", "18"); + ini_set_value(ini, "Zhang San", "height", "178"); + ini_set_value(ini, "Zhang San", "email", "123456@qq.com"); + + ini_set_value(ini, "Li Si", "age", "20"); + ini_set_value(ini, "Li Si", "gender", "man"); + ini_set_value(ini, "Li Si", "weight", "65"); + + ini_set_value(ini, "Wang Wu", "age", "22"); + + ini_preview(ini); + + printf("=================================\r\n"); + + ini_remove_key(ini, "Li Si", "weight"); + + ini_preview(ini); + + printf("=================================\r\n"); + + printf("ini[Zhang San][height] = %s\r\n", ini_get_value(ini, "Zhang San", "height")); + + ini_delete(ini); +} + +static void test_get_key(void) +{ + ini_t ini = NULL; + int count = 0; + + ini = ini_create(); + if (ini) + { + printf("ini_create success!!! %p\r\n", ini); + } + + ini_set_value(ini, "Zhang San", "age", "18"); + ini_set_value(ini, "Zhang San", "height", "178"); + ini_set_value(ini, "Zhang San", "email", "123456@qq.com"); + ini_set_value(ini, "Li Si", "age", "20"); + ini_set_value(ini, "Li Si", "gender", "man"); + ini_set_value(ini, "Li Si", "weight", "65"); + ini_set_value(ini, "Wang Wu", "age", "22"); + + count = ini_pair_count(ini, "Zhang San"); + + for (int i = 0; i < count; i++) + { + printf("ini[%d] = %s\r\n", i, ini_key_name(ini, "Zhang San", i)); + } + + printf("pair index = %d\r\n", ini_key_index(ini, "Zhang San", "age")); + printf("pair index = %d\r\n", ini_key_index(ini, "Zhang San", "height")); + printf("pair index = %d\r\n", ini_key_index(ini, "Zhang San", "email")); + + ini_delete(ini); +} + static void test_dump(void) { ini_t ini = NULL; // 定义ini对象,习惯初始化为NULL @@ -82,33 +276,60 @@ static void test_load(void) ini_delete(ini); } +static void test_base(void) +{ + // test_dump(); + test_load(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: ini [opt] [arg]\n" +"Usage: ini [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test operate section functions\n" +" Test get section infomations functions\n" +" Test operate pair functions\n" +" Test get key infomations functions\n" +" Test dump functions\n" +" Test load functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("ini version %d.%d.%d\r\n", INI_V_MAJOR, INI_V_MINOR, INI_V_PATCH); return 0; @@ -122,12 +343,66 @@ static int test(int argc, char *argv[]) } } - // test_dump(); - test_load(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_ini) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "opsect")) + { + test_op_sections(); + } + else if (!strcmp(execute, "getsect")) + { + test_get_sections(); + } + else if (!strcmp(execute, "oppair")) + { + test_op_pairs(); + } + else if (!strcmp(execute, "getkey")) + { + test_get_key(); + } + else if (!strcmp(execute, "dump")) + { + test_dump(); + } + else if (!strcmp(execute, "load")) + { + test_load(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_ini) int main(int argc, char *argv[]) { diff --git a/test/test_init.c b/test/test_init.c index 7abed08..1337a69 100644 --- a/test/test_init.c +++ b/test/test_init.c @@ -1,13 +1,59 @@ #include #if defined(TEST_TARGET_init) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "init.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + void test_init_hardware(void) { printf("hardware init!\r\n"); @@ -47,30 +93,41 @@ init_export_system(test_init_system1); static void usage(void) { printf( -"Usage: init [opt] [arg]\n" +"Usage: init [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("init version %d.%d.%d\r\n", INIT_V_MAJOR, INIT_V_MINOR, INIT_V_PATCH); return 0; @@ -87,6 +144,10 @@ static int test(int argc, char *argv[]) return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_init) int main(int argc, char *argv[]) { diff --git a/test/test_intl.c b/test/test_intl.c index 1711fb0..2a9c4ef 100644 --- a/test/test_intl.c +++ b/test/test_intl.c @@ -2,13 +2,59 @@ #include #if defined(TEST_TARGET_intl) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "intl.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define INTL_P_DEC 0x01 #define INTL_P_BIN 0x02 #define INTL_P_HEX 0x04 @@ -102,33 +148,55 @@ static void test_calculate(void) print_intl(intl_dec(b), INTL_P_DEC); } +static void test_base(void) +{ + // test_define(); + // test_print(); + test_calculate(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: intl [opt] [arg]\n" +"Usage: intl [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("intl version %d.%d.%d\r\n", INTL_V_MAJOR, INTL_V_MINOR, INTL_V_PATCH); return 0; @@ -142,13 +210,38 @@ static int test(int argc, char *argv[]) } } - // test_define(); - // test_print(); - test_calculate(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_intl) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_intl) int main(int argc, char *argv[]) { diff --git a/test/test_json.c b/test/test_json.c index d8019b0..3d0f33f 100644 --- a/test/test_json.c +++ b/test/test_json.c @@ -4,17 +4,360 @@ #include #if defined(TEST_TARGET_json) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "json.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define READ_FILE "test/file/read.json" #define WRITE_FILE "test/file/write.json" -static void test_read(void) +static void json_preview(json_t json) +{ + char *text = NULL; + text = json_dumps(json, 0, 0, NULL); + if (text) + { + printf("%s\r\n", text); + free(text); + } + else + { + printf("[ERROR] dumps fail!!!\r\n"); + } +} + +static const char *typename(int type) +{ + const char *name = NULL; + + if (type == JSON_TYPE_UNKNOW) name = "unknow"; + else if (type == JSON_TYPE_NULL ) name = "null"; + else if (type == JSON_TYPE_BOOL ) name = "bool"; + else if (type == JSON_TYPE_INT ) name = "number int"; + else if (type == JSON_TYPE_FLOAT ) name = "number float"; + else if (type == JSON_TYPE_STRING) name = "string"; + else if (type == JSON_TYPE_ARRAY ) name = "array"; + else if (type == JSON_TYPE_OBJECT) name = "object"; + + return name; +} + +static void print_value(json_t json) +{ + int type = json_type(json); + if (type == JSON_TYPE_UNKNOW) printf("unknow type!\r\n"); + else if (type == JSON_TYPE_NULL ) printf("null type!\r\n"); + else if (type == JSON_TYPE_BOOL ) printf("%s\r\n", json_value_bool(json) ? "true" : "false"); + else if (type == JSON_TYPE_INT ) printf("%d\r\n", json_value_int(json)); + else if (type == JSON_TYPE_FLOAT ) printf("%f\r\n", json_value_float(json)); + else if (type == JSON_TYPE_STRING) printf("%s\r\n", json_value_string(json)); + else if (type == JSON_TYPE_ARRAY ) printf("[...]\r\n"); + else if (type == JSON_TYPE_OBJECT) printf("{...}\r\n"); +} + +static void test_create(void) +{ + json_t json = NULL; + json = json_create(); + if (json) + { + printf("json_create success!!! %p\r\n", json); + } + json_delete(json); +} + +static void test_add(void) +{ + json_t json = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + json_add_null_to_object(json, "null"); + json_add_true_to_object(json, "true"); + json_add_false_to_object(json, "false"); + json_add_bool_to_object(json, "bool", JSON_TRUE); + json_add_int_to_object(json, "int", 178); + json_add_float_to_object(json, "float", 12.3333); + json_add_string_to_object(json, "string", "18"); + json_add_array_to_object(json, "array"); + json_add_object_to_object(json, "object"); + json_add_array_to_object(json, "数组"); + + json_preview(json); + + json_delete(json); +} + +static void test_erase(void) +{ + json_t json = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + json_add_null_to_object(json, "null"); + json_add_true_to_object(json, "true"); + json_add_false_to_object(json, "false"); + json_add_bool_to_object(json, "bool", JSON_TRUE); + json_add_bool_to_object(json, "bool", JSON_FALSE); + json_add_int_to_object(json, "int", 178); + json_add_float_to_object(json, "float", 12.3333); + json_add_string_to_object(json, "string", "18"); + json_add_array_to_object(json, "array"); + json_add_object_to_object(json, "object"); + json_add_array_to_object(json, "数组"); + + json_preview(json); + + json_erase(json, "数组", 0); // erase the index 0 "数组" + json_erase(json, "bool", 1); // erase the index 1 "bool" + json_erase(json, NULL, 1); // erase the index 1 + + json_preview(json); + + json_delete(json); +} + +static void test_info(void) +{ + json_t json = NULL; + json_t node = NULL; + + /* create root node */ + json = json_create(); + + printf("json prev type: %s\r\n", typename(json_type(json))); + json_set_object(json, NULL); + printf("json prev type: %s\r\n", typename(json_type(json))); + + json_add_null_to_object(json, "null"); + json_add_true_to_object(json, "true"); + json_add_false_to_object(json, "false"); + json_add_bool_to_object(json, "bool", JSON_TRUE); + json_add_int_to_object(json, "int", 178); + json_add_float_to_object(json, "float", 12.3333); + json_add_string_to_object(json, "string", "18"); + json_add_array_to_object(json, "array"); + json_add_object_to_object(json, "object"); + json_add_array_to_object(json, "数组"); + + int size = json_size(json); + printf("json size: %d\r\n", size); + for (int i = 0; i < size; i++) + { + node = json_to_index(json, i); + printf("node type: %s\r\n", typename(json_type(node))); + } + + json_delete(json); +} + +static void test_get(void) +{ + json_t json = NULL; + json_t node = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + json_add_null_to_object(json, "null"); + json_add_true_to_object(json, "true"); + json_add_false_to_object(json, "false"); + json_add_bool_to_object(json, "bool", JSON_TRUE); + json_add_int_to_object(json, "int", 178); + json_add_float_to_object(json, "float", 12.3333); + json_add_string_to_object(json, "string", "18"); + json_add_array_to_object(json, "array"); + json_add_object_to_object(json, "object"); + json_add_array_to_object(json, "数组"); + + int size = json_size(json); + for (int i = 0; i < size; i++) + { + node = json_to_index(json, i); + + int type = json_type(node); + + printf("key<%s> : ", json_key(node)); + + if (type == JSON_TYPE_UNKNOW) printf("unknow type!\r\n"); + else if (type == JSON_TYPE_NULL ) printf("null type!\r\n"); + else if (type == JSON_TYPE_BOOL ) printf("%s\r\n", json_value_bool(node) ? "true" : "false"); + else if (type == JSON_TYPE_INT ) printf("%d\r\n", json_value_int(node)); + else if (type == JSON_TYPE_FLOAT ) printf("%f\r\n", json_value_float(node)); + else if (type == JSON_TYPE_STRING) printf("%s\r\n", json_value_string(node)); + else if (type == JSON_TYPE_ARRAY ) printf("[...]\r\n"); + else if (type == JSON_TYPE_OBJECT) printf("{...}\r\n"); + } + + json_delete(json); +} + +static void test_set(void) +{ + json_t json = NULL; + json_t node = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + node = json_add_null_to_object(json, "null"); + + json_set_bool(node, JSON_TRUE); json_preview(json); + json_set_bool(node, JSON_FALSE); json_preview(json); + json_set_int(node, 1024); json_preview(json); + json_set_float(node, 3.14159); json_preview(json); + json_set_string(node, "String"); json_preview(json); + json_set_array(node, NULL); json_preview(json); + json_set_object(node, NULL); json_preview(json); + json_set_key(node, "new"); json_preview(json); + + json_delete(json); +} + +static void test_attach(void) +{ + json_t json = NULL; + json_t node = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + node = json_create_int_for_object("node", 0); json_attach(json, 0, node); + node = json_create_int_for_object("node", 1); json_attach(json, 1, node); + node = json_create_int_for_object("node", 2); json_attach(json, 2, node); + node = json_create_int_for_object("node", 3); json_attach(json, 3, node); + + json_preview(json); + + node = json_detach(json, "node", 2); + if (node) + { + json_delete(node); + } + + json_preview(json); + + json_delete(json); +} + +static void test_minify(void) +{ + json_t json = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + json_add_null_to_object(json, "null"); + json_add_true_to_object(json, "true"); + json_add_false_to_object(json, "false"); + json_add_bool_to_object(json, "bool", JSON_TRUE); + json_add_int_to_object(json, "int", 178); + json_add_float_to_object(json, "float", 12.3333); + json_add_string_to_object(json, "string", "18"); + json_add_array_to_object(json, "array"); + json_add_object_to_object(json, "object"); + json_add_array_to_object(json, "数组"); + + json_preview(json); + + char *text = json_dumps(json, 0, 0, NULL); + if (text) + { + json_minify(text); + printf("%s\r\n", text); + free(text); + } + + json_delete(json); +} + +static void test_copy(void) +{ + json_t json = NULL; + json_t copy = NULL; + + /* create root node */ + json = json_create(); + json_set_object(json, NULL); + + json_add_null_to_object(json, "null"); + json_add_true_to_object(json, "true"); + json_add_false_to_object(json, "false"); + json_add_bool_to_object(json, "bool", JSON_TRUE); + json_add_int_to_object(json, "int", 178); + json_add_float_to_object(json, "float", 12.3333); + json_add_string_to_object(json, "string", "18"); + json_add_array_to_object(json, "array"); + json_add_object_to_object(json, "object"); + json_add_array_to_object(json, "数组"); + + copy = json_copy(json); + + json_preview(json); + json_preview(copy); + + json_delete(json); + json_delete(copy); +} + +static void test_load(void) { json_t root = NULL, x = NULL; char *s = NULL; @@ -48,7 +391,7 @@ static void test_read(void) json_delete(root); } -static void test_write(void) +static void test_dump(void) { json_t json, t, j; int aa[10] = {0,1,2,3,4,5,6,7,8,9}; @@ -115,33 +458,65 @@ static void test_write(void) json_delete(json); } +static void test_base(void) +{ + // test_dump(); + test_load(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: json [opt] [arg]\n" +"Usage: json [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test add json functions\n" +" Test erase json functions\n" +" Test get json infomations\n" +" Test get json key and values functions\n" +" Test set json key and values functions\n" +" Test set attach and dettach functions\n" +" Test minify functions\n" +" Test copy functions\n" +" Test dump functions\n" +" Test load functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("json version %d.%d.%d\r\n", JSON_V_MAJOR, JSON_V_MINOR, JSON_V_PATCH); return 0; @@ -155,12 +530,82 @@ static int test(int argc, char *argv[]) } } - // test_write(); - test_read(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_json) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "add")) + { + test_add(); + } + else if (!strcmp(execute, "erase")) + { + test_erase(); + } + else if (!strcmp(execute, "info")) + { + test_info(); + } + else if (!strcmp(execute, "value")) + { + test_get(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "attach")) + { + test_attach(); + } + else if (!strcmp(execute, "minify")) + { + test_minify(); + } + else if (!strcmp(execute, "copy")) + { + test_copy(); + } + else if (!strcmp(execute, "dump")) + { + test_dump(); + } + else if (!strcmp(execute, "load")) + { + test_load(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_json) int main(int argc, char *argv[]) { diff --git a/test/test_kern.c b/test/test_kern.c index 1fbb29a..05b309b 100644 --- a/test/test_kern.c +++ b/test/test_kern.c @@ -4,13 +4,59 @@ #include #if defined(TEST_TARGET_kern) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "kern.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static unsigned int get_msec(void) { struct timeval mstime; @@ -32,46 +78,8 @@ static void task2(void) printf("task2 running! %d\r\n", ++count); } -static void usage(void) +static void test_base(void) { - printf( -"Usage: kern [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("kern version %d.%d.%d\r\n", KERN_V_MAJOR, KERN_V_MINOR, KERN_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - #if defined(TEST_TARGET_kern) if (kern_init(get_msec, 1) == KE_OK) { @@ -91,10 +99,94 @@ static int test(int argc, char *argv[]) printf("create task %d\r\n", task_create(1000, task1)); printf("create task %d\r\n", task_create(500, task2)); #endif +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: kern [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("kern version %d.%d.%d\r\n", KERN_V_MAJOR, KERN_V_MINOR, KERN_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_kern) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_kern) int main(int argc, char *argv[]) { diff --git a/test/test_list.c b/test/test_list.c index 49515f2..0e11d8d 100644 --- a/test/test_list.c +++ b/test/test_list.c @@ -3,17 +3,138 @@ #include #if defined(TEST_TARGET_list) #include +#include #include #include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "list.h" #include "vector.h" #include "tool.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + list_t list = list(int); + + if (list) + { + printf("list create success!!!\r\n"); + } + else + { + printf("[ERROR] list create fail!!!\r\n"); + } + + _list(list); +} + +static void test_insert(void) +{ + list_t list = list(int); // 定义并创建一个int型的list + int i = 0; + void *data = NULL; + + /* 插入数据 */ + for (i = 0; i < 6; i++) + { + data = list_push_back(list, &i); // 尾插 0~5 + if (data) printf("insert %d\r\n", *(int*)data); // 插入后打印出来 + } + + _list(list); // 成对使用,用完即删除 +} + +static void test_erase(void) +{ + list_t list = list(int); // 定义并创建一个int型的list + int i = 0; + + /* 插入数据 */ + for (i = 0; i < 6; i++) + { + list_push_back(list, &i); // 尾插 0~5 + } + + list_erase(list, 0, 2); + + _list(list); // 成对使用,用完即删除 +} + +static void test_at(void) +{ + list_t list = list(int); + int i = 0; + + for (i = 0; i < 6; i++) + { + list_push_back(list, &i); + } + + for (i = 0; i < 6; i++) // 正向遍历 + { + printf("list[%d] = %d\r\n", i, list_at(list, int, i)); + } + + _list(list); +} + +static void test_size(void) +{ + list_t list = list(int); + int i = 5; + while (i--) list_push_back(list, NULL); // 尾插5个空数据,也就是只开辟空间,但不赋值 + printf("size = %d, data size = %d\r\n", list_size(list), list_dsize(list)); + _list(list); +} + static void test_base(void) { list_t list = list(int); // 定义并创建int型list @@ -56,33 +177,53 @@ static void test_base(void) _vector(vt); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: list [opt] [arg]\n" +"Usage: list [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test insert function\n" +" Test erase function\n" +" Test get data function\n" +" Test size function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("list version %d.%d.%d\r\n", LIST_V_MAJOR, LIST_V_MINOR, LIST_V_PATCH); return 0; @@ -96,11 +237,58 @@ static int test(int argc, char *argv[]) } } - test_base(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_list) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "erase")) + { + test_erase(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + else if (!strcmp(execute, "size")) + { + test_size(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_list) int main(int argc, char *argv[]) { diff --git a/test/test_map.c b/test/test_map.c index 77552ba..da58aad 100644 --- a/test/test_map.c +++ b/test/test_map.c @@ -3,13 +3,147 @@ #include #if defined(TEST_TARGET_map) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "map.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + map_t map_char = map(char, int); + map_t map_int = map(int, int); + map_t map_float = map(float, int); + map_t map_double = map(double, int); + map_t map_string = map(string, int); + + _map(map_char); + _map(map_int); + _map(map_float); + _map(map_double); + _map(map_string); +} + +static void test_insert(void) +{ + int value; + map_t map = map(string, int); + if (!map) return; + + value = 100; map_insert(map, mpair("hello", &value)); + value = 110; map_insert(map, mpair("Zhang", &value)); + + printf("map[hello] = %d\r\n", map_at(map, int, "hello")); + printf("map[Zhang] = %d\r\n", map_at(map, int, "Zhang")); + map_erase(map, "hello"); + + _map(map); +} + +static void test_at(void) +{ + map_t map = map(string, int); + int value; + + value = 100; map_insert(map, mpair("hello", &value)); + value = 1; map_insert(map, mpair("ZhangSan", &value)); + value = 2; map_insert(map, mpair("LiSi", &value)); + value = 3; map_insert(map, mpair("WangWu", &value)); + value = 4; map_insert(map, mpair("SunLiu", &value)); + value = 5; map_insert(map, mpair("QianQi", &value)); + + printf("map[%s] = %d\r\n", "hello", map_at(map, int, "hello")); + printf("map[%s] = %d\r\n", "ZhangSan", map_at(map, int, "ZhangSan")); + printf("map[%s] = %d\r\n", "LiSi", map_at(map, int, "LiSi")); + printf("map[%s] = %d\r\n", "WangWu", map_at(map, int, "WangWu")); + printf("map[%s] = %d\r\n", "SunLiu", map_at(map, int, "SunLiu")); + printf("map[%s] = %d\r\n", "QianQi", map_at(map, int, "QianQi")); + + _map(map); +} + +static void test_find(void) +{ + map_t map = map(string, int); + int value; + + value = 100; map_insert(map, mpair("hello", &value)); + value = 1; map_insert(map, mpair("ZhangSan", &value)); + value = 2; map_insert(map, mpair("LiSi", &value)); + value = 3; map_insert(map, mpair("WangWu", &value)); + value = 4; map_insert(map, mpair("SunLiu", &value)); + value = 5; map_insert(map, mpair("QianQi", &value)); + + printf("find ""hello"" %d\r\n", map_find(map, "hello")); + printf("find ""lllll"" %d\r\n", map_find(map, "lllll")); + + _map(map); +} + +static void test_size(void) +{ + map_t map = map(string, int); + int value; + + value = 100; map_insert(map, mpair("hello", &value)); + value = 1; map_insert(map, mpair("ZhangSan", &value)); + value = 2; map_insert(map, mpair("LiSi", &value)); + value = 3; map_insert(map, mpair("WangWu", &value)); + value = 4; map_insert(map, mpair("SunLiu", &value)); + value = 5; map_insert(map, mpair("QianQi", &value)); + + printf("size = %d, key size = %d, value size = %d\r\n", map_size(map), map_ksize(map), map_vsize(map)); + + _map(map); +} + static void test_it(void) { map_t map = map(string, int); @@ -36,24 +170,6 @@ static void test_it(void) _map(map); } -static void test_base(void) -{ - int value; - map_t map = map(string, int); - if (!map) return; - - value = 100; map_insert(map, mpair("hello", &value)); - value = 110; map_insert(map, mpair("Zhang", &value)); - - printf("size = %d, key size = %d, value size = %d\r\n", map_size(map), map_ksize(map), map_vsize(map)); - - printf("map[hello] = %d\r\n", map_at(map, int, "hello")); - printf("map[Zhang] = %d\r\n", map_at(map, int, "Zhang")); - map_erase(map, "hello"); - - _map(map); -} - static void test_float_map(void) { int value; @@ -67,33 +183,61 @@ static void test_float_map(void) _map(map); } +static void test_base(void) +{ + test_it(); + // test_float_map(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: map [opt] [arg]\n" +"Usage: map [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test insert and erase function\n" +" Test get data function\n" +" Test find function\n" +" Test sizes function\n" +" Test iterator function\n" +" Test other key types function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("map version %d.%d.%d\r\n", MAP_V_MAJOR, MAP_V_MINOR, MAP_V_PATCH); return 0; @@ -107,13 +251,66 @@ static int test(int argc, char *argv[]) } } - test_it(); - // test_base(); - // test_float_map(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_map) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + else if (!strcmp(execute, "find")) + { + test_find(); + } + else if (!strcmp(execute, "size")) + { + test_size(); + } + else if (!strcmp(execute, "it")) + { + test_it(); + } + else if (!strcmp(execute, "other")) + { + test_float_map(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_map) int main(int argc, char *argv[]) { diff --git a/test/test_oscp.c b/test/test_oscp.c index a930a71..2a1e450 100644 --- a/test/test_oscp.c +++ b/test/test_oscp.c @@ -5,15 +5,61 @@ #include #if defined(TEST_TARGET_oscp) #include +#include #include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "oscp.h" #include "kern.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + int level = 0; static unsigned int get_msec(void) @@ -40,33 +86,76 @@ static void task2(void) level = RESOLUTION / 2 * sin((double)count / 10) + RESOLUTION / 2; } +static void test_base(void) +{ +#if defined(TEST_TARGET_oscp) + uint32_t count = 0; + + oscp_set_scale(O_SCALE_50MS); + oscp_set_monitor(&level); + + while (1) + { + count++; + if (count >= 25200000) count = 0; + + + if (count % 5 == 0) task1(); + if (count % 50 == 0) task2(); + + usleep(1000); + } +#else + printf("create task %d\r\n", task_create(5, task1)); + printf("create task %d\r\n", task_create(50, task2)); + + oscp_set_scale(O_SCALE_50MS); + oscp_set_monitor(&level); +#endif +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: oscp [opt] [arg]\n" +"Usage: oscp [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("oscp version %d.%d.%d\r\n", OSCP_V_MAJOR, OSCP_V_MINOR, OSCP_V_PATCH); return 0; @@ -80,27 +169,38 @@ static int test(int argc, char *argv[]) } } - if (kern_init(get_msec, 1) == KE_OK) + if (execute) { - printf("kern init success!\r\n"); + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_oscp) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } } else { - printf("*** kern init fail!\r\n"); - return 0; + test_base(); } - - printf("create task %d\r\n", task_create(5, task1)); - printf("create task %d\r\n", task_create(50, task2)); - - oscp_set_scale(O_SCALE_50MS); - oscp_set_monitor(&level); - - kern_schedule(); return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_oscp) int main(int argc, char *argv[]) { diff --git a/test/test_pid.c b/test/test_pid.c index 7f2a7b2..10364d9 100644 --- a/test/test_pid.c +++ b/test/test_pid.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_pid) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "pid.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_sim(void) { PIDC pid; @@ -32,33 +78,53 @@ static void test_sim(void) } } +static void test_base(void) +{ + test_sim(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: pid [opt] [arg]\n" +"Usage: pid [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("pid version %d.%d.%d\r\n", PID_V_MAJOR, PID_V_MINOR, PID_V_PATCH); return 0; @@ -72,11 +138,38 @@ static int test(int argc, char *argv[]) } } - test_sim(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_pid) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_pid) int main(int argc, char *argv[]) { diff --git a/test/test_queue.c b/test/test_queue.c index f1eb7c3..e5299de 100644 --- a/test/test_queue.c +++ b/test/test_queue.c @@ -3,13 +3,125 @@ #include #if defined(TEST_TARGET_queue) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "queue.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + queue_t queue = queue(int, 10); + + if (queue) + { + printf("queue create success!!!\r\n"); + } + else + { + printf("[ERROR] queue create fail!!!\r\n"); + } + + _queue(queue); +} + +static void test_push(void) +{ + queue_t queue = queue(int, 10); + int i = 0; + + for (i = 0; i < queue_capacity(queue); i++) + { + queue_push(queue, &i); + } + queue_pop(queue, NULL); + queue_pop(queue, NULL); + + _queue(queue); // 成对使用,用完即删除 +} + +static void test_info(void) +{ + queue_t queue = queue(int, 10); + int i = 0; + + for (i = 0; i < queue_capacity(queue); i++) + { + queue_push(queue, &i); + } + queue_pop(queue, NULL); + queue_pop(queue, NULL); + printf("queue capacity=%d, size=%d, dsize=%d\r\n", queue_capacity(queue), queue_size(queue), queue_dsize(queue)); + + _queue(queue); +} + +static void test_at(void) +{ + queue_t queue = queue(int, 10); + int i = 0; + + for (i = 0; i < queue_capacity(queue); i++) + { + queue_push(queue, &i); + } + queue_pop(queue, NULL); + queue_pop(queue, NULL); + for (i = 0; i < queue_size(queue); i++) + { + printf("queue[%d] = %d\r\n", i, queue_at(queue, int, i)); + } + + _queue(queue); +} + static void test_base(void) { queue_t queue = queue(int, 10); @@ -30,33 +142,52 @@ static void test_base(void) _queue(queue); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: queue [opt] [arg]\n" +"Usage: queue [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test push and pop functions\n" +" Test info(size, capacity, data size) functions\n" +" Test get data function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("queue version %d.%d.%d\r\n", QUEUE_V_MAJOR, QUEUE_V_MINOR, QUEUE_V_PATCH); return 0; @@ -70,11 +201,54 @@ static int test(int argc, char *argv[]) } } - test_base(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_queue) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "push")) + { + test_push(); + } + else if (!strcmp(execute, "info")) + { + test_info(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_queue) int main(int argc, char *argv[]) { diff --git a/test/test_ramt.c b/test/test_ramt.c index 9019e11..3157fb8 100644 --- a/test/test_ramt.c +++ b/test/test_ramt.c @@ -4,14 +4,60 @@ #if defined(TEST_TARGET_ramt) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "ramt.h" #include "kern.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define TASK_PERIOD 5 // ms static uint8_t buffer[128]; @@ -21,66 +67,18 @@ static void test_task(void) { static int count = 0; - count += TASK_PERIOD; - ramt_task(&ramt_object); if (count % 1000 == 0) { printf("result %08X\r\n", ramt_result(&ramt_object)); } + + count += TASK_PERIOD; } -static void usage(void) +static void test_base(void) { - printf( -"Usage: ramt [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - int period = 1000; // ms - - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "p:hv"); - if (opt == -1) break; - - switch (opt) - { - case 'p' : - period = atoi(command_optarg); - break; - case 'v' : - printf("ramt version %d.%d.%d\r\n", RAMT_V_MAJOR, RAMT_V_MINOR, RAMT_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - if (1 == command_optind) // no opt - { - test_task(); - return 0; - } - ramt_object.base = (void *)buffer; ramt_object.size = sizeof(buffer); ramt_init(&ramt_object); @@ -96,10 +94,98 @@ static int test(int argc, char *argv[]) #else printf("create task %d\r\n", task_create(TASK_PERIOD, test_task)); #endif +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: ramt [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + int period = 1000; // ms + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::p:"); + if (opt == -1) break; + + switch (opt) + { + case 'p' : + period = atoi(command_optarg); + break; + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("ramt version %d.%d.%d\r\n", RAMT_V_MAJOR, RAMT_V_MINOR, RAMT_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_ramt) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_ramt) int main(int argc, char *argv[]) { diff --git a/test/test_romt.c b/test/test_romt.c new file mode 100644 index 0000000..642f1c4 --- /dev/null +++ b/test/test_romt.c @@ -0,0 +1,237 @@ +#include +#include +#include + +#if defined(TEST_TARGET_romt) +#include +#include +#include +#else +#include "init.h" +#include "command.h" +#include "unitt.h" +#include "kern.h" +#include "romt.h" +#include "kern.h" +#endif + +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +#define TASK_PERIOD 5 // ms + +static uint8_t buffer[1024]; +static ROMT romt_object; + +static int rom_read(uint32_t address, uint8_t *data, uint32_t length) +{ + uint32_t i = 0; + + if (address + length > sizeof(buffer)) + { + return 0; + } + + for (i = 0; i < length; i++) + { + data[i] = buffer[address + i]; + } + + return i; + +} + +static int romt_write(uint32_t address, uint8_t *data, uint32_t length) +{ + uint32_t i = 0; + + if (address + length > sizeof(buffer)) + { + return 0; + } + + for (i = 0; i < length; i++) + { + buffer[address + i] = data[i]; + } + + return i; +} + +static void test_task(void) +{ + static int count = 0; + + romt_task(&romt_object); + + if (count % 1000 == 0) + { + printf("result %08X\r\n", romt_result(&romt_object)); + } + + count += TASK_PERIOD; +} + +static void test_base(void) +{ + romt_object.base = 0; + romt_object.size = sizeof(buffer); + romt_object.read = rom_read; + romt_object.write = romt_write; + romt_init(&romt_object); + + romt_start(&romt_object, ROMT_MODE_NORMAL, 0xFFFFFFFF); + +#if defined(TEST_TARGET_romt) + while (1) + { + test_task(); + usleep(1000 * period); + } +#else + printf("create task %d\r\n", task_create(TASK_PERIOD, test_task)); +#endif +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: romt [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + int period = 1000; // ms + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::p:"); + if (opt == -1) break; + + switch (opt) + { + case 'p' : + period = atoi(command_optarg); + break; + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("romt version %d.%d.%d\r\n", ROMT_V_MAJOR, ROMT_V_MINOR, ROMT_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_romt) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } + + return 0; +} + +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + +#if defined(TEST_TARGET_romt) +int main(int argc, char *argv[]) +{ + return test(argc, argv); +} +#else +void test_romt(void) +{ + command_export("romt", test); +} +init_export_app(test_romt); +#endif diff --git a/test/test_sList.c b/test/test_sList.c index da9e2bf..03ed28b 100644 --- a/test/test_sList.c +++ b/test/test_sList.c @@ -3,15 +3,61 @@ #include #if defined(TEST_TARGET_sList) #include +#include #include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "sList.h" #include "tool.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_create(void); static void test_set(void); static void test_insert(void); @@ -325,46 +371,8 @@ FAIL: sList_delete(list); } -static void usage(void) +static void test_base(void) { - printf( -"Usage: sList [opt] [arg]\n" -"\n" -"options:\n" -" -h Print help\n" -" -v Print version\n" -"\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" - ); -} - -static int test(int argc, char *argv[]) -{ - /* reset getopt */ - command_opt_init(); - - while (1) - { - int opt = command_getopt(argc, argv, "hv"); - if (opt == -1) break; - - switch (opt) - { - case 'v' : - printf("sList version %d.%d.%d\r\n", SLIST_V_MAJOR, SLIST_V_MINOR, SLIST_V_PATCH); - return 0; - case '?': - printf("Unknown option `%c`\r\n", command_optopt); - return -1; - case 'h' : - default: - usage(); - return 0; - } - } - // test_create(); test_set(); // test_insert(); @@ -378,10 +386,160 @@ static int test(int argc, char *argv[]) // test_append(); // test_copy(); // test_reverse(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: sList [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test set function\n" +" Test insert function\n" +" Test erase function\n" +" Test attach function\n" +" Test detach function\n" +" Test push function\n" +" Test pop function\n" +" Test locate to specify index function\n" +" Test size function\n" +" Test append function\n" +" Test copy function\n" +" Test reverse function\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("sList version %d.%d.%d\r\n", SLIST_V_MAJOR, SLIST_V_MINOR, SLIST_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_sList) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "erase")) + { + test_erase(); + } + else if (!strcmp(execute, "attach")) + { + test_attach(); + } + else if (!strcmp(execute, "detach")) + { + test_detach(); + } + else if (!strcmp(execute, "push")) + { + test_push(); + } + else if (!strcmp(execute, "pop")) + { + test_pop(); + } + else if (!strcmp(execute, "to")) + { + test_to(); + } + else if (!strcmp(execute, "size")) + { + test_size(); + } + else if (!strcmp(execute, "append")) + { + test_append(); + } + else if (!strcmp(execute, "copy")) + { + test_copy(); + } + else if (!strcmp(execute, "reverse")) + { + test_reverse(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_sList) int main(int argc, char *argv[]) { diff --git a/test/test_search.c b/test/test_search.c index 73190a9..cfbb303 100644 --- a/test/test_search.c +++ b/test/test_search.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_search) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "search.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void* int_array_addr(void *array, int index) { return (void *)(&((int *)array)[index]); @@ -62,33 +108,56 @@ static void test_binary(void) target = -9, printf("search %d, result %d\r\n", target, search_binary(array, 0, sizeof(array) / sizeof(array[0]) - 1, &target, &ops)); } +static void test_base(void) +{ + // test_linear(); + test_binary(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: search [opt] [arg]\n" +"Usage: search [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test linear function\n" +" Test binary function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("search version %d.%d.%d\r\n", SEARCH_V_MAJOR, SEARCH_V_MINOR, SEARCH_V_PATCH); return 0; @@ -102,12 +171,46 @@ static int test(int argc, char *argv[]) } } - // test_linear(); - test_binary(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_search) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "linear")) + { + test_linear(); + } + else if (!strcmp(execute, "binary")) + { + test_binary(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_search) int main(int argc, char *argv[]) { diff --git a/test/test_set.c b/test/test_set.c index 0e17169..b237617 100644 --- a/test/test_set.c +++ b/test/test_set.c @@ -3,13 +3,149 @@ #include #if defined(TEST_TARGET_set) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "set.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + set_t set = set(int); + + if (set) + { + printf("set create success!!!\r\n"); + } + else + { + printf("[ERROR] set create fail!!!\r\n"); + } + + _set(set); +} + +static void test_insert(void) +{ + set_t set = set(int); + int i; + + for (i = 0; i < 100; i++) + { + set_insert(set, i, &i); + } + printf("size = %d, data size = %d\r\n", set_size(set), set_dsize(set)); + + i = -100; set_insert(set, i, &i); + i = 1024; set_insert(set, i, &i); + + printf("set[6] = %d\r\n", set_at(set, int, 6)); + printf("set[-100] = %d\r\n", set_at(set, int, -100)); + printf("set[1024] = %d\r\n", set_at(set, int, 1024)); + + set_at(set, int, 6) = 11111; + printf("set[6] = %d\r\n", set_at(set, int, 6)); + + _set(set); +} + +static void test_at(void) +{ + set_t set = set(int); + int i; + + for (i = 0; i < 100; i++) + { + set_insert(set, i, &i); + } + i = -100; set_insert(set, i, &i); + i = 1024; set_insert(set, i, &i); + + printf("set[6] = %d\r\n", set_at(set, int, 6)); + printf("set[-100] = %d\r\n", set_at(set, int, -100)); + printf("set[1024] = %d\r\n", set_at(set, int, 1024)); + + set_at(set, int, 6) = 11111; + printf("set[6] = %d\r\n", set_at(set, int, 6)); + + _set(set); +} + +static void test_find(void) +{ + set_t set = set(int); + int value; + + value = -100; set_insert(set, 20, &value); + value = 1024; set_insert(set, -1, &value); + + printf("find 20 %d\r\n", set_find(set, 20)); + printf("find 21 %d\r\n", set_find(set, 21)); + + _set(set); +} + +static void test_size(void) +{ + set_t set = set(int); + int i; + + for (i = 0; i < 100; i++) + { + set_insert(set, i, &i); + } + printf("size = %d, data size = %d\r\n", set_size(set), set_dsize(set)); + + _set(set); +} + static void test_it(void) { set_t set = set(int); @@ -36,57 +172,60 @@ static void test_it(void) _set(set); } -static void test_base(void) +static void test_base(void) { - set_t set = set(int); - int i; - - for (i = 0; i < 100; i++) - { - set_insert(set, i, &i); - } - printf("size = %d, data size = %d\r\n", set_size(set), set_dsize(set)); - - i = -100; set_insert(set, i, &i); - i = 1024; set_insert(set, i, &i); - - printf("set[6] = %d\r\n", set_at(set, int, 6)); - printf("set[-100] = %d\r\n", set_at(set, int, -100)); - printf("set[1024] = %d\r\n", set_at(set, int, 1024)); - - set_at(set, int, 6) = 11111; - printf("set[6] = %d\r\n", set_at(set, int, 6)); - - _set(set); + // test_insert(); + test_it(); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: set [opt] [arg]\n" +"Usage: set [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test insert and erase function\n" +" Test get data function\n" +" Test find function\n" +" Test sizes function\n" +" Test iterator function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("set version %d.%d.%d\r\n", SET_V_MAJOR, SET_V_MINOR, SET_V_PATCH); return 0; @@ -100,12 +239,62 @@ static int test(int argc, char *argv[]) } } - // test_base(); - test_it(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_set) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + else if (!strcmp(execute, "find")) + { + test_find(); + } + else if (!strcmp(execute, "size")) + { + test_size(); + } + else if (!strcmp(execute, "it")) + { + test_it(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_set) int main(int argc, char *argv[]) { diff --git a/test/test_sort.c b/test/test_sort.c index 50dbee5..b5dc137 100644 --- a/test/test_sort.c +++ b/test/test_sort.c @@ -3,15 +3,61 @@ #include #if defined(TEST_TARGET_sort) #include +#include #include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "sort.h" #include "list.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_basic_usage(void) { /* 针对int型数组,进行升序 @@ -213,33 +259,56 @@ static void test_struct(void) printf("---------------------------------\r\n"); } +static void test_base(void) +{ + // test_basic_usage(); + // test_interval(); + // test_list_sort(); + test_struct(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: sort [opt] [arg]\n" +"Usage: sort [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("sort version %d.%d.%d\r\n", SORT_V_MAJOR, SORT_V_MINOR, SORT_V_PATCH); return 0; @@ -253,14 +322,38 @@ static int test(int argc, char *argv[]) } } - // test_basic_usage(); - // test_interval(); - // test_list_sort(); - test_struct(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_sort) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_sort) int main(int argc, char *argv[]) { diff --git a/test/test_stack.c b/test/test_stack.c index 1fbdf21..2ac9ebb 100644 --- a/test/test_stack.c +++ b/test/test_stack.c @@ -3,13 +3,125 @@ #include #if defined(TEST_TARGET_stack) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "stack.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + stack_t stack = stack(int, 10); + + if (stack) + { + printf("stack create success!!!\r\n"); + } + else + { + printf("[ERROR] stack create fail!!!\r\n"); + } + + _stack(stack); +} + +static void test_push(void) +{ + stack_t stack = stack(int, 10); + int i = 0; + + for (i = 0; i < stack_capacity(stack); i++) + { + stack_push(stack, &i); + } + stack_pop(stack, NULL); + stack_pop(stack, NULL); + + _stack(stack); // 成对使用,用完即删除 +} + +static void test_info(void) +{ + stack_t stack = stack(int, 10); + int i = 0; + + for (i = 0; i < stack_capacity(stack); i++) + { + stack_push(stack, &i); + } + stack_pop(stack, NULL); + stack_pop(stack, NULL); + printf("stack capacity=%d, size=%d, dsize=%d\r\n", stack_capacity(stack), stack_size(stack), stack_dsize(stack)); + + _stack(stack); +} + +static void test_at(void) +{ + stack_t stack = stack(int, 10); + int i = 0; + + for (i = 0; i < stack_capacity(stack); i++) + { + stack_push(stack, &i); + } + stack_pop(stack, NULL); + stack_pop(stack, NULL); + for (i = 0; i < stack_size(stack); i++) + { + printf("stack[%d] = %d\r\n", i, stack_at(stack, int, i)); + } + + _stack(stack); +} + static void test_base(void) { stack_t stack = stack(int, 10); @@ -29,33 +141,52 @@ static void test_base(void) _stack(stack); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: stack [opt] [arg]\n" +"Usage: stack [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test push and pop functions\n" +" Test info(size, capacity, data size) functions\n" +" Test get data function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("stack version %d.%d.%d\r\n", STACK_V_MAJOR, STACK_V_MINOR, STACK_V_PATCH); return 0; @@ -69,11 +200,54 @@ static int test(int argc, char *argv[]) } } - test_base(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_stack) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "push")) + { + test_push(); + } + else if (!strcmp(execute, "info")) + { + test_info(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_stack) int main(int argc, char *argv[]) { diff --git a/test/test_str.c b/test/test_str.c index 60e2da1..e9553ba 100644 --- a/test/test_str.c +++ b/test/test_str.c @@ -3,40 +3,286 @@ #include #if defined(TEST_TARGET_str) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "str.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + str_t ss = str("Hello str!"); // 用数组字符串进行赋值构造 + str_t copy = str(ss); // 用str字符串进行拷贝构造 + _str(ss); // 删除str + _str(copy); // 删除str +} + +static void test_c_str(void) +{ + str_t ss = str("Hello str!"); // 用数组字符串进行赋值构造 + str_t copy = str(ss); // 用str字符串进行拷贝构造 + printf("ss: %s\r\n", _S(ss)); + printf("copy: %s\r\n", _S(copy)); + _str(ss); // 删除str + _str(copy); // 删除str +} + +static void test_assign(void) +{ + str_t ss = str(""); // 初始化为空字符串 + printf("ss: %s\r\n", _S(ss)); + str_assign(ss, "Hello str!"); // 赋值为 Hello str! + printf("ss: %s\r\n", _S(ss)); + str_assign(ss, "This is the assignment method!"); // 再赋值为 This is the assignment method! + printf("ss: %s\r\n", _S(ss)); + _str(ss); +} + +static void test_append(void) +{ + str_t name = str("123456789"); + str_t ident = str("qq"); + str_t email = str(""); + + str_append(email, name, "@", ident, ".com"); // 拼接若干个字符串 + printf("%s\r\n", str_c_str(email)); + + _str(name); + _str(ident); + _str(email); +} + +static void test_insert(void) +{ + str_t ss = str("0123456"); + str_insert(ss, 3, "|insert|"); // 在位置3插入字符串 + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); +} + +static void test_erase(void) +{ + str_t ss = str("0123456789"); + str_erase(ss, 3, 3); // 移除位置3后面三个字符,也就是移除 345 + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); +} + +static void test_op_end(void) +{ + str_t ss = str("0123456789"); + str_push_back(ss, 'A'); + printf("ss: %s\r\n", str_c_str(ss)); + printf("pop %c\r\n", str_pop_back(ss)); + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); +} + +static void test_compare(void) +{ + str_t ss = str("01234"); + str_t copy = str(ss); + printf("compare: %d\r\n", str_compare(ss, copy)); + printf("compare: %d\r\n", str_compare(ss, "01233")); + printf("compare: %d\r\n", str_compare(ss, "012345")); + _str(ss); + _str(copy); +} + +static void test_substr(void) +{ + str_t ss = str("0123456789"); + str_t sub = str_substr(ss, 4, 2); + printf("ss: %s\r\n", str_c_str(ss)); + printf("sub: %s\r\n", str_c_str(sub)); + _str(ss); + _str(sub); +} + +static void test_at(void) +{ + str_t ss = str("0123456789"); + printf("ss[3] = %c\r\n", str_at(ss, 3)); + str_at(ss, 3) = 'A'; + printf("ss: %s\r\n", _S(ss)); + _str(ss); +} + +static void test_find(void) +{ + str_t ss = str("0123456789"); + printf("find = %d\r\n", str_find(ss, "3456", 0)); // 从0位置开始找 + printf("rfind = %d\r\n", str_rfind(ss, "23", str_length(ss) - 1)); // 从尾端开始往回找 + printf("find = %d\r\n", str_find(ss, "0000", 0)); // 找一个不存在的字符串 + _str(ss); +} + +static void test_find_of(void) +{ + str_t ss = str("C:/workspace/project/C/00 - vlib/Project/main.c"); // 先给定一个文件路径 + str_t pan = NULL; // 初始化为NULL,不构建 + str_t filename = NULL; // 初始化为NULL,不构建 + pan = str_substr(ss, 0, str_find_first_of(ss, ":", 0)); // 先找到第一个 : + filename = str_substr(ss, str_find_last_of(ss, "\\/", str_length(ss)-1) + 1, str_length(ss)); // 找到最后一个 \ 或者 / 路径分隔符 + printf("pan: %s\r\n", str_c_str(pan)); + printf("filename: %s\r\n", str_c_str(filename)); + _str(ss); + _str(pan); + _str(filename); +} + +static void test_reverse(void) +{ + str_t ss = str("0123456789"); + str_reverse(ss, 2, 8); + printf("ss = %s\r\n", str_c_str(ss)); + _str(ss); +} + +static void test_replace(void) +{ + str_t ss = str("My name is ZhangSan!"); + printf("ss = %s\r\n", str_c_str(ss)); + str_replace(ss, 11, 8, "Lisi"); // 把ZhangSan替换成Lisi + printf("ss = %s\r\n", str_c_str(ss)); + _str(ss); +} + +static void test_copy(void) +{ + str_t ss = str("0123456789"); + char array[32] = { 0 }; + int length = 0; + length = str_copy(ss, 5, sizeof(array), array); // 从位置开始复制,复制最大长度为数组的长度 + array[length] = '\0'; // 在后面添加结束符,方便打印 + printf("length = %d, copy: %s\r\n", length, array); + _str(ss); +} + +static void test_format(void) +{ + str_t ss = str(""); + str_t pp = str("format"); + str_format(ss, "Hello str! %s %s! int:%d, float:%.2f, char:%c", pp, "function", 18, 175.5, 'A'); + printf("ss: %s\r\n", str_c_str(ss)); + _str(ss); + _str(pp); +} + +static void test_base(void) +{ + str_t s = str("0123456789"); + str_replace(s, 9, 10, "AAAAAAAAAA"); + printf("len %d, %d\r\n", str_length(s), str_capacity(s)); + printf("%s\r\n", _S(s)); + _str(s); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: str [opt] [arg]\n" +"Usage: str [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test get c string function\n" +" Test insert function\n" +" Test erase function\n" +" Test assign function\n" +" Test append function\n" +" Test operate end (push, pop) function\n" +" Test compare function\n" +" Test create sub-string function\n" +" Test get data function\n" +" Test find function\n" +" Test find-of function\n" +" Test reverse function\n" +" Test replace function\n" +" Test copy function\n" +" Test format function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("str version %d.%d.%d\r\n", STR_V_MAJOR, STR_V_MINOR, STR_V_PATCH); return 0; @@ -50,15 +296,102 @@ static int test(int argc, char *argv[]) } } - str_t s = str("0123456789"); - str_replace(s, 9, 10, "AAAAAAAAAA"); - printf("len %d, %d\r\n", str_length(s), str_capacity(s)); - printf("%s\r\n", _S(s)); - _str(s); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_str) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "cstr")) + { + test_c_str(); + } + else if (!strcmp(execute, "assign")) + { + test_assign(); + } + else if (!strcmp(execute, "append")) + { + test_append(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "erase")) + { + test_erase(); + } + else if (!strcmp(execute, "opend")) + { + test_op_end(); + } + else if (!strcmp(execute, "compare")) + { + test_compare(); + } + else if (!strcmp(execute, "substr")) + { + test_substr(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + else if (!strcmp(execute, "find")) + { + test_find(); + } + else if (!strcmp(execute, "findof")) + { + test_find_of(); + } + else if (!strcmp(execute, "reverse")) + { + test_reverse(); + } + else if (!strcmp(execute, "replace")) + { + test_replace(); + } + else if (!strcmp(execute, "copy")) + { + test_copy(); + } + else if (!strcmp(execute, "format")) + { + test_format(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_str) int main(int argc, char *argv[]) { diff --git a/test/test_tool.c b/test/test_tool.c index f267ea2..106bff4 100644 --- a/test/test_tool.c +++ b/test/test_tool.c @@ -2,13 +2,59 @@ #include #if defined(TEST_TARGET_tool) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "tool.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_GetStringHex(void) { char out[12]; @@ -54,33 +100,55 @@ static void test_print(void) printString(s); } +static void test_base(void) +{ + // test_GetStringHex(); + // test_ASSERT(); + test_print(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: tool [opt] [arg]\n" +"Usage: tool [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("tool version %d.%d.%d\r\n", TOOL_V_MAJOR, TOOL_V_MINOR, TOOL_V_PATCH); return 0; @@ -94,13 +162,38 @@ static int test(int argc, char *argv[]) } } - // test_GetStringHex(); - // test_ASSERT(); - test_print(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_tool) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_tool) int main(int argc, char *argv[]) { diff --git a/test/test_tree.c b/test/test_tree.c index 563531b..4fcfd05 100644 --- a/test/test_tree.c +++ b/test/test_tree.c @@ -3,18 +3,69 @@ #include #if defined(TEST_TARGET_tree) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "tree.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void print_int(tree_t tree) { if (tree_data(tree)) printf("%d", *(int *)tree_data(tree)); } +static void print_string(tree_t tree) +{ + if (tree_data(tree)) printf("%s", (char *)tree_data(tree)); +} + static void print_dir(tree_t tree) { if (tree_data(tree)) printf("%s", (char *)tree_data(tree)); @@ -137,7 +188,188 @@ static void test_dir(void) tree_delete(tree, NULL); } -static void test_create(void) +static void test_create(void) +{ + tree_t tree = tree_create(); + + if (tree) + { + printf("tree create success!!!\r\n"); + } + else + { + printf("[ERROR] tree create fail!!!\r\n"); + } + + tree_delete(tree, NULL); +} + +static void test_set(void) +{ + tree_t tree = tree_create(); + int set = 100; + int get = 0; + + tree_set_data(tree, &set, sizeof(set)); + + tree_get_data(tree, &get, sizeof(get)); + + printf("get %d\r\n", get); + printf("data %d\r\n", *(int *)tree_data(tree)); + + tree_delete(tree, NULL); +} + +static void test_insert(void) +{ + tree_t tree = tree_create(); + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + printf("tree child size %d\r\n", tree_csize(tree)); + + tree_erase(tree, 2); + + printf("tree child size %d\r\n", tree_csize(tree)); + + tree_delete(tree, NULL); +} + +static void test_attach(void) +{ + tree_t tree = tree_create(); + tree_t node = NULL; + int data = 0; + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + for (int i = 0; i < tree_csize(tree); i++) + { + node = tree_create(); + data = 1000 + i; + tree_attach(tree, i, node); + tree_set_data(node, &data, sizeof(data)); + } + + tree_print(tree, 0, print_int); + + node = tree_detach(tree, 1); + tree_delete(node, NULL); + + tree_print(tree, 0, print_int); + + tree_delete(tree, NULL); +} + +static void test_parent(void) +{ + tree_t tree = tree_create(); + tree_t node = NULL; + tree_t parent, child; + + tree_insert(tree, 0); + tree_insert(tree, 1); + tree_insert(tree, 2); + tree_insert(tree, 3); + + tree_set_data(tree, "Parent", 7); + + node = tree_create(); tree_set_data(node, "Child0", 7); tree_attach(tree, 0, node); + node = tree_create(); tree_set_data(node, "Child1", 7); tree_attach(tree, 1, node); + node = tree_create(); tree_set_data(node, "Child2", 7); tree_attach(tree, 2, node); + node = tree_create(); tree_set_data(node, "Child3", 7); tree_attach(tree, 3, node); + + tree_print(tree, 0, print_string); + + parent = tree_parent(node); print_string(parent); printf("\r\n"); + + child = tree_child(tree, 0); print_string(child); printf("\r\n"); + child = tree_child(tree, 1); print_string(child); printf("\r\n"); + child = tree_child(tree, 2); print_string(child); printf("\r\n"); + child = tree_child(tree, 3); print_string(child); printf("\r\n"); + + tree_delete(tree, NULL); +} + +static void test_attr(void) +{ + tree_t tree = tree_create(); + char attr[20]; + + tree_set_attribute(tree, "This attribute!!!", 18); + tree_get_attribute(tree, attr, sizeof(attr)); + + printf("attr %s\r\n", attr); + + tree_delete(tree, NULL); +} + +static void test_depth(void) +{ + tree_t root = tree_create(); + tree_t node, temp; + + tree_insert(root, 0); + tree_insert(root, 1); + tree_insert(root, 2); + + tree_attach(root, 0, tree_create()); + tree_attach(root, 1, tree_create()); + + node = tree_child(root, 0); + tree_insert(node, 0); + tree_insert(node, 1); + + node = tree_child(root, 1); + tree_insert(node, 0); + tree_insert(node, 1); + tree_attach(node, 0, tree_create()); + tree_attach(node, 1, tree_create()); + + tree_print(root, 0, NULL); + + printf("depth %d\r\n", tree_depth(root)); + + tree_delete(root, NULL); +} + +static void test_to(void) +{ + tree_t root = tree_create(); + tree_t node, temp; + + tree_insert(root, 0); + tree_insert(root, 1); + tree_insert(root, 2); + + tree_attach(root, 0, tree_create()); + tree_attach(root, 1, tree_create()); + + node = tree_child(root, 0); + tree_insert(node, 0); + tree_insert(node, 1); + + node = tree_child(root, 1); + tree_insert(node, 0); + tree_insert(node, 1); + tree_attach(node, 0, tree_create()); + tree_attach(node, 1, tree_create()); + + temp = tree_to(root, 1, 1); + tree_set_data(temp, "To function!!!", 15); + + tree_print(root, 0, print_string); + + tree_delete(root, NULL); +} + +static void test_common(void) { tree_t root, n, t; int i, data; @@ -201,33 +433,64 @@ static void test_bitree(void) tree_delete(tree, NULL); } +static void test_base(void) +{ + // test_common(); + test_dir(); + // test_ini(); + // test_bitree(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: tree [opt] [arg]\n" +"Usage: tree [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test set and get data function\n" +" Test insert and erase child function\n" +" Test attach and dettach child function\n" +" Test get parent and child node function\n" +" Test set and get attrubute function\n" +" Test get tree depth function\n" +" Test locate to specify node function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("tree version %d.%d.%d\r\n", TREE_V_MAJOR, TREE_V_MINOR, TREE_V_PATCH); return 0; @@ -241,14 +504,70 @@ static int test(int argc, char *argv[]) } } - // test_create(); - test_dir(); - // test_ini(); - // test_bitree(); + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_tree) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "attach")) + { + test_attach(); + } + else if (!strcmp(execute, "parent")) + { + test_parent(); + } + else if (!strcmp(execute, "attr")) + { + test_attr(); + } + else if (!strcmp(execute, "depth")) + { + test_depth(); + } + else if (!strcmp(execute, "to")) + { + test_to(); + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_tree) int main(int argc, char *argv[]) { diff --git a/test/test_txls.c b/test/test_txls.c index 9e7dccc..ea38224 100644 --- a/test/test_txls.c +++ b/test/test_txls.c @@ -2,17 +2,63 @@ #include #if defined(TEST_TARGET_txls) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "txls.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define READ_FILE "test/file/read.md" #define WRITE_FILE "test/file/write.md" -void txls_preview(txls_t txls) +static void txls_preview(txls_t txls) { char* s; int len = 0; @@ -24,7 +70,166 @@ void txls_preview(txls_t txls) free(s); } -static void test_read(void) +static void test_create(void) +{ + txls_t txls = NULL; + txls = txls_create(3, 5); + if (txls) + { + printf("txls_create success!!! %p\r\n", txls); + } + txls_delete(txls); +} + +static void test_ranks(void) +{ + txls_t txls = NULL; + + txls = txls_create(3, 5); + + printf("txls_row %d\r\n", txls_row(txls)); + printf("txls_col %d\r\n", txls_col(txls)); + + txls_delete(txls); +} + +static void test_set(void) +{ + txls_t txls = NULL; + unsigned int row = 0, col = 0; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + txls = txls_create(3, 5); + + row = txls_row(txls); + col = txls_col(txls); + printf("row %u\r\n", row); + printf("col %u\r\n", col); + + for (int c = 1; c <= col; c++) + { + txls_set_head(txls, c, array[0][c]); + } + for (int r = 1; r <= row; r++) + { + for (int c = 1; c <= col; c++) + { + txls_set_text(txls, r - 1, c, array[r - 1][c - 1]); + } + } + + txls_preview(txls); + + printf("txls[1][2] %s\r\n", txls_get_text(txls, 1, 2)); + + txls_delete(txls); +} + +static void test_op_ranks(void) +{ + txls_t txls = NULL; + unsigned int row = 0, col = 0; + char *text = NULL; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + for (int i = 0; i < 5; i++) + { + printf("-------------------------------------\r\n"); + txls = txls_create(3, 5); + row = txls_row(txls); + col = txls_col(txls); + for (int c = 1; c <= col; c++) + { + txls_set_head(txls, c, array[0][c]); + } + for (int r = 1; r <= row; r++) + { + for (int c = 1; c <= col; c++) + { + txls_set_text(txls, r - 1, c, array[r - 1][c - 1]); + } + } + + switch (i) + { + case 0: + { + printf("Before OP:\r\n"); + } break; + case 1: + { + printf("txls_insert_row:\r\n"); + txls_insert_row(txls, 2); + } break; + case 2: + { + printf("txls_insert_col:\r\n"); + txls_insert_col(txls, 2); + } break; + case 3: + { + printf("txls_delete_row:\r\n"); + txls_delete_row(txls, 2); + } break; + case 4: + { + printf("txls_delete_col:\r\n"); + txls_delete_col(txls, 2); + } break; + default: + break; + } + + txls_preview(txls); + txls_delete(txls); + } +} + +static void test_align(void) +{ + txls_t txls = NULL; + unsigned int row = 0, col = 0; + const char *array[3][5] = { + {"ID", "Name", "Gender", "Age", "Height"}, + {"20240107001", "ZhangSan", "Man", "18", "178"}, + {"20240107002", "LiSi", "Woman", "24", "162"}, + }; + + txls = txls_create(3, 5); + + row = txls_row(txls); + col = txls_col(txls); + + for (int c = 1; c <= col; c++) + { + txls_set_head(txls, c, array[0][c]); + } + for (int r = 1; r <= row; r++) + { + for (int c = 1; c <= col; c++) + { + txls_set_text(txls, r - 1, c, array[r - 1][c - 1]); + } + } + + txls_set_align(txls, 1, TXLS_ALIGN_UNKNOW); + txls_set_align(txls, 2, TXLS_ALIGN_LEFT); + txls_set_align(txls, 3, TXLS_ALIGN_RIGHT); + txls_set_align(txls, 4, TXLS_ALIGN_CENTER); + txls_preview(txls); + + txls_delete(txls); +} + +static void test_load(void) { txls_t x = NULL; // Define the txls object, initialized to NULL as usual @@ -65,7 +270,7 @@ static void test_read(void) txls_delete(x); } -static void test_write(void) +static void test_dump(void) { txls_t x = NULL; // Define the txls object, initialized to NULL as usual @@ -117,33 +322,61 @@ static void test_write(void) txls_delete(x); } +static void test_base(void) +{ + test_load(); + // test_dump(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: txls [opt] [arg]\n" +"Usage: txls [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test get ranks infomation functions\n" +" Test set and get functions\n" +" Test operate rows and cols functions\n" +" Test set align functions\n" +" Test dump functions\n" +" Test load functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("txls version %d.%d.%d\r\n", TXLS_V_MAJOR, TXLS_V_MINOR, TXLS_V_PATCH); return 0; @@ -157,21 +390,65 @@ static int test(int argc, char *argv[]) } } - for (int index = command_optind; index < argc; index++) + if (execute) { - if (!strcmp(argv[index], "read")) + if (!strcmp(execute, "base")) { - test_read(); + test_base(); } - else if (!strcmp(argv[index], "write")) + else if (!strcmp(execute, "ut")) { - test_write(); + #if defined(TEST_TARGET_txls) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "ranks")) + { + test_ranks(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "opranks")) + { + test_op_ranks(); + } + else if (!strcmp(execute, "align")) + { + test_align(); + } + else if (!strcmp(execute, "dump")) + { + test_dump(); + } + else if (!strcmp(execute, "load")) + { + test_load(); + } + } + else + { + test_base(); } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_txls) int main(int argc, char *argv[]) { diff --git a/test/test_unitt.c b/test/test_unitt.c index a2b9ed7..aa3b4f7 100644 --- a/test/test_unitt.c +++ b/test/test_unitt.c @@ -5,13 +5,59 @@ #if defined(TEST_TARGET_unitt) #include #include +#include #else #include "init.h" #include "command.h" #include "unitt.h" #include "kern.h" +#include "unitt.h" +#include "kern.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #if 0 #ifdef _WIN32 #include @@ -47,14 +93,7 @@ void get_memory_usage(int* resident_set_size, int* virtual_memory_size) #define SINGLE_TCOUNT 1000 -uint64_t unitt_clock(void) -{ - struct timeval mstime; - uint64_t us = 0; - gettimeofday(&mstime, NULL); - us = mstime.tv_sec * 1000000 + mstime.tv_usec; - return us; -} +extern uint64_t unitt_clock(void); int unitt_add(int a, int b) { @@ -162,10 +201,31 @@ void unitt_main() UNITT_EXE(suites); } +int period = 1000; // ms + +static void test_base(void) +{ + srand((unsigned int)time(NULL)); + +#if defined(TEST_TARGET_unitt) + while (1) + { + unitt_main(); + usleep(1000 * period); + } +#else + printf("create task %d\r\n", task_create(period, unitt_main)); +#endif +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: unitt [opt] [arg]\n" +"Usage: unitt [opt] [arg] ...\n" "\n" "options:\n" " -p Test period, unit ms\n" @@ -177,14 +237,15 @@ static void usage(void) static int test(int argc, char *argv[]) { - int period = 1000; // ms + char *execute = NULL; + int ut_period = 1000; /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "p:hv"); + int opt = command_getopt(argc, argv, "e:hvu::p:"); if (opt == -1) break; switch (opt) @@ -192,6 +253,12 @@ static int test(int argc, char *argv[]) case 'p' : period = atoi(command_optarg); break; + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("unitt version %d.%d.%d\r\n", UNITT_V_MAJOR, UNITT_V_MINOR, UNITT_V_PATCH); return 0; @@ -204,27 +271,38 @@ static int test(int argc, char *argv[]) return 0; } } - if (1 == command_optind) // no opt - { - unitt_main(); - return 0; - } - srand((unsigned int)time(NULL)); - -#if defined(TEST_TARGET_unitt) - while (1) + if (execute) { - unitt_main(); - usleep(1000 * period); + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_unitt) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); } -#else - printf("create task %d\r\n", task_create(period, unitt_main)); -#endif return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_unitt) int main(int argc, char *argv[]) { diff --git a/test/test_valloc.c b/test/test_valloc.c index 672f56a..601eddd 100644 --- a/test/test_valloc.c +++ b/test/test_valloc.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_valloc) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "valloc.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + static void test_base(void) { void* p = NULL; @@ -26,14 +72,22 @@ static void test_base(void) printf("used = %d\r\n", v_check_used()); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: valloc [opt] [arg]\n" +"Usage: valloc [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" "argument:\n" " Test default read function\n" @@ -42,16 +96,25 @@ static void usage(void) static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("valloc version %d.%d.%d\r\n", VALLOC_V_MAJOR, VALLOC_V_MINOR, VALLOC_V_PATCH); return 0; @@ -76,6 +139,10 @@ static int test(int argc, char *argv[]) return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_valloc) int main(int argc, char *argv[]) { diff --git a/test/test_vector.c b/test/test_vector.c index d66967d..5bef020 100644 --- a/test/test_vector.c +++ b/test/test_vector.c @@ -2,40 +2,188 @@ #include #if defined(TEST_TARGET_vector) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "vector.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_create(void) +{ + vector_t vector = vector(int, 16); // 创建长度为16的int型vector + int array[16]; + + if (vector) + { + printf("vector create success!!!\r\n"); + } + else + { + printf("[ERROR] vector create fail!!!\r\n"); + } + + _vector(vector); +} + +static void test_at(void) +{ + vector_t vector = vector(int, 8);; // 定义并创建长度为8的int型vector + int i = 0; + + for (i = 0; i < 8; i++) + { + vector_at(vector, int, i) = i; // 使用at方法访问 + } + + for (i = 0; i < 8; i++) + { + printf("vector[%d] = %d\r\n", i, v2a(vector, int)[i]); // 使用下标访问 + } + + _vector(vector); // 用完即删除 +} + +static void test_size(void) +{ + vector_t vector = vector(int, 10); + printf("size=%d, capacity=%d\r\n", vector_size(vector), vector_capacity(vector)); + _vector(vector); +} + +static void test_resize(void) +{ + vector_t vector = vector(int, 10); + printf("size=%d\r\n", vector_size(vector)); + vector_resize(vector, 32); + printf("size=%d\r\n", vector_size(vector)); + vector_resize(vector, 14); + printf("size=%d\r\n", vector_size(vector)); + _vector(vector); +} + +static void test_insert(void) +{ + vector_t vector = vector(int, 0); // 创建0长度的vector容器 + int array[10] = {0,1,2,3,4,5,6,7,8,9}; + int i = 0; + + printf("insert:\r\n"); + vector_insert(vector, 0, array, 10); // 把array插入到vector当中,相当于用array给vector赋值 + for (i = 0; i < vector_size(vector); i++) + { + printf("vector[%d]=%d\r\n", i, v2a(vector, int)[i]); + } + + printf("erase:\r\n"); + vector_erase(vector, 5, 2); // 移除索引5后面的两个数据,也就是索引5和索引6 + for (i = 0; i < vector_size(vector); i++) + { + printf("vector[%d]=%d\r\n", i, v2a(vector, int)[i]); + } + + _vector(vector); +} + +static void test_base(void) +{ + // test_resize(); + test_at(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: vector [opt] [arg]\n" +"Usage: vector [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test get data function\n" +" Test get size and capacity function\n" +" Test resize function\n" +" Test insert and erase function\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("vector version %d.%d.%d\r\n", VECTOR_V_MAJOR, VECTOR_V_MINOR, VECTOR_V_PATCH); return 0; @@ -49,9 +197,58 @@ static int test(int argc, char *argv[]) } } + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_vector) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "at")) + { + test_at(); + } + else if (!strcmp(execute, "size")) + { + test_size(); + } + else if (!strcmp(execute, "resize")) + { + test_resize(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + } + else + { + test_base(); + } + return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_vector) int main(int argc, char *argv[]) { diff --git a/test/test_vlog.c b/test/test_vlog.c index 752ccbb..b883a96 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -3,13 +3,59 @@ #include #if defined(TEST_TARGET_vlog) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "vlog.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define DEFAULT_OFFLINEFILE "./test/file/log.txt" static void vlog_callback(char *buf, int len) @@ -46,12 +92,24 @@ static void test_offline(void) vlog_set_offline(VLOG_CHANNEL_0, NULL); } +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: vlog [opt] [arg]\n" +"Usage: vlog [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" ...\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" + " -c [] Select the output channel and select by bit, channel 0 by default\n" " 1: channel 0\n" " 2: channel 1\n" @@ -71,6 +129,8 @@ static void usage(void) static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; char *logfile = DEFAULT_OFFLINEFILE; vlogChnType chn = VLOG_CHANNEL_0; @@ -79,7 +139,7 @@ static int test(int argc, char *argv[]) while (1) { - int opt = command_getopt(argc, argv, "c::f::hv"); + int opt = command_getopt(argc, argv, "e:hvu::c:f::"); if (opt == -1) break; switch (opt) @@ -97,6 +157,12 @@ static int test(int argc, char *argv[]) logfile = DEFAULT_OFFLINEFILE; } break; + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("vlog version %d.%d.%d\r\n", VLOG_V_MAJOR, VLOG_V_MINOR, VLOG_V_PATCH); return 0; @@ -110,30 +176,48 @@ static int test(int argc, char *argv[]) } } - if (logfile) vlog_set_offline(chn, logfile); - vlog(chn, "Hello vlog!\r\n"); - if (logfile) vlog_set_offline(chn, NULL); - - // vlog_set_func(vlog_callback); - for (int index = command_optind; index < argc; index++) + if (execute) { - if (!strcmp(argv[index], "base")) + if (!strcmp(execute, "base")) { test_base(); } - else if (!strcmp(argv[index], "channel")) + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_vlog) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + else if (!strcmp(execute, "channel")) { test_channel(); } - else if (!strcmp(argv[index], "offline")) + else if (!strcmp(execute, "offline")) { test_offline(); } } + else + { + if (logfile) vlog_set_offline(chn, logfile); + vlog(chn, "Hello vlog!\r\n"); + if (logfile) vlog_set_offline(chn, NULL); + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_vlog) int main(int argc, char *argv[]) { diff --git a/test/test_vstd.c b/test/test_vstd.c index e790b01..0016b6a 100644 --- a/test/test_vstd.c +++ b/test/test_vstd.c @@ -1,13 +1,60 @@ #if defined(TEST_TARGET_vstd) #include +#include #include #else #include "init.h" +#include "command.h" +#include "unitt.h" +#include "kern.h" #include "vstdlib.h" #include "vmem.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + extern int printf (const char *__format, ...); static int test(int argc, char *argv[]) @@ -32,10 +79,39 @@ static int test(int argc, char *argv[]) v_free(p); printf("used %d\r\n", vmem_used()); + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_vstd) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_vstd) int main(int argc, char *argv[]) { diff --git a/test/test_xml.c b/test/test_xml.c index 4e0511f..133a50e 100644 --- a/test/test_xml.c +++ b/test/test_xml.c @@ -2,18 +2,186 @@ #include #if defined(TEST_TARGET_xml) #include +#include #include #else #include "init.h" #include "command.h" +#include "unitt.h" +#include "kern.h" #include "xml.h" #endif +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + #define READ_FILE "test/file/read.xml" #define WRITE_FILE "test/file/write.xml" -static void test_read(void) +static void xml_preview(xml_t xml) +{ + char *text = NULL; + text = xml_dumps(xml, 0, 0, NULL); + if (text) + { + printf("%s\r\n", text); + free(text); + } + else + { + printf("[ERROR] dumps fail!!!\r\n"); + } +} + +static void test_create(void) +{ + xml_t xml = NULL; + xml = xml_create("root"); + if (xml) + { + printf("xml_create success!!! %p\r\n", xml); + } + xml_delete(xml); +} + +static void test_set(void) +{ + xml_t xml = NULL; + + /* create root node */ + xml = xml_create("root"); + + xml_set_text(xml, "This is xml!!!"); + + xml_preview(xml); + + printf("xml_get_text %s\r\n", xml_get_text(xml)); + + xml_delete(xml); +} + +static void test_attr(void) +{ + xml_t xml = NULL; + + /* create root node */ + xml = xml_create("root"); + + xml_add_attribute(xml, "Author", "Lamdonn"); + xml_add_attribute(xml, "Email", "Lamdonn@163.com"); + xml_add_attribute(xml, "License", "GPL-2"); + + xml_preview(xml); + + xml_remove_attribute(xml, "Email", 0); + + xml_preview(xml); + + printf("attr %s\r\n", xml_get_attribute(xml, "Author", 0)); + + xml_delete(xml); +} + +static void test_insert(void) +{ + xml_t xml = NULL; + xml_t node = NULL; + + /* create root node */ + xml = xml_create("root"); + + node = xml_create("Author"); + xml_set_text(node, "Lamdonn"); + xml_insert(xml, 0, node); + + node = xml_create("Email"); + xml_set_text(node, "Lamdonn@163.com"); + xml_insert(xml, 1, node); + + node = xml_create("License"); + xml_set_text(node, "GPL-2"); + xml_insert(xml, 2, node); + + xml_preview(xml); + + xml_remove(xml, "Email", 0); + + xml_delete(xml); +} + +static void test_to(void) +{ + xml_t xml = NULL; + xml_t node = NULL; + + /* create root node */ + xml = xml_create("root"); + + node = xml_create("node"); + xml_set_text(node, "Chinese"); + xml_insert(xml, 0, node); + + node = xml_create("node"); + xml_set_text(node, "English"); + xml_insert(xml, 1, node); + + node = xml_create("node"); + xml_set_text(node, "Franch"); + xml_insert(xml, 2, node); + + xml_preview(xml); + + node = xml_to(xml, "node", 0); + printf("xml_get_text %s\r\n", xml_get_text(node)); + + node = xml_to(xml, "node", 2); + printf("xml_get_text %s\r\n", xml_get_text(node)); + + xml_delete(xml); +} + +static void test_load(void) { xml_t root, x; @@ -31,7 +199,7 @@ static void test_read(void) xml_delete(root); } -static void test_write(void) +static void test_dump(void) { xml_t root, x; @@ -53,7 +221,7 @@ static void test_write(void) char *s = xml_dumps(root, 1, 0, NULL); if (s) { - printf(s); + printf("%s", s); free(s); } @@ -62,33 +230,61 @@ static void test_write(void) xml_delete(root); } +static void test_base(void) +{ + // test_dump(); + test_load(); +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + static void usage(void) { printf( -"Usage: xml [opt] [arg]\n" +"Usage: xml [opt] [arg] ...\n" "\n" "options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" Test create and delete functions\n" +" Test set and get value functions\n" +" Test set, get and remove attrubutes functions\n" +" Test insert and remove node functions\n" +" Test get child node functions\n" +" Test dump functions\n" +" Test load functions\n" " -h Print help\n" " -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" "\n" -"argument:\n" -" Test default read function\n" -" Test default write function\n" + ); } static int test(int argc, char *argv[]) { + char *execute = NULL; + int ut_period = 1000; + /* reset getopt */ command_opt_init(); while (1) { - int opt = command_getopt(argc, argv, "hv"); + int opt = command_getopt(argc, argv, "e:hvu::"); if (opt == -1) break; switch (opt) { + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; case 'v' : printf("xml version %d.%d.%d\r\n", XML_V_MAJOR, XML_V_MINOR, XML_V_PATCH); return 0; @@ -102,21 +298,66 @@ static int test(int argc, char *argv[]) } } - for (int index = command_optind; index < argc; index++) + if (execute) { - if (!strcmp(argv[index], "read")) + if (!strcmp(execute, "base")) { - test_read(); + test_base(); } - else if (!strcmp(argv[index], "write")) + else if (!strcmp(execute, "ut")) { - test_write(); + srand((unsigned int)time(NULL)); + #if defined(TEST_TARGET_json) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif } + else if (!strcmp(execute, "create")) + { + test_create(); + } + else if (!strcmp(execute, "set")) + { + test_set(); + } + else if (!strcmp(execute, "attr")) + { + test_attr(); + } + else if (!strcmp(execute, "insert")) + { + test_insert(); + } + else if (!strcmp(execute, "to")) + { + test_to(); + } + else if (!strcmp(execute, "dump")) + { + test_dump(); + } + else if (!strcmp(execute, "load")) + { + test_load(); + } + } + else + { + test_base(); } return 0; } +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + #if defined(TEST_TARGET_xml) int main(int argc, char *argv[]) { diff --git a/test/test_xxx.c.template b/test/test_xxx.c.template new file mode 100644 index 0000000..26152e7 --- /dev/null +++ b/test/test_xxx.c.template @@ -0,0 +1,182 @@ +#include +#include +#include +#if defined(TEST_TARGET_xxx) +#include +#include +#include +#else +#include "init.h" +#include "command.h" +#include "unitt.h" +#include "kern.h" +#include "xxx.h" +#endif + +/************************************************************************************/ +/************************************* Unit Test ************************************/ +/************************************************************************************/ + +// #define EXIT_TEST +extern uint64_t unitt_clock(void); + +static int test_0(void) +{ + for (int i = 0; i < 100; i++) + { + if (0) + { + + #if defined (EXIT_TEST) + exit(0); + #endif + return UNITT_E_FAIL; + } + } + + return UNITT_E_OK; +} + +static void unitt_task(void) +{ + static UNITT_TCASE rand_tests[] = { + UNITT_TCASE(test_0), + // UNITT_TCASE(test_1), + // UNITT_TCASE(test_2), + }; + + static UNITT suites[] = { + { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock }, + }; + + UNITT_EXE(suites); +} + +/************************************************************************************/ +/************************************* Base Test ************************************/ +/************************************************************************************/ + +static void test_base(void) +{ + +} + +/************************************************************************************/ +/************************************* Command ************************************/ +/************************************************************************************/ + +static void usage(void) +{ + printf( +"Usage: xxx [opt] [arg] ...\n" +"\n" +"options:\n" +" -e Specifies the function to execute, the default is the test\n" +" Test base function\n" +" Unit test\n" +" ...\n" +" -h Print help\n" +" -v Print version\n" +" -u [] Unit test period, unit ms, the default is 1000ms\n" +"\n" + ); +} + +static int test(int argc, char *argv[]) +{ + char *execute = NULL; + int ut_period = 1000; + + /* reset getopt */ + command_opt_init(); + + while (1) + { + int opt = command_getopt(argc, argv, "e:hvu::"); + if (opt == -1) break; + + switch (opt) + { + // Add others opt here + case 'u' : + if (command_optarg) ut_period = atoi(command_optarg); + break; + case 'e' : + execute = command_optarg; + break; + case 'v' : + printf("xxx version %d.%d.%d\r\n", XXX_V_MAJOR, XXX_V_MINOR, XXX_V_PATCH); + return 0; + case '?': + printf("Unknown option `%c`\r\n", command_optopt); + return -1; + case 'h' : + default: + usage(); + return 0; + } + } + + if (execute) + { + if (!strcmp(execute, "base")) + { + test_base(); + } + else if (!strcmp(execute, "ut")) + { + #if defined(TEST_TARGET_xxx) + while (1) + { + unitt_task(); + usleep(1000 * ut_period); + } + #else + printf("create task %d\r\n", task_create(ut_period, unitt_task)); + #endif + } + } + else + { + test_base(); + } + +#if 0 + if (1 == command_optind) // no opt + { + + } + + for (int index = command_optind; index < argc; index++) + { + if (!strcmp(argv[index], "base")) + { + test_base(); + } + } + + if (1 == argc) + { + test_base(); + } +#endif + + return 0; +} + +/************************************************************************************/ +/************************************ Test entry ************************************/ +/************************************************************************************/ + +#if defined(TEST_TARGET_xxx) +int main(int argc, char *argv[]) +{ + return test(argc, argv); +} +#else +void test_xxx(void) +{ + command_export("xxx", test); +} +init_export_app(test_xxx); +#endif