From f1a13cde5c94f195bb83a2ed308e4a09c6ed8b67 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 13 Feb 2019 17:49:24 +0900 Subject: [PATCH] Fixes to Unit Test, attempt to improve hash use --- src/UDPC_HashMap.c | 65 ++++++++++++++++++++++++++++++++++++---- src/UDPC_HashMap.h | 12 +++++++- src/test/UDPC_UnitTest.c | 19 ++++++++++-- src/test/UDPC_UnitTest.h | 12 ++++++++ 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/src/UDPC_HashMap.c b/src/UDPC_HashMap.c index f81d2c3..6b31f2e 100644 --- a/src/UDPC_HashMap.c +++ b/src/UDPC_HashMap.c @@ -90,7 +90,7 @@ void* UDPC_HashMap_insert(UDPC_HashMap *hm, uint32_t key, void *data) } } - uint32_t hash = UDPC_HASH32(key) % hm->capacity; + uint32_t hash = UDPC_HASHMAP_MOD(key, hm->capacity); char *temp = malloc(4 + hm->unitSize); memcpy(temp, &key, 4); @@ -101,7 +101,19 @@ void* UDPC_HashMap_insert(UDPC_HashMap *hm, uint32_t key, void *data) if(UDPC_Deque_get_available(hm->buckets[hash]) == 0) { - if(UDPC_Deque_push_back(hm->overflow, temp, 4 + hm->unitSize) == 0) + if(UDPC_Deque_get_available(hm->overflow) == 0) + { + free(temp); + if(UDPC_HashMap_realloc(hm, hm->capacity * 2) != 0) + { + return UDPC_HashMap_INTERNAL_reinsert(hm, key, data); + } + else + { + return NULL; + } + } + else if(UDPC_Deque_push_back(hm->overflow, temp, 4 + hm->unitSize) == 0) { free(temp); return NULL; @@ -126,7 +138,7 @@ int UDPC_HashMap_remove(UDPC_HashMap *hm, uint32_t key) return 0; } - uint32_t hash = UDPC_HASH32(key) % hm->capacity; + uint32_t hash = UDPC_HASHMAP_MOD(key, hm->capacity); for(int x = 0; x * (4 + hm->unitSize) < hm->buckets[hash]->size; ++x) { @@ -178,7 +190,7 @@ void* UDPC_HashMap_get(UDPC_HashMap *hm, uint32_t key) return NULL; } - uint32_t hash = UDPC_HASH32(key) % hm->capacity; + uint32_t hash = UDPC_HASHMAP_MOD(key, hm->capacity); for(int x = 0; x * (4 + hm->unitSize) < hm->buckets[hash]->size; ++x) { @@ -239,7 +251,7 @@ int UDPC_HashMap_realloc(UDPC_HashMap *hm, uint32_t newCapacity) for(int y = 0; y * (4 + hm->unitSize) < hm->buckets[x]->size; ++y) { data = UDPC_Deque_index_ptr(hm->buckets[x], 4 + hm->unitSize, y); - hash = UDPC_HASH32(*((uint32_t*)data)) % newCapacity; + hash = UDPC_HASHMAP_MOD(*((uint32_t*)data), newCapacity); if(newBuckets[hash]->size < newBuckets[hash]->alloc_size) { if(UDPC_Deque_push_back(newBuckets[hash], data, 4 + hm->unitSize) == 0) @@ -265,7 +277,7 @@ int UDPC_HashMap_realloc(UDPC_HashMap *hm, uint32_t newCapacity) for(int x = 0; x * (4 + hm->unitSize) < hm->overflow->size; ++x) { data = UDPC_Deque_index_ptr(hm->overflow, 4 + hm->unitSize, x); - hash = UDPC_HASH32(*((uint32_t*)data)) % newCapacity; + hash = UDPC_HASHMAP_MOD(*((uint32_t*)data), newCapacity); if(newBuckets[hash]->size < newBuckets[hash]->alloc_size) { if(UDPC_Deque_push_back(newBuckets[hash], data, 4 + hm->unitSize) == 0) @@ -328,3 +340,44 @@ uint32_t UDPC_HashMap_get_capacity(UDPC_HashMap *hm) { return hm->capacity; } + +void* UDPC_HashMap_INTERNAL_reinsert(UDPC_HashMap *hm, uint32_t key, void *data) +{ + if(hm->capacity <= hm->size) + { + return NULL; + } + + uint32_t hash = UDPC_HASHMAP_MOD(key, hm->capacity); + + char *temp = malloc(4 + hm->unitSize); + memcpy(temp, &key, 4); + if(hm->unitSize > 0) + { + memcpy(temp + 4, data, hm->unitSize); + } + + if(UDPC_Deque_get_available(hm->buckets[hash]) == 0) + { + if(UDPC_Deque_get_available(hm->overflow) == 0) + { + free(temp); + return NULL; + } + else if(UDPC_Deque_push_back(hm->overflow, temp, 4 + hm->unitSize) == 0) + { + free(temp); + return NULL; + } + } + else if(UDPC_Deque_push_back(hm->buckets[hash], temp, 4 + hm->unitSize) == 0) + { + free(temp); + return NULL; + } + + free(temp); + ++hm->size; + temp = UDPC_Deque_get_back_ptr(hm->buckets[hash], 4 + hm->unitSize); + return temp + 4; +} diff --git a/src/UDPC_HashMap.h b/src/UDPC_HashMap.h index e58712c..bbe35b0 100644 --- a/src/UDPC_HashMap.h +++ b/src/UDPC_HashMap.h @@ -15,9 +15,11 @@ ) ^ 0x96969696 \ ) -#define UDPC_HASHMAP_INIT_CAPACITY 8 +#define UDPC_HASHMAP_INIT_CAPACITY 13 #define UDPC_HASHMAP_BUCKET_SIZE 4 +#define UDPC_HASHMAP_MOD(k, m) ((UDPC_HASH32(k) % (m * 2 + 1)) % m) + #include "UDPC_Deque.h" typedef struct { @@ -46,6 +48,9 @@ void UDPC_HashMap_destroy(UDPC_HashMap *hashMap); * Note if size already equals capacity, the hash map's capacity is doubled * with UDPC_HashMap_realloc(). realloc requires rehashing of all items which * may be costly. + * Also, if the hash map runs out of space for a specific key to insert, it will + * also invoke realloc() with double the previous capacity and will attempt to + * insert again afterwards. * It is possible to insert items with duplicate keys. In that case, the first * duplicate inserted will be the first returned with get() and first removed * with remove(). @@ -86,4 +91,9 @@ uint32_t UDPC_HashMap_get_size(UDPC_HashMap *hm); uint32_t UDPC_HashMap_get_capacity(UDPC_HashMap *hm); +/*! + * \brief A variant of insert that does not try to realloc() on no more space + */ +void* UDPC_HashMap_INTERNAL_reinsert(UDPC_HashMap *hm, uint32_t key, void *data); + #endif diff --git a/src/test/UDPC_UnitTest.c b/src/test/UDPC_UnitTest.c index 0fd71a8..9da1f86 100644 --- a/src/test/UDPC_UnitTest.c +++ b/src/test/UDPC_UnitTest.c @@ -281,7 +281,7 @@ void TEST_HASHMAP() temp = x * 100; ASSERT_EQ_MEM(UDPC_HashMap_get(hm, x), &temp, sizeof(int)); } - ASSERT_EQ(hm->capacity, 8); + ASSERT_GTE(hm->capacity, 8); temp = 800; ASSERT_NEQ(UDPC_HashMap_insert(hm, 8, &temp), NULL); @@ -290,14 +290,27 @@ void TEST_HASHMAP() temp = x * 100; ASSERT_EQ_MEM(UDPC_HashMap_get(hm, x), &temp, sizeof(int)); } - ASSERT_EQ(hm->capacity, 16); + ASSERT_GTE(hm->capacity, 16); for(int x = 0; x < 9; ++x) { ASSERT_NEQ(UDPC_HashMap_remove(hm, x), 0); } ASSERT_EQ(hm->size, 0); - ASSERT_EQ(hm->capacity, 16); + ASSERT_GTE(hm->capacity, 16); + + for(int x = 0; x < 32; ++x) + { + temp = x * 100; + ASSERT_NEQ(UDPC_HashMap_insert(hm, x, &temp), NULL); + } + ASSERT_EQ(hm->size, 32); + + for(int x = 0; x < 32; ++x) + { + temp = x * 100; + ASSERT_EQ_MEM(UDPC_HashMap_get(hm, x), &temp, sizeof(int)); + } // TODO DEBUG /* diff --git a/src/test/UDPC_UnitTest.h b/src/test/UDPC_UnitTest.h index b98950c..1940761 100644 --- a/src/test/UDPC_UnitTest.h +++ b/src/test/UDPC_UnitTest.h @@ -26,6 +26,18 @@ #define ASSERT_NEQ_MEM(x, y, size) \ if(memcmp(x, y, size) == 0) { printf("%d: ASSERT_NEQ_MEM(%s, %s, %s) FAILED\n", \ __LINE__, #x, #y, #size); ++UDPC_uts.failed; } ++UDPC_uts.total; +#define ASSERT_GT(x, y) \ + if(x <= y) { printf("%d: ASSERT_GT(%s, %s) FAILED\n", __LINE__, #x, #y); \ + ++UDPC_uts.failed; } ++UDPC_uts.total; +#define ASSERT_GTE(x, y) \ + if(x < y) { printf("%d: ASSERT_GTE(%s, %s) FAILED\n", __LINE__, #x, #y); \ + ++UDPC_uts.failed; } ++UDPC_uts.total; +#define ASSERT_LT(x, y) \ + if(x >= y) { printf("%d: ASSERT_LT(%s, %s) FAILED\n", __LINE__, #x, #y); \ + ++UDPC_uts.failed; } ++UDPC_uts.total; +#define ASSERT_LTE(x, y) \ + if(x > y) { printf("%d: ASSERT_LTE(%s, %s) FAILED\n", __LINE__, #x, #y); \ + ++UDPC_uts.failed; } ++UDPC_uts.total; #define UNITTEST_REPORT(x) { \ printf("%s: %d/%d tests failed\n", #x, UDPC_uts.failed, UDPC_uts.total); \