diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 45df5f5d5..8ec292238 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -240,5 +240,8 @@ menu "test app" bool "Config test soft timer" default n + menuconfig USER_TEST_HASH + bool "Config test hash table" + default n endif endmenu diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 1cf919846..e063cb03e 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -99,6 +99,10 @@ ifeq ($(CONFIG_ADD_XIZI_FETURES),y) ifeq ($(CONFIG_USER_TEST_TIMER),y) SRC_FILES += test_timer.c + endif + + ifeq ($(CONFIG_USER_TEST_HASH),y) + SRC_FILES += test_hash/test_hash.c endif include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Applications/app_test/test_hash/1.png b/APP_Framework/Applications/app_test/test_hash/1.png new file mode 100644 index 000000000..232f04ab8 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/1.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/2.png b/APP_Framework/Applications/app_test/test_hash/2.png new file mode 100644 index 000000000..629de8770 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/2.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/3.png b/APP_Framework/Applications/app_test/test_hash/3.png new file mode 100644 index 000000000..35c27a587 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/3.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/4.png b/APP_Framework/Applications/app_test/test_hash/4.png new file mode 100644 index 000000000..27baaf207 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/4.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/5.png b/APP_Framework/Applications/app_test/test_hash/5.png new file mode 100644 index 000000000..b47b04431 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/5.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/6.png b/APP_Framework/Applications/app_test/test_hash/6.png new file mode 100644 index 000000000..f8e4fb4d6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/6.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/README.md b/APP_Framework/Applications/app_test/test_hash/README.md new file mode 100644 index 000000000..7df7e27e5 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/README.md @@ -0,0 +1,74 @@ +# 哈希表 + +## 1. 简介 +散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。 + +## 2. 数据结构设计说明 +哈希表节点元素及哈希表设计如下: +``` +typedef struct kv +{ + struct kv* next; + char* key; + void* value; +}kv; + +/* HashTable */ +typedef struct HashTable +{ + struct kv ** table; +}HashTable; +``` +其中,一共实现了8个函数,分别为: +- `init_kv`:构造一个哈希表节点并初始化 +- `free_kv`:删除一个哈希表节点 +- `hash_33`:经典Times33哈希函数 +- `hash_table_delete`:销毁一个哈希表 +- `hash_table_new`:构造一个哈希表并初始化 +- `hash_table_put2`:根据key插入或更新value +- `hash_table_get`:根据key获取value +- `hash_table_rm`:根据key移除一个键值对 +## 3. 测试程序说明 +测试程序 `TestHash` 已经注册为 shell 命令,可以调用执行。 + +测试程序定义了以下键值对: +``` + 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.在工作区终端中输入命令,进入配置页面 +``` +cd /xiuos/Ubiquitous/XiZi_IIoT +make BOARD=cortex-m3-emulator menuconfig +``` +![fig1](1.png) +2.依次进入 `APP_Framework` -> `Applications` -> `test app` 目录,将 `Enable application test function` 选项置为 `Y` +![fig2](2.png) +3.进入 `Enable application test function` 将 `Config test hash table` 选项置为 `Y` +![fig3](3.png) +4.一直选择 `Exit` 退出配置,在最后需要确认的页面选择 `Yes` 保存配置 +5.执行编译命令:`make BOARD=cortex-m3-emulator`,正常情况下应当编译无误 +![fig4](4.png) +6.在 `qemu` 中运行:`qemu-system-arm -machine lm3s6965evb -nographic -kernel build/XiZi-cortex-m3-emulator.elf` +![fig5](5.png) +7.在 shell 中运行命令 `TestHash`,执行结果与预期一致,验证完成。 +![fig6](6.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.c b/APP_Framework/Applications/app_test/test_hash/test_hash.c new file mode 100644 index 000000000..aa3ad2091 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.c @@ -0,0 +1,292 @@ +/** +* @file: test_hash.c +* @brief: Implement a simple hash +* @version: 1.0 +* @date: 2023/5/29 +*/ +#include +#include "test_hash.h" + +#define TABLE_SIZE 1024 +#define MAX_WORD_LEN 128 + + +/** + * @description: element of the hash table's chain list + * free_value is the destructor of value + */ +typedef struct kv +{ + struct kv* next; + char* key; + void* value; + // void(*free_value)(void*); +}kv; + +/* HashTable */ +typedef struct HashTable +{ + struct kv ** table; +}HashTable; + +/** + * @description: constructor of struct kv + * @param kv - element of the hash table's chain list + * @return void + */ +void init_kv(struct kv* kv) +{ + kv->next = NULL; + kv->key = NULL; + kv->value = NULL; + // kv->free_value = NULL; +} + +/** + * @description: destructor of struct kv + * @param kv - element of the hash table's chain list + * @return void + */ +static void free_kv(struct kv* kv) +{ + if (kv) { + // if (kv->free_value) { + // kv->free_value(kv->value); + // } + free(kv->key); + // free(kv->value); + kv->key = NULL; + free(kv); + } +} + +/** + * @description: the classic Times33 hash function + * @param key - hash key + * @return unsigned int + */ +static unsigned int hash_33(char* key) +{ + unsigned int hash = 0; + while (*key) { + hash = (hash << 5) + hash + *key++; + } + return hash; +} + +/** + * @description: delete a HashTable instance + * @param ht - HashTable* + * @return void + */ +void hash_table_delete(HashTable* ht) +{ + if (ht) { + if (ht->table) { + int i = 0; + for (i = 0; itable[i]!=NULL; i++) { + struct kv* p = ht->table[i]; + struct kv* q = NULL; + while (p) { + q = p->next; + free_kv(p); + p = q; + } + } + free(ht->table); + ht->table = NULL; + } + free(ht); + } +} + + +/** + * @description: new a HashTable instance + * @return HashTable* + */ +HashTable* hash_table_new() +{ + HashTable* ht = malloc(sizeof(HashTable)); + if (NULL == ht) { + hash_table_delete(ht); + return NULL; + } + ht->table = malloc(sizeof(struct kv*) * TABLE_SIZE); + if (NULL == ht->table) { + hash_table_delete(ht); + return NULL; + } + memset(ht->table, 0, sizeof(struct kv*) * TABLE_SIZE); + + return ht; +} + + +/** + * @description: insert or update a value indexed by key + * @param ht - radix tree root + * @param key - new key + * @param value - new value + * @return int + */ +int hash_table_put2(HashTable* ht, char* key, void* value) +{ + int i = hash_33(key) % TABLE_SIZE; + struct kv* p = ht->table[i]; + struct kv* prep = p; + + while (p) { /* if key is already stroed, update its value */ + if (strcmp(p->key, key) == 0) { + // free(p->value); + p->value = value; + break; + } + prep = p; + p = p->next; + } + + if (p == NULL) {/* if key has not been stored, then add it */ + char* kstr = malloc(strlen(key) + 1); + if (kstr == NULL) { + return -1; + } + struct kv * kv = malloc(sizeof(struct kv)); + if (NULL == kv) { + free(kstr); + kstr = NULL; + return -1; + } + init_kv(kv); + kv->next = NULL; + strcpy(kstr, key); + kv->key = kstr; + kv->value = value; + + if (prep == NULL) { + ht->table[i] = kv; + } + else { + prep->next = kv; + } + } + return 0; +} + +/** + * @description: get a value indexed by key + * @param ht - radix tree root + * @param key - new key + * @return void* value + */ +void* hash_table_get(HashTable* ht, char* key) +{ + if(ht == NULL)return NULL; + int i = hash_33(key) % TABLE_SIZE; + struct kv* p = ht->table[i]; + while (p) { + if (strcmp(key, p->key) == 0) { + return p->value; + } + p = p->next; + } + return NULL; +} + +/** + * @description: remove a value indexed by key + * @param ht - radix tree root + * @param key - new key + * @return void + */ +void hash_table_rm(HashTable* ht, char* key) +{ + int i = hash_33(key) % TABLE_SIZE; + + struct kv* p = ht->table[i]; + struct kv* prep = p; + while (p) { + if (strcmp(key, p->key) == 0) { + free_kv(p); + if (p == prep) { + if(NULL==p->next) + ht->table[i] = NULL; + else + ht->table[i]=p->next; + } + else { + prep->next = p->next; + } + break; + } + prep = p; + p = p->next; + } +} + +void TestHash() +{ + char keys[][MAX_WORD_LEN] = { + "what", + "where", + "why", + "how", + "hello!", + "apple", + "12345" + }; + int values[] = {1, 2, 3, 4, 5, 6, 7}; + + printf("\nCreate hash table and add key & value:\n"); + HashTable* ht = hash_table_new(); + if (NULL == ht) { + printf("Create hash table failed.\n"); + return; + } + + // insert key&value into hash table + int num = sizeof(keys) / sizeof(keys[0]); + for (int i = 0; i < num - 1; ++i) { + hash_table_put2(ht, keys[i], &values[i]); + } + + //find value by key + for (int i = 0; i < num; ++i) { + int* v = (int*)hash_table_get(ht, 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"); + hash_table_rm(ht, keys[1]); + hash_table_rm(ht, keys[3]); + + for (int i = 0; i < num; ++i) { + int* v = (int*)hash_table_get(ht, 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"); + hash_table_put2(ht, keys[1], &values[1]); + hash_table_put2(ht, keys[6], &values[6]); + + for (int i = 0; i < num; ++i) { + int* v = (int*)hash_table_get(ht, 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 hash table:\n"); + hash_table_delete(ht); + ht = NULL; + + for (int i = 0; i < num; ++i) { + int* v = (int*)hash_table_get(ht, 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]); + } + return; +} + +PRIV_SHELL_CMD_FUNCTION(TestHash, Implement a simple hash, PRIV_SHELL_CMD_MAIN_ATTR); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.h b/APP_Framework/Applications/app_test/test_hash/test_hash.h new file mode 100644 index 000000000..91215541b --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.h @@ -0,0 +1,32 @@ +#pragma once +typedef struct HashTable HashTable; + +#ifdef __cplusplus +extern "C" { +#endif + + /* new an instance of HashTable */ + HashTable* hash_table_new(); + + /* + delete an instance of HashTable, + all values are removed auotmatically. + */ + void hash_table_delete(HashTable* ht); + + /* + add or update a value to ht, + free_value(if not NULL) is called automatically when the value is removed. + return 0 if success, -1 if error occurred. + */ + int hash_table_put2(HashTable* ht, char* key, void* value); + + /* get a value indexed by key, return NULL if not found. */ + void* hash_table_get(HashTable* ht, char* key); + + /* remove a value indexed by key */ + void hash_table_rm(HashTable* ht, char* key); + +#ifdef __cplusplus +} +#endif \ No newline at end of file