created HashMap iterator
This commit is contained in:
parent
2de044711f
commit
6a1067a612
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user