#pragma once #include "../std.h" #include "../string/str.h" #include "Array.h" //TODO: sorting of bucket and binary search //TODO: delayed deletion #define __HashMap_HASH_FUNC str_hash32 #define __HashMapBucket_MAX_LEN 16 #define HashMap_DESTROY_VALUE_FUNC_NULL ((void (*)(void*))NULL) /// call this in a header file ///@param T Value type #define HashMap_declare(T)\ typedef struct KeyValue_##T {\ str key;\ T value;\ u32 hash;\ } KeyValue_##T;\ \ List_declare(KeyValue_##T);\ \ typedef struct HashMapBucket_##T {\ List_KeyValue_##T kvs;\ } HashMapBucket_##T;\ \ typedef struct HashMap_##T {\ HashMapBucket_##T* table;\ u32 height;\ u16 height_n;\ } HashMap_##T;\ \ void HashMap_##T##_alloc(HashMap_##T* ptr);\ void HashMap_##T##_free(HashMap_##T* ptr);\ NULLABLE(T*) HashMap_##T##_tryGetPtr(HashMap_##T* ptr, str key);\ bool HashMap_##T##_tryPush(HashMap_##T* ptr, str key, T value);\ bool HashMap_##T##_tryDelete(HashMap_##T* ptr, str key);\ /// call this in a source code file ///@param T Value type ///@param DESTROY_VALUE_FUNC `void foo (T*)` or HashMap_DESTROY_VALUE_FUNC_NULL #define HashMap_define(T, DESTROY_VALUE_FUNC)\ List_define(KeyValue_##T);\ \ static const Array_u32 __HashMap_##T##_heights = ARRAY(u32, {\ 17, 31, 61, 127, 257, 521, 1021, 2053, 4099, 8191, 16381, 32771,\ 65521, 131071, 262147, 524287, 1048583, 2097169, 4194319,\ 8388617, 16777213, 33554467, 67108859, 134217757, 268435493\ });\ \ void HashMap_##T##_alloc(HashMap_##T* ptr){\ ptr->height_n = 0;\ ptr->height = __HashMap_##T##_heights.data[0];\ ptr->table = (HashMapBucket_##T*)malloc(ptr->height * sizeof(HashMapBucket_##T));\ memset(ptr->table, 0, ptr->height * sizeof(HashMapBucket_##T));\ }\ \ void HashMap_##T##_free(HashMap_##T* ptr){\ for(u32 i = 0; i < ptr->height; i++){\ for(u32 j = 0; j < ptr->table[i].kvs.len; j++){\ KeyValue_##T* kv_ptr = &ptr->table[i].kvs.data[j];\ if(DESTROY_VALUE_FUNC){\ DESTROY_VALUE_FUNC(&kv_ptr->value);\ }\ free(kv_ptr->key.data);\ }\ \ free(ptr->table[i].kvs.data);\ }\ \ free(ptr->table);\ }\ \ NULLABLE(T*) HashMap_##T##_tryGetPtr(HashMap_##T* ptr, str key){\ u32 hash = __HashMap_HASH_FUNC(key);\ HashMapBucket_##T* bu = &ptr->table[hash % ptr->height];\ for(u32 i = 0; i < bu->kvs.len; i++){\ if(bu->kvs.data[i].hash == hash && str_equals(bu->kvs.data[i].key, key)){\ return &bu->kvs.data[i].value;\ }\ }\ \ return NULL;\ }\ \ bool HashMap_##T##_tryPush(HashMap_##T* ptr, str key, T value){\ u32 hash = __HashMap_HASH_FUNC(key);\ HashMapBucket_##T* bu = &ptr->table[hash % ptr->height];\ for(u32 i = 0; i < bu->kvs.len; i++){\ if(bu->kvs.data[i].hash == hash && str_equals(bu->kvs.data[i].key, key)){\ return false;\ }\ }\ \ if(bu->kvs.len >= __HashMapBucket_MAX_LEN){\ u32 height_expanded_n = ptr->height_n + 1;\ if(height_expanded_n >= __HashMap_u32_heights.len){\ printf("ERROR: HashMap_" #T " IS FULL\n");\ return false;\ }\ \ u32 height_expanded = __HashMap_##T##_heights.data[height_expanded_n];\ HashMapBucket_##T* table_expanded = (HashMapBucket_##T*)malloc(height_expanded * sizeof(HashMapBucket_##T));\ memset(table_expanded, 0, height_expanded * sizeof(HashMapBucket_##T));\ for(u32 i = 0; i < height_expanded; i++){\ for(u32 j = 0; j < ptr->table[i].kvs.len; j++){\ KeyValue_##T kv = ptr->table[i].kvs.data[j];\ List_KeyValue_##T##_push(&table_expanded[kv.hash % height_expanded].kvs, kv);\ }\ \ free(ptr->table[i].kvs.data);\ }\ free(ptr->table);\ ptr->table = table_expanded;\ ptr->height = height_expanded;\ ptr->height_n = height_expanded_n;\ bu = &ptr->table[hash % ptr->height];\ }\ \ KeyValue_##T kv = { .key = str_copy(key), .value = value, .hash = hash };\ List_KeyValue_##T##_push(&bu->kvs, kv);\ return true;\ }\ \ bool HashMap_##T##_tryDelete(HashMap_##T* ptr, str key){\ u32 hash = __HashMap_HASH_FUNC(key);\ HashMapBucket_##T* bu = &ptr->table[hash % ptr->height];\ for(u32 i = 0; i < bu->kvs.len; i++){\ if(bu->kvs.data[i].hash == hash && str_equals(bu->kvs.data[i].key, key)){\ return List_KeyValue_##T##_tryRemoveAt(&bu->kvs, i);\ }\ }\ \ return false;\ }