HashMap
This commit is contained in:
parent
fd9e5dda78
commit
b443367f46
@ -1,20 +1,24 @@
|
||||
#pragma once
|
||||
#include "../std.h"
|
||||
|
||||
#define Array_construct(T, DATA, LEN) ((Array_##T){ .data = DATA, .len = LEN })
|
||||
|
||||
/// creates Array_##T from a const array
|
||||
#define ARRAY(T, A...) Array_construct(T, ((u32[])A), ARRAY_SIZE(((u32[])A)))
|
||||
|
||||
#define Array_declare(T)\
|
||||
typedef struct Array_##T {\
|
||||
T* data;\
|
||||
u32 len;\
|
||||
} Array_##T;\
|
||||
\
|
||||
static inline Array_##T Array_##T##_construct(T* data_ptr, u32 len) {\
|
||||
return (Array_##T){ .data = data_ptr, .len = len };\
|
||||
}\
|
||||
\
|
||||
static inline Array_##T Array_##T##_alloc(u32 len){\
|
||||
return Array_##T##_construct((T*)malloc(len * sizeof(T)), len);\
|
||||
return Array_construct(T, (T*)malloc(len * sizeof(T)), len);\
|
||||
}\
|
||||
static inline void Array_##T##_realloc(Array_##T* ptr, u32 new_len){\
|
||||
ptr->data = (T*)realloc(ptr->data, new_len * sizeof(T));\
|
||||
ptr->len = new_len;\
|
||||
}
|
||||
|
||||
Array_declare(u8)
|
||||
Array_declare(u32)
|
||||
|
||||
139
src/collections/HashMap.h
Normal file
139
src/collections/HashMap.h
Normal file
@ -0,0 +1,139 @@
|
||||
#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;\
|
||||
}
|
||||
@ -26,7 +26,7 @@ typedef u8 bool;
|
||||
|
||||
typedef const char* cstr;
|
||||
|
||||
#define ARRAY_SIZE(A) sizeof(A)/sizeof(A[0])
|
||||
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
|
||||
#define ALIGN_TO(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))
|
||||
|
||||
#define __count_args( \
|
||||
|
||||
@ -97,3 +97,12 @@ bool str_endsWith(str src, str fragment){
|
||||
src.len = fragment.len;
|
||||
return str_equals(src, fragment);
|
||||
}
|
||||
|
||||
u32 str_hash32(str s){
|
||||
u8* ubuf = (u8*)s.data;
|
||||
u32 len = s.len;
|
||||
u32 hash=0;
|
||||
for (u32 i = 0; i < len; i++)
|
||||
hash = (hash<<6) + (hash<<16) - hash + ubuf[i];
|
||||
return hash;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ typedef struct str {
|
||||
bool isZeroTerminated;
|
||||
} str;
|
||||
|
||||
// creates str from a string literal
|
||||
/// creates str from a string literal
|
||||
#define STR(LITERAL) str_construct(LITERAL, ARRAY_SIZE(LITERAL) - 1, true)
|
||||
|
||||
#define str_construct(DATA, LEN, ZERO_TERMINATED) ((str){ .data = DATA, .len = LEN, .isZeroTerminated = ZERO_TERMINATED })
|
||||
@ -32,3 +32,7 @@ i32 str_seekCharReverse(str src, char c, u32 startIndex);
|
||||
|
||||
bool str_startsWith(str src, str fragment);
|
||||
bool str_endsWith(str src, str fragment);
|
||||
|
||||
/// @brief calculates string hash using sdbm32 algorythm (something like lightweight crc32)
|
||||
/// @return non-cryptografic hash of the string
|
||||
u32 str_hash32(str s);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user