diff --git a/src/collections/Array.h b/src/collections/Array.h index 93394c3..40aae36 100644 --- a/src/collections/Array.h +++ b/src/collections/Array.h @@ -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) diff --git a/src/collections/HashMap.h b/src/collections/HashMap.h new file mode 100644 index 0000000..3adb61b --- /dev/null +++ b/src/collections/HashMap.h @@ -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;\ +} diff --git a/src/std.h b/src/std.h index 8af3dc3..a5e4b16 100644 --- a/src/std.h +++ b/src/std.h @@ -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( \ diff --git a/src/string/str.c b/src/string/str.c index 6c297eb..fe3ee00 100644 --- a/src/string/str.c +++ b/src/string/str.c @@ -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; +} diff --git a/src/string/str.h b/src/string/str.h index d5f854b..6db3c66 100644 --- a/src/string/str.h +++ b/src/string/str.h @@ -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);