diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 45df5f5d5..cd2e3980b 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_HASH + bool "Config test hash" + default n + endif endmenu diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 1cf919846..2fb03e414 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -101,5 +101,9 @@ ifeq ($(CONFIG_ADD_XIZI_FETURES),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 endif 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..d7a9814ae --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/README.md @@ -0,0 +1,63 @@ +# 热身赛一级赛题1:基于cortex-m3-emulator实现哈希表并测试验证 + +## 1. 简介 +基于矽璓模拟器cortex-m3-emulator,实现哈希表,哈希函数此采用除留余数法,并采用开散列的方法,使用链表结构将所有相同位置的关键码串联起来,解决冲突问题,并编写测试程序在shell终端打印结果 +## 2. 数据结构设计说明 +//hash结点结构 +typedef struct hash_node +{ + void *key; //键 + void *value; //值 + struct hash_node *pre; //链表前驱 + struct hash_node *next; //链表后继 +} hash_node_t; + +// hash表结构 +typedef struct hash +{ + unsigned int buckets; //对应的桶的大小 + hashfunc_t hash_func; //保存的hash函数的地址 + hash_node_t **nodes; //二级指针,存放桶结点的首地址 +}hash_t; + +包括以下函数功能: +`hash_fun`: 哈希函数 +`hash_alloc`:哈希表初始化 +`hash_get_node_by_key`: 获取当前key值对应的桶结点的地址 +`hash_lookup_entry`: 获取key值对应的value值 +`hash_add_entry`: 向哈希表中添加数据 +`hash_free_entry`: 释放指定key值的结点 +`printhash`: 打印哈希表 +`testhash`: 测试函数 + + +## 3. 测试程序说明 +测试程序提供了对哈希表的基本操作的测试,具体步骤如下: +1. 创建哈希表 +2. 插入多组键值对数据 +3. 将哈希表中数据打印出来 +4. 查找指定键的值 +5. 删除指定键的结点 +6. 重新打印哈希表 + +在步骤二中,Tom和Bob都在bucket 0中,二者采用链地址头插法解决哈希冲突。 +## 4. 运行结果 +![](test_hash.png) + +## 附录 + +修改/新增的文件列表: +``` +APP_Framework/Applications/app_test/Kconfig +APP_Framework/Applications/app_test/Makefile +APP_Framework/Applications/app_test/test_hash/ +APP_Framework/Applications/app_test/test_hash/README.md +APP_Framework/Applications/app_test/test_hash/test_hash.c +APP_Framework/Applications/app_test/test_hash/test_hash.h +APP_Framework/Applications/app_test/test_hash/est_hash.png +``` + +赛题链接: https://www.gitlink.org.cn/xuos/xiuos/issues/261 + +提交指南: https://www.gitlink.org.cn/competitions/index/track1_2023_xiuos + 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..f3cda6ab8 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.c @@ -0,0 +1,212 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: test_hash.c +* @brief: a application of hash function +* @version: 2.0 +* @author: XiUOS Team: oscar1 +* @date: 2023/6/18 +*/ + +#include +#include +#include +#include +#include "test_hash.h" + + +#ifdef ADD_XIZI_FETURES +//hash_func是自己定义的,这里存放的是函数地址。 +unsigned int hash_func(unsigned int bucket_size, void* key) +{ + return (*(unsigned int*)key) % bucket_size; +} + + +//hash初始化,并对hash_nodes所指的内存空间进行内存的初始化,指针置为NULL。 +hash_t *hash_alloc(unsigned int buckets, hashfunc_t hash_func) +{ + hash_t *hash = (hash_t *)malloc(sizeof(hash_t)); + //assert(hash != NULL); + hash->buckets = buckets; + hash->hash_func = hash_func; + int size = buckets * sizeof(hash_node_t *); + hash->nodes = (hash_node_t **)malloc(size); + memset(hash->nodes, 0, size); + return hash; +} + +//获取关键码对应的桶结点的位置,通过hash函数映射之后计算出对应的bucket,将对应结点的地址返回。 +hash_node_t** hash_get_bucket(hash_t* hash, void* key) +{ + unsigned int bucket = hash->hash_func(hash->buckets, key); + if (bucket >= hash->buckets) + { + printf("bad bucket lookup\n"); + //exit(1); + } + return &(hash->nodes[bucket]); +} + + +//查询功能 +//获取当前key值对应的桶结点的地址。 +hash_node_t* hash_get_node_by_key(hash_t* hash, void* key, unsigned int key_size) +{ + hash_node_t** bucket = hash_get_bucket(hash, key); + hash_node_t* node = *bucket; + if (node == NULL) + { + return NULL; + } + while (node != NULL && memcmp(node->key, key, key_size) != 0) + { + node = node->next; + } + return node; +} + +//获取key值对应的value值。 +void* hash_lookup_entry(hash_t* hash, void* key, unsigned int key_size) +{ + hash_node_t* node = hash_get_node_by_key(hash, key, key_size); + if (node == NULL) + { + return NULL; + } + return node->value; +} + + +//添加结点 +void hash_add_entry(hash_t* hash, void* key, unsigned int key_size, void* value, unsigned int value_size) +{ + //判断表中是否已有key + if (hash_lookup_entry(hash, key, key_size)) + { + //printf( "duplicate hash key\n"); + return; + } + //创建结点并进行赋值 + hash_node_t* node = malloc(sizeof(hash_node_t)); + node->pre = NULL; + node->next = NULL; + node->key = malloc(key_size); + memcpy(node->key, key, key_size); + node->value = malloc(value_size); + memcpy(node->value, value, value_size); + //寻找结点插入位置 + hash_node_t** bucket = hash_get_bucket(hash, key); + if (*bucket == NULL) + { + *bucket = node; + } + else + { + // 将新结点插入到链表头部,当前结点的后继指向下一个结点。 + node->next = *bucket; + (*bucket)->pre = node; + *bucket = node; + } +} + + +//释放结点 +void hash_free_entry(hash_t* hash, void* key, unsigned int key_size) +{ + + //获取结点地址 + hash_node_t* node = hash_get_node_by_key(hash, key, key_size); + if (node == NULL) + { + return; + } + //释放键值空间 + free(node->key); + free(node->value); + //调整指针 + if (node->pre) + { + node->pre->next = node->next; + } + else + { + hash_node_t** bucket = hash_get_bucket(hash, key); + *bucket = node->next; + } + if (node->next) + node->next->pre = node->pre; + // 释放结点 + free(node); +} + + +//打印hash表 +void printhash(hash_t* hash1) +{ + hash_node_t** z = hash1->nodes; + int i = 0; + for (i = 0; i < hash1->buckets; ++i) + { + if (z[i] == NULL) + continue; + printf("bucket:%d\n",i); + hash_node_t* p=z[i]; + while (z[i]!= NULL) + { + printf("key:%d,value:%s\n", *(int*)(z[i]->key), (char*)(z[i]->value)); + z[i] = z[i]->next; + } + z[i]=p; + } +} + + +//测试函数 +static struct hash* count_hash; +void testhash() +{ + printf("create a hash, the buckets is 10\n"); + count_hash = hash_alloc(10, hash_func); + int key1=10; + char value1[]="Bob"; + int key2= 20; + char value2[]="Tom"; + int key3=33; + char value3[]="Cindy"; + int key4=31; + char value4[]="Jerry"; + int key5=42; + char value5[]="Ross"; + hash_add_entry(count_hash, &key1, sizeof(key1), &value1,sizeof(value1)); + hash_add_entry(count_hash, &key2, sizeof(key2), &value2, sizeof(value2)); + hash_add_entry(count_hash, &key3, sizeof(key3), &value3, sizeof(value3)); + hash_add_entry(count_hash, &key4, sizeof(key4), &value4, sizeof(value4)); + hash_add_entry(count_hash, &key5, sizeof(key5), &value5, sizeof(value5)); + printf("======================================\n"); + printhash(count_hash); + printf("======================================\n"); + void * query_value1=hash_lookup_entry(count_hash, &key1, sizeof(key1)); + printf("Query key:%d,the answer value is %s\n",key1, (char*)query_value1); + printf("======================================\n"); + printf("Delete the key: %d\n",key2); + hash_free_entry(count_hash, &key2, sizeof(key2)); + printf("======================================\n"); + printhash(count_hash); + printf("======================================\n"); +} +PRIV_SHELL_CMD_FUNCTION(testhash, a hash test sample, PRIV_SHELL_CMD_MAIN_ATTR); +#endif + + + 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..2f4287a99 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.h @@ -0,0 +1,21 @@ +//hash结点结构 +typedef struct hash_node +{ + void *key; //键 + void *value; //值 + struct hash_node *pre; //链表前驱 + struct hash_node *next; //链表后继 +} hash_node_t; + +//函数指针 对应的函数名是hashfunc_t。经过typedef之后,可以通过hashfunc_t来代表函数指针。 +typedef unsigned int (*hashfunc_t)(unsigned int, void*); + +// hash表结构 +typedef struct hash +{ + unsigned int buckets; //对应的桶的大小 + hashfunc_t hash_func; //保存的hash函数的地址 + hash_node_t **nodes; //二级指针,存放桶结点的首地址 +}hash_t; + + diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.png b/APP_Framework/Applications/app_test/test_hash/test_hash.png new file mode 100644 index 000000000..528fcfb35 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/test_hash.png differ