diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 2e0ccd7a7..5d11ed30f 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -239,6 +239,10 @@ menu "test app" menuconfig USER_TEST_TIMER bool "Config test soft timer" default n + + menuconfig USER_TEST_HASH + bool "Config test hash" + default n menuconfig USER_TEST_RADIX bool "Config test radix tree" diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 12033cd4c..738644ddb 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -104,6 +104,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) ifeq ($(CONFIG_USER_TEST_RADIX),y) SRC_FILES += test_radix_tree/test_radix_tree.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/CompileSuccess.png b/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png new file mode 100644 index 000000000..04caae4d1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png b/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png new file mode 100644 index 000000000..b62acec61 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/ConfigOpen.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..876cd5d08 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/README.md @@ -0,0 +1,58 @@ +# 基于cortex-m3-emulator实现哈希表并测试验证## + +## 1. 简介 +利用c语言实现了哈希表(HashMap),包括添加键值对(Put),获取键对应的值(Get), 删除健(Delete),清空哈希表(Clear), 迭代遍历哈希表(hashMapIterator)等功能 +操作。 + +利用数组(Entry)作为存储空间,利用链表(*next)解决冲突。当哈希表的大小超过数组大小后,为避免发生冲突过多的情况,可以对哈希表扩容。 + +## 2. 数据结构设计说明 +键值对结构 +typedef struct entry { + void * key; // 键 + void * value; // 值 + struct entry * next; // 冲突链表 +}*Entry; + +哈希结构 +typedef struct hashMap { + int size; // 当前大小 + int listSize; // 有效空间大小 + HashCode hashCode; // 哈希函数 + Equal equal; // 判等函数 + Entry list; // 存储区域 + Put put; // 添加键的函数 + Get get; // 获取键对应值的函数 + Remove remove; // 删除键 + Clear clear; // 清空Map + Exists exists; // 判断键是否存在 + Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 +}*HashMap; + +包括以下函数功能,分别为: +`createHashMap`:创建一个哈希结构 +`defaultPut`:添加键值对 +`defaultGet`:获取键对应值 +`defaultRemove`:删除指定键的键值对 +`defaultExists`:判断键值是否存在 +`defaultClear`:清空Map的函数类型 +`resetHashMap`:重新构建哈希表 + + +## 3. 测试程序说明 +测试了哈希表的插入键值对(Put),判断键是否存在(Exist),获取键对应的值(Get), 删除健(Delete),迭代遍历哈希表(hashMapIterator),清空哈希表(Clear)等操作。 +并展示了利用链地址法解决哈希冲突的示例, 两个不同的人(Bob和Li Ming)的hashcode相同。 + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) +![image](ConfigOpen.png) +打开menuconfig之后,将test_hash_map开启(y),保存后退出 + +![image](CompileSuccess.png) +编译XiZi-cortex-m3-emulator.elf成功 + +![image](ShellCommand.png) +启动qemu模拟Xiuos操作系统,验证TestHash注册Shell命令 + +![image](TestHash.png) +执行TestHash命令,打印测试结果。 + diff --git a/APP_Framework/Applications/app_test/test_hash/ShellCommand.png b/APP_Framework/Applications/app_test/test_hash/ShellCommand.png new file mode 100644 index 000000000..f9338e7e1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/ShellCommand.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/TestHash.png b/APP_Framework/Applications/app_test/test_hash/TestHash.png new file mode 100644 index 000000000..3170edfef Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/TestHash.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..d6eabc4c7 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.c @@ -0,0 +1,323 @@ + +/** +* @file: test_hash.c +* @brief: a application of test hash function +* @version: 3.0 +* @author: Yao wenying +* @date: 2023/05/26 +*/ + +#include +#include"test_hash.h" + +int defaultHashCode(HashMap hashMap, let key) { + char * k = (char *)key; + unsigned long h = 0; + while (*k) { + h = (h << 4) + *k++; + unsigned long g = h & 0xF0000000L; + if (g) { + h ^= g >> 24; + } + h &= ~g; + } + return h % hashMap->listSize; +} + +Boolean defaultEqual(let key1, let key2) { + return strcmp((string)key1, (string)key2) ? False : True; +} + +void resetHashMap(HashMap hashMap, int listSize) { + + if (listSize < 8) return; + + // 键值对临时存储空间 + Entry tempList = newEntryList(hashMap->size); + + HashMapIterator iterator = createHashMapIterator(hashMap); + int length = hashMap->size; + for (int index = 0; hasNextHashMapIterator(iterator); index++) { + // 迭代取出所有键值对 + iterator = nextHashMapIterator(iterator); + tempList[index].key = iterator->entry->key; + tempList[index].value = iterator->entry->value; + tempList[index].next = NULL; + } + freeHashMapIterator(&iterator); + + // 清除原有键值对数据 + hashMap->size = 0; + for (int i = 0; i < hashMap->listSize; i++) { + Entry current = &hashMap->list[i]; + current->key = NULL; + current->value = NULL; + if (current->next != NULL) { + while (current->next != NULL) { + Entry temp = current->next->next; + free(current->next); + current->next = temp; + } + } + } + + // 更改内存大小 + hashMap->listSize = listSize; + Entry relist = (Entry)realloc(hashMap->list, hashMap->listSize * sizeof(struct entry)); + if (relist != NULL) { + hashMap->list = relist; + relist = NULL; + } + + // 初始化数据 + for (int i = 0; i < hashMap->listSize; i++) { + hashMap->list[i].key = NULL; + hashMap->list[i].value = NULL; + hashMap->list[i].next = NULL; + } + + // 将所有键值对重新写入内存 + for (int i = 0; i < length; i++) { + hashMap->put(hashMap, tempList[i].key, tempList[i].value); + } + free(tempList); +} + +void defaultPut(HashMap hashMap, let key, let value) { + // 获取哈希值 + int index = hashMap->hashCode(hashMap, key); + + if (hashMap->list[index].key == NULL) { + hashMap->size++; + // 该地址为空时直接存储 + hashMap->list[index].key = key; + hashMap->list[index].value = value; + } + else { + + Entry current = &hashMap->list[index]; + while (current != NULL) { + if (hashMap->equal(key, current->key)) { + // 对于键值已经存在的直接覆盖 + current->value = value; + return; + } + current = current->next; + }; + + // 发生冲突则创建节点挂到相应位置的next上 + Entry entry = newEntry(); + entry->key = key; + entry->value = value; + entry->next = hashMap->list[index].next; + hashMap->list[index].next = entry; + hashMap->size++; + } + + if (hashMap->autoAssign && hashMap->size >= hashMap->listSize) { + + // 内存扩充至原来的两倍 + // *注: 扩充时考虑的是当前存储元素数量与存储空间的大小关系,而不是存储空间是否已经存满, + // 例如: 存储空间为10,存入了10个键值对,但是全部冲突了,所以存储空间空着9个,其余的全部挂在一个上面, + // 这样检索的时候和遍历查询没有什么区别了,可以简单这样理解,当我存入第11个键值对的时候一定会发生冲突, + // 这是由哈希函数本身的特性(取模)决定的,冲突就会导致检索变慢,所以这时候扩充存储空间,对原有键值对进行 + // 再次散列,会把冲突的数据再次分散开,加快索引定位速度。 + resetHashMap(hashMap, hashMap->listSize * 2); + } +} + +let defaultGet(HashMap hashMap, let key) { + if (hashMap->exists(hashMap, key)) { + int index = hashMap->hashCode(hashMap, key); + Entry entry = &hashMap->list[index]; + while (entry != NULL) { + if (hashMap->equal(entry->key, key)) { + return entry->value; + } + entry = entry->next; + } + } + return NULL; +} + +let defaultRemove(HashMap hashMap, let key) { + int index = hashMap->hashCode(hashMap, key); + Entry entry = &hashMap->list[index]; + if (entry->key == NULL) { + return NULL; + } + let entryKey = entry->key; + Boolean result = False; + if (hashMap->equal(entry->key, key)) { + hashMap->size--; + if (entry->next != NULL) { + Entry temp = entry->next; + entry->key = temp->key; + entry->value = temp->value; + entry->next = temp->next; + free(temp); + } + else { + entry->key = NULL; + entry->value = NULL; + } + result = True; + } + else { + Entry p = entry; + entry = entry->next; + while (entry != NULL) { + if (hashMap->equal(entry->key, key)) { + hashMap->size--; + p->next = entry->next; + free(entry); + result = True; + break; + } + p = entry; + entry = entry->next; + }; + } + + // 如果空间占用不足一半,则释放多余内存 + if (result && hashMap->autoAssign && hashMap->size < hashMap->listSize / 2) { + resetHashMap(hashMap, hashMap->listSize / 2); + } + return entryKey; +} + +Boolean defaultExists(HashMap hashMap, let key) { + int index = hashMap->hashCode(hashMap, key); + Entry entry = &hashMap->list[index]; + if (entry->key == NULL) { + return False; + } + else { + while (entry != NULL) { + if (hashMap->equal(entry->key, key)) { + return True; + } + entry = entry->next; + } + return False; + } +} + +void defaultClear(HashMap hashMap) { + for (int i = 0; i < hashMap->listSize; i++) { + // 释放冲突值内存 + Entry entry = hashMap->list[i].next; + while (entry != NULL) { + Entry next = entry->next; + free(entry); + entry = next; + } + hashMap->list[i].next = NULL; + } + // 释放存储空间 + free(hashMap->list); + hashMap->list = NULL; + hashMap->size = -1; + hashMap->listSize = 0; +} + +HashMap createHashMap(HashCode hashCode, Equal equal) { + HashMap hashMap = newHashMap(); + if (hashMap == NULL) { + return NULL; + } + hashMap->size = 0; + hashMap->listSize = 8; + hashMap->hashCode = hashCode == NULL ? defaultHashCode : hashCode; + hashMap->equal = equal == NULL ? defaultEqual : equal; + hashMap->exists = defaultExists; + hashMap->get = defaultGet; + hashMap->put = defaultPut; + hashMap->remove = defaultRemove; + hashMap->clear = defaultClear; + hashMap->autoAssign = True; + + // 起始分配8个内存空间,溢出时会自动扩充 + hashMap->list = newEntryList(hashMap->listSize); + if (hashMap->list == NULL) { + return NULL; + } + Entry p = hashMap->list; + for (int i = 0; i < hashMap->listSize; i++) { + p[i].key = p[i].value = p[i].next = NULL; + } + return hashMap; +} + +HashMapIterator createHashMapIterator(HashMap hashMap) { + HashMapIterator iterator = newHashMapIterator(); + if (iterator == NULL) { + return NULL; + } + iterator->hashMap = hashMap; + iterator->count = 0; + iterator->hashCode = -1; + iterator->entry = NULL; + return iterator; +} + +Boolean hasNextHashMapIterator(HashMapIterator iterator) { + return iterator->count < iterator->hashMap->size ? True : False; +} + +HashMapIterator nextHashMapIterator(HashMapIterator iterator) { + if (hasNextHashMapIterator(iterator)) { + if (iterator->entry != NULL && iterator->entry->next != NULL) { + iterator->count++; + iterator->entry = iterator->entry->next; + return iterator; + } + while (++iterator->hashCode < iterator->hashMap->listSize) { + Entry entry = &iterator->hashMap->list[iterator->hashCode]; + if (entry->key != NULL) { + iterator->count++; + iterator->entry = entry; + break; + } + } + } + return iterator; +} + +void freeHashMapIterator(HashMapIterator * iterator) { + free(*iterator); + *iterator = NULL; +} + +#define Put(map, key, value) map->put(map, (void *)key, (void *)value); +#define Get(map, key) (char *)map->get(map, (void *)key) +#define Remove(map, key) map->remove(map, (void *)key) +#define Existe(map, key) map->exists(map, (void *)key) + +void TestHash() { + HashMap map = createHashMap(NULL, NULL); + Put(map, "000123", "Annie"); + Put(map, "000245", "Bob"); + Put(map, "000284", "Daniel"); + Put(map, "000281", "Luna"); + Put(map, "000587", "Yao"); + Put(map, "000985", "Li Ming"); + Put(map, "000852", "Janne"); + + printf("print the key-values in hashmap:\n"); + HashMapIterator iterator = createHashMapIterator(map); + while (hasNextHashMapIterator(iterator)) { + iterator = nextHashMapIterator(iterator); + printf("{ key: %s, key: %s, hashcode: %d }\n", + (char *)iterator->entry->key, (char *)iterator->entry->value, iterator->hashCode); + } + printf("key: 000852, exists: %s\n", Existe(map, "000852") ? "true" : "false"); + printf("000852: %s\n", Get(map, "000852")); + printf("remove 000852 %s\n", Remove(map, "000852") ? "true" : "false"); + printf("key: 000852, exists: %s\n", Existe(map, "000852") ? "true" : "false"); + + map->clear(map); + freeHashMapIterator(&iterator); +} + +PRIV_SHELL_CMD_FUNCTION(TestHash, Implement hash_map, 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..3f7358411 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.h @@ -0,0 +1,129 @@ + +/** +* @file: test_hash.h +* @brief: a application of test hash function +* @version: 3.0 +* @author: Yao wenying +* @date: 2023/05/26 +*/ + + +#ifndef __HASHMAP_H__ +#define __HASHMAP_H__ + +#include +#include +#include + + +// 实现数据的基本类型 +// 字符串类型 +#define string char * +#define newString(str) strcpy((char *)malloc(strlen(str) + 1), str) +#define NEW(type) (type *)malloc(sizeof(type)) + +// 布尔类型 +enum _Boolean { True = 1, False = 0 }; +typedef enum _Boolean Boolean; + +#define let void * + +typedef struct entry { + let key; // 键 + let value; // 值 + struct entry * next; // 冲突链表 +}*Entry; + +#define newEntry() NEW(struct entry) +#define newEntryList(length) (Entry)malloc(length * sizeof(struct entry)) + +// 哈希结构 +typedef struct hashMap *HashMap; + +#define newHashMap() NEW(struct hashMap) + +// 哈希函数类型 +typedef int(*HashCode)(HashMap, let key); + +// 判等函数类型 +typedef Boolean(*Equal)(let key1, let key2); + +// 添加键函数类型 +typedef void(*Put)(HashMap hashMap, let key, let value); + +// 获取键对应值的函数类型 +typedef let(*Get)(HashMap hashMap, let key); + +// 删除键的函数类型 +typedef let(*Remove)(HashMap hashMap, let key); + +// 清空Map的函数类型 +typedef void(*Clear)(HashMap hashMap); + +// 判断键值是否存在的函数类型 +typedef Boolean(*Exists)(HashMap hashMap, let key); + +typedef struct hashMap { + int size; // 当前大小 + int listSize; // 有效空间大小 + HashCode hashCode; // 哈希函数 + Equal equal; // 判等函数 + Entry list; // 存储区域 + Put put; // 添加键的函数 + Get get; // 获取键对应值的函数 + Remove remove; // 删除键 + Clear clear; // 清空Map + Exists exists; // 判断键是否存在 + Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 +}*HashMap; + +// 迭代器结构 +typedef struct hashMapIterator { + Entry entry; // 迭代器当前指向 + int count; // 迭代次数 + int hashCode; // 键值对的哈希值 + HashMap hashMap; +}*HashMapIterator; + +#define newHashMapIterator() NEW(struct hashMapIterator) + +// 默认哈希函数 +static int defaultHashCode(HashMap hashMap, let key); + +// 默认判断键值是否相等 +static Boolean defaultEqual(let key1, let key2); + +// 默认添加键值对 +static void defaultPut(HashMap hashMap, let key, let value); + +// 默认获取键对应值 +static let defaultGet(HashMap hashMap, let key); + +// 默认删除键 +static let defaultRemove(HashMap hashMap, let key); + +// 默认判断键是否存在 +static Boolean defaultExists(HashMap hashMap, let key); + +// 默认清空Map +static void defaultClear(HashMap hashMap); + +// 重新构建 +static void resetHashMap(HashMap hashMap, int listSize); + +// 创建一个哈希结构 +HashMap createHashMap(HashCode hashCode, Equal equal); + +// 创建哈希结构迭代器 +HashMapIterator createHashMapIterator(HashMap hashMap); + +// 迭代器是否有下一个 +Boolean hasNextHashMapIterator(HashMapIterator iterator); + +// 迭代到下一次 +HashMapIterator nextHashMapIterator(HashMapIterator iterator); + +// 释放迭代器内存 +void freeHashMapIterator(HashMapIterator * iterator); + +#endif // !__HASHMAP_H__ \ No newline at end of file