created HashMap iterator

This commit is contained in:
Timerix 2025-11-10 10:30:22 +05:00
parent 2de044711f
commit 6a1067a612
2 changed files with 81 additions and 19 deletions

View File

@ -6,13 +6,13 @@
typedef void (*FreeFunction)(void*); typedef void (*FreeFunction)(void*);
typedef struct KeyHash { typedef struct HashMapKeyHash {
str key; str key;
u32 hash; u32 hash;
} KeyHash; } HashMapKeyHash;
typedef struct HashMapBucket { typedef struct HashMapBucket {
List(KeyHash) key_hash_list; List(HashMapKeyHash) key_hash_list;
List(T) value_list; List(T) value_list;
} HashMapBucket; } HashMapBucket;
@ -34,3 +34,23 @@ bool HashMap_tryPush(HashMap_* self, const str key, void* value_ptr);
void HashMap_pushOrUpdate(HashMap_* self, const str key, void* value_ptr); void HashMap_pushOrUpdate(HashMap_* self, const str key, void* value_ptr);
NULLABLE(void*) HashMap_tryGetPtr(const HashMap_* self, const str key); NULLABLE(void*) HashMap_tryGetPtr(const HashMap_* self, const str key);
bool HashMap_tryDelete(HashMap_* self, const str key); bool HashMap_tryDelete(HashMap_* self, const str key);
typedef struct {
const HashMap_* map;
i32 bucket_n;
i32 elem_n;
} HashMapIter;
typedef struct HashMapKeyValue {
str key;
void* value;
} HashMapKeyValue;
static inline HashMapIter HashMapIter_create(const HashMap_* table){
return (HashMapIter){ .map = table, .bucket_n = -1, .elem_n = -1 };
}
bool HashMapIter_moveNext(HashMapIter* self);
/// don't forget to call HashMapIter_moveNext() first
bool HashMapIter_getCurrent(HashMapIter* self, HashMapKeyValue* kv);

View File

@ -27,11 +27,11 @@ void HashMap_destroy(HashMap_* self){
for(u32 i = 0; i < self->height; i++){ for(u32 i = 0; i < self->height; i++){
HashMapBucket* bu = &self->table[i]; HashMapBucket* bu = &self->table[i];
u32 len = List_len(bu->key_hash_list, KeyHash); u32 len = List_len(bu->key_hash_list, HashMapKeyHash);
// free key strings // free key strings
for(u32 j = 0; j < len; j++){ for(u32 j = 0; j < len; j++){
KeyHash* kh = (KeyHash*)bu->key_hash_list.data + j; HashMapKeyHash* kh = (HashMapKeyHash*)bu->key_hash_list.data + j;
free(kh->key.data); free(kh->key.data);
} }
@ -68,8 +68,8 @@ static BucketAndIndex __HashMap_search(const HashMap_* self, const str key, u32
BucketAndIndex r; BucketAndIndex r;
r.bu = &self->table[hash % self->height]; r.bu = &self->table[hash % self->height];
for(r.i = 0; r.i < (i32)List_len(r.bu->key_hash_list, KeyHash); r.i++){ for(r.i = 0; r.i < (i32)List_len(r.bu->key_hash_list, HashMapKeyHash); r.i++){
KeyHash* kh = (KeyHash*)r.bu->key_hash_list.data + r.i; HashMapKeyHash* kh = (HashMapKeyHash*)r.bu->key_hash_list.data + r.i;
if(kh->hash == hash && str_equals(kh->key, key)){ if(kh->hash == hash && str_equals(kh->key, key)){
return r; return r;
} }
@ -85,7 +85,7 @@ void* HashMap_tryGetPtr(const HashMap_* self, const str key){
// key not found // key not found
if(r.i == -1) if(r.i == -1)
return NULL; return NULL;
return ((u8*)r.bu->value_list.data) + r.i * self->value_t_size; return (u8*)r.bu->value_list.data + r.i * self->value_t_size;
} }
@ -102,12 +102,12 @@ static void __HashMap_expand(HashMap_* self){
// copy values from old buckets to new // copy values from old buckets to new
for(u32 i = 0; i < self->height; i++){ for(u32 i = 0; i < self->height; i++){
HashMapBucket* old_bucket = &self->table[i]; HashMapBucket* old_bucket = &self->table[i];
u32 len = List_len(old_bucket->key_hash_list, KeyHash); u32 len = List_len(old_bucket->key_hash_list, HashMapKeyHash);
for(u32 j = 0; j < len; j++){ for(u32 j = 0; j < len; j++){
KeyHash kh = ((KeyHash*)old_bucket->key_hash_list.data)[j]; HashMapKeyHash kh = ((HashMapKeyHash*)old_bucket->key_hash_list.data)[j];
HashMapBucket* new_bucket = &table_expanded[kh.hash % height_expanded]; HashMapBucket* new_bucket = &table_expanded[kh.hash % height_expanded];
List_push(&new_bucket->key_hash_list, KeyHash, kh); List_push(&new_bucket->key_hash_list, HashMapKeyHash, kh);
void* old_value_ptr = (u8*)old_bucket->value_list.data + j * self->value_t_size; void* old_value_ptr = (u8*)old_bucket->value_list.data + j * self->value_t_size;
List_push_size(&new_bucket->value_list, old_value_ptr, self->value_t_size); List_push_size(&new_bucket->value_list, old_value_ptr, self->value_t_size);
} }
@ -129,13 +129,13 @@ bool HashMap_tryPush(HashMap_* self, const str key, void* value_ptr){
return false; return false;
HashMapBucket* bu = r.bu; HashMapBucket* bu = r.bu;
if(bu == NULL || List_len(bu->key_hash_list, KeyHash) >= __HashMapBucket_MAX_LEN){ if(bu == NULL || List_len(bu->key_hash_list, HashMapKeyHash) >= __HashMapBucket_MAX_LEN){
__HashMap_expand(self); __HashMap_expand(self);
bu = &self->table[hash % self->height]; bu = &self->table[hash % self->height];
} }
KeyHash kh = { .key = str_copy(key), .hash = hash }; HashMapKeyHash kh = { .key = str_copy(key), .hash = hash };
List_push(&bu->key_hash_list, KeyHash, kh); List_push(&bu->key_hash_list, HashMapKeyHash, kh);
List_push_size(&bu->value_list, value_ptr, self->value_t_size); List_push_size(&bu->value_list, value_ptr, self->value_t_size);
return true; return true;
} }
@ -145,18 +145,19 @@ void HashMap_pushOrUpdate(HashMap_* self, const str key, void* value_ptr){
BucketAndIndex r = __HashMap_search(self, key, hash); BucketAndIndex r = __HashMap_search(self, key, hash);
// found existing item with the same key // found existing item with the same key
if(r.i != -1){ if(r.i != -1){
void* existing_item = ((u8*)r.bu->value_list.data) + r.i * self->value_t_size; void* existing_item = (u8*)r.bu->value_list.data + r.i * self->value_t_size;
memcpy(existing_item, value_ptr, self->value_t_size); memcpy(existing_item, value_ptr, self->value_t_size);
return;
} }
HashMapBucket* bu = r.bu; HashMapBucket* bu = r.bu;
if(bu == NULL || List_len(bu->key_hash_list, KeyHash) >= __HashMapBucket_MAX_LEN){ if(bu == NULL || List_len(bu->key_hash_list, HashMapKeyHash) >= __HashMapBucket_MAX_LEN){
__HashMap_expand(self); __HashMap_expand(self);
bu = &self->table[hash % self->height]; bu = &self->table[hash % self->height];
} }
KeyHash kh = { .key = str_copy(key), .hash = hash }; HashMapKeyHash kh = { .key = str_copy(key), .hash = hash };
List_push(&bu->key_hash_list, KeyHash, kh); List_push(&bu->key_hash_list, HashMapKeyHash, kh);
List_push_size(&bu->value_list, value_ptr, self->value_t_size); List_push_size(&bu->value_list, value_ptr, self->value_t_size);
} }
@ -167,7 +168,48 @@ bool HashMap_tryDelete(HashMap_* self, const str key){
if(r.i == -1) if(r.i == -1)
return false; return false;
List_removeAt(&r.bu->key_hash_list, KeyHash, r.i, 1); List_removeAt(&r.bu->key_hash_list, HashMapKeyHash, r.i, 1);
List_removeAt_size(&r.bu->value_list, r.i, self->value_t_size); List_removeAt_size(&r.bu->value_list, r.i, self->value_t_size);
return true; return true;
} }
bool HashMapIter_moveNext(HashMapIter* self){
if(self->map == NULL)
return false;
if(self->bucket_n == -1){
self->bucket_n = 0;
}
// move to next element in current bucket
const HashMapBucket* bu = &self->map->table[self->bucket_n];
i32 bu_elem_len = List_len(bu->key_hash_list, HashMapKeyHash);
if(self->elem_n + 1 < bu_elem_len){
self->elem_n++;
return true;
}
else {
// move to next non-empty bucket
while(self->bucket_n + 1 < (i32)self->map->height){
self->bucket_n++;
self->elem_n = 0;
bu = &self->map->table[self->bucket_n];
if(bu->key_hash_list.size != 0)
return true;
}
}
return false;
}
bool HashMapIter_getCurrent(HashMapIter* self, HashMapKeyValue* kv){
if(self->map == NULL || self->bucket_n == -1 || self->elem_n == -1)
return false;
const HashMapBucket* bu = &self->map->table[self->bucket_n];
// table is empty
if(bu->key_hash_list.size == 0)
return false;
kv->key = List_index(bu->key_hash_list, HashMapKeyHash, self->elem_n).key;
kv->value = (u8*)bu->value_list.data + self->map->value_t_size * self->elem_n;
return true;
}