Compare commits

..

2 Commits

Author SHA1 Message Date
b443367f46 HashMap 2025-02-03 12:30:01 +05:00
fd9e5dda78 List_tryRemoveAt 2025-02-03 12:29:31 +05:00
6 changed files with 175 additions and 7 deletions

View File

@ -1,20 +1,24 @@
#pragma once #pragma once
#include "../std.h" #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)\ #define Array_declare(T)\
typedef struct Array_##T {\ typedef struct Array_##T {\
T* data;\ T* data;\
u32 len;\ u32 len;\
} Array_##T;\ } 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){\ 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){\ static inline void Array_##T##_realloc(Array_##T* ptr, u32 new_len){\
ptr->data = (T*)realloc(ptr->data, new_len * sizeof(T));\ ptr->data = (T*)realloc(ptr->data, new_len * sizeof(T));\
ptr->len = new_len;\ ptr->len = new_len;\
} }
Array_declare(u8)
Array_declare(u32)

139
src/collections/HashMap.h Normal file
View 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;\
}

View File

@ -20,6 +20,7 @@
T* List_##T##_expand(List_##T* ptr, u32 count);\ T* List_##T##_expand(List_##T* ptr, u32 count);\
void List_##T##_push(List_##T* ptr, T value);\ void List_##T##_push(List_##T* ptr, T value);\
void List_##T##_pushMany(List_##T* ptr, T* values, u32 count);\ void List_##T##_pushMany(List_##T* ptr, T* values, u32 count);\
bool List_##T##_tryRemoveAt(List_##T* ptr, u32 i);\
#define List_define(T)\ #define List_define(T)\
@ -52,6 +53,17 @@
T* empty_cell_ptr = List_##T##_expand(ptr, count);\ T* empty_cell_ptr = List_##T##_expand(ptr, count);\
memcpy(empty_cell_ptr, values, count * sizeof(T));\ memcpy(empty_cell_ptr, values, count * sizeof(T));\
}\ }\
\
bool List_##T##_tryRemoveAt(List_##T* ptr, u32 i){\
if(ptr->len == 0 || i >= ptr->len)\
return false;\
\
ptr->len--;\
for(; i < ptr->len; i++){\
ptr->data[i] = ptr->data[i + 1];\
}\
return true;\
}\
List_declare(u32); List_declare(u32);

View File

@ -26,7 +26,7 @@ typedef u8 bool;
typedef const char* cstr; 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 ALIGN_TO(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))
#define __count_args( \ #define __count_args( \

View File

@ -97,3 +97,12 @@ bool str_endsWith(str src, str fragment){
src.len = fragment.len; src.len = fragment.len;
return str_equals(src, fragment); 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;
}

View File

@ -8,7 +8,7 @@ typedef struct str {
bool isZeroTerminated; bool isZeroTerminated;
} str; } 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(LITERAL) str_construct(LITERAL, ARRAY_SIZE(LITERAL) - 1, true)
#define str_construct(DATA, LEN, ZERO_TERMINATED) ((str){ .data = DATA, .len = LEN, .isZeroTerminated = ZERO_TERMINATED }) #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_startsWith(str src, str fragment);
bool str_endsWith(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);