140 lines
4.5 KiB
C
140 lines
4.5 KiB
C
#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;\
|
|
}
|