diff --git a/source/libs/executor/inc/tsimplehash.h b/source/libs/executor/inc/tsimplehash.h index a1ba70c7021c1ea263c38a5cc9d450b5f879bd50..a56f8e8c049cca1cf606541ea8938f4f648bb32b 100644 --- a/source/libs/executor/inc/tsimplehash.h +++ b/source/libs/executor/inc/tsimplehash.h @@ -45,6 +45,8 @@ SSHashObj *tSimpleHashInit(size_t capacity, _hash_fn_t fn, size_t keyLen, size_t */ int32_t tSimpleHashGetSize(const SSHashObj *pHashObj); +int32_t tSimpleHashPrint(const SSHashObj *pHashObj); + /** * put element into hash table, if the element with the same key exists, update it * @param pHashObj @@ -98,6 +100,15 @@ size_t tSimpleHashGetMemSize(const SSHashObj *pHashObj); */ void *tSimpleHashGetKey(const SSHashObj* pHashObj, void *data, size_t* keyLen); +/** + * Create the hash table iterator + * @param pHashObj + * @param data + * @param iter + * @return void* + */ +void *tSimpleHashIterate(const SSHashObj *pHashObj, void *data, int32_t *iter); + #ifdef __cplusplus } #endif diff --git a/source/libs/executor/src/tsimplehash.c b/source/libs/executor/src/tsimplehash.c index 5ba2be1f21d9bf4c021c167a7b4fa2eb8675892c..7989ad2b5a44e6ca35074cff15ec865492025328 100644 --- a/source/libs/executor/src/tsimplehash.c +++ b/source/libs/executor/src/tsimplehash.c @@ -62,7 +62,7 @@ SSHashObj *tSimpleHashInit(size_t capacity, _hash_fn_t fn, size_t keyLen, size_t } SSHashObj *pHashObj = (SSHashObj *)taosMemoryCalloc(1, sizeof(SSHashObj)); - if (pHashObj == NULL) { + if (!pHashObj) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } @@ -78,7 +78,7 @@ SSHashObj *tSimpleHashInit(size_t capacity, _hash_fn_t fn, size_t keyLen, size_t pHashObj->dataLen = dataLen; pHashObj->hashList = (SHNode **)taosMemoryCalloc(pHashObj->capacity, sizeof(void *)); - if (pHashObj->hashList == NULL) { + if (!pHashObj->hashList) { taosMemoryFree(pHashObj); terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; @@ -87,7 +87,7 @@ SSHashObj *tSimpleHashInit(size_t capacity, _hash_fn_t fn, size_t keyLen, size_t } int32_t tSimpleHashGetSize(const SSHashObj *pHashObj) { - if (pHashObj == NULL) { + if (!pHashObj) { return 0; } return (int32_t)atomic_load_64((int64_t *)&pHashObj->size); @@ -95,7 +95,7 @@ int32_t tSimpleHashGetSize(const SSHashObj *pHashObj) { static SHNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal) { SHNode *pNewNode = taosMemoryMalloc(sizeof(SHNode) + keyLen + dsize); - if (pNewNode == NULL) { + if (!pNewNode) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } @@ -120,7 +120,7 @@ static void taosHashTableResize(SSHashObj *pHashObj) { int64_t st = taosGetTimestampUs(); void *pNewEntryList = taosMemoryRealloc(pHashObj->hashList, sizeof(void *) * newCapacity); - if (pNewEntryList == NULL) { + if (!pNewEntryList) { // qWarn("hash resize failed due to out of memory, capacity remain:%zu", pHashObj->capacity); return; } @@ -133,14 +133,13 @@ static void taosHashTableResize(SSHashObj *pHashObj) { for (int32_t idx = 0; idx < pHashObj->capacity; ++idx) { SHNode *pNode = pHashObj->hashList[idx]; - if (pNode == NULL) { + if (!pNode) { continue; } SHNode *pNext = NULL; SHNode *pPrev = NULL; - while (pNode != NULL) { void *key = GET_SHASH_NODE_KEY(pNode, pHashObj->dataLen); uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)pHashObj->keyLen); @@ -148,7 +147,7 @@ static void taosHashTableResize(SSHashObj *pHashObj) { int32_t newIdx = HASH_INDEX(hashVal, pHashObj->capacity); pNext = pNode->next; if (newIdx != idx) { - if (pPrev == NULL) { + if (!pPrev) { pHashObj->hashList[idx] = pNext; } else { pPrev->next = pNext; @@ -172,7 +171,7 @@ static void taosHashTableResize(SSHashObj *pHashObj) { } int32_t tSimpleHashPut(SSHashObj *pHashObj, const void *key, const void *data) { - if (pHashObj == NULL || key == NULL) { + if (!pHashObj || !key) { return -1; } @@ -186,9 +185,9 @@ int32_t tSimpleHashPut(SSHashObj *pHashObj, const void *key, const void *data) { int32_t slot = HASH_INDEX(hashVal, pHashObj->capacity); SHNode *pNode = pHashObj->hashList[slot]; - if (pNode == NULL) { + if (!pNode) { SHNode *pNewNode = doCreateHashNode(key, pHashObj->keyLen, data, pHashObj->dataLen, hashVal); - if (pNewNode == NULL) { + if (!pNewNode) { return -1; } @@ -204,9 +203,9 @@ int32_t tSimpleHashPut(SSHashObj *pHashObj, const void *key, const void *data) { pNode = pNode->next; } - if (pNode == NULL) { + if (!pNode) { SHNode *pNewNode = doCreateHashNode(key, pHashObj->keyLen, data, pHashObj->dataLen, hashVal); - if (pNewNode == NULL) { + if (!pNewNode) { return -1; } pNewNode->next = pHashObj->hashList[slot]; @@ -235,7 +234,7 @@ static FORCE_INLINE SHNode *doSearchInEntryList(SSHashObj *pHashObj, const void static FORCE_INLINE bool taosHashTableEmpty(const SSHashObj *pHashObj) { return tSimpleHashGetSize(pHashObj) == 0; } void *tSimpleHashGet(SSHashObj *pHashObj, const void *key) { - if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || key == NULL) { + if (!pHashObj || taosHashTableEmpty(pHashObj) || !key) { return NULL; } @@ -243,7 +242,7 @@ void *tSimpleHashGet(SSHashObj *pHashObj, const void *key) { int32_t slot = HASH_INDEX(hashVal, pHashObj->capacity); SHNode *pNode = pHashObj->hashList[slot]; - if (pNode == NULL) { + if (!pNode) { return NULL; } @@ -257,19 +256,43 @@ void *tSimpleHashGet(SSHashObj *pHashObj, const void *key) { } int32_t tSimpleHashRemove(SSHashObj *pHashObj, const void *key) { - // todo + if (!pHashObj || !key) { + return TSDB_CODE_FAILED; + } + + uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)pHashObj->keyLen); + + int32_t slot = HASH_INDEX(hashVal, pHashObj->capacity); + + SHNode *pNode = pHashObj->hashList[slot]; + SHNode *pPrev = NULL; + while (pNode) { + if ((*(pHashObj->equalFp))(GET_SHASH_NODE_KEY(pNode, pHashObj->dataLen), key, pHashObj->keyLen) == 0) { + if (!pPrev) { + pHashObj->hashList[slot] = pNode->next; + } else { + pPrev->next = pNode->next; + } + FREE_HASH_NODE(pNode); + atomic_sub_fetch_64(&pHashObj->size, 1); + break; + } + pPrev = pNode; + pNode = pNode->next; + } + return TSDB_CODE_SUCCESS; } void tSimpleHashClear(SSHashObj *pHashObj) { - if (pHashObj == NULL) { + if (!pHashObj || taosHashTableEmpty(pHashObj)) { return; } - SHNode *pNode, *pNext; + SHNode *pNode = NULL, *pNext = NULL; for (int32_t i = 0; i < pHashObj->capacity; ++i) { pNode = pHashObj->hashList[i]; - if (pNode == NULL) { + if (!pNode) { continue; } @@ -279,11 +302,11 @@ void tSimpleHashClear(SSHashObj *pHashObj) { pNode = pNext; } } - pHashObj->size = 0; + atomic_store_64(&pHashObj->size, 0); } void tSimpleHashCleanup(SSHashObj *pHashObj) { - if (pHashObj == NULL) { + if (!pHashObj) { return; } @@ -292,7 +315,7 @@ void tSimpleHashCleanup(SSHashObj *pHashObj) { } size_t tSimpleHashGetMemSize(const SSHashObj *pHashObj) { - if (pHashObj == NULL) { + if (!pHashObj) { return 0; } @@ -300,11 +323,58 @@ size_t tSimpleHashGetMemSize(const SSHashObj *pHashObj) { } void *tSimpleHashGetKey(const SSHashObj *pHashObj, void *data, size_t *keyLen) { +#if 0 int32_t offset = offsetof(SHNode, data); SHNode *node = ((SHNode *)(char *)data - offset); - if (keyLen != NULL) { + if (keyLen) { *keyLen = pHashObj->keyLen; } + return POINTER_SHIFT(data, pHashObj->dataLen); + return GET_SHASH_NODE_KEY(node, pHashObj->dataLen); +#endif + if (keyLen) { + *keyLen = pHashObj->keyLen; + } + + return POINTER_SHIFT(data, pHashObj->dataLen); +} + +void *tSimpleHashIterate(const SSHashObj *pHashObj, void *data, int32_t *iter) { + if (!pHashObj) { + return NULL; + } + + SHNode *pNode = NULL; + + if (!data) { + for (int32_t i = 0; i < pHashObj->capacity; ++i) { + pNode = pHashObj->hashList[i]; + if (!pNode) { + continue; + } + *iter = i; + return GET_SHASH_NODE_DATA(pNode); + } + return NULL; + } + + pNode = (SHNode *)((char *)data - offsetof(SHNode, data)); + + if (pNode->next) { + return GET_SHASH_NODE_DATA(pNode->next); + } + + ++(*iter); + for (int32_t i = *iter; i < pHashObj->capacity; ++i) { + pNode = pHashObj->hashList[i]; + if (!pNode) { + continue; + } + *iter = i; + return GET_SHASH_NODE_DATA(pNode); + } + + return NULL; } \ No newline at end of file diff --git a/source/libs/executor/test/CMakeLists.txt b/source/libs/executor/test/CMakeLists.txt index acab27ec0876b881dc72aca67927ea3359ef9d57..18ca95481352e1bac61cef21eacc53bb4b94d39d 100644 --- a/source/libs/executor/test/CMakeLists.txt +++ b/source/libs/executor/test/CMakeLists.txt @@ -17,4 +17,19 @@ IF(NOT TD_DARWIN) PUBLIC "${TD_SOURCE_DIR}/include/libs/executor/" PRIVATE "${TD_SOURCE_DIR}/source/libs/executor/inc" ) -ENDIF () \ No newline at end of file +ENDIF () + +# SET(CMAKE_CXX_STANDARD 11) +# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_LIST) + +# ADD_EXECUTABLE(tSimpleHashTest tSimpleHashTests.cpp) +# TARGET_LINK_LIBRARIES( +# tSimpleHashTest +# PRIVATE os util common executor gtest_main +# ) + +# TARGET_INCLUDE_DIRECTORIES( +# tSimpleHashTest +# PUBLIC "${TD_SOURCE_DIR}/include/common" +# PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../inc" +# ) \ No newline at end of file diff --git a/source/libs/executor/test/tSimpleHashTests.cpp b/source/libs/executor/test/tSimpleHashTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9284d2477e51f9f3eda23dd658f1e1c1cefe59f --- /dev/null +++ b/source/libs/executor/test/tSimpleHashTests.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "taos.h" +#include "thash.h" +#include "tsimplehash.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wwrite-strings" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wsign-compare" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +TEST(testCase, tSimpleHashTest) { + SSHashObj *pHashObj = + tSimpleHashInit(8, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), sizeof(int64_t), sizeof(int64_t)); + + assert(pHashObj != nullptr); + + ASSERT_EQ(0, tSimpleHashGetSize(pHashObj)); + + int64_t originKeySum = 0; + for (int64_t i = 1; i <= 100; ++i) { + originKeySum += i; + tSimpleHashPut(pHashObj, (const void *)&i, (const void *)&i); + ASSERT_EQ(i, tSimpleHashGetSize(pHashObj)); + } + + for (int64_t i = 1; i <= 100; ++i) { + void *data = tSimpleHashGet(pHashObj, (const void *)&i); + ASSERT_EQ(i, *(int64_t *)data); + } + + + void *data = NULL; + int32_t iter = 0; + int64_t keySum = 0; + int64_t dataSum = 0; + while ((data = tSimpleHashIterate(pHashObj, data, &iter))) { + void *key = tSimpleHashGetKey(pHashObj, data, NULL); + keySum += *(int64_t *)key; + dataSum += *(int64_t *)data; + } + + ASSERT_EQ(keySum, dataSum); + ASSERT_EQ(keySum, originKeySum); + + for (int64_t i = 1; i <= 100; ++i) { + tSimpleHashRemove(pHashObj, (const void *)&i); + ASSERT_EQ(100 - i, tSimpleHashGetSize(pHashObj)); + } +} + +#pragma GCC diagnostic pop \ No newline at end of file