Fixes to Unit Test, attempt to improve hash use

This commit is contained in:
Stephen Seo 2019-02-13 17:49:24 +09:00
parent 1bab7694cf
commit f1a13cde5c
4 changed files with 98 additions and 10 deletions

View file

@ -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); char *temp = malloc(4 + hm->unitSize);
memcpy(temp, &key, 4); 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_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); free(temp);
return NULL; return NULL;
@ -126,7 +138,7 @@ int UDPC_HashMap_remove(UDPC_HashMap *hm, uint32_t key)
return 0; 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) 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; 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) 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) 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); 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(newBuckets[hash]->size < newBuckets[hash]->alloc_size)
{ {
if(UDPC_Deque_push_back(newBuckets[hash], data, 4 + hm->unitSize) == 0) 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) for(int x = 0; x * (4 + hm->unitSize) < hm->overflow->size; ++x)
{ {
data = UDPC_Deque_index_ptr(hm->overflow, 4 + hm->unitSize, 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(newBuckets[hash]->size < newBuckets[hash]->alloc_size)
{ {
if(UDPC_Deque_push_back(newBuckets[hash], data, 4 + hm->unitSize) == 0) 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; 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;
}

View file

@ -15,9 +15,11 @@
) ^ 0x96969696 \ ) ^ 0x96969696 \
) )
#define UDPC_HASHMAP_INIT_CAPACITY 8 #define UDPC_HASHMAP_INIT_CAPACITY 13
#define UDPC_HASHMAP_BUCKET_SIZE 4 #define UDPC_HASHMAP_BUCKET_SIZE 4
#define UDPC_HASHMAP_MOD(k, m) ((UDPC_HASH32(k) % (m * 2 + 1)) % m)
#include "UDPC_Deque.h" #include "UDPC_Deque.h"
typedef struct { 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 * Note if size already equals capacity, the hash map's capacity is doubled
* with UDPC_HashMap_realloc(). realloc requires rehashing of all items which * with UDPC_HashMap_realloc(). realloc requires rehashing of all items which
* may be costly. * 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 * 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 * duplicate inserted will be the first returned with get() and first removed
* with remove(). * with remove().
@ -86,4 +91,9 @@ uint32_t UDPC_HashMap_get_size(UDPC_HashMap *hm);
uint32_t UDPC_HashMap_get_capacity(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 #endif

View file

@ -281,7 +281,7 @@ void TEST_HASHMAP()
temp = x * 100; temp = x * 100;
ASSERT_EQ_MEM(UDPC_HashMap_get(hm, x), &temp, sizeof(int)); ASSERT_EQ_MEM(UDPC_HashMap_get(hm, x), &temp, sizeof(int));
} }
ASSERT_EQ(hm->capacity, 8); ASSERT_GTE(hm->capacity, 8);
temp = 800; temp = 800;
ASSERT_NEQ(UDPC_HashMap_insert(hm, 8, &temp), NULL); ASSERT_NEQ(UDPC_HashMap_insert(hm, 8, &temp), NULL);
@ -290,14 +290,27 @@ void TEST_HASHMAP()
temp = x * 100; temp = x * 100;
ASSERT_EQ_MEM(UDPC_HashMap_get(hm, x), &temp, sizeof(int)); 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) for(int x = 0; x < 9; ++x)
{ {
ASSERT_NEQ(UDPC_HashMap_remove(hm, x), 0); ASSERT_NEQ(UDPC_HashMap_remove(hm, x), 0);
} }
ASSERT_EQ(hm->size, 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 // TODO DEBUG
/* /*

View file

@ -26,6 +26,18 @@
#define ASSERT_NEQ_MEM(x, y, size) \ #define ASSERT_NEQ_MEM(x, y, size) \
if(memcmp(x, y, size) == 0) { printf("%d: ASSERT_NEQ_MEM(%s, %s, %s) FAILED\n", \ 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; __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) { \ #define UNITTEST_REPORT(x) { \
printf("%s: %d/%d tests failed\n", #x, UDPC_uts.failed, UDPC_uts.total); \ printf("%s: %d/%d tests failed\n", #x, UDPC_uts.failed, UDPC_uts.total); \