diff --git a/ds/hashmap.c b/ds/hashmap.c index fb7c0a0..4e9afa1 100644 --- a/ds/hashmap.c +++ b/ds/hashmap.c @@ -1,32 +1,42 @@ -#include "hashmap.h" - #include #include #include #include -#include #include #include #define HASH_OFFSET_BASIS 0xcbf29ce484222325 #define HASH_PRIME 0x100000001b3 -/* clang-format off */ +/*** PRIVATE DATA *************************************************************/ + +struct b_hashmap_bucket_item { + struct b_queue_entry bi_entry; + struct b_hashmap_key bi_key; + struct b_hashmap_value bi_value; +}; + +struct b_hashmap_bucket { + struct b_btree_node bk_node; + uint64_t bk_hash; + struct b_queue bk_items; +}; + +struct b_hashmap_p { + struct b_btree h_buckets; + b_hashmap_key_destructor h_key_dtor; + b_hashmap_value_destructor h_value_dtor; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + static B_BTREE_DEFINE_SIMPLE_GET( - struct b_hashmap_bucket, - uint64_t, - bk_node, - bk_hash, - get_bucket) + struct b_hashmap_bucket, uint64_t, bk_node, bk_hash, get_bucket); static B_BTREE_DEFINE_SIMPLE_INSERT( - struct b_hashmap_bucket, - bk_node, - bk_hash, - put_bucket) + struct b_hashmap_bucket, bk_node, bk_hash, put_bucket); static uint64_t hash_data(const void *p, size_t size) { - /* clang-format on */ const unsigned char *s = p; uint64_t hash = HASH_OFFSET_BASIS; @@ -38,8 +48,6 @@ static uint64_t hash_data(const void *p, size_t size) return hash; } -static void hashmap_release(struct b_dsref *obj); - static uint64_t hash_key(const struct b_hashmap_key *key) { if (key->key_flags & B_HASHMAP_KEY_F_INTVALUE) { @@ -79,270 +87,6 @@ static bool compare_key( return memcmp(a_data, b_data, cmp_len) == 0; } -static struct b_dsref_type hashmap_type = { - .t_name = "corelib::hashmap", - .t_flags = B_DSREF_FUNDAMENTAL, - .t_id = B_DSREF_TYPE_HASHMAP, - .t_instance_size = sizeof(struct b_hashmap), - .t_release = hashmap_release, -}; - -struct b_hashmap *b_hashmap_create( - b_hashmap_key_destructor key_dtor, b_hashmap_value_destructor value_dtor) -{ - struct b_hashmap *hashmap - = (struct b_hashmap *)b_dsref_type_instantiate(&hashmap_type); - if (!hashmap) { - return NULL; - } - - return hashmap; -} - -struct b_hashmap *b_hashmap_create_with_items(const b_hashmap_item *items) -{ - struct b_hashmap *hashmap = b_hashmap_create(NULL, NULL); - if (!hashmap) { - return NULL; - } - - for (size_t i = 0; items[i].key.key_data && items[i].key.key_size; i++) { - b_hashmap_put(hashmap, &items[i].key, &items[i].value); - } - - return hashmap; -} - -static struct b_hashmap_bucket *create_bucket(void) -{ - /* clang-format on */ - struct b_hashmap_bucket *bucket = malloc(sizeof *bucket); - if (!bucket) { - return NULL; - } - - memset(bucket, 0x0, sizeof *bucket); - return bucket; -} - -static struct b_hashmap_bucket_item *create_bucket_item(void) -{ - struct b_hashmap_bucket_item *item = malloc(sizeof *item); - if (!item) { - return NULL; - } - - memset(item, 0x0, sizeof *item); - return item; -} - -b_status b_hashmap_put( - struct b_hashmap *hashmap, const b_hashmap_key *key, - const b_hashmap_value *value) -{ - uint64_t hash = hash_key(key); - struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash); - - if (!bucket) { - bucket = create_bucket(); - if (!bucket) { - return B_ERR_NO_MEMORY; - } - - bucket->bk_hash = hash; - put_bucket(&hashmap->h_buckets, bucket); - } - - b_queue_iterator it; - b_queue_foreach (&it, &bucket->bk_items) { - struct b_hashmap_bucket_item *item = b_unbox( - struct b_hashmap_bucket_item, it.entry, bi_entry); - - if (compare_key(&item->bi_key, key)) { - memcpy(&item->bi_value, value, sizeof *value); - return B_SUCCESS; - } - } - - struct b_hashmap_bucket_item *item = create_bucket_item(); - if (!item) { - return B_ERR_NO_MEMORY; - } - - memcpy(&item->bi_key, key, sizeof *key); - memcpy(&item->bi_value, value, sizeof *value); - - b_queue_push_back(&bucket->bk_items, &item->bi_entry); - - return B_SUCCESS; -} - -const struct b_hashmap_value *b_hashmap_get( - const struct b_hashmap *hashmap, const struct b_hashmap_key *key) -{ - uint64_t hash = hash_key(key); - - struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash); - if (!bucket) { - return NULL; - } - - b_queue_iterator it; - b_queue_foreach (&it, &bucket->bk_items) { - struct b_hashmap_bucket_item *item = b_unbox( - struct b_hashmap_bucket_item, it.entry, bi_entry); - - if (compare_key(&item->bi_key, key)) { - return &item->bi_value; - } - } - - return NULL; -} - -bool b_hashmap_has_key(const struct b_hashmap *hashmap, const b_hashmap_key *key) -{ - uint64_t hash = hash_key(key); - struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash); - if (!bucket) { - return false; - } - - b_queue_iterator it; - b_queue_foreach (&it, &bucket->bk_items) { - struct b_hashmap_bucket_item *item = b_unbox( - struct b_hashmap_bucket_item, it.entry, bi_entry); - - if (compare_key(&item->bi_key, key)) { - return true; - } - } - - return false; -} - -size_t b_hashmap_get_size(const struct b_hashmap *hashmap) -{ - size_t count = 0; - b_btree_iterator it1; - b_queue_iterator it2; - b_btree_foreach (&it1, &hashmap->h_buckets) { - struct b_hashmap_bucket *bucket - = b_unbox(struct b_hashmap_bucket, it1.node, bk_node); - - b_queue_foreach (&it2, &bucket->bk_items) { - count++; - } - } - - return count; -} - -bool b_hashmap_is_empty(const b_hashmap *hashmap) -{ - b_btree_node *first_node = b_btree_first(&hashmap->h_buckets); - struct b_hashmap_bucket *first_bucket - = b_unbox(struct b_hashmap_bucket, first_node, bk_node); - if (!first_bucket) { - return true; - } - - b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items); - struct b_hashmap_bucket_item *first_item - = b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry); - if (!first_item) { - return true; - } - - return false; -} - -static b_status delete_item( - struct b_hashmap *hashmap, struct b_hashmap_bucket *bucket, - struct b_hashmap_bucket_item *item) -{ - b_queue_delete(&bucket->bk_items, &item->bi_entry); - - if (hashmap->h_key_dtor) { - hashmap->h_key_dtor((void *)item->bi_key.key_data); - } - - if (hashmap->h_value_dtor) { - hashmap->h_value_dtor((void *)item->bi_value.value_data); - } - - free(item); - - if (b_queue_empty(&bucket->bk_items)) { - b_btree_delete(&hashmap->h_buckets, &bucket->bk_node); - free(bucket); - } - - return B_SUCCESS; -} - -static bool hashmap_iterator_next(struct b_iterator *it) -{ - return b_hashmap_iterator_next((struct b_hashmap_iterator *)it); -} - -static b_status hashmap_iterator_erase(struct b_iterator *it) -{ - return b_hashmap_iterator_erase((struct b_hashmap_iterator *)it); -} - -static bool hashmap_iterator_is_valid(const struct b_iterator *it) -{ - return b_hashmap_iterator_is_valid((struct b_hashmap_iterator *)it); -} - -static struct b_iterator_ops it_ops = { - .it_next = hashmap_iterator_next, - .it_erase = hashmap_iterator_erase, - .it_close = NULL, - .it_is_valid = hashmap_iterator_is_valid, -}; - -int b_hashmap_iterator_begin( - struct b_hashmap *hashmap, struct b_hashmap_iterator *it) -{ - it->_h = hashmap; - it->_base.it_ops = &it_ops; - - it->i = 0; - if (b_hashmap_is_empty(hashmap)) { - it->key = NULL; - it->value = NULL; - return -1; - } - - struct b_btree_node *first_node = b_btree_first(&hashmap->h_buckets); - struct b_hashmap_bucket *first_bucket - = b_unbox(struct b_hashmap_bucket, first_node, bk_node); - if (!first_bucket) { - it->key = NULL; - it->value = NULL; - return -1; - } - - struct b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items); - struct b_hashmap_bucket_item *first_item - = b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry); - if (!first_item) { - it->key = NULL; - it->value = NULL; - return -1; - } - - it->key = &first_item->bi_key; - it->value = &first_item->bi_value; - - it->_cbn = first_node; - it->_cqe = first_entry; - - return 0; -} - static bool get_next_node( struct b_btree_node *cur_node, struct b_queue_entry *cur_entry, struct b_btree_node **out_next_node, struct b_queue_entry **out_next_entry) @@ -391,6 +135,349 @@ static bool get_next_node( return true; } +static struct b_hashmap_bucket *create_bucket(void) +{ + struct b_hashmap_bucket *bucket = malloc(sizeof *bucket); + if (!bucket) { + return NULL; + } + + memset(bucket, 0x0, sizeof *bucket); + return bucket; +} + +static struct b_hashmap_bucket_item *create_bucket_item(void) +{ + struct b_hashmap_bucket_item *item = malloc(sizeof *item); + if (!item) { + return NULL; + } + + memset(item, 0x0, sizeof *item); + return item; +} + +static b_status hashmap_put( + struct b_hashmap_p *hashmap, const b_hashmap_key *key, + const b_hashmap_value *value) +{ + uint64_t hash = hash_key(key); + struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash); + + if (!bucket) { + bucket = create_bucket(); + if (!bucket) { + return B_ERR_NO_MEMORY; + } + + bucket->bk_hash = hash; + put_bucket(&hashmap->h_buckets, bucket); + } + + b_queue_iterator it; + b_queue_foreach (&it, &bucket->bk_items) { + struct b_hashmap_bucket_item *item = b_unbox( + struct b_hashmap_bucket_item, it.entry, bi_entry); + + if (compare_key(&item->bi_key, key)) { + memcpy(&item->bi_value, value, sizeof *value); + return B_SUCCESS; + } + } + + struct b_hashmap_bucket_item *item = create_bucket_item(); + if (!item) { + return B_ERR_NO_MEMORY; + } + + memcpy(&item->bi_key, key, sizeof *key); + memcpy(&item->bi_value, value, sizeof *value); + + b_queue_push_back(&bucket->bk_items, &item->bi_entry); + + return B_SUCCESS; +} + +static const struct b_hashmap_value *hashmap_get( + const struct b_hashmap_p *hashmap, const struct b_hashmap_key *key) +{ + uint64_t hash = hash_key(key); + + struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash); + if (!bucket) { + return NULL; + } + + b_queue_iterator it; + b_queue_foreach (&it, &bucket->bk_items) { + struct b_hashmap_bucket_item *item = b_unbox( + struct b_hashmap_bucket_item, it.entry, bi_entry); + + if (compare_key(&item->bi_key, key)) { + return &item->bi_value; + } + } + + return NULL; +} + +static bool hashmap_has_key( + const struct b_hashmap_p *hashmap, const b_hashmap_key *key) +{ + uint64_t hash = hash_key(key); + struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash); + if (!bucket) { + return false; + } + + b_queue_iterator it; + b_queue_foreach (&it, &bucket->bk_items) { + struct b_hashmap_bucket_item *item = b_unbox( + struct b_hashmap_bucket_item, it.entry, bi_entry); + + if (compare_key(&item->bi_key, key)) { + return true; + } + } + + return false; +} + +static size_t hashmap_get_size(const struct b_hashmap_p *hashmap) +{ + size_t count = 0; + b_btree_iterator it1; + b_queue_iterator it2; + b_btree_foreach (&it1, &hashmap->h_buckets) { + struct b_hashmap_bucket *bucket + = b_unbox(struct b_hashmap_bucket, it1.node, bk_node); + + b_queue_foreach (&it2, &bucket->bk_items) { + count++; + } + } + + return count; +} + +static bool hashmap_is_empty(const struct b_hashmap_p *hashmap) +{ + b_btree_node *first_node = b_btree_first(&hashmap->h_buckets); + struct b_hashmap_bucket *first_bucket + = b_unbox(struct b_hashmap_bucket, first_node, bk_node); + if (!first_bucket) { + return true; + } + + b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items); + struct b_hashmap_bucket_item *first_item + = b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry); + if (!first_item) { + return true; + } + + return false; +} + +static b_status delete_item( + struct b_hashmap_p *hashmap, struct b_hashmap_bucket *bucket, + struct b_hashmap_bucket_item *item) +{ + b_queue_delete(&bucket->bk_items, &item->bi_entry); + + if (hashmap->h_key_dtor) { + hashmap->h_key_dtor((void *)item->bi_key.key_data); + } + + if (hashmap->h_value_dtor) { + hashmap->h_value_dtor((void *)item->bi_value.value_data); + } + + free(item); + + if (b_queue_empty(&bucket->bk_items)) { + b_btree_delete(&hashmap->h_buckets, &bucket->bk_node); + free(bucket); + } + + return B_SUCCESS; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +b_hashmap *b_hashmap_create( + b_hashmap_key_destructor key_dtor, b_hashmap_value_destructor value_dtor) +{ + b_hashmap *hashmap = b_object_create(B_TYPE_HASHMAP); + if (!hashmap) { + return NULL; + } + + return hashmap; +} + +b_hashmap *b_hashmap_create_with_items(const b_hashmap_item *items) +{ + b_hashmap *hashmap = b_hashmap_create(NULL, NULL); + if (!hashmap) { + return NULL; + } + + struct b_hashmap_p *p = b_object_get_private(hashmap, B_TYPE_HASHMAP); + + for (size_t i = 0; items[i].key.key_data && items[i].key.key_size; i++) { + hashmap_put(p, &items[i].key, &items[i].value); + } + + return hashmap; +} + +b_status b_hashmap_put( + b_hashmap *hashmap, const b_hashmap_key *key, const b_hashmap_value *value) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_HASHMAP, hashmap_put, hashmap, key, value); +} + +const struct b_hashmap_value *b_hashmap_get( + const b_hashmap *hashmap, const struct b_hashmap_key *key) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_HASHMAP, hashmap_get, hashmap, key); +} + +bool b_hashmap_has_key(const b_hashmap *hashmap, const b_hashmap_key *key) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_HASHMAP, hashmap_has_key, hashmap, key); +} + +size_t b_hashmap_get_size(const b_hashmap *hashmap) +{ + B_CLASS_DISPATCH_STATIC_0(B_TYPE_HASHMAP, hashmap_get_size, hashmap); +} + +bool b_hashmap_is_empty(const b_hashmap *hashmap) +{ + B_CLASS_DISPATCH_STATIC_0(B_TYPE_HASHMAP, hashmap_is_empty, hashmap); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void hashmap_init(b_object *obj, void *priv) +{ + struct b_hashmap_p *map = priv; +} + +static void hashmap_fini(b_object *obj, void *priv) +{ + struct b_hashmap_p *map = priv; + + b_btree_iterator it1; + b_btree_iterator_begin(&map->h_buckets, &it1); + while (b_btree_iterator_is_valid(&it1)) { + struct b_hashmap_bucket *b + = b_unbox(struct b_hashmap_bucket, it1.node, bk_node); + b_btree_iterator_erase(&it1); + + b_queue_iterator it2; + b_queue_iterator_begin(&b->bk_items, &it2); + while (b_queue_iterator_is_valid(&it2)) { + struct b_hashmap_bucket_item *item = b_unbox( + struct b_hashmap_bucket_item, it2.entry, bi_entry); + b_queue_iterator_erase(&it2); + + if (map->h_key_dtor) { + map->h_key_dtor((void *)item->bi_key.key_data); + } + + if (map->h_value_dtor) { + map->h_value_dtor((void *)item->bi_value.value_data); + } + + free(item); + } + + free(b); + } +} + +/*** CLASS DEFINITION *********************************************************/ + +B_TYPE_CLASS_DEFINITION_BEGIN(b_hashmap) + B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT) + B_INTERFACE_ENTRY(to_string) = NULL; + B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT) +B_TYPE_CLASS_DEFINITION_END(b_hashmap) + +B_TYPE_DEFINITION_BEGIN(b_hashmap) + B_TYPE_ID(0x7bf5bcd1, 0x1ff3, 0x4e43, 0xbed8, 0x7c74f28348bf); + B_TYPE_CLASS(b_hashmap_class); + B_TYPE_INSTANCE_INIT(hashmap_init); + B_TYPE_INSTANCE_INIT(hashmap_fini); +B_TYPE_DEFINITION_END(b_hashmap) + +/*** ITERATOR FUNCTIONS *******************************************************/ + +static bool hashmap_iterator_next(struct b_iterator *it) +{ + return b_hashmap_iterator_next((struct b_hashmap_iterator *)it); +} + +static b_status hashmap_iterator_erase(struct b_iterator *it) +{ + return b_hashmap_iterator_erase((struct b_hashmap_iterator *)it); +} + +static bool hashmap_iterator_is_valid(const struct b_iterator *it) +{ + return b_hashmap_iterator_is_valid((struct b_hashmap_iterator *)it); +} + +static struct b_iterator_ops it_ops = { + .it_next = hashmap_iterator_next, + .it_erase = hashmap_iterator_erase, + .it_close = NULL, + .it_is_valid = hashmap_iterator_is_valid, +}; + +int b_hashmap_iterator_begin(b_hashmap *hashmap, struct b_hashmap_iterator *it) +{ + it->_h = hashmap; + it->_h_p = b_object_get_private(hashmap, B_TYPE_HASHMAP); + it->_base.it_ops = &it_ops; + + it->i = 0; + if (b_hashmap_is_empty(hashmap)) { + it->key = NULL; + it->value = NULL; + return -1; + } + + struct b_btree_node *first_node = b_btree_first(&it->_h_p->h_buckets); + struct b_hashmap_bucket *first_bucket + = b_unbox(struct b_hashmap_bucket, first_node, bk_node); + if (!first_bucket) { + it->key = NULL; + it->value = NULL; + return -1; + } + + struct b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items); + struct b_hashmap_bucket_item *first_item + = b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry); + if (!first_item) { + it->key = NULL; + it->value = NULL; + return -1; + } + + it->key = &first_item->bi_key; + it->value = &first_item->bi_value; + + it->_cbn = first_node; + it->_cqe = first_entry; + + return 0; +} + bool b_hashmap_iterator_next(struct b_hashmap_iterator *it) { struct b_btree_node *next_node; @@ -446,7 +533,7 @@ b_status b_hashmap_iterator_erase(struct b_hashmap_iterator *it) struct b_hashmap_bucket_item *next_item = b_unbox(struct b_hashmap_bucket_item, next_entry, bi_entry); - b_status status = delete_item(it->_h, cur_bucket, cur_item); + b_status status = delete_item(it->_h_p, cur_bucket, cur_item); if (B_ERR(status)) { return status; } @@ -472,19 +559,3 @@ bool b_hashmap_iterator_is_valid(const struct b_hashmap_iterator *it) { return it->key != NULL; } - -static void hashmap_release(struct b_dsref *obj) -{ - struct b_hashmap *map = B_HASHMAP(obj); - - b_hashmap_iterator it; - b_hashmap_iterator_begin(map, &it); - while (b_hashmap_iterator_is_valid(&it)) { - b_hashmap_iterator_erase(&it); - } -} - -b_dsref_type_id b_hashmap_type_id(void) -{ - return (b_dsref_type_id)&hashmap_type; -} diff --git a/ds/hashmap.h b/ds/hashmap.h deleted file mode 100644 index 76de029..0000000 --- a/ds/hashmap.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _B_HASHMAP_H_ -#define _B_HASHMAP_H_ - -#include -#include -#include - -struct b_hashmap_bucket_item { - struct b_queue_entry bi_entry; - struct b_hashmap_key bi_key; - struct b_hashmap_value bi_value; -}; - -struct b_hashmap_bucket { - struct b_btree_node bk_node; - uint64_t bk_hash; - struct b_queue bk_items; -}; - -struct b_hashmap { - struct b_dsref h_base; - struct b_btree h_buckets; - b_hashmap_key_destructor h_key_dtor; - b_hashmap_value_destructor h_value_dtor; -}; - -#endif diff --git a/ds/include/blue/ds/hashmap.h b/ds/include/blue/ds/hashmap.h index a99e9e7..52f651a 100644 --- a/ds/include/blue/ds/hashmap.h +++ b/ds/include/blue/ds/hashmap.h @@ -1,15 +1,23 @@ -#ifndef BLUELIB_HASHMAP_H_ -#define BLUELIB_HASHMAP_H_ +#ifndef BLUE_DS_HASHMAP_H_ +#define BLUE_DS_HASHMAP_H_ #include +#include #include #include #include -#include -#include #include -#define B_HASHMAP(p) ((b_hashmap *)(p)) +B_DECLS_BEGIN; + +struct b_hashmap_p; + +#define B_TYPE_HASHMAP (b_hashmap_get_type()) + +B_DECLARE_TYPE(b_hashmap); + +B_TYPE_CLASS_DECLARATION_BEGIN(b_hashmap) +B_TYPE_CLASS_DECLARATION_END(b_hashmap) #define B_HASHMAP_KEY(k, ks) {.key_data = (k), .key_size = (ks)} #define B_HASHMAP_VALUE(v, vs) {.value_data = (v), .value_size = (vs)} @@ -23,8 +31,6 @@ for (int z__b_unique_name() = b_hashmap_iterator_begin(hashmap, it); \ (it)->key != NULL; b_hashmap_iterator_next(it)) -typedef struct b_hashmap b_hashmap; - typedef void (*b_hashmap_key_destructor)(void *); typedef void (*b_hashmap_value_destructor)(void *); @@ -55,23 +61,17 @@ typedef struct b_hashmap_iterator { const b_hashmap_value *value; b_hashmap *_h; + struct b_hashmap_p *_h_p; b_btree_node *_cbn; b_queue_entry *_cqe; } b_hashmap_iterator; +BLUE_API b_type b_hashmap_get_type(void); + BLUE_API b_hashmap *b_hashmap_create( b_hashmap_key_destructor key_dtor, b_hashmap_value_destructor value_dtor); BLUE_API b_hashmap *b_hashmap_create_with_items(const b_hashmap_item *items); -static inline b_hashmap *b_hashmap_retain(b_hashmap *hashmap) -{ - return B_HASHMAP(b_retain(B_DSREF(hashmap))); -} -static inline void b_hashmap_release(b_hashmap *hashmap) -{ - b_release(B_DSREF(hashmap)); -} - BLUE_API b_status b_hashmap_put( b_hashmap *hashmap, const b_hashmap_key *key, const b_hashmap_value *value); BLUE_API const b_hashmap_value *b_hashmap_get( @@ -86,4 +86,6 @@ BLUE_API bool b_hashmap_iterator_next(b_hashmap_iterator *it); BLUE_API b_status b_hashmap_iterator_erase(b_hashmap_iterator *it); BLUE_API bool b_hashmap_iterator_is_valid(const b_hashmap_iterator *it); +B_DECLS_END; + #endif