diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 3a103a6b3..2e0ccd7a7 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -240,5 +240,9 @@ menu "test app" bool "Config test soft timer" default n + menuconfig USER_TEST_RADIX + bool "Config test radix tree" + default n + endif endmenu diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 200e9e02a..12033cd4c 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -99,6 +99,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) ifeq ($(CONFIG_USER_TEST_TIMER),y) SRC_FILES += test_timer.c + endif + + ifeq ($(CONFIG_USER_TEST_RADIX),y) + SRC_FILES += test_radix_tree/test_radix_tree.c endif include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Applications/app_test/test_radix_tree/README.md b/APP_Framework/Applications/app_test/test_radix_tree/README.md new file mode 100644 index 000000000..b2bfe9cb9 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_radix_tree/README.md @@ -0,0 +1,67 @@ +# 基于k210-emulator实现基数树并测试验证 + +## 1. 简介 + +基于矽璓模拟器k210-emulator,实现基数树,并编写测试程序在shell终端打印结果。 + +## 2. 数据结构设计说明 + +基数树节点设计为: + +```c +typedef struct _node { +​ void* value; +​ struct _node* next[NODE_SIZE]; +} node; +``` + +其中,节点在树中的路径即为键,`value` 存储值,`NODE_SIZE` 定义为 128,足以容纳所有 ASCII 值。 + +一共实现了 5 个函数,分别为: + +- `CreateNode`:创建一个基数树节点 +- `InsertNode`:将一对键值对插入基数树 +- `DeleteNode`:删除指定键的键值对 +- `FindNode`:查找指定键对应的值 +- `DestroyTree`:销毁整个基数树 + +## 3. 测试程序说明 + +测试程序 `TestRadix` 已经注册为 shell 命令,可以调用执行。 + +测试程序定义了以下键值对: + +```c +char keys[][MAX_WORD_LEN] = { + "what", + "where", + "why", + "how", + "hello!", + "apple", + "12345" +}; +int values[] = {1, 2, 3, 4, 5, 6, 7}; +``` + +1. 程序的第一部分创建了基数树,并且将定义的 7 个键值对的前 6 个插入了基数树,然后分别查找 7 个键,前 6 个均可以找到对应的值,最后一个未插入,因此无法找到 +2. 程序的第二部分从基数树中删除了 `where` 和 `how` 两个键,再次分别查找 7 个键,删除的键值对和未插入的键值对均无法找到 +3. 程序的第三部分重新插入了已删除的 `where` 和未插入过的 `12345` ,再次分别查找 7 个键,新插入的值可以检索到 +4. 程序的第四部分将基数树销毁,再次分别查找 7 个键,所有的键值对均无法找到 + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) + +1. 在工作区终端中输入命令:`make BOARD=k210-emulator menuconfig`,进入配置页面 +![fig1](fig1.png) +2. 依次进入 `APP_Framework` -> `Applications` -> `test app` 目录,将 `Enable application test function` 选项置为 `Y` +![fig2](fig2.png) +3. 进入 `Enable application test function` 将 `Config test radix tree` 选项置为 `Y` +![fig3](fig3.png) +4. 一直选择 `Exit` 退出配置,在最后需要确认的页面选择 `Yes` 保存配置 +![fig4](fig4.png) +5. 执行编译命令:`make BOARD=k210-emulator`,正常情况下应当编译无误 +![fig5](fig5.png) +6. 在 `qemu` 中运行:`qemu-system-riscv64 -nographic -machine sifive_u -bios build/XiZi-k210-emulator.elf` +![fig6](fig6.png) +7. 在 shell 中运行命令 `TestRadix`,执行结果与预期一致,验证完成。 +![fig7](fig7.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig1.png b/APP_Framework/Applications/app_test/test_radix_tree/fig1.png new file mode 100644 index 000000000..ce51222cb Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig1.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig2.png b/APP_Framework/Applications/app_test/test_radix_tree/fig2.png new file mode 100644 index 000000000..b35f3ecb6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig2.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig3.png b/APP_Framework/Applications/app_test/test_radix_tree/fig3.png new file mode 100644 index 000000000..5d5fa2276 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig3.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig4.png b/APP_Framework/Applications/app_test/test_radix_tree/fig4.png new file mode 100644 index 000000000..3df214d59 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig4.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig5.png b/APP_Framework/Applications/app_test/test_radix_tree/fig5.png new file mode 100644 index 000000000..99cd9ec2e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig5.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig6.png b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png new file mode 100644 index 000000000..80bf28136 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig7.png b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png new file mode 100644 index 000000000..7fad83c9e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c new file mode 100644 index 000000000..c54a49805 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c @@ -0,0 +1,187 @@ +/** +* @file: test_radix_tree.c +* @brief: Implement a simple radix tree +* @version: 1.0 +* @date: 2023/5/24 +*/ + +#include +#include "test_radix_tree.h" + +/** + * @description: Create a radix tree node + * @return node pointer + */ +node* CreateNode() +{ + node* n = (node*)malloc(sizeof(node)); + n->value = NULL; + for (int i = 0; i < NODE_SIZE; i++) { + n->next[i] = NULL; + } + return n; +} + +/** + * @description: Insert a new node to radix tree + * @param root - radix tree root + * @param key - new node key + * @param value - new node value + * @return void + */ +void InsertNode(node* root, const char* key, void* value) +{ + if (root == NULL) { + return; + } + node* cur = root; + size_t len = strlen(key); + for (size_t i = 0; i < len; i++) { + uint8_t b = (uint8_t)key[i]; + if (cur->next[b] == NULL) { + cur->next[b] = CreateNode(); + } + cur = cur->next[b]; + } + cur->value = value; +} + +/** + * @description: Delete a node from radix tree + * @param root - radix tree root + * @param key - key which is needed to delete + * @return void + */ +void DeleteNode(node* root, const char* key) +{ + if (root == NULL) { + return; + } + node** cur = &root; + size_t len = strlen(key); + for (size_t i = 0; i < len; i++) { + uint8_t b = (uint8_t)key[i]; + if ((*cur)->next[b] == NULL) { + return; + } + cur = &((*cur)->next[b]); + } + + if ((*cur)->value == NULL) { + return; + } + + (*cur)->value = NULL; + + int has_children = 0; + for (int i = 0; i < NODE_SIZE; i++) { + if ((*cur)->next[i] != NULL) { + has_children = 1; + break; + } + } + if (!has_children) { + free(*cur); + (*cur) = NULL; + } +} + +/** + * @description: find a node by key + * @param root - radix tree root + * @param key - key which is needed to find + * @return value pointer corresponding to key + */ +void* FindNode(node* root, const char* key) +{ + if (root == NULL) { + return NULL; + } + node* cur = root; + size_t len = strlen(key); + for (size_t i = 0; i < len; i++) { + uint8_t b = (uint8_t)key[i]; + if (cur->next[b] == NULL) { + return NULL; + } + cur = cur->next[b]; + } + return cur->value; +} + +/** + * @description: Destroy the radix tree + * @param root - radix tree root + * @return void + */ +void DestroyTree(node* root) +{ + if (root == NULL) { + return; + } + for (int i = 0; i < NODE_SIZE; i++) { + DestroyTree(root->next[i]); + } + free(root); +} + +void TestRadix() +{ + char keys[][MAX_WORD_LEN] = { + "what", + "where", + "why", + "how", + "hello!", + "apple", + "12345" + }; + int values[] = {1, 2, 3, 4, 5, 6, 7}; + + printf("\nCreate tree and add key & value:\n"); + node* root = CreateNode(); + if (!root) printf("Create node failed.\n"); + + int num = sizeof(keys) / sizeof(keys[0]); + for (int i = 0; i < num - 1; ++i) { + InsertNode(root, keys[i], &values[i]); + } + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } + + printf("\nDelete \"where\" and \"how\":\n"); + DeleteNode(root, keys[1]); + DeleteNode(root, keys[3]); + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } + + printf("\nInsert \"where\" and \"12345\":\n"); + InsertNode(root, keys[1], &values[1]); + InsertNode(root, keys[6], &values[6]); + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } + + printf("\nDestroy tree:\n"); + DestroyTree(root); + root = NULL; + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } +} + +PRIV_SHELL_CMD_FUNCTION(TestRadix, Implement a simple radix tree, PRIV_SHELL_CMD_MAIN_ATTR); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h new file mode 100644 index 000000000..ea9e7b7fa --- /dev/null +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h @@ -0,0 +1,20 @@ +/** +* @file: test_radix_tree.h +* @brief: Implement a simple radix tree +* @version: 1.0 +* @date: 2023/5/24 +*/ + +#define NODE_SIZE 128 +#define MAX_WORD_LEN 128 + +typedef struct _node { + void* value; + struct _node* next[NODE_SIZE]; +} node; + +node* CreateNode(); +void InsertNode(node* root, const char* key, void* value); +void DeleteNode(node* root, const char* key); +void* FindNode(node* root, const char* key); +void DestroyTree(node* root); \ No newline at end of file