/** * @file cstl_hash.c * @copyright Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * @brief cstl_hash 实现源码 * @details 哈希表实现源码 * @date 2021-05-14 * @version v0.1.0 * ******************************************************************************************* * @par 修改日志: * *
Date Version Author Description *
* ******************************************************************************************* */ #include "cstl_rawlist.h" #include "securec.h" #include "cstl_public_inner.h" #include "cstl_hash.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define CSTL_HASH_OPTION3 3 #define CSTL_HASH_OPTION2 2 #define CSTL_HASH_OPTION1 1 struct TagHashNode { CstlRawListNode node; /**< 链表node */ uintptr_t key; /**< key或保存key的地址 */ uintptr_t value; /**< value或保存value的地址 */ }; typedef struct TagHashNode CstlHashNode; struct CstlHashInfo { CstlDupFreeFuncPair keyFunc; /**< key拷贝、释放函数对 */ CstlDupFreeFuncPair valueFunc; /**< value拷贝、释放函数对 */ CstlHashMatchFunc matchFunc; /**< 匹配函数 */ CstlHashCodeCalcFunc hashFunc; /**< 哈希函数 */ size_t bucketSize; /**< Hash表大小 */ size_t hashCount; /**< Hash表中表项的个数 */ CstlRawList listArray[0]; /**< 链表控制块数组 */ }; /* murmurhash算法 */ /* 定义常量 */ #define HASH_VC1 0xCC9E2D51 #define HASH_VC2 0x1B873593 #define HASH_HC1 0xE6546B64 #define HASH_HC2 0x85EBCA6B #define HASH_HC3 0xC2B2AE35 #define HASH_HC4 5 #define CHAR_BIT 8 #define CHAR_FOR_PER_LOOP 4 #define HASH_V_ROTATE 15 #define HASH_H_ROTATE 13 #define SYS_BUS_WIDTH sizeof(size_t) #define HASH_SEED 0x3B9ACA07 /* 大质数1000000007,种子可以随机也可以指定 */ enum CstlByte { ONE_BYTE = 1, TWO_BYTE = 2, }; enum CstlShiftBit { SHIFT8 = 8, SHIFT13 = 13, SHIFT16 = 16, SHIFT24 = 24 }; CstlHashIterator CstlHashPrev(const CstlHash *hash, CstlHashIterator hashNode); CSTL_STATIC size_t CstlHashRotate(size_t v, uint32_t offset) { return ((v << offset) | (v >> (SYS_BUS_WIDTH * CHAR_BIT - offset))); } CSTL_STATIC size_t CstlHashMixV(size_t v) { v = v * HASH_VC1; v = CstlHashRotate(v, HASH_V_ROTATE); v = v * HASH_VC2; return v; } CSTL_STATIC size_t CstlHashMixH(size_t h, size_t v) { size_t res = h; res ^= v; res = CstlHashRotate(res, HASH_H_ROTATE); res = res * HASH_HC4 + HASH_HC1; return res; } CSTL_STATIC size_t CstlHashCodeCalc(void *key, size_t keySize) { uint8_t *tmpKey = (uint8_t *)key; size_t i = 0; size_t v; size_t h = HASH_SEED; uint8_t c0, c1, c2, c3; size_t tmpLen = keySize - keySize % CHAR_FOR_PER_LOOP; while ((i + CHAR_FOR_PER_LOOP) <= tmpLen) { c0 = tmpKey[i++]; c1 = tmpKey[i++]; c2 = tmpKey[i++]; c3 = tmpKey[i++]; v = (size_t)c0 | ((size_t)c1 << SHIFT8) | ((size_t)c2 << SHIFT16) | ((size_t)c3 << SHIFT24); v = CstlHashMixV(v); h = CstlHashMixH(h, v); } v = 0; switch (keySize & CSTL_HASH_OPTION3) { case CSTL_HASH_OPTION3: v ^= ((size_t)tmpKey[i + TWO_BYTE] << SHIFT16); /* (keySize % 4)等于3,fallthrough,其他分支类似。 */ /* fall-through */ case CSTL_HASH_OPTION2: v ^= ((size_t)tmpKey[i + ONE_BYTE] << SHIFT8); /* fall-through */ case CSTL_HASH_OPTION1: v ^= tmpKey[i]; v = CstlHashMixV(v); h ^= v; break; default: break; } h ^= h >> SHIFT16; h *= HASH_HC2; h ^= h >> SHIFT13; h *= HASH_HC3; h ^= h >> SHIFT16; return h; } /* 内部接口定义 */ CSTL_STATIC void CstlHashHookRegister(CstlHash *hash, CstlHashCodeCalcFunc hashFunc, CstlHashMatchFunc matchFunc, CstlDupFreeFuncPair *keyFunc, CstlDupFreeFuncPair *valueFunc) { CstlDupFreeFuncPair *hashKeyFunc = &hash->keyFunc; CstlDupFreeFuncPair *hashValueFunc = &hash->valueFunc; if (hashFunc == NULL) { hash->hashFunc = CstlHashCodeCalcInt; } else { hash->hashFunc = hashFunc; } if (matchFunc == NULL) { hash->matchFunc = CstlHashMatchInt; } else { hash->matchFunc = matchFunc; } if (keyFunc == NULL) { hashKeyFunc->dupFunc = NULL; hashKeyFunc->freeFunc = NULL; } else { hashKeyFunc->dupFunc = keyFunc->dupFunc; hashKeyFunc->freeFunc = keyFunc->freeFunc; } if (valueFunc == NULL) { hashValueFunc->dupFunc = NULL; hashValueFunc->freeFunc = NULL; } else { hashValueFunc->dupFunc = valueFunc->dupFunc; hashValueFunc->freeFunc = valueFunc->freeFunc; } } CSTL_STATIC inline CstlHashIterator CstlHashIterEndGet(const CstlHash *hash) { return (CstlHashIterator)(uintptr_t)(&hash->listArray[hash->bucketSize].head); } CSTL_STATIC CstlHashNode *CstlHashNodeCreate(const CstlHash *hash, uintptr_t key, size_t keySize, uintptr_t value, size_t valueSize) { uintptr_t tmpKey; uintptr_t tmpValue; CstlHashNode *hashNode; void *tmpPtr; hashNode = (CstlHashNode *)malloc(sizeof(CstlHashNode)); if (hashNode == NULL) { CSTL_PRINTF("[Cstlhash]: malloc failed. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } if (hash->keyFunc.dupFunc != NULL) { tmpPtr = hash->keyFunc.dupFunc((void *)key, keySize); tmpKey = (uintptr_t)tmpPtr; if (tmpKey == (uintptr_t)NULL) { /* 考虑用户返回失败的场景 */ free(hashNode); return NULL; } } else { tmpKey = key; } if (hash->valueFunc.dupFunc != NULL) { tmpPtr = hash->valueFunc.dupFunc((void *)value, valueSize); tmpValue = (uintptr_t)tmpPtr; if (tmpValue == (uintptr_t)NULL) { /* 考虑用户返回失败的场景 */ if (hash->keyFunc.freeFunc != NULL) { hash->keyFunc.freeFunc((void *)tmpKey); } free(hashNode); return NULL; } } else { tmpValue = value; } hashNode->key = tmpKey; hashNode->value = tmpValue; return hashNode; } CSTL_STATIC CstlHashNode *CstlHashFindNode(const CstlRawList *list, uintptr_t key, CstlHashMatchFunc matchFunc) { CstlHashNode *hashNode; CstlRawListNode *rawListNode; for (rawListNode = CstlRawListFront(list); rawListNode != NULL; rawListNode = CstlRawListNext(list, rawListNode)) { hashNode = CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); if (matchFunc(hashNode->key, key)) { return hashNode; } } return NULL; } CSTL_STATIC CstlHashIterator CstlHashFront(const CstlHash *hash) { uint32_t i = 0; const CstlRawList *list; CstlRawListNode *rawListNode; while (i < hash->bucketSize) { list = &hash->listArray[i]; rawListNode = CstlRawListFront(list); if (rawListNode != NULL) { return CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); } i++; } return CstlHashIterEndGet(hash); } CSTL_STATIC CstlHashIterator CstlHashNext(const CstlHash *hash, CstlHashIterator hashNode) { size_t i; size_t hashCode; const CstlRawList *list; CstlRawListNode *rawListNode; hashCode = hash->hashFunc(hashNode->key, hash->bucketSize); if (hashCode >= hash->bucketSize) { CSTL_PRINTF("[Cstlhash]: hashCode is invalid. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CstlHashIterEndGet(hash); } list = hash->listArray + hashCode; rawListNode = CstlRawListNext(list, &hashNode->node); if (rawListNode != NULL) { return CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); } for (i = hashCode + 1; i < hash->bucketSize; ++i) { list = &hash->listArray[i]; rawListNode = CstlRawListFront(list); if (rawListNode != NULL) { return CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); } } return CstlHashIterEndGet(hash); } CstlHashIterator CstlHashPrev(const CstlHash *hash, CstlHashIterator hashNode) { ssize_t i; size_t hashCode; const CstlRawList *list; CstlRawListNode *rawListNode; hashCode = hash->hashFunc(hashNode->key, hash->bucketSize); if (hashCode >= hash->bucketSize) { CSTL_PRINTF("[Cstlhash]: hashCode is invalid. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CstlHashIterEndGet(hash); } list = hash->listArray + hashCode; rawListNode = CstlRawListPrev(list, &hashNode->node); if (rawListNode != NULL) { return CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); } for (i = (ssize_t)(hashCode - 1); i >= 0; --i) { list = &hash->listArray[i]; rawListNode = CstlRawListBack(list); if (rawListNode != NULL) { return CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); } } return CstlHashIterEndGet(hash); } CSTL_STATIC void CstlHashNodeFree(CstlHash *hash, CstlHashNode *node) { CstlFreeFunc keyFreeFunc = hash->keyFunc.freeFunc; CstlFreeFunc valueFreeFunc = hash->valueFunc.freeFunc; if (keyFreeFunc != NULL) { keyFreeFunc((void *)node->key); } if (valueFreeFunc != NULL) { valueFreeFunc((void *)node->value); } free(node); } __attribute__((visibility("default"))) size_t CstlHashCodeCalcInt(uintptr_t key, size_t bktSize) { size_t hashCode = CstlHashCodeCalc(&key, sizeof(key)); return hashCode % bktSize; } __attribute__((visibility("default"))) bool CstlHashMatchInt(uintptr_t key1, uintptr_t key2) { return key1 == key2; } __attribute__((visibility("default"))) size_t CstlHashCodeCalcStr(uintptr_t key, size_t bktSize) { char *tmpKey = (char *)key; size_t hashCode = CstlHashCodeCalc(tmpKey, strlen(tmpKey)); return hashCode % bktSize; } __attribute__((visibility("default"))) bool CstlHashMatchStr(uintptr_t key1, uintptr_t key2) { char *tkey1 = (char *)key1; char *tkey2 = (char *)key2; if (strcmp(tkey1, tkey2) == 0) { return true; } return false; } __attribute__((visibility("default"))) CstlHash *CstlHashCreate(size_t bktSize, CstlHashCodeCalcFunc hashFunc, CstlHashMatchFunc matchFunc, CstlDupFreeFuncPair *keyFunc, CstlDupFreeFuncPair *valueFunc) { size_t i; size_t size; CstlHash *hash; CstlRawList *listAddr; if (bktSize == 0U) { CSTL_PRINTF("[Cstlhash]: bktSize is 0. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } size = (bktSize + 1) * sizeof(CstlRawList); if (IsMultiOverflow((bktSize + 1), sizeof(CstlRawList)) || IsAddOverflow(size, sizeof(CstlHash))) { CSTL_PRINTF("[Cstlhash]: bktSize is too big. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } size += sizeof(CstlHash); hash = (CstlHash *)malloc(size); if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: malloc failed. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } (void)memset_s(hash, size, 0, size); hash->bucketSize = bktSize; CstlHashHookRegister(hash, hashFunc, matchFunc, keyFunc, valueFunc); listAddr = hash->listArray; for (i = 0; i <= bktSize; ++i) { CstlRawListInit(listAddr + i, NULL); } return hash; } CSTL_STATIC int32_t CstlHashInsertNode(CstlHash *hash, CstlRawList *rawList, const CstlUserData *inputKey, const CstlUserData *inputValue) { CstlHashNode *hashNode = CstlHashNodeCreate(hash, inputKey->inputData, inputKey->dataSize, inputValue->inputData, inputValue->dataSize); if (hashNode == NULL) { CSTL_PRINTF("[Cstlhash]: hashNode create failed. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CSTL_ERROR; } CstlRawListPushBack(rawList, &hashNode->node); hash->hashCount++; return CSTL_OK; } static int32_t CstlHashUpdateNode(CstlHash *hash, CstlHashNode *node, uintptr_t value, size_t valueSize) { uintptr_t tmpValue; void *tmpPtr; if (hash->valueFunc.dupFunc != NULL) { tmpPtr = hash->valueFunc.dupFunc((void *)value, valueSize); tmpValue = (uintptr_t)tmpPtr; if (tmpValue == (uintptr_t)NULL) { /* 考虑用户返回失败的场景 */ return CSTL_ERROR; } if (hash->valueFunc.freeFunc != NULL) { hash->valueFunc.freeFunc((void *)node->value); } } else { tmpValue = value; } node->value = tmpValue; return CSTL_OK; } CSTL_STATIC inline int32_t CstlHashCodeCheck(const CstlHash *hash, uintptr_t key, size_t *hashCode) { if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CSTL_ERROR; } *hashCode = hash->hashFunc(key, hash->bucketSize); if (*hashCode >= hash->bucketSize) { CSTL_PRINTF("[Cstlhash]: hashCode is invalid. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CSTL_ERROR; } return CSTL_OK; } __attribute__((visibility("default"))) int32_t CstlHashInsert(CstlHash *hash, uintptr_t key, size_t keySize, uintptr_t value, size_t valueSize) { int32_t ret; size_t hashCode; CstlHashNode *hashNode; CstlRawList *rawList; CstlUserData inputKey; CstlUserData inputValue; ret = CstlHashCodeCheck(hash, key, &hashCode); if (ret != CSTL_OK) { return CSTL_ERROR; } rawList = &hash->listArray[hashCode]; hashNode = CstlHashFindNode(rawList, key, hash->matchFunc); if (hashNode != NULL) { CSTL_PRINTF("[Cstlhash]: key is exist. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CSTL_ERROR; } inputKey.inputData = key; inputKey.dataSize = keySize; inputValue.inputData = value; inputValue.dataSize = valueSize; return CstlHashInsertNode(hash, rawList, &inputKey, &inputValue); } __attribute__((visibility("default"))) int32_t CstlHashPut(CstlHash *hash, uintptr_t key, size_t keySize, uintptr_t value, size_t valueSize) { int32_t ret; size_t hashCode; CstlRawList *rawList; CstlHashNode *hashNode; CstlUserData inputKey; CstlUserData inputValue; ret = CstlHashCodeCheck(hash, key, &hashCode); if (ret != CSTL_OK) { return CSTL_ERROR; } rawList = &hash->listArray[hashCode]; hashNode = CstlHashFindNode(rawList, key, hash->matchFunc); if (hashNode != NULL) { return CstlHashUpdateNode(hash, hashNode, value, valueSize); } inputKey.inputData = key; inputKey.dataSize = keySize; inputValue.inputData = value; inputValue.dataSize = valueSize; return CstlHashInsertNode(hash, rawList, &inputKey, &inputValue); } __attribute__((visibility("default"))) int32_t CstlHashAt(const CstlHash *hash, uintptr_t key, uintptr_t *value) { CstlHashNode *hashNode = CstlHashFind(hash, key); if (hashNode == CstlHashIterEndGet(hash)) { return CSTL_ERROR; } *value = hashNode->value; return CSTL_OK; } __attribute__((visibility("default"))) CstlHashIterator CstlHashFind(const CstlHash *hash, uintptr_t key) { int32_t ret; size_t hashCode; CstlHashNode *hashNode; ret = CstlHashCodeCheck(hash, key, &hashCode); if (ret != CSTL_OK) { return hash == NULL ? NULL : CstlHashIterEndGet(hash); } hashNode = CstlHashFindNode(&hash->listArray[hashCode], key, hash->matchFunc); if (hashNode == NULL) { CSTL_PRINTF("[Cstlhash]: key is not exist. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CstlHashIterEndGet(hash); } return hashNode; } __attribute__((visibility("default"))) bool CstlHashEmpty(const CstlHash *hash) { if ((hash == NULL) || (hash->hashCount == 0U)) { return true; } return false; } __attribute__((visibility("default"))) size_t CstlHashSize(const CstlHash *hash) { if (hash == NULL) { return 0; } return hash->hashCount; } __attribute__((visibility("default"))) CstlHashIterator CstlHashErase(CstlHash *hash, uintptr_t key) { size_t hashCode; CstlHashNode *hashNode; CstlHashNode *nextHashNode; CstlHashMatchFunc matchFunc; CstlHashCodeCalcFunc hashFunc; if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } hashFunc = hash->hashFunc; hashCode = hashFunc(key, hash->bucketSize); if (hashCode >= hash->bucketSize) { CSTL_PRINTF("[Cstlhash]: hashCode is invalid. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CstlHashIterEndGet(hash); } matchFunc = hash->matchFunc; hashNode = CstlHashFindNode(&hash->listArray[hashCode], key, matchFunc); if (hashNode == NULL) { return CstlHashIterEndGet(hash); } nextHashNode = CstlHashNext(hash, hashNode); CstlRawListErase(&hash->listArray[hashCode], &hashNode->node); CstlHashNodeFree(hash, hashNode); --hash->hashCount; return nextHashNode; } __attribute__((visibility("default"))) void CstlHashClear(CstlHash *hash) { uint32_t i; CstlRawList *list; CstlHashNode *hashNode; CstlRawListNode *rawListNode; if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return; } for (i = 0; i < hash->bucketSize; ++i) { list = &hash->listArray[i]; while (!CstlRawListEmpty(list)) { rawListNode = CstlRawListFront(list); hashNode = CSTL_CONTAINER_OF(rawListNode, CstlHashNode, node); CstlRawListErase(list, rawListNode); CstlHashNodeFree(hash, hashNode); } } hash->hashCount = 0; } __attribute__((visibility("default"))) void CstlHashDestory(CstlHash *hash) { if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return; } CstlHashClear(hash); free(hash); } __attribute__((visibility("default"))) CstlHashIterator CstlHashIterBegin(const CstlHash *hash) { if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } return CstlHashFront(hash); } __attribute__((visibility("default"))) CstlHashIterator CstlHashIterEnd(const CstlHash *hash) { if (hash == NULL) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return NULL; } return CstlHashIterEndGet(hash); } __attribute__((visibility("default"))) CstlHashIterator CstlHashIterNext(const CstlHash *hash, CstlHashIterator it) { if ((hash == NULL) || (it == CstlHashIterEnd(hash))) { CSTL_PRINTF("[Cstlhash]: hash is NULL. File = %s, line = %d.\r\n", __FILE__, __LINE__); return CstlHashIterEnd(hash); } return CstlHashNext(hash, it); } __attribute__((visibility("default"))) uintptr_t CstlHashIterKey(const CstlHash *hash, CstlHashIterator it) { if (it == CstlHashIterEnd(hash)) { return 0; } return it->key; } __attribute__((visibility("default"))) uintptr_t CstlHashIterValue(const CstlHash *hash, CstlHashIterator it) { if (it == CstlHashIterEnd(hash)) { return 0; } return it->value; } #ifdef __cplusplus } #endif /* __cplusplus */