diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 45df5f5d5..d60f20208 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -1,9 +1,13 @@ menu "test app" menuconfig USER_TEST bool "Enable application test function " - default n + default y if USER_TEST + menuconfig USER_HASH_TABLE + bool "Config test hash table" + default y + menuconfig USER_TEST_ADC bool "Config test adc" default n diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 1cf919846..90bcad1b2 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -25,6 +25,10 @@ endif ifeq ($(CONFIG_ADD_XIZI_FETURES),y) SRC_FILES := test_shell.c + ifeq ($(CONFIG_USER_HASH_TABLE),y) + SRC_FILES += test_hash/test_hash.c + endif + ifeq ($(CONFIG_USER_TEST_ADC),y) SRC_FILES += test_adc.c 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..805fdd122 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/README.md @@ -0,0 +1,108 @@ + +# 热身赛一级赛题1:基于cortex-m3-emulator实现哈希表并测试验证 + +[toc] + +参赛战队:bdislab_0 +赛题issue:热身赛一级赛题1 +完成情况:实现哈希表,支持链地址法解决哈希冲突。 + +## 1. 简介:注册shell命令以及哈希命令集说明 +**0. 注册TestHashTable命令** +源文件见:APP_Framework/Applications/app_test/test_hash/test_hash.c + +**1. 插入(key, value)对** +``` +TestHashTable 1 key value +``` +说明: +- 当插入的key不存在时,返回 success。 +- 当插入的key已经存在时,新值value会覆盖旧值。 +- 注意:key支持`int`或`string`等若干类型数值,但value仅支持`int`范围内的数值。 + + +**2. 查询key对应的(key, value)对** +``` +TestHashTable 2 key value +``` +说明: +- 当查询的key存在时,输出对应的(key, value)对。 +- 当查询的key不存在时,返回 not exist。 + + +**3. 删除key对应的(key, value)对** +``` +TestHashTable 3 key value +``` +说明: +- 当删除的key存在时,删除对应的(key, value)对。 +- 当删除的key不存在时,返回 not exist。 + +**4. 执行帮助** +``` +TestHashTable help +``` +输出指令集帮助。 + +## 2. 数据结构设计说明 + +首先,设计了一个结构体 `struct Hash_entry` 表示每一项哈希结构,字段如下:包含原始的key变量(字符串类型的okey),哈希后的key变量(int类型的key),和value变量。 +``` +struct Hash_entry { + char *okey; + int key; + int val; + struct Hash_entry *next; + struct Hash_entry *prev; +}; +``` + +第二,哈希的模数为MOD。通过拉链法解决哈希冲突,所以定义了MOD个哈希链表的头,即`struct Hash_entry *hts[MOD]`。并定义了一个数组记录每种哈希值对应链表的长度,即`int htlen[MOD]`。 + +第三,获取哈希值的函数:`int get_hash_val(char *okey)`。 + +第四,插入:`int insert_kv(char *okey, char *val)`。 + +第五,查询:`int query_key(char *okey)`。 + +第六,删除:`int delete_key(char *okey)`。 + +## 3. 测试程序说明 + +测试结果,打印验证功能:均正确。 + +## 4. 运行结果:测试流程及截图 + +注意:key支持`int`或`string`等若干类型数值,但value仅支持`int`范围内的数值。 + +测试流程: +1. 首先查看哈希表指令集执行帮助:`TestHashTable help` +2. 插入(1, 1)对:`TestHashTable 1 1 1`,插入成功返回success +3. 重复插入(1, 2)对:`TestHashTable 1 1 2`,新值2覆盖旧值1 +4. 查询2对应的(key, value)对:`TestHashTable 2 2`,返回not exist +5. 查询1对应的(key, value)对:`TestHashTable 2 1`,输出key(1), value(2) +6. 删除1对应的(key, value)对:`TestHashTable 3 1`,删除成功返回success +7. 查询1对应的(key, value)对:`TestHashTable 2 1`,返回not exist +8. 插入(1, 10)对:`TestHashTable 1 1 10`,插入成功返回success +9. 查询1对应的(key, value)对:`TestHashTable 2 1`,输出key(1), value(10) + +测试截图: +![](evaluation.png) + +总结:注册shell命令,打印验证功能均正确。 + +## 附录 + +修改/新增的文件列表: +``` +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/evaluation.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/evaluation.png b/APP_Framework/Applications/app_test/test_hash/evaluation.png new file mode 100644 index 000000000..bd8c70eb5 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/evaluation.png differ 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..70e467abe --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.c @@ -0,0 +1,265 @@ +/* +* 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: bdislab_0 +* @date: 2023/6/5 +*/ + +#include +#include +#include +#include +#include "test_hash.h" + +/** + * @brief Priv-shell Command definition + * + * @param _func Command function + * @param _desc Command description + * @param _attr Command attributes if need + */ +#define PRIV_SHELL_CMD_FUNCTION(_func, _desc, _attr) SHELL_EXPORT_CMD(_attr, _func, _func, _desc) + + +struct Hash_entry *create_hash_entry(char *okey_, int key_, int val_) { + return NULL; +} + +struct Hash_entry *hts[MOD]; +int htlen[MOD]; + +int get_hash_val(char *okey) { + int ans = 0; + while(*okey) { + ans = (ans * 233 + *okey) % MOD; + okey ++; + } + return ans; +} + +char * str_copy(char *s, char *t) { + int len = strlen(t) + 1; + s = (char *)malloc(len); + for(int i = 0; i < len; ++i) s[i] = t[i]; + s[len] = '\0'; + return s; +} + +void insert_tail(struct Hash_entry *head, struct Hash_entry *last, struct Hash_entry *tmp) { + last->next = tmp; + tmp->next = head; + tmp->prev = last; + head->prev = tmp; +} + +int str_to_int(char *str) { + int ans = 0; + while(*str) { + ans = (ans * 10 + *str - '0'); + str ++; + } + return ans; +} + +int insert_kv(char *okey, char *val) { + int key = get_hash_val(okey); + int ret = NOT_EXIST_VAL; + if(htlen[key] == 0) { + hts[key] = (struct Hash_entry *)malloc(sizeof(struct Hash_entry)); + hts[key]->prev = hts[key]; + hts[key]->next = hts[key]; + hts[key]->okey = str_copy(hts[key]->okey, okey); + hts[key]->key = key; + hts[key]->val = str_to_int(val); + // printf("insert_kv: %p %s %d %d\n", hts[key], hts[key]->okey, key, hts[key]->val); + htlen[key] += 1; + return ret; + }else { + struct Hash_entry *head = hts[key]; + if(strcmp(head->okey, okey) == 0) { + ret = head->val; + head->val = str_to_int(val); + }else { + head = head->next; + while(head != hts[key]) { + if(strcmp(head->okey, okey) == 0) { + ret = head->val; + head->val = str_to_int(val); + break; + } + head = head->next; + } + } + if(ret != NOT_EXIST_VAL) { + return ret; + } + struct Hash_entry *new_he = (struct Hash_entry *)malloc(sizeof(struct Hash_entry)); + new_he->prev = new_he; + new_he->next = new_he; + new_he->okey = str_copy(new_he->okey, okey); + new_he->key = key; + new_he->val = str_to_int(val); + insert_tail(hts[key], hts[key]->prev, new_he); + htlen[key] += 1; + return ret; + } +} + +int query_key(char *okey) { + int val = NOT_EXIST_VAL; + int key = get_hash_val(okey); + if(htlen[key] != 0) { + struct Hash_entry *head = hts[key]; + if(strcmp(head->okey, okey) == 0) { + val = head->val; + }else { + head = head->next; + while(head != hts[key]) { + if(strcmp(head->okey, okey) == 0) { + val = head->val; + break; + } + head = head->next; + } + } + } + return val; +} + +int delete_key(char *okey) { + int ret = NOT_EXIST_VAL; + int key = get_hash_val(okey); + if(htlen[key] != 0) { + struct Hash_entry *head = hts[key]; + if(strcmp(head->okey, okey) == 0) { + ret = head->val; + struct Hash_entry * le = head->prev, *ri = head->next; + le->next = ri; + ri->prev = le; + hts[key] = ri; + free(head->okey); + free(head); + htlen[key] -= 1; + }else { + head = head->next; + while(head != hts[key]) { + if(strcmp(head->okey, okey) == 0) { + ret = head->val; + struct Hash_entry * le = head->prev, *ri = head->next; + le->next = ri; + ri->prev = le; + // hts[key] = ri; + free(head->okey); + free(head); + htlen[key] -= 1; + break; + } + head = head->next; + } + } + } + return ret; +} + +void TestHashTable(int argc, char *argv[]) +{ + printf("%d parameter(s): ", argc); + for (char i = 1; i < argc; i++) { + printf("%s ", argv[i]); + } + printf("\r\n"); + if(argc == 4 && argv[1][0] == '1') {// x.c 1 key val + int val = insert_kv(argv[2], argv[3]); + if(val == NOT_EXIST_VAL) { + printf("insert key (%s), val (%s) success\n", argv[2], argv[3]); + }else { + printf("insert key (%s), val (%s) repeat, old value (%d) is overwritten\n", argv[2], argv[3], 1); + } + }else if(argc == 3 && argv[1][0] == '2') {// x.c 2 key + int val = query_key(argv[2]); + if(val == NOT_EXIST_VAL) { + printf("query key(%s), not exist\n", argv[2]); + }else { + printf("query key(%s), answer: %d\n", argv[2], val); + } + }else if(argc == 3 && argv[1][0] == '3') {// x.c 3 key + // del + int val = delete_key(argv[2]); + if(val == NOT_EXIST_VAL) { + printf("delete key(%s), not exist\n", argv[2]); + }else { + printf("delete key(%s), val(%d) success\n", argv[2], val); + } + }else if(argc == 2 && argv[1][0] == 'h') {// x.c help + // help + printf("TestHashTable help:\n"); + printf("- 1 key val: insert (key, val), duplicate keys will overwrite old values. Only `int` range data is supported\n"); + printf("- 2 key: query key\n"); + printf("- 3 key: delete key\n"); + printf("- help: help\n"); + } +} +PRIV_SHELL_CMD_FUNCTION(TestHashTable, a hashtable sample, PRIV_SHELL_CMD_MAIN_ATTR); + + +// int main() { +// int n; +// int opt; +// char key[55], val[55]; +// printf("input opertion numbers:\n"); +// scanf("%d", &n); +// while(n --) { +// printf("\ninput opertion:\n"); +// scanf("%d", &opt); +// if(opt == 1) { +// scanf("%s%s", key, val); +// int ret = insert_kv(key, val); +// printf("insert key: %s, val: %s, ret: %d\n", key, val, ret); +// }else if(opt == 2) { +// scanf("%s", key); +// int val = query_key(key); +// printf("query key: %s, answer: %d\n", key, val); +// }else if(opt == 3) { +// scanf("%s", key); +// int val = delete_key(key); +// printf("query key: %s, answer: %d\n", key, val); +// } +// else { +// printf("error opt\n"); +// break; +// } +// } +// return 0; +// } + +/* +gcc test_hash.c +./a.out +12 +1 10 10 +2 10 + + +1 1 1 +1 1 2 +2 2 +2 1 +3 1 +2 1 +1 1 10 +2 1 + +*/ \ 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..250f8c52c --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.h @@ -0,0 +1,13 @@ + +const int INF = 0x3f3f3f3f; + +#define MOD (1000 + 5) +#define NOT_EXIST_VAL (-INF) + +struct Hash_entry { + char *okey; + int key; + int val; + struct Hash_entry *next; + struct Hash_entry *prev; +};