}
}
- 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);
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;
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)
{
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 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)
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)
{
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;
+}
) ^ 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 {
* 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().
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
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);
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
/*
#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); \