#include "dict.h" #include #include #include #include #include #include #include #define HASH_OFFSET_BASIS 0xcbf29ce484222325 #define HASH_PRIME 0x100000001b3 uint64_t b_cstr_hash(const char *s) { uint64_t hash = HASH_OFFSET_BASIS; for (size_t i = 0; s[i]; i++) { hash *= HASH_PRIME; hash ^= s[i]; } return hash; } static void dict_release(struct b_object *obj); static void dict_to_string(struct b_object *obj, struct b_stringstream *out); static struct b_object_type dict_type = { .t_name = "corelib::dict", .t_flags = B_OBJECT_FUNDAMENTAL, .t_id = B_OBJECT_TYPE_DICT, .t_instance_size = sizeof(struct b_dict), .t_release = dict_release, .t_to_string = dict_to_string, }; struct b_dict *b_dict_create(void) { struct b_dict *dict = (struct b_dict *)b_object_type_instantiate(&dict_type); if (!dict) { return NULL; } return dict; } struct b_dict *b_dict_create_with_items(const b_dict_item *items) { struct b_dict *dict = b_dict_create(); if (!dict) { return NULL; } for (size_t i = 0; items[i].key; i++) { b_dict_put(dict, items[i].key, items[i].value); } return dict; } static B_BTREE_DEFINE_SIMPLE_GET( struct b_dict_bucket, uint64_t, bk_node, bk_hash, get_bucket) static B_BTREE_DEFINE_SIMPLE_INSERT(struct b_dict_bucket, bk_node, bk_hash, put_bucket) static struct b_dict_bucket *create_bucket(void) { struct b_dict_bucket *bucket = malloc(sizeof *bucket); if (!bucket) { return NULL; } memset(bucket, 0x0, sizeof *bucket); return bucket; } static struct b_dict_bucket_item *create_bucket_item(void) { struct b_dict_bucket_item *item = malloc(sizeof *item); if (!item) { return NULL; } memset(item, 0x0, sizeof *item); return item; } b_status b_dict_put(struct b_dict *dict, const char *key, b_object *value) { uint64_t hash = b_cstr_hash(key); struct b_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash); if (!bucket) { bucket = create_bucket(); if (!bucket) { return B_ERR_NO_MEMORY; } bucket->bk_hash = hash; put_bucket(&dict->d_buckets, bucket); } struct b_dict_bucket_item *item = create_bucket_item(); if (!item) { return B_ERR_NO_MEMORY; } item->bi_str = b_strdup(key); item->bi_value = b_retain(value); b_queue_push_back(&bucket->bk_items, &item->bi_entry); return B_SUCCESS; } b_object *b_dict_at(const struct b_dict *dict, const char *key) { uint64_t hash = b_cstr_hash(key); struct b_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash); if (!bucket) { return NULL; } b_queue_iterator it; b_queue_foreach (&it, &bucket->bk_items) { struct b_dict_bucket_item *item = b_unbox(struct b_dict_bucket_item, it.entry, bi_entry); if (!strcmp(item->bi_str, key)) { return item->bi_value; } } return NULL; } b_object *b_dict_get(struct b_dict *dict, const char *key) { b_object *value = b_dict_at(dict, key); if (value) { b_retain(value); } return value; } bool b_dict_has_key(const struct b_dict *dict, const char *key) { return b_dict_at(dict, key) != NULL; } size_t b_dict_get_size(const struct b_dict *dict) { size_t count = 0; b_btree_iterator it1; b_queue_iterator it2; b_btree_foreach (&it1, &dict->d_buckets) { struct b_dict_bucket *bucket = b_unbox(struct b_dict_bucket, it1.node, bk_node); b_queue_foreach (&it2, &bucket->bk_items) { count++; } } return count; } bool b_dict_is_empty(const b_dict *dict) { b_btree_node *first_node = b_btree_first(&dict->d_buckets); struct b_dict_bucket *first_bucket = b_unbox(struct b_dict_bucket, first_node, bk_node); if (!first_bucket) { return true; } b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items); struct b_dict_bucket_item *first_item = b_unbox(struct b_dict_bucket_item, first_entry, bi_entry); if (!first_item) { return true; } return false; } static void dict_to_string(struct b_object *obj, struct b_stringstream *out) { struct b_dict *dict = B_DICT(obj); if (b_dict_is_empty(dict)) { b_stringstream_add(out, "{}"); return; } b_stringstream_add(out, "{\n"); b_stringstream_push_indent(out, 1); size_t len = b_dict_get_size(dict); b_dict_iterator it; b_dict_foreach(&it, dict) { b_stringstream_addf(out, "%s: ", it.key); b_to_string(it.value, out); if (it.i < len - 1) { b_stringstream_add(out, ","); } b_stringstream_add(out, "\n"); } b_stringstream_pop_indent(out); b_stringstream_add(out, "}"); } static bool dict_iterator_next(struct b_iterator *it) { return b_dict_iterator_next((struct b_dict_iterator *)it); } static b_status dict_iterator_erase(struct b_iterator *it) { return b_dict_iterator_erase((struct b_dict_iterator *)it); } static bool dict_iterator_is_valid(const struct b_iterator *it) { return b_dict_iterator_is_valid((struct b_dict_iterator *)it); } static struct b_iterator_ops it_ops = {.it_next = dict_iterator_next, .it_close = NULL, .it_erase = dict_iterator_erase, .it_is_valid = dict_iterator_is_valid}; int b_dict_iterator_begin(struct b_dict *dict, b_dict_iterator *it) { it->_base.it_ops = &it_ops; it->i = 0; if (b_dict_is_empty(dict)) { it->key = NULL; it->value = NULL; return -1; } b_btree_node *first_node = b_btree_first(&dict->d_buckets); struct b_dict_bucket *first_bucket = b_unbox(struct b_dict_bucket, first_node, bk_node); if (!first_bucket) { it->key = NULL; it->value = NULL; return -1; } b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items); struct b_dict_bucket_item *first_item = b_unbox(struct b_dict_bucket_item, first_entry, bi_entry); if (!first_item) { it->key = NULL; it->value = NULL; return -1; } it->key = first_item->bi_str; it->value = first_item->bi_value; it->_d = dict; 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) { struct b_dict_bucket *cur_bucket = b_unbox(struct b_dict_bucket, cur_node, bk_node); if (!cur_bucket) { return false; } struct b_dict_bucket_item *cur_item = b_unbox(struct b_dict_bucket_item, cur_entry, bi_entry); if (!cur_item) { return false; } struct b_btree_node *next_node = cur_node; struct b_queue_entry *next_entry = b_queue_next(cur_entry); if (!next_entry) { next_node = b_btree_next(cur_node); if (!next_node) { return false; } struct b_dict_bucket *next_bucket = b_unbox(struct b_dict_bucket, next_node, bk_node); if (!next_bucket) { return false; } next_entry = b_queue_first(&next_bucket->bk_items); if (!next_entry) { return false; } } struct b_dict_bucket_item *next_item = b_unbox(struct b_dict_bucket_item, next_entry, bi_entry); if (!next_item) { return false; } *out_next_node = next_node; *out_next_entry = next_entry; return true; } bool b_dict_iterator_next(b_dict_iterator *it) { struct b_btree_node *next_node; struct b_queue_entry *next_entry; if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) { it->key = NULL; it->value = NULL; return -1; } struct b_dict_bucket_item *next_item = b_unbox(struct b_dict_bucket_item, next_entry, bi_entry); if (!next_item) { it->key = NULL; it->value = NULL; return -1; } it->i++; it->key = next_item->bi_str; it->value = next_item->bi_value; it->_cbn = next_node; it->_cqe = next_entry; return 0; } static b_status delete_item( struct b_dict *dict, struct b_dict_bucket *bucket, struct b_dict_bucket_item *item) { b_queue_delete(&bucket->bk_items, &item->bi_entry); free(item); if (b_queue_empty(&bucket->bk_items)) { b_btree_delete(&dict->d_buckets, &bucket->bk_node); free(bucket); } return B_SUCCESS; } b_status b_dict_iterator_erase(struct b_dict_iterator *it) { if ((it->key || it->value) && !(it->_cbn && it->_cqe)) { return B_ERR_BAD_STATE; } if (!it->key || !it->_cqe) { return B_ERR_OUT_OF_BOUNDS; } struct b_btree_node *next_node; struct b_queue_entry *next_entry; if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) { it->key = NULL; it->value = NULL; return false; } struct b_dict_bucket *cur_bucket = b_unbox(struct b_dict_bucket, it->_cbn, bk_node); struct b_dict_bucket_item *cur_item = b_unbox(struct b_dict_bucket_item, it->_cqe, bi_entry); struct b_dict_bucket_item *next_item = b_unbox(struct b_dict_bucket_item, next_entry, bi_entry); b_status status = delete_item(it->_d, cur_bucket, cur_item); if (B_ERR(status)) { return status; } if (next_item) { it->key = next_item->bi_str; it->value = next_item->bi_value; it->_cbn = next_node; it->_cqe = next_entry; } else { it->key = NULL; it->value = NULL; it->_cbn = NULL; it->_cqe = NULL; } return B_SUCCESS; } bool b_dict_iterator_is_valid(const struct b_dict_iterator *it) { return it->key != NULL; } static void dict_release(struct b_object *obj) { struct b_dict *dict = B_DICT(obj); b_btree_iterator tree_it; b_btree_iterator_begin(&dict->d_buckets, &tree_it); while (b_btree_iterator_is_valid(&tree_it)) { struct b_dict_bucket *bucket = b_unbox(struct b_dict_bucket, tree_it.node, bk_node); b_btree_iterator_erase(&tree_it); if (!bucket) { continue; } b_queue_iterator bucket_it; b_queue_iterator_begin(&bucket->bk_items, &bucket_it); while (b_queue_iterator_is_valid(&bucket_it)) { struct b_dict_bucket_item *item = b_unbox( struct b_dict_bucket_item, bucket_it.entry, bi_entry); b_queue_iterator_erase(&bucket_it); if (!item) { continue; } free(item->bi_str); b_release(item->bi_value); free(item); } free(bucket); } #if 0 b_dict_iterator it; b_dict_iterator_begin(dict, &it); while (b_dict_iterator_is_valid(&it)) { b_dict_iterator_erase(&it); } #endif } b_object_type_id b_dict_type_id(void) { return (b_object_type_id)&dict_type; }