diff --git a/include/tlibc/collections/HashMap.h b/include/tlibc/collections/HashMap.h index 81de8b0..660cb98 100755 --- a/include/tlibc/collections/HashMap.h +++ b/include/tlibc/collections/HashMap.h @@ -6,13 +6,13 @@ typedef void (*FreeFunction)(void*); -typedef struct KeyHash { +typedef struct HashMapKeyHash { str key; u32 hash; -} KeyHash; +} HashMapKeyHash; typedef struct HashMapBucket { - List(KeyHash) key_hash_list; + List(HashMapKeyHash) key_hash_list; List(T) value_list; } 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); NULLABLE(void*) HashMap_tryGetPtr(const 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); diff --git a/src/collections/HashMap.c b/src/collections/HashMap.c index 9303cae..e962b0b 100755 --- a/src/collections/HashMap.c +++ b/src/collections/HashMap.c @@ -27,11 +27,11 @@ void HashMap_destroy(HashMap_* self){ for(u32 i = 0; i < self->height; 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 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); } @@ -68,8 +68,8 @@ static BucketAndIndex __HashMap_search(const HashMap_* self, const str key, u32 BucketAndIndex r; r.bu = &self->table[hash % self->height]; - for(r.i = 0; r.i < (i32)List_len(r.bu->key_hash_list, KeyHash); r.i++){ - KeyHash* kh = (KeyHash*)r.bu->key_hash_list.data + r.i; + for(r.i = 0; r.i < (i32)List_len(r.bu->key_hash_list, HashMapKeyHash); r.i++){ + HashMapKeyHash* kh = (HashMapKeyHash*)r.bu->key_hash_list.data + r.i; if(kh->hash == hash && str_equals(kh->key, key)){ return r; } @@ -85,7 +85,7 @@ void* HashMap_tryGetPtr(const HashMap_* self, const str key){ // key not found if(r.i == -1) 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 for(u32 i = 0; i < self->height; 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++){ - 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]; - 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; 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; 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); bu = &self->table[hash % self->height]; } - KeyHash kh = { .key = str_copy(key), .hash = hash }; - List_push(&bu->key_hash_list, KeyHash, kh); + HashMapKeyHash kh = { .key = str_copy(key), .hash = hash }; + List_push(&bu->key_hash_list, HashMapKeyHash, kh); List_push_size(&bu->value_list, value_ptr, self->value_t_size); return true; } @@ -145,18 +145,19 @@ void HashMap_pushOrUpdate(HashMap_* self, const str key, void* value_ptr){ BucketAndIndex r = __HashMap_search(self, key, hash); // found existing item with the same key 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); + return; } 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); bu = &self->table[hash % self->height]; } - KeyHash kh = { .key = str_copy(key), .hash = hash }; - List_push(&bu->key_hash_list, KeyHash, kh); + HashMapKeyHash kh = { .key = str_copy(key), .hash = hash }; + List_push(&bu->key_hash_list, HashMapKeyHash, kh); 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) 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); 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; +}