Prechádzať zdrojové kódy

Use makros for hashmap instancing

Kajetan Johannes Hammerle 7 mesiacov pred
rodič
commit
cc02cbe812

+ 16 - 9
include/core/Components.h

@@ -4,13 +4,18 @@
 #include "core/HashMap.h"
 #include "core/List.h"
 
+#define isInvalidKeySize(key) ((key) == 0)
+#define equalSize(a, b) ((a) == (b))
+#define hashSize(key) (key)
+
 LIST(size_t, Size)
+HASHMAP(size_t, size_t, Size)
 
 typedef size_t Entity;
 
 #define COMPONENTS(T, N)                                                       \
     typedef struct {                                                           \
-        CoreHashMap entityToIndex;                                             \
+        HashMapSize entityToIndex;                                             \
         ListSize indexToEntity;                                                \
         List##N components;                                                    \
     } Components##N;                                                           \
@@ -41,13 +46,13 @@ typedef size_t Entity;
 
 #define COMPONENTS_SOURCE(T, N)                                                \
     void initComponents##N(Components##N* c) {                                 \
-        coreInitHashMap(&c->entityToIndex, sizeof(Entity), sizeof(size_t));    \
+        initHashMapSize(&c->entityToIndex);                                    \
         initListSize(&c->indexToEntity);                                       \
         initList##N(&c->components);                                           \
     }                                                                          \
                                                                                \
     void destroyComponents##N(Components##N* c) {                              \
-        coreDestroyHashMap(&c->entityToIndex);                                 \
+        destroyHashMapSize(&c->entityToIndex);                                 \
         destroyListSize(&c->indexToEntity);                                    \
         destroyList##N(&c->components);                                        \
     }                                                                          \
@@ -58,13 +63,13 @@ typedef size_t Entity;
             return component;                                                  \
         }                                                                      \
         size_t index = c->components.length;                                   \
-        corePutHashMapPair(&c->entityToIndex, &e, &index);                     \
+        *putHashMapKeySize(&c->entityToIndex, e) = index;                      \
         addListDataSize(&c->indexToEntity, e);                                 \
         return addEmptyListData##N(&c->components);                            \
     }                                                                          \
                                                                                \
     T* searchComponent##N(Components##N* c, Entity e) {                        \
-        size_t* index = coreSearchHashMapKey(&c->entityToIndex, &e);           \
+        size_t* index = searchHashMapKeySize(&c->entityToIndex, e);            \
         if(index == nullptr) {                                                 \
             return nullptr;                                                    \
         }                                                                      \
@@ -72,13 +77,13 @@ typedef size_t Entity;
     }                                                                          \
                                                                                \
     bool removeComponent##N(Components##N* c, Entity e) {                      \
-        size_t* indexP = coreSearchHashMapKey(&c->entityToIndex, &e);          \
+        size_t* indexP = searchHashMapKeySize(&c->entityToIndex, e);           \
         if(indexP == nullptr) {                                                \
             return false;                                                      \
         }                                                                      \
         size_t lastIndex = c->components.length - 1;                           \
         size_t index = *indexP;                                                \
-        coreRemoveHashMapKey(&c->entityToIndex, &e);                           \
+        removeHashMapKeySize(&c->entityToIndex, e);                            \
         removeListIndexBySwap##N(&c->components, index);                       \
         if(index == lastIndex) {                                               \
             removeListIndexBySwapSize(&c->indexToEntity, index);               \
@@ -86,7 +91,7 @@ typedef size_t Entity;
         }                                                                      \
         Entity other = *getListIndexSize(&c->indexToEntity, lastIndex);        \
         removeListIndexBySwapSize(&c->indexToEntity, index);                   \
-        corePutHashMapPair(&c->entityToIndex, &other, &index);                 \
+        *putHashMapKeySize(&c->entityToIndex, other) = index;                  \
         return true;                                                           \
     }                                                                          \
                                                                                \
@@ -117,6 +122,8 @@ typedef size_t Entity;
                                                                                \
     T* getComponentsEnd##N(Components##N* c) {                                 \
         return getListEnd##N(&c->components);                                  \
-    }
+    }                                                                          \
+    LIST_SOURCE(size_t, Size)                                                  \
+    HASHMAP_SOURCE(size_t, size_t, Size)
 
 #endif

+ 224 - 79
include/core/HashMap.h

@@ -1,89 +1,234 @@
 #ifndef CORE_HASHMAP_H
 #define CORE_HASHMAP_H
 
+#include "core/ToString.h"
 #include "core/Types.h"
+#include "core/Utility.h"
 
-struct CoreHashMapT;
-typedef struct CoreHashMapT CoreHashMap;
+size_t hashString(const char* key);
+size_t roundUp2(size_t n);
 
-typedef size_t (*CoreHasher)(const void* key, size_t n);
-typedef bool (*CoreEqual)(const void* keyA, const void* keyB, size_t n);
-typedef void* (*CoreSearchValue)(const CoreHashMap* m, const void* key);
+#define isInvalidKeyInt(key) ((key) == 0)
+#define equalInt(a, b) ((a) == (b))
+#define hashInt(key) ((size_t)key)
+#define isInvalidKeySize(key) ((key) == 0)
+#define equalSize(a, b) ((a) == (b))
+#define hashSize(key) (key)
 
-struct CoreHashMapT {
-    void* keys;
-    void* values;
-    size_t keySize;
-    size_t valueSize;
-    size_t capacity;
-    size_t entries;
-    CoreHasher hasher;
-    CoreEqual equal;
-    CoreSearchValue search;
-};
+#define HASHMAP(K, V, N)                                                       \
+    typedef struct {                                                           \
+        K* keys;                                                               \
+        V* values;                                                             \
+        size_t capacity;                                                       \
+        size_t entries;                                                        \
+    } HashMap##N;                                                              \
+                                                                               \
+    void initHashMap##N(HashMap##N* m);                                        \
+    void destroyHashMap##N(HashMap##N* m);                                     \
+    void rehashHashMap##N(HashMap##N* m, size_t minCapacity);                  \
+    V* putHashMapKey##N(HashMap##N* m, K key);                                 \
+    V* searchHashMapKey##N(const HashMap##N* m, K key);                        \
+    void clearHashMap##N(HashMap##N* m);                                       \
+    bool removeHashMapKey##N(HashMap##N* m, K key);                            \
+                                                                               \
+    typedef struct {                                                           \
+        const K* key;                                                          \
+        V* value;                                                              \
+    } HashMapNode##N;                                                          \
+                                                                               \
+    typedef struct {                                                           \
+        K* key;                                                                \
+        K* endKey;                                                             \
+        V* value;                                                              \
+        V* endValue;                                                           \
+        const HashMap##N* map;                                                 \
+        HashMapNode##N node;                                                   \
+    } HashMapIterator##N;                                                      \
+                                                                               \
+    void initHashMapIterator##N(HashMapIterator##N* mi, const HashMap##N* m);  \
+    bool hasNextHashMapNode##N(HashMapIterator##N* mi);                        \
+    HashMapNode##N* nextHashMapNode##N(HashMapIterator##N* mi);                \
+    size_t toStringHashMap##N(const HashMap##N* m, char* buffer, size_t n,     \
+                              CoreToString keyString,                          \
+                              CoreToString valueString);
 
-void coreInitHashMap(CoreHashMap* m, size_t keySize, size_t valueSize);
-void coreInitHashMapF(CoreHashMap* m, size_t keySize, size_t valueSize,
-                      CoreHasher hasher, CoreEqual equal);
-void coreDestroyHashMap(CoreHashMap* m);
-void coreRehashHashMap(CoreHashMap* m, size_t minCapacity);
-void* corePutHashMapPair(CoreHashMap* m, const void* key, const void* value);
-#define corePutTypedHashMapPair(m, kType, key, vType, ...)                     \
-    corePutHashMapPair(m, &(kType){key}, &(vType){__VA_ARGS__})
-void* coreSearchHashMapKey(const CoreHashMap* m, const void* key);
-#define coreSearchTypedHashMapKey(m, kType, key, vType)                        \
-    ((vType*)coreSearchHashMapKey(m, &(kType){key}))
-void coreClearHashMap(CoreHashMap* m);
-bool coreRemoveHashMapKey(CoreHashMap* m, const void* key);
-#define coreRemoveTypedHashMapKey(m, kType, key)                               \
-    coreRemoveHashMapKey(m, &(kType){key})
-
-size_t coreHashString(const void* key, size_t n);
-size_t coreHashKey(const void* key, size_t n);
-bool coreCompareKeys(const void* keyA, const void* keyB, size_t n);
-
-typedef struct {
-    const void* key;
-    void* value;
-} CoreHashMapNode;
-
-typedef struct {
-    char* key;
-    char* endKey;
-    char* value;
-    char* endValue;
-    const CoreHashMap* map;
-    CoreHashMapNode node;
-} CoreHashMapIterator;
-
-void coreInitHashMapIterator(CoreHashMapIterator* mi, const CoreHashMap* m);
-bool coreHasNextHashMapNode(CoreHashMapIterator* mi);
-CoreHashMapNode* coreNextHashMapNode(CoreHashMapIterator* mi);
-
-#ifdef IMPORT_CORE
-#define HashMap CoreHashMap
-#define Hasher CoreHasher
-#define Equal CoreEqual
-#define SearchValue CoreSearchValue
-#define initHashMap coreInitHashMap
-#define initHashMapF coreInitHashMapF
-#define destroyHashMap coreDestroyHashMap
-#define rehashHashMap coreRehashHashMap
-#define putHashMapPair corePutHashMapPair
-#define putTypedHashMapPair corePutTypedHashMapPair
-#define searchHashMapKey coreSearchHashMapKey
-#define searchTypedHashMapKey coreSearchTypedHashMapKey
-#define clearHashMap coreClearHashMap
-#define removeHashMapKey coreRemoveHashMapKey
-#define removeTypedHashMapKey coreRemoveTypedHashMapKey
-#define hashString coreHashString
-#define hashKey coreHashKey
-#define compareKeys coreCompareKeys
-#define HashMapNode CoreHashMapNode
-#define HashMapIterator CoreHashMapIterator
-#define initHashMapIterator coreInitHashMapIterator
-#define hasNextHashMapNode coreHasNextHashMapNode
-#define nextHashMapNode coreNextHashMapNode
-#endif
+#define HASHMAP_SOURCE(K, V, N)                                                \
+    static size_t searchSlot##N(HashMap##N* m, K key) {                        \
+        size_t rehashFactor = 2;                                               \
+        while(true) {                                                          \
+            rehashHashMap##N(m, m->entries* rehashFactor + 1);                 \
+            if(isInvalidKey##N(key)) {                                         \
+                return m->capacity - 1;                                        \
+            }                                                                  \
+            size_t baseHash = hash##N(key) * 514685581u;                       \
+            size_t end = m->capacity - 2;                                      \
+            /* rehash on bad clustering */                                     \
+            for(size_t i = 0; i <= 5; i++) {                                   \
+                size_t hash = (baseHash + i) & end;                            \
+                K keyEntry = m->keys[hash];                                    \
+                if(isInvalidKey##N(keyEntry) || equal##N(keyEntry, key)) {     \
+                    return hash;                                               \
+                }                                                              \
+            }                                                                  \
+            rehashFactor *= 2;                                                 \
+        }                                                                      \
+    }                                                                          \
+                                                                               \
+    void initHashMap##N(HashMap##N* m) {                                       \
+        m->keys = nullptr;                                                     \
+        m->values = nullptr;                                                   \
+        m->capacity = 0;                                                       \
+        m->entries = 0;                                                        \
+    }                                                                          \
+                                                                               \
+    void destroyHashMap##N(HashMap##N* m) {                                    \
+        coreFree(m->keys);                                                     \
+        coreFree(m->values);                                                   \
+        *m = (HashMap##N){0};                                                  \
+    }                                                                          \
+                                                                               \
+    void rehashHashMap##N(HashMap##N* m, size_t minCapacity) {                 \
+        if(minCapacity <= m->capacity) {                                       \
+            return;                                                            \
+        }                                                                      \
+        size_t l = roundUp2(coreMaxSize(minCapacity, 8lu)) + 1;                \
+        HashMap##N map;                                                        \
+        initHashMap##N(&map);                                                  \
+        size_t keyBytes = l * sizeof(K);                                       \
+        map.keys = coreAllocate(keyBytes);                                     \
+        memset(map.keys, 0, keyBytes);                                         \
+        memset(map.keys + (l - 1), 1, sizeof(K));                              \
+        map.values = coreAllocate(l * sizeof(V));                              \
+        map.capacity = l;                                                      \
+                                                                               \
+        size_t length = m->capacity;                                           \
+        if(length > 0) {                                                       \
+            length--;                                                          \
+            for(size_t i = 0; i < length; i++) {                               \
+                K keyEntry = m->keys[i];                                       \
+                if(!isInvalidKey##N(keyEntry)) {                               \
+                    *putHashMapKey##N(&map, keyEntry) = m->values[i];          \
+                }                                                              \
+            }                                                                  \
+            K keyEntry = m->keys[length];                                      \
+            if(isInvalidKey##N(keyEntry)) {                                    \
+                *putHashMapKey##N(&map, keyEntry) = m->values[length];         \
+            }                                                                  \
+        }                                                                      \
+        coreSwap(&map, m);                                                     \
+        destroyHashMap##N(&map);                                               \
+    }                                                                          \
+                                                                               \
+    V* putHashMapKey##N(HashMap##N* m, K key) {                                \
+        size_t index = searchSlot##N(m, key);                                  \
+        K* keyEntry = m->keys + index;                                         \
+        if(equal##N(*keyEntry, key)) {                                         \
+            return m->values + index;                                          \
+        }                                                                      \
+        m->entries++;                                                          \
+        *keyEntry = key;                                                       \
+        return m->values + index;                                              \
+    }                                                                          \
+                                                                               \
+    V* searchHashMapKey##N(const HashMap##N* m, K key) {                       \
+        if(m->capacity != 0) {                                                 \
+            if(isInvalidKey##N(key)) {                                         \
+                size_t i = m->capacity - 1;                                    \
+                return isInvalidKey##N(m->keys[i]) ? m->values + i : nullptr;  \
+            }                                                                  \
+            size_t baseHash = hash##N(key) * 514685581u;                       \
+            size_t end = m->capacity - 2;                                      \
+            for(size_t i = 0; i <= end; i++) {                                 \
+                size_t hash = (baseHash + i) & end;                            \
+                K keyEntry = m->keys[hash];                                    \
+                if(equal##N(keyEntry, key)) {                                  \
+                    return m->values + hash;                                   \
+                } else if(isInvalidKey##N(keyEntry)) {                         \
+                    return nullptr;                                            \
+                }                                                              \
+            }                                                                  \
+        }                                                                      \
+        return nullptr;                                                        \
+    }                                                                          \
+                                                                               \
+    void clearHashMap##N(HashMap##N* m) {                                      \
+        if(m->keys == nullptr) {                                               \
+            return;                                                            \
+        }                                                                      \
+        m->entries = 0;                                                        \
+        memset(m->keys, 0, m->capacity * sizeof(K));                           \
+        memset(m->keys + (m->capacity - 1), 1, sizeof(K));                     \
+    }                                                                          \
+                                                                               \
+    bool removeHashMapKey##N(HashMap##N* m, K key) {                           \
+        /* ToDo: This is a very slow remove */                                 \
+        HashMap##N n;                                                          \
+        initHashMap##N(&n);                                                    \
+        HashMapIterator##N i;                                                  \
+        initHashMapIterator##N(&i, m);                                         \
+        bool r = false;                                                        \
+        while(hasNextHashMapNode##N(&i)) {                                     \
+            HashMapNode##N* node = nextHashMapNode##N(&i);                     \
+            if(!equal##N(key, *node->key)) {                                   \
+                *putHashMapKey##N(&n, *node->key) = *node->value;              \
+            } else {                                                           \
+                r = true;                                                      \
+            }                                                                  \
+        }                                                                      \
+        coreSwap(&n, m);                                                       \
+        destroyHashMap##N(&n);                                                 \
+        return r;                                                              \
+    }                                                                          \
+                                                                               \
+    void initHashMapIterator##N(HashMapIterator##N* mi, const HashMap##N* m) { \
+        mi->key = m->keys;                                                     \
+        mi->endKey = mi->key + m->capacity;                                    \
+        mi->value = m->values;                                                 \
+        mi->endValue = mi->value + m->capacity;                                \
+        mi->map = m;                                                           \
+    }                                                                          \
+                                                                               \
+    bool hasNextHashMapNode##N(HashMapIterator##N* mi) {                       \
+        while(mi->key != mi->endKey) {                                         \
+            K* nextKey = mi->key + 1;                                          \
+            if(isInvalidKey##N(*mi->key) == (nextKey == mi->endKey)) {         \
+                break;                                                         \
+            }                                                                  \
+            mi->key = nextKey;                                                 \
+            mi->value++;                                                       \
+        }                                                                      \
+        return mi->key != mi->endKey;                                          \
+    }                                                                          \
+                                                                               \
+    HashMapNode##N* nextHashMapNode##N(HashMapIterator##N* mi) {               \
+        mi->node.key = mi->key;                                                \
+        mi->node.value = mi->value;                                            \
+        mi->key++;                                                             \
+        mi->value++;                                                           \
+        return &mi->node;                                                      \
+    }                                                                          \
+                                                                               \
+    size_t toStringHashMap##N(const HashMap##N* m, char* buffer, size_t n,     \
+                              CoreToString keyString,                          \
+                              CoreToString valueString) {                      \
+        size_t w = 0;                                                          \
+        coreStringAdd(&w, &buffer, &n, coreToString(buffer, n, "["));          \
+        bool notFirst = false;                                                 \
+        HashMapIterator##N i;                                                  \
+        initHashMapIterator##N(&i, m);                                         \
+        while(hasNextHashMapNode##N(&i)) {                                     \
+            HashMapNode##N* node = nextHashMapNode##N(&i);                     \
+            if(notFirst) {                                                     \
+                coreStringAdd(&w, &buffer, &n, coreToString(buffer, n, ", ")); \
+            }                                                                  \
+            notFirst = true;                                                   \
+            coreStringAdd(&w, &buffer, &n, keyString(node->key, buffer, n));   \
+            coreStringAdd(&w, &buffer, &n, coreToString(buffer, n, " = "));    \
+            coreStringAdd(&w, &buffer, &n,                                     \
+                          valueString(node->value, buffer, n));                \
+        }                                                                      \
+        stringAdd(&w, &buffer, &n, coreToString(buffer, n, "]"));              \
+        return w;                                                              \
+    }
 
 #endif

+ 1 - 6
include/core/ToString.h

@@ -13,9 +13,6 @@ void coreStringAdd(size_t* w, char** buffer, size_t* n, size_t shift);
 struct CoreQueueT;
 size_t coreToStringQueue(const struct CoreQueueT* r, char* buffer, size_t n,
                          CoreToString c);
-struct CoreHashMapT;
-size_t coreToStringHashMap(const struct CoreHashMapT* m, char* buffer, size_t n,
-                           CoreToString keyString, CoreToString valueString);
 
 #define CORE_STRUCT_TO_STRING(type)                                            \
     struct Core##type##T;                                                      \
@@ -32,7 +29,6 @@ CORE_STRUCT_TO_STRING(Plane)
 #define toStringInt coreToStringInt
 #define stringAdd coreStringAdd
 #define toStringQueue coreToStringQueue
-#define toStringHashMap coreToStringHashMap
 #define toStringBitArray coreToStringBitArray
 #define toStringBox coreToStringBox
 #define toStringMatrix coreToStringMatrix
@@ -52,8 +48,7 @@ CORE_STRUCT_TO_STRING(Plane)
         CORE_STRUCT_PAIR(Box),                                                 \
         CORE_STRUCT_PAIR(Matrix),                                              \
         CORE_STRUCT_PAIR(Plane),                                               \
-        CORE_STRUCT_PAIR(Queue),                                               \
-        CORE_STRUCT_PAIR(HashMap))(t, __VA_ARGS__)
+        CORE_STRUCT_PAIR(Queue))(t, __VA_ARGS__)
 #endif
 
 #endif

+ 18 - 15
performance/Main.c

@@ -5,12 +5,15 @@
 #include "core/Random.h"
 #include "core/Utility.h"
 
-static i64 testSearch(const HashMap* m) {
+HASHMAP(int, int, Int)
+HASHMAP_SOURCE(int, int, Int)
+
+static i64 testSearch(const HashMapInt* m) {
     i64 nanos = coreGetNanos();
     volatile int sum = 0;
     for(int i = 0; i < 10000; i++) {
         for(int k = -5000; k < 5000; k++) {
-            const int* s = coreSearchTypedHashMapKey(m, int, i + k, int);
+            const int* s = searchHashMapKeyInt(m, i + k);
             if(s != nullptr) {
                 sum = sum + *s;
             }
@@ -19,11 +22,11 @@ static i64 testSearch(const HashMap* m) {
     return coreGetNanos() - nanos;
 }
 
-static i64 testEmptySearch(const HashMap* m) {
+static i64 testEmptySearch(const HashMapInt* m) {
     i64 nanos = coreGetNanos();
     volatile int sum = 0;
     for(int i = 0; i < 100'000'000; i++) {
-        const int* s = coreSearchTypedHashMapKey(m, int, -i, int);
+        const int* s = searchHashMapKeyInt(m, -i);
         if(s != nullptr) {
             sum = sum + *s;
         }
@@ -31,26 +34,26 @@ static i64 testEmptySearch(const HashMap* m) {
     return coreGetNanos() - nanos;
 }
 
-static void fillOrder(HashMap* m) {
+static void fillOrder(HashMapInt* m) {
     i64 nanos = coreGetNanos();
     for(int i = 0; i < 10000; i++) {
-        corePutTypedHashMapPair(m, int, i, int, i* i);
+        *putHashMapKeyInt(m, i) = i * i;
     }
     printf("Fill Order: %ldns\n", coreGetNanos() - nanos);
 }
 
-static void fillChaos(HashMap* m) {
+static void fillChaos(HashMapInt* m) {
     i64 nanos = coreGetNanos();
     Random random;
     coreInitRandom(&random, 0);
     for(int i = 0; i < 10000; i++) {
         int r = coreRandomI32(&random, 0, 10000);
-        corePutTypedHashMapPair(m, int, r, int, r* r);
+        *putHashMapKeyInt(m, r) = r * r;
     }
     printf("Fill Chaos: %ldns\n", coreGetNanos() - nanos);
 }
 
-static i64 average(HashMap* m, i64 (*f)(const HashMap* m), int n) {
+static i64 average(HashMapInt* m, i64 (*f)(const HashMapInt* m), int n) {
     i64 sum = 0;
     for(int i = 0; i < n; i++) {
         sum += f(m);
@@ -59,23 +62,23 @@ static i64 average(HashMap* m, i64 (*f)(const HashMap* m), int n) {
 }
 
 static void order(int n) {
-    HashMap m;
-    coreInitHashMap(&m, sizeof(int), sizeof(int));
+    HashMapInt m;
+    initHashMapInt(&m);
     fillOrder(&m);
     puts("Order Probing");
     printf("Search | %ld ms\n", average(&m, testSearch, n));
     printf("EmptySearch | %ld ms\n", average(&m, testEmptySearch, n));
-    coreDestroyHashMap(&m);
+    destroyHashMapInt(&m);
 }
 
 static void chaos(int n) {
-    HashMap m;
-    coreInitHashMap(&m, sizeof(int), sizeof(int));
+    HashMapInt m;
+    initHashMapInt(&m);
     fillChaos(&m);
     puts("Chaos Probing");
     printf("Search | %ld ms\n", average(&m, testSearch, n));
     printf("EmptySearch | %ld ms\n", average(&m, testEmptySearch, n));
-    coreDestroyHashMap(&m);
+    destroyHashMapInt(&m);
 }
 
 int main() {

+ 6 - 264
src/HashMap.c

@@ -1,275 +1,17 @@
-#define IMPORT_CORE
 #include "core/HashMap.h"
 
-#include "core/Utility.h"
-
-static bool isInvalidKey(const HashMap* m, const void* key) {
-    const char* c = key;
-    for(size_t i = m->keySize; i > 0; i--) {
-        if(*(c++) != 0) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static void* getKey(const HashMap* m, size_t index) {
-    return (char*)m->keys + m->keySize * index;
-}
-
-static void* getValue(const HashMap* m, size_t index) {
-    return (char*)m->values + m->valueSize * index;
-}
-
-static size_t searchSlot(HashMap* m, const void* key) {
-    size_t rehashFactor = 2;
-    while(true) {
-        rehashHashMap(m, m->entries * rehashFactor + 1);
-        if(isInvalidKey(m, key)) {
-            return m->capacity - 1;
-        }
-        size_t baseHash = m->hasher(key, m->keySize) * 514685581u;
-        size_t end = m->capacity - 2;
-        // rehash on bad clustering
-        for(size_t i = 0; i <= 5; i++) {
-            size_t hash = (baseHash + i) & end;
-            void* keyEntry = getKey(m, hash);
-            if(isInvalidKey(m, keyEntry) ||
-               m->equal(keyEntry, key, m->keySize)) {
-                return hash;
-            }
-        }
-        rehashFactor *= 2;
-    }
-}
-
-#define SEARCH_VALUE(type, Type)                                               \
-    static void* searchValue##Type(const HashMap* m, const void* rawKey) {     \
-        type key = *(const type*)rawKey;                                       \
-        if(m->capacity != 0) {                                                 \
-            if(key == 0) {                                                     \
-                size_t i = m->capacity - 1;                                    \
-                return ((type*)m->keys)[i] == 0 ? getValue(m, i) : nullptr;    \
-            }                                                                  \
-            size_t baseHash = ((size_t)key) * 514685581u;                      \
-            size_t end = m->capacity - 2;                                      \
-            for(size_t i = 0; i <= end; i++) {                                 \
-                size_t hash = (baseHash + i) & end;                            \
-                type keyEntry = *(type*)getKey(m, hash);                       \
-                if(keyEntry == key) {                                          \
-                    return getValue(m, hash);                                  \
-                } else if(keyEntry == 0) {                                     \
-                    return nullptr;                                            \
-                }                                                              \
-            }                                                                  \
-        }                                                                      \
-        return nullptr;                                                        \
-    }
-SEARCH_VALUE(u32, U32)
-SEARCH_VALUE(u64, U64)
-
-static void* searchValue(const HashMap* m, const void* key) {
-    if(m->capacity != 0) {
-        if(isInvalidKey(m, key)) {
-            size_t i = m->capacity - 1;
-            return isInvalidKey(m, getKey(m, i)) ? getValue(m, i) : nullptr;
-        }
-        size_t baseHash = m->hasher(key, m->keySize) * 514685581u;
-        size_t end = m->capacity - 2;
-        for(size_t i = 0; i <= end; i++) {
-            size_t hash = (baseHash + i) & end;
-            void* keyEntry = getKey(m, hash);
-            if(m->equal(keyEntry, key, m->keySize)) {
-                return getValue(m, hash);
-            } else if(isInvalidKey(m, keyEntry)) {
-                return nullptr;
-            }
-        }
+size_t hashString(const char* key) {
+    size_t h = 0;
+    while(*key != '\0') {
+        h = 2120251889lu * h + (size_t)(*(key++));
     }
-    return nullptr;
+    return h;
 }
 
-static size_t roundUp2(size_t n) {
+size_t roundUp2(size_t n) {
     size_t w = 1;
     while(w < n) {
         w *= 2;
     }
     return w;
 }
-
-void initHashMap(HashMap* m, size_t keySize, size_t valueSize) {
-    initHashMapF(m, keySize, valueSize, hashKey, compareKeys);
-}
-
-void initHashMapF(HashMap* m, size_t keySize, size_t valueSize, Hasher hasherF,
-                  Equal equal) {
-    SearchValue search = searchValue;
-    if(hasherF == hashKey && equal == compareKeys) {
-        switch(keySize) {
-            case sizeof(u32): search = searchValueU32; break;
-            case sizeof(u64): search = searchValueU64; break;
-        }
-    }
-    m->keys = nullptr;
-    m->values = nullptr;
-    m->keySize = keySize;
-    m->valueSize = valueSize;
-    m->capacity = 0;
-    m->entries = 0;
-    m->hasher = hasherF;
-    m->equal = equal;
-    m->search = search;
-}
-
-void destroyHashMap(HashMap* m) {
-    cFree(m->keys);
-    cFree(m->values);
-    *m = (HashMap){0};
-}
-
-void rehashHashMap(HashMap* m, size_t minCapacity) {
-    if(minCapacity <= m->capacity) {
-        return;
-    }
-    size_t l = roundUp2(maxSize(minCapacity, 8lu)) + 1;
-    HashMap map;
-    initHashMapF(&map, m->keySize, m->valueSize, m->hasher, m->equal);
-    size_t keyBytes = l * m->keySize;
-    map.keys = cAllocate(keyBytes);
-    memset(map.keys, 0, keyBytes);
-    memset(getKey(&map, l - 1), 1, m->keySize);
-    map.values = cAllocate(l * m->valueSize);
-    map.capacity = l;
-
-    size_t length = m->capacity;
-    if(length > 0) {
-        length--;
-        for(size_t i = 0; i < length; i++) {
-            void* keyEntry = getKey(m, i);
-            if(!isInvalidKey(m, keyEntry)) {
-                putHashMapPair(&map, keyEntry, getValue(m, i));
-            }
-        }
-        void* keyEntry = getKey(m, length);
-        if(isInvalidKey(m, keyEntry)) {
-            putHashMapPair(&map, keyEntry, getValue(m, length));
-        }
-    }
-    swap(&map, m);
-    destroyHashMap(&map);
-}
-
-void* putHashMapPair(HashMap* m, const void* key, const void* value) {
-    size_t index = searchSlot(m, key);
-    void* keyEntry = getKey(m, index);
-    if(m->equal(keyEntry, key, m->keySize)) {
-        void* v = getValue(m, index);
-        memcpy(v, value, m->valueSize);
-        return v;
-    }
-    void* v = getValue(m, index);
-    memcpy(v, value, m->valueSize);
-    m->entries++;
-    memcpy(keyEntry, key, m->keySize);
-    return v;
-}
-
-void* searchHashMapKey(const HashMap* m, const void* key) {
-    return m->search(m, key);
-}
-
-void clearHashMap(HashMap* m) {
-    if(m->keys == nullptr) {
-        return;
-    }
-    m->entries = 0;
-    memset(m->keys, 0, m->capacity * m->keySize);
-    memset(getKey(m, m->capacity - 1), 1, m->keySize);
-}
-
-bool removeHashMapKey(HashMap* m, const void* key) {
-    // ToDo: This is a very slow remove
-    HashMap n;
-    initHashMapF(&n, m->keySize, m->valueSize, m->hasher, m->equal);
-    HashMapIterator i;
-    initHashMapIterator(&i, m);
-    bool r = false;
-    while(hasNextHashMapNode(&i)) {
-        HashMapNode* node = nextHashMapNode(&i);
-        if(!m->equal(key, node->key, n.keySize)) {
-            putHashMapPair(&n, node->key, node->value);
-        } else {
-            r = true;
-        }
-    }
-    swap(&n, m);
-    destroyHashMap(&n);
-    return r;
-}
-
-size_t hashString(const void* key, size_t n) {
-    if(n != sizeof(char*)) {
-        return 0;
-    }
-    const char* s = *(const char* const*)key;
-    size_t h = 0;
-    while(*s != '\0') {
-        h = 2120251889lu * h + (size_t)(*(s++));
-    }
-    return h;
-}
-
-size_t hashKey(const void* key, size_t n) {
-    size_t h = 0;
-    if(n <= sizeof(size_t)) {
-        memcpy(&h, key, n);
-        return h;
-    }
-    const char* cKey = key;
-    while(n > 0) {
-        size_t m = 0;
-        if(n >= sizeof(size_t)) {
-            memcpy(&m, cKey, sizeof(size_t));
-            cKey += sizeof(size_t);
-            n -= sizeof(size_t);
-        } else {
-            memcpy(&m, cKey, n);
-            cKey += n;
-            n = 0;
-        }
-        h ^= m;
-    }
-    return h;
-}
-
-bool compareKeys(const void* keyA, const void* keyB, size_t n) {
-    return memcmp(keyA, keyB, n) == 0;
-}
-
-void initHashMapIterator(HashMapIterator* mi, const HashMap* m) {
-    mi->key = m->keys;
-    mi->endKey = mi->key + m->capacity * m->keySize;
-    mi->value = m->values;
-    mi->endValue = mi->value + m->capacity * m->valueSize;
-    mi->map = m;
-}
-
-bool hasNextHashMapNode(HashMapIterator* mi) {
-    while(mi->key != mi->endKey) {
-        char* nextKey = mi->key + mi->map->keySize;
-        if(isInvalidKey(mi->map, mi->key) == (nextKey == mi->endKey)) {
-            break;
-        }
-        mi->key = nextKey;
-        mi->value += mi->map->valueSize;
-    }
-    return mi->key != mi->endKey;
-}
-
-HashMapNode* nextHashMapNode(HashMapIterator* mi) {
-    mi->node.key = mi->key;
-    mi->node.value = mi->value;
-    mi->key += mi->map->keySize;
-    mi->value += mi->map->valueSize;
-    return &mi->node;
-}

+ 0 - 21
src/ToString.c

@@ -98,24 +98,3 @@ size_t toStringQueue(const Queue* r, char* buffer, size_t n, ToString c) {
     stringAdd(&w, &buffer, &n, coreToString(buffer, n, "]"));
     return w;
 }
-
-size_t toStringHashMap(const HashMap* m, char* buffer, size_t n,
-                       ToString keyString, ToString valueString) {
-    size_t w = 0;
-    stringAdd(&w, &buffer, &n, coreToString(buffer, n, "["));
-    bool notFirst = false;
-    HashMapIterator i;
-    initHashMapIterator(&i, m);
-    while(hasNextHashMapNode(&i)) {
-        HashMapNode* node = nextHashMapNode(&i);
-        if(notFirst) {
-            stringAdd(&w, &buffer, &n, coreToString(buffer, n, ", "));
-        }
-        notFirst = true;
-        stringAdd(&w, &buffer, &n, keyString(node->key, buffer, n));
-        stringAdd(&w, &buffer, &n, coreToString(buffer, n, " = "));
-        stringAdd(&w, &buffer, &n, valueString(node->value, buffer, n));
-    }
-    stringAdd(&w, &buffer, &n, coreToString(buffer, n, "]"));
-    return w;
-}

+ 196 - 150
test/modules/HashMapTests.c

@@ -4,267 +4,313 @@
 #include "core/ToString.h"
 #include "core/Utility.h"
 
-static HashMap createIntMap() {
-    HashMap map;
-    initHashMap(&map, sizeof(int), sizeof(int));
-    return map;
+HASHMAP(size_t, size_t, Size)
+
+typedef struct {
+    size_t a;
+    size_t b;
+} A;
+
+static size_t hashA(A key) {
+    return key.a ^ key.b;
 }
 
-static HashMap getTestIntMap() {
-    HashMap map = createIntMap();
-    putTypedHashMapPair(&map, int, 1, int, 3);
-    putTypedHashMapPair(&map, int, 2, int, 4);
-    putTypedHashMapPair(&map, int, 3, int, 5);
-    putTypedHashMapPair(&map, int, 0, int, 20);
+static bool equalA(A a, A b) {
+    return a.a == b.a && a.b == b.b;
+}
+
+static bool isInvalidKeyA(A key) {
+    return key.a == 0 && key.b == 0;
+}
+
+HASHMAP(A, int, A)
+HASHMAP_SOURCE(A, int, A)
+
+static HashMapSize getTestSizeMap() {
+    HashMapSize map;
+    initHashMapSize(&map);
+    *putHashMapKeySize(&map, 1) = 3;
+    *putHashMapKeySize(&map, 2) = 4;
+    *putHashMapKeySize(&map, 3) = 5;
+    *putHashMapKeySize(&map, 0) = 20;
     return map;
 }
 
-static void checkIntMap(HashMap* map) {
-    int* a = searchTypedHashMapKey(map, int, 1, int);
-    int* b = searchTypedHashMapKey(map, int, 2, int);
-    int* c = searchTypedHashMapKey(map, int, 3, int);
-    int* d = searchTypedHashMapKey(map, int, 0, int);
+static void checkSizeMap(HashMapSize* map) {
+    size_t* a = searchHashMapKeySize(map, 1);
+    size_t* b = searchHashMapKeySize(map, 2);
+    size_t* c = searchHashMapKeySize(map, 3);
+    size_t* d = searchHashMapKeySize(map, 0);
     if(TEST_NOT_NULL(a) && TEST_NOT_NULL(b) && TEST_NOT_NULL(c) &&
        TEST_NOT_NULL(d)) {
-        TEST_INT(3, *a);
-        TEST_INT(4, *b);
-        TEST_INT(5, *c);
-        TEST_INT(20, *d);
+        TEST_SIZE(3, *a);
+        TEST_SIZE(4, *b);
+        TEST_SIZE(5, *c);
+        TEST_SIZE(20, *d);
     }
 }
 
 static void testAdd() {
-    HashMap map = createIntMap();
-    putTypedHashMapPair(&map, int, 5, int, 4);
-    int* value = searchTypedHashMapKey(&map, int, 5, int);
+    HashMapSize map;
+    initHashMapSize(&map);
+    *putHashMapKeySize(&map, 5) = 4;
+    size_t* value = searchHashMapKeySize(&map, 5);
     if(TEST_NOT_NULL(value)) {
-        TEST_INT(4, *value);
+        TEST_SIZE(4, *value);
     }
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testMultipleAdd() {
-    HashMap map = getTestIntMap();
-    TEST_NOT_NULL(searchTypedHashMapKey(&map, int, 0, int));
-    TEST_NOT_NULL(searchTypedHashMapKey(&map, int, 1, int));
-    TEST_NOT_NULL(searchTypedHashMapKey(&map, int, 2, int));
-    TEST_NOT_NULL(searchTypedHashMapKey(&map, int, 3, int));
-    checkIntMap(&map);
-    destroyHashMap(&map);
+    HashMapSize map = getTestSizeMap();
+    TEST_NOT_NULL(searchHashMapKeySize(&map, 0));
+    TEST_NOT_NULL(searchHashMapKeySize(&map, 1));
+    TEST_NOT_NULL(searchHashMapKeySize(&map, 2));
+    TEST_NOT_NULL(searchHashMapKeySize(&map, 3));
+    checkSizeMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testSearch() {
-    HashMap map = getTestIntMap();
-    TEST_NULL(searchTypedHashMapKey(&map, int, 6, int));
-    putTypedHashMapPair(&map, int, 5, int, 4);
-    putTypedHashMapPair(&map, int, 10, int, 3);
-    putTypedHashMapPair(&map, int, 15, int, 2);
-    TEST_NULL(searchTypedHashMapKey(&map, int, 6, int));
-    destroyHashMap(&map);
+    HashMapSize map = getTestSizeMap();
+    TEST_NULL(searchHashMapKeySize(&map, 6));
+    *putHashMapKeySize(&map, 5) = 4;
+    *putHashMapKeySize(&map, 10) = 3;
+    *putHashMapKeySize(&map, 15) = 2;
+    TEST_NULL(searchHashMapKeySize(&map, 6));
+    destroyHashMapSize(&map);
 }
 
 static void testSearchEmpty() {
-    HashMap map = createIntMap();
-    TEST_NULL(searchTypedHashMapKey(&map, int, 6, int));
-    destroyHashMap(&map);
+    HashMapSize map;
+    initHashMapSize(&map);
+    TEST_NULL(searchHashMapKeySize(&map, 6));
+    destroyHashMapSize(&map);
 }
 
 static void testAddReplace() {
-    HashMap map = getTestIntMap();
-    putTypedHashMapPair(&map, int, 5, int, 4);
-    putTypedHashMapPair(&map, int, 5, int, 10);
-    TEST_NOT_NULL(searchTypedHashMapKey(&map, int, 5, int));
-    int* a = searchTypedHashMapKey(&map, int, 5, int);
+    HashMapSize map = getTestSizeMap();
+    *putHashMapKeySize(&map, 5) = 4;
+    *putHashMapKeySize(&map, 5) = 10;
+    TEST_NOT_NULL(searchHashMapKeySize(&map, 5));
+    size_t* a = searchHashMapKeySize(&map, 5);
     if(TEST_NOT_NULL(a)) {
-        TEST_INT(10, *a);
+        TEST_SIZE(10, *a);
     }
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testClear() {
-    HashMap map = getTestIntMap();
-    putTypedHashMapPair(&map, int, 5, int, 4);
-    putTypedHashMapPair(&map, int, 4, int, 10);
-    clearHashMap(&map);
-    TEST_NULL(searchTypedHashMapKey(&map, int, 5, int));
-    TEST_NULL(searchTypedHashMapKey(&map, int, 4, int));
-    destroyHashMap(&map);
+    HashMapSize map = getTestSizeMap();
+    *putHashMapKeySize(&map, 5) = 4;
+    *putHashMapKeySize(&map, 4) = 10;
+    clearHashMapSize(&map);
+    TEST_NULL(searchHashMapKeySize(&map, 5));
+    TEST_NULL(searchHashMapKeySize(&map, 4));
+    destroyHashMapSize(&map);
 }
 
 static void testClearEmpty() {
-    HashMap map = createIntMap();
-    clearHashMap(&map);
-    destroyHashMap(&map);
+    HashMapSize map;
+    initHashMapSize(&map);
+    clearHashMapSize(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testOverflow(bool light) {
-    int limit = light ? 10000 : 100000;
-    HashMap map = getTestIntMap();
-    for(int i = 0; i < limit; i++) {
-        putTypedHashMapPair(&map, int, i, int, i);
+    size_t limit = light ? 10000 : 100000;
+    HashMapSize map = getTestSizeMap();
+    for(size_t i = 0; i < limit; i++) {
+        *putHashMapKeySize(&map, i) = i;
     }
-    for(int i = 0; i < limit; i++) {
-        TEST_NOT_NULL(searchTypedHashMapKey(&map, int, i, int));
+    for(size_t i = 0; i < limit; i++) {
+        TEST_NOT_NULL(searchHashMapKeySize(&map, i));
     }
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testToString() {
-    HashMap map = getTestIntMap();
+    HashMapSize map = getTestSizeMap();
     char buffer[128];
-    size_t n = toString(&map, buffer, sizeof(buffer), toStringInt, toStringInt);
+    size_t n = toStringHashMapSize(&map, buffer, sizeof(buffer), toStringSize,
+                                   toStringSize);
     TEST_SIZE(29, n);
     TEST_STRING("[2 = 4, 1 = 3, 3 = 5, 0 = 20]", buffer);
 
-    clearHashMap(&map);
-    putTypedHashMapPair(&map, int, 1, int, 3);
-    n = toString(&map, buffer, sizeof(buffer), toStringInt, toStringInt);
+    clearHashMapSize(&map);
+    *putHashMapKeySize(&map, 1) = 3;
+    n = toStringHashMapSize(&map, buffer, sizeof(buffer), toStringSize,
+                            toStringSize);
     TEST_SIZE(7, n);
     TEST_STRING("[1 = 3]", buffer);
 
-    clearHashMap(&map);
-    n = toString(&map, buffer, sizeof(buffer), toStringInt, toStringInt);
+    clearHashMapSize(&map);
+    n = toStringHashMapSize(&map, buffer, sizeof(buffer), toStringSize,
+                            toStringSize);
     TEST_SIZE(2, n);
     TEST_STRING("[]", buffer);
 
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testEntryForEach() {
-    HashMap map = createIntMap();
-    putTypedHashMapPair(&map, int, 0, int, -1);
-    putTypedHashMapPair(&map, int, 5, int, 4);
-    putTypedHashMapPair(&map, int, 10, int, 3);
-    putTypedHashMapPair(&map, int, 15, int, 2);
-
-    int counter = 0;
-    HashMapIterator i;
-    initHashMapIterator(&i, &map);
-    while(hasNextHashMapNode(&i)) {
-        HashMapNode* n = nextHashMapNode(&i);
-        counter += *(const int*)n->key + *(int*)n->value;
+    HashMapSize map;
+    initHashMapSize(&map);
+    *putHashMapKeySize(&map, 0) = 1;
+    *putHashMapKeySize(&map, 5) = 4;
+    *putHashMapKeySize(&map, 10) = 3;
+    *putHashMapKeySize(&map, 15) = 2;
+
+    size_t counter = 0;
+    HashMapIteratorSize i;
+    initHashMapIteratorSize(&i, &map);
+    while(hasNextHashMapNodeSize(&i)) {
+        HashMapNodeSize* n = nextHashMapNodeSize(&i);
+        counter += *n->key + *n->value;
     }
-    TEST_INT(38, counter);
+    TEST_SIZE(40, counter);
 
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testInvalidPut() {
-    HashMap map = createIntMap();
+    HashMapSize map;
+    initHashMapSize(&map);
 
     char buffer[128];
-    toString(&map, buffer, sizeof(buffer), toStringInt, toStringInt);
+    toStringHashMapSize(&map, buffer, sizeof(buffer), toStringSize,
+                        toStringSize);
     TEST_STRING("[]", buffer);
 
-    putTypedHashMapPair(&map, int, 0, int, 3);
-    int* v = searchTypedHashMapKey(&map, int, 0, int);
+    *putHashMapKeySize(&map, 0) = 3;
+    size_t* v = searchHashMapKeySize(&map, 0);
     if(TEST_NOT_NULL(v)) {
-        TEST_INT(3, *v);
+        TEST_SIZE(3, *v);
     }
-    toString(&map, buffer, sizeof(buffer), toStringInt, toStringInt);
+    toStringHashMapSize(&map, buffer, sizeof(buffer), toStringSize,
+                        toStringSize);
     TEST_STRING("[0 = 3]", buffer);
 
-    clearHashMap(&map);
-    TEST_NULL(searchTypedHashMapKey(&map, int, 0, int));
+    clearHashMapSize(&map);
+    TEST_NULL(searchHashMapKeySize(&map, 0));
 
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testAddCollisions() {
-    HashMap map = getTestIntMap();
-    for(int i = 0; i < 16; i++) {
-        putTypedHashMapPair(&map, int, i * 64, int, i);
+    HashMapSize map = getTestSizeMap();
+    for(size_t i = 0; i < 16; i++) {
+        *putHashMapKeySize(&map, i * 64) = i;
     }
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testRemove() {
-    HashMap map = createIntMap();
-    putTypedHashMapPair(&map, int, 1, int, 3);
-    putTypedHashMapPair(&map, int, 2, int, 4);
-    putTypedHashMapPair(&map, int, 3, int, 5);
+    HashMapSize map;
+    initHashMapSize(&map);
+    *putHashMapKeySize(&map, 1) = 3;
+    *putHashMapKeySize(&map, 2) = 4;
+    *putHashMapKeySize(&map, 3) = 5;
 
-    TEST_TRUE(removeTypedHashMapKey(&map, size_t, 2));
-    TEST_FALSE(removeTypedHashMapKey(&map, size_t, 7));
+    TEST_TRUE(removeHashMapKeySize(&map, 2));
+    TEST_FALSE(removeHashMapKeySize(&map, 7));
 
-    int* a = searchTypedHashMapKey(&map, int, 1, int);
-    int* b = searchTypedHashMapKey(&map, int, 2, int);
-    int* c = searchTypedHashMapKey(&map, int, 3, int);
+    size_t* a = searchHashMapKeySize(&map, 1);
+    size_t* b = searchHashMapKeySize(&map, 2);
+    size_t* c = searchHashMapKeySize(&map, 3);
 
     TEST_NULL(b);
     if(TEST_NOT_NULL(a) && TEST_NOT_NULL(c)) {
-        TEST_INT(3, *a);
-        TEST_INT(5, *c);
+        TEST_SIZE(3, *a);
+        TEST_SIZE(5, *c);
     }
 
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 static void testHash() {
-    u32 buffer[] = {0xFFAA00BB, 0x00000000, 0x00FF00FF};
-    TEST_SIZE(0xFF550044, hashKey(buffer, sizeof(buffer)));
-
     const char* s = "wusi";
-    TEST_TRUE(hashString(&s, sizeof(&s)) != 0);
-    TEST_SIZE(0, hashString(&s, 1));
+    TEST_TRUE(hashString(s) != 0);
 }
 
-typedef struct {
-    size_t a;
-    size_t b;
-} A;
+static size_t toStringA(const A* a, char* buffer, size_t n) {
+    return coreToString(buffer, n, "{%zu, %zu}", a->a, a->b);
+}
 
 static void testSearchStruct() {
-    HashMap map;
-    initHashMap(&map, sizeof(A), sizeof(int));
+    HashMapA map;
+    initHashMapA(&map);
+    clearHashMapA(&map);
     A a = {1, 2};
     A b = {1, 3};
     A c = {0, 0};
-    TEST_NULL(searchHashMapKey(&map, &a));
-    TEST_NULL(searchHashMapKey(&map, &b));
-    TEST_NULL(searchHashMapKey(&map, &c));
-    int v = 3;
-    putHashMapPair(&map, &a, &v);
+    TEST_NULL(searchHashMapKeyA(&map, a));
+    TEST_NULL(searchHashMapKeyA(&map, b));
+    TEST_NULL(searchHashMapKeyA(&map, c));
+    *putHashMapKeyA(&map, a) = 3;
+    *putHashMapKeyA(&map, (A){5, 6}) = 5;
 
-    int* ap = searchHashMapKey(&map, &a);
+    char buffer[128];
+    size_t n = toStringHashMapA(&map, buffer, sizeof(buffer),
+                                (CoreToString)toStringA, coreToStringInt);
+    TEST_SIZE(24, n);
+    TEST_STRING("[{5, 6} = 5, {1, 2} = 3]", buffer);
+
+    int* ap = searchHashMapKeyA(&map, a);
     if(TEST_NOT_NULL(ap)) {
         TEST_INT(3, *ap);
     }
-    TEST_NULL(searchHashMapKey(&map, &b));
-    TEST_NULL(searchHashMapKey(&map, &c));
+    TEST_NULL(searchHashMapKeyA(&map, b));
+    TEST_NULL(searchHashMapKeyA(&map, c));
 
-    v = 4;
-    putHashMapPair(&map, &c, &v);
-    int* cp = searchHashMapKey(&map, &c);
+    *putHashMapKeyA(&map, c) = 4;
+    int* cp = searchHashMapKeyA(&map, c);
     if(TEST_NOT_NULL(cp)) {
         TEST_INT(4, *cp);
     }
 
-    destroyHashMap(&map);
+    for(size_t i = 0; i < 16; i++) {
+        *putHashMapKeyA(&map, (A){i * 64, 0}) = (int)i;
+    }
+
+    HashMapIteratorA iter;
+    initHashMapIteratorA(&iter, &map);
+    size_t sum = 0;
+    while(hasNextHashMapNodeA(&iter)) {
+        HashMapNodeA* node = nextHashMapNodeA(&iter);
+        sum += node->key->b;
+    }
+    TEST_SIZE(8, sum);
+
+    removeHashMapKeyA(&map, (A){0, 1});
+    removeHashMapKeyA(&map, (A){0, 0});
+    clearHashMapA(&map);
+
+    destroyHashMapA(&map);
 }
 
 static void testSearchSize() {
-    HashMap map;
-    initHashMap(&map, sizeof(size_t), sizeof(int));
-    TEST_NULL(searchTypedHashMapKey(&map, size_t, 0, int));
-    TEST_NULL(searchTypedHashMapKey(&map, size_t, 1, int));
-    TEST_NULL(searchTypedHashMapKey(&map, size_t, 2, int));
-    putTypedHashMapPair(&map, size_t, 1, int, 3);
-
-    int* ap = searchTypedHashMapKey(&map, size_t, 1, int);
+    HashMapSize map;
+    initHashMapSize(&map);
+    TEST_NULL(searchHashMapKeySize(&map, 0));
+    TEST_NULL(searchHashMapKeySize(&map, 1));
+    TEST_NULL(searchHashMapKeySize(&map, 2));
+    *putHashMapKeySize(&map, 1) = 3;
+
+    size_t* ap = searchHashMapKeySize(&map, 1);
     if(TEST_NOT_NULL(ap)) {
-        TEST_INT(3, *ap);
+        TEST_SIZE(3, *ap);
     }
-    TEST_NULL(searchTypedHashMapKey(&map, size_t, 0, int));
-    TEST_NULL(searchTypedHashMapKey(&map, size_t, 2, int));
+    TEST_NULL(searchHashMapKeySize(&map, 0));
+    TEST_NULL(searchHashMapKeySize(&map, 2));
 
-    putTypedHashMapPair(&map, size_t, 0, int, 4);
-    int* cp = searchTypedHashMapKey(&map, size_t, 0, int);
+    *putHashMapKeySize(&map, 0) = 4;
+    size_t* cp = searchHashMapKeySize(&map, 0);
     if(TEST_NOT_NULL(cp)) {
-        TEST_INT(4, *cp);
+        TEST_SIZE(4, *cp);
     }
 
-    destroyHashMap(&map);
+    destroyHashMapSize(&map);
 }
 
 void testHashMap(bool light) {

+ 1 - 2
test/modules/ListTests.c

@@ -5,7 +5,6 @@
 #include "core/Vector.h"
 
 LIST(size_t, Size)
-LIST_SOURCE(size_t, Size)
 LIST(Vector3, V3)
 LIST_SOURCE(Vector3, V3)
 LIST(int, Int)
@@ -182,7 +181,7 @@ static void testStruct() {
     Vector3 v = {{1, 2, 3}};
     initListV3(&list);
     addListDataV3(&list, v);
-    addListDataV3(&list, (Vector3){2, 3, 4});
+    addListDataV3(&list, (Vector3){{2, 3, 4}});
     char buffer[128];
     size_t n = toStringListV3(&list, buffer, sizeof(buffer), coreToStringV3);
     TEST_SIZE(46, n);