diff --git a/fx.collections/dict.c b/fx.collections/dict.c deleted file mode 100644 index d19ca5a..0000000 --- a/fx.collections/dict.c +++ /dev/null @@ -1,732 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -/*** PRIVATE DATA *************************************************************/ - -struct fx_dict_bucket_item { - fx_queue_entry bi_entry; - fx_string *bi_str; - fx_object *bi_value; -}; - -struct fx_dict_bucket { - fx_bst_node bk_node; - uint64_t bk_hash; - fx_queue bk_items; -}; - -struct fx_dict_p { - fx_bst d_buckets; -}; - -struct fx_dict_iterator_p { - size_t i; - fx_dict_item item; - - fx_dict *_d; - struct fx_dict_p *_d_p; - fx_bst_node *_cbn; - fx_queue_entry *_cqe; -}; - -/*** MISC FUNCTIONS ***********************************************************/ - -static FX_BST_DEFINE_SIMPLE_GET( - struct fx_dict_bucket, - uint64_t, - bk_node, - bk_hash, - get_bucket); -static FX_BST_DEFINE_SIMPLE_INSERT( - struct fx_dict_bucket, - bk_node, - bk_hash, - put_bucket); - -/*** PRIVATE FUNCTIONS ********************************************************/ - -static struct fx_dict_bucket *create_bucket(void) -{ - struct fx_dict_bucket *bucket = malloc(sizeof *bucket); - if (!bucket) { - return NULL; - } - - memset(bucket, 0x0, sizeof *bucket); - return bucket; -} - -static struct fx_dict_bucket_item *create_bucket_item(void) -{ - struct fx_dict_bucket_item *item = malloc(sizeof *item); - if (!item) { - return NULL; - } - - memset(item, 0x0, sizeof *item); - return item; -} - -static fx_status dict_put( - struct fx_dict_p *dict, - const char *key, - fx_object *value) -{ - uint64_t hash = fx_hash_cstr(key); - struct fx_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash); - if (!bucket) { - bucket = create_bucket(); - if (!bucket) { - return FX_ERR_NO_MEMORY; - } - - bucket->bk_hash = hash; - put_bucket(&dict->d_buckets, bucket); - } - - struct fx_dict_bucket_item *item = create_bucket_item(); - if (!item) { - return FX_ERR_NO_MEMORY; - } - - item->bi_str = fx_string_create_from_cstr(key); - item->bi_value = fx_object_ref(value); - - fx_queue_push_back(&bucket->bk_items, &item->bi_entry); - - return FX_SUCCESS; -} - -static fx_status dict_put_sk( - struct fx_dict_p *dict, - const fx_string *key, - fx_object *value) -{ - uint64_t hash = fx_string_hash(key); - struct fx_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash); - if (!bucket) { - bucket = create_bucket(); - if (!bucket) { - return FX_ERR_NO_MEMORY; - } - - bucket->bk_hash = hash; - put_bucket(&dict->d_buckets, bucket); - } - - struct fx_dict_bucket_item *item = create_bucket_item(); - if (!item) { - return FX_ERR_NO_MEMORY; - } - - item->bi_str = fx_string_duplicate(key); - item->bi_value = fx_object_ref(value); - - fx_queue_push_back(&bucket->bk_items, &item->bi_entry); - - return FX_SUCCESS; -} - -static fx_object *dict_at(const struct fx_dict_p *dict, const char *key) -{ - uint64_t hash = fx_hash_cstr(key); - struct fx_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash); - if (!bucket) { - return NULL; - } - - struct fx_queue_entry *entry = fx_queue_first(&bucket->bk_items); - while (entry) { - struct fx_dict_bucket_item *item - = fx_unbox(struct fx_dict_bucket_item, entry, bi_entry); - - if (!strcmp(fx_string_get_cstr(item->bi_str), key)) { - return item->bi_value; - } - - entry = fx_queue_next(entry); - } - - return NULL; -} - -static fx_object *dict_at_sk(const struct fx_dict_p *dict, const fx_string *key) -{ - uint64_t hash = fx_string_hash(key); - struct fx_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash); - if (!bucket) { - return NULL; - } - - struct fx_queue_entry *entry = fx_queue_first(&bucket->bk_items); - while (entry) { - struct fx_dict_bucket_item *item - = fx_unbox(struct fx_dict_bucket_item, entry, bi_entry); - - if (fx_string_compare(item->bi_str, key)) { - return item->bi_value; - } - - entry = fx_queue_next(entry); - } - - return NULL; -} - -static fx_object *dict_get(struct fx_dict_p *dict, const char *key) -{ - fx_object *value = dict_at(dict, key); - if (value) { - fx_object_ref(value); - } - - return value; -} - -static fx_object *dict_get_sk(struct fx_dict_p *dict, const fx_string *key) -{ - fx_object *value = dict_at_sk(dict, key); - if (value) { - fx_object_ref(value); - } - - return value; -} - -static bool dict_has_key(const struct fx_dict_p *dict, const char *key) -{ - return dict_at(dict, key) != NULL; -} - -static bool dict_has_skey(const struct fx_dict_p *dict, const fx_string *key) -{ - return dict_at_sk(dict, key) != NULL; -} - -static size_t dict_get_size(const struct fx_dict_p *dict) -{ - size_t count = 0; -#if 0 - fx_bst_iterator it1; - fx_queue_iterator it2; - - fx_bst_foreach (&it1, &dict->d_buckets) { - struct fx_dict_bucket *bucket - = fx_unbox(struct fx_dict_bucket, it1.node, bk_node); - - fx_queue_foreach (&it2, &bucket->bk_items) { - count++; - } - } -#endif - - return count; -} - -static bool dict_is_empty(const struct fx_dict_p *dict) -{ - fx_bst_node *first_node = fx_bst_first(&dict->d_buckets); - struct fx_dict_bucket *first_bucket - = fx_unbox(struct fx_dict_bucket, first_node, bk_node); - if (!first_bucket) { - return true; - } - - fx_queue_entry *first_entry = fx_queue_first(&first_bucket->bk_items); - struct fx_dict_bucket_item *first_item - = fx_unbox(struct fx_dict_bucket_item, first_entry, bi_entry); - if (!first_item) { - return true; - } - - return false; -} - -static bool get_next_node( - struct fx_bst_node *cur_node, - struct fx_queue_entry *cur_entry, - struct fx_bst_node **out_next_node, - struct fx_queue_entry **out_next_entry) -{ - struct fx_dict_bucket *cur_bucket - = fx_unbox(struct fx_dict_bucket, cur_node, bk_node); - if (!cur_bucket) { - return false; - } - - struct fx_dict_bucket_item *cur_item - = fx_unbox(struct fx_dict_bucket_item, cur_entry, bi_entry); - if (!cur_item) { - return false; - } - - struct fx_bst_node *next_node = cur_node; - struct fx_queue_entry *next_entry = fx_queue_next(cur_entry); - if (!next_entry) { - next_node = fx_bst_next(cur_node); - if (!next_node) { - return false; - } - - struct fx_dict_bucket *next_bucket - = fx_unbox(struct fx_dict_bucket, next_node, bk_node); - if (!next_bucket) { - return false; - } - - next_entry = fx_queue_first(&next_bucket->bk_items); - if (!next_entry) { - return false; - } - } - - struct fx_dict_bucket_item *next_item - = fx_unbox(struct fx_dict_bucket_item, next_entry, bi_entry); - if (!next_item) { - return false; - } - - *out_next_node = next_node; - *out_next_entry = next_entry; - - return true; -} - -static fx_status delete_item( - struct fx_dict_p *dict, - struct fx_dict_bucket *bucket, - struct fx_dict_bucket_item *item) -{ - fx_queue_delete(&bucket->bk_items, &item->bi_entry); - free(item); - - if (fx_queue_empty(&bucket->bk_items)) { - fx_bst_delete(&dict->d_buckets, &bucket->bk_node); - free(bucket); - } - - return FX_SUCCESS; -} - -/*** PUBLIC FUNCTIONS *********************************************************/ - -#if 0 -fx_dict *fx_dict_create_with_items(const fx_dict_item *items) -{ - fx_dict *dict = fx_dict_create(); - if (!dict) { - return NULL; - } - - struct fx_dict_p *p = fx_object_get_private(dict, FX_TYPE_DICT); - - for (size_t i = 0; items[i].key; i++) { - dict_put(p, items[i].key, items[i].value); - } - - return dict; -} -#endif - -fx_status fx_dict_put(fx_dict *dict, const char *key, fx_object *value) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_put, dict, key, value); -} - -fx_status fx_dict_put_sk(fx_dict *dict, const fx_string *key, fx_object *value) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_put_sk, dict, key, value); -} - -fx_object *fx_dict_at(const fx_dict *dict, const char *key) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_at, dict, key); -} - -fx_object *fx_dict_at_sk(const fx_dict *dict, const fx_string *key) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_at_sk, dict, key); -} - -fx_object *fx_dict_get(fx_dict *dict, const char *key) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_get, dict, key); -} - -fx_object *fx_dict_get_sk(fx_dict *dict, const fx_string *key) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_get_sk, dict, key); -} - -bool fx_dict_has_key(const fx_dict *dict, const char *key) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_has_key, dict, key); -} - -bool fx_dict_has_skey(const fx_dict *dict, const fx_string *key) -{ - FX_CLASS_DISPATCH_STATIC(FX_TYPE_DICT, dict_has_skey, dict, key); -} - -size_t fx_dict_get_size(const fx_dict *dict) -{ - FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_DICT, dict_get_size, dict); -} - -bool fx_dict_is_empty(const fx_dict *dict) -{ - FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_DICT, dict_is_empty, dict); -} - -/*** PUBLIC ALIAS FUNCTIONS ***************************************************/ -/*** VIRTUAL FUNCTIONS ********************************************************/ - -static void dict_init(fx_object *obj, void *priv) -{ - struct fx_dict_p *dict = priv; -} - -static void dict_fini(fx_object *obj, void *priv) -{ - struct fx_dict_p *dict = priv; - - struct fx_bst_node *node = fx_bst_first(&dict->d_buckets); - while (node) { - struct fx_dict_bucket *bucket - = fx_unbox(struct fx_dict_bucket, node, bk_node); - struct fx_bst_node *next_node = fx_bst_next(node); - fx_bst_delete(&dict->d_buckets, node); - - struct fx_queue_entry *entry - = fx_queue_first(&bucket->bk_items); - while (entry) { - struct fx_dict_bucket_item *item = fx_unbox( - struct fx_dict_bucket_item, - entry, - bi_entry); - struct fx_queue_entry *next_entry - = fx_queue_next(entry); - fx_queue_delete(&bucket->bk_items, entry); - - free(item->bi_str); - fx_object_unref(item->bi_value); - free(item); - - entry = next_entry; - } - - free(bucket); - node = next_node; - } -} - -static void dict_to_string(const fx_object *obj, fx_stream *out) -{ - struct fx_dict_p *dict = fx_object_get_private(obj, FX_TYPE_DICT); - - if (dict_is_empty(dict)) { - fx_stream_write_cstr(out, "{}", NULL); - return; - } - - fx_stream_write_cstr(out, "{\n", NULL); - - fx_stream_push_indent(out, 1); - size_t len = dict_get_size(dict); - size_t i = 0; - - struct fx_bst_node *node = fx_bst_first(&dict->d_buckets); - while (node) { - struct fx_dict_bucket *bucket - = fx_unbox(struct fx_dict_bucket, node, bk_node); - - struct fx_queue_entry *entry - = fx_queue_first(&bucket->bk_items); - while (entry) { - struct fx_dict_bucket_item *item = fx_unbox( - struct fx_dict_bucket_item, - entry, - bi_entry); - - fx_object_to_string(item->bi_str, out); - fx_stream_write_cstr(out, ": ", NULL); - - bool is_string = fx_object_is_type( - item->bi_value, - FX_TYPE_STRING); - - if (is_string) { - fx_stream_write_char(out, '"'); - } - - fx_object_to_string(item->bi_value, out); - - if (is_string) { - fx_stream_write_char(out, '"'); - } - - if (i < len - 1) { - fx_stream_write_cstr(out, ",", NULL); - } - - fx_stream_write_char(out, '\n'); - entry = fx_queue_next(entry); - i++; - } - node = fx_bst_next(node); - } - - fx_stream_pop_indent(out); - fx_stream_write_char(out, '}'); -} - -/*** ITERATOR FUNCTIONS *******************************************************/ - -static fx_iterator *iterable_begin(fx_dict *dict) -{ - fx_iterator *it_obj = fx_object_create(FX_TYPE_DICT_ITERATOR); - if (!it_obj) { - return NULL; - } - - struct fx_dict_iterator_p *it - = fx_object_get_private(it_obj, FX_TYPE_DICT_ITERATOR); - - it->i = 0; - it->_d = dict; - it->_d_p = fx_object_get_private(dict, FX_TYPE_DICT); - - if (dict_is_empty(it->_d_p)) { - it->item.key = NULL; - it->item.value = NULL; - fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); - return it_obj; - } - - fx_bst_node *first_node = fx_bst_first(&it->_d_p->d_buckets); - struct fx_dict_bucket *first_bucket - = fx_unbox(struct fx_dict_bucket, first_node, bk_node); - if (!first_bucket) { - it->item.key = NULL; - it->item.value = NULL; - fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); - return it_obj; - } - - fx_queue_entry *first_entry = fx_queue_first(&first_bucket->bk_items); - struct fx_dict_bucket_item *first_item - = fx_unbox(struct fx_dict_bucket_item, first_entry, bi_entry); - if (!first_item) { - it->item.key = NULL; - it->item.value = NULL; - fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); - return it_obj; - } - - it->item.key = first_item->bi_str; - it->item.value = first_item->bi_value; - - it->_d = dict; - it->_cbn = first_node; - it->_cqe = first_entry; - - return it_obj; -} - -static const fx_iterator *iterable_cbegin(const fx_dict *dict) -{ - fx_iterator *it_obj = fx_object_create(FX_TYPE_DICT_ITERATOR); - if (!it_obj) { - return NULL; - } - - struct fx_dict_iterator_p *it - = fx_object_get_private(it_obj, FX_TYPE_DICT_ITERATOR); - - it->i = 0; - it->_d = (fx_dict *)dict; - it->_d_p = fx_object_get_private(dict, FX_TYPE_DICT); - - if (dict_is_empty(it->_d_p)) { - it->item.key = NULL; - it->item.value = NULL; - fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); - return it_obj; - } - - fx_bst_node *first_node = fx_bst_first(&it->_d_p->d_buckets); - struct fx_dict_bucket *first_bucket - = fx_unbox(struct fx_dict_bucket, first_node, bk_node); - if (!first_bucket) { - it->item.key = NULL; - it->item.value = NULL; - fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); - return it_obj; - } - - fx_queue_entry *first_entry = fx_queue_first(&first_bucket->bk_items); - struct fx_dict_bucket_item *first_item - = fx_unbox(struct fx_dict_bucket_item, first_entry, bi_entry); - if (!first_item) { - it->item.key = NULL; - it->item.value = NULL; - fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); - return it_obj; - } - - it->item.key = first_item->bi_str; - it->item.value = first_item->bi_value; - - it->_cbn = first_node; - it->_cqe = first_entry; - - return it_obj; -} - -static enum fx_status iterator_move_next(const fx_iterator *obj) -{ - struct fx_dict_iterator_p *it - = fx_object_get_private(obj, FX_TYPE_DICT_ITERATOR); - - struct fx_bst_node *next_node; - struct fx_queue_entry *next_entry; - if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) { - it->item.key = NULL; - it->item.value = NULL; - return FX_ERR_NO_DATA; - } - - struct fx_dict_bucket_item *next_item - = fx_unbox(struct fx_dict_bucket_item, next_entry, bi_entry); - - if (!next_item) { - it->item.key = NULL; - it->item.value = NULL; - return FX_ERR_NO_DATA; - } - - it->i++; - it->item.key = next_item->bi_str; - it->item.value = next_item->bi_value; - - it->_cbn = next_node; - it->_cqe = next_entry; - - return FX_SUCCESS; -} - -static enum fx_status iterator_erase(fx_iterator *obj) -{ - struct fx_dict_iterator_p *it - = fx_object_get_private(obj, FX_TYPE_DICT_ITERATOR); - - if ((it->item.key || it->item.value) && !(it->_cbn && it->_cqe)) { - return FX_ERR_BAD_STATE; - } - - if (!it->item.key || !it->_cqe) { - return FX_ERR_NO_DATA; - } - - struct fx_bst_node *next_node; - struct fx_queue_entry *next_entry; - if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) { - it->item.key = NULL; - it->item.value = NULL; - return FX_ERR_NO_DATA; - } - - struct fx_dict_bucket *cur_bucket - = fx_unbox(struct fx_dict_bucket, it->_cbn, bk_node); - struct fx_dict_bucket_item *cur_item - = fx_unbox(struct fx_dict_bucket_item, it->_cqe, bi_entry); - - struct fx_dict_bucket_item *next_item - = fx_unbox(struct fx_dict_bucket_item, next_entry, bi_entry); - - fx_status status = delete_item(it->_d_p, cur_bucket, cur_item); - if (FX_ERR(status)) { - return status; - } - - if (next_item) { - it->item.key = next_item->bi_str; - it->item.value = next_item->bi_value; - - it->_cbn = next_node; - it->_cqe = next_entry; - } else { - it->item.key = NULL; - it->item.value = NULL; - - it->_cbn = NULL; - it->_cqe = NULL; - } - - return FX_SUCCESS; -} - -static fx_iterator_value iterator_get_value(fx_iterator *obj) -{ - struct fx_dict_iterator_p *it - = fx_object_get_private(obj, FX_TYPE_DICT_ITERATOR); - - return FX_ITERATOR_VALUE_PTR(&it->item); -} - -static const fx_iterator_value iterator_get_cvalue(const fx_iterator *obj) -{ - struct fx_dict_iterator_p *it - = fx_object_get_private(obj, FX_TYPE_DICT_ITERATOR); - - return FX_ITERATOR_VALUE_CPTR(&it->item); -} - -/*** CLASS DEFINITION *********************************************************/ - -// ---- fx_dict DEFINITION -FX_TYPE_CLASS_BEGIN(fx_dict) - FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) - FX_INTERFACE_ENTRY(to_string) = dict_to_string; - FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) - - FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterable, FX_TYPE_ITERABLE) - FX_INTERFACE_ENTRY(it_begin) = iterable_begin; - FX_INTERFACE_ENTRY(it_cbegin) = iterable_cbegin; - FX_TYPE_VTABLE_INTERFACE_END(fx_iterable, FX_TYPE_ITERABLE) -FX_TYPE_CLASS_END(fx_dict) - -FX_TYPE_DEFINITION_BEGIN(fx_dict) - FX_TYPE_ID(0xd2af61d9, 0xd0be, 0x4960, 0xbe3f, 0x509749814c10); - FX_TYPE_CLASS(fx_dict_class); - FX_TYPE_IMPLEMENTS(FX_TYPE_ITERABLE); - FX_TYPE_INSTANCE_PRIVATE(struct fx_dict_p); - FX_TYPE_INSTANCE_INIT(dict_init); - FX_TYPE_INSTANCE_FINI(dict_fini); -FX_TYPE_DEFINITION_END(fx_dict) - -// ---- fx_dict_iterator DEFINITION -FX_TYPE_CLASS_BEGIN(fx_dict_iterator) - FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) - FX_INTERFACE_ENTRY(to_string) = NULL; - FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) - - FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterator, FX_TYPE_ITERATOR) - FX_INTERFACE_ENTRY(it_move_next) = iterator_move_next; - FX_INTERFACE_ENTRY(it_erase) = iterator_erase; - FX_INTERFACE_ENTRY(it_get_value) = iterator_get_value; - FX_INTERFACE_ENTRY(it_get_cvalue) = iterator_get_cvalue; - FX_TYPE_VTABLE_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR) -FX_TYPE_CLASS_END(fx_dict_iterator) - -FX_TYPE_DEFINITION_BEGIN(fx_dict_iterator) - FX_TYPE_ID(0x9ea96701, 0x1713, 0x4a3e, 0xbf63, 0xdc856b456f3b); - FX_TYPE_EXTENDS(FX_TYPE_ITERATOR); - FX_TYPE_CLASS(fx_dict_iterator_class); - FX_TYPE_INSTANCE_PRIVATE(struct fx_dict_iterator_p); -FX_TYPE_DEFINITION_END(fx_dict_iterator) diff --git a/fx.collections/hashtable.c b/fx.collections/hashtable.c new file mode 100644 index 0000000..ccb1fb3 --- /dev/null +++ b/fx.collections/hashtable.c @@ -0,0 +1,640 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/*** PRIVATE DATA *************************************************************/ + +/* these primes are used as array capacities to avoid hash collisions. */ +static const size_t primes[] = { + 11, + 23, + 47, + 97, + 197, + 397, + 797, + 1597, + 3203, + 6421, + 12853, + 25717, + 51437, +}; + +struct table_item { + fx_hashtable_item *i_item; +}; + +struct fx_hashtable_item_p { + fx_hashtable_item *i_self; + fx_value i_key, i_value; +}; + +struct fx_hashtable_p { + struct table_item *t_items; + /* number of items currently stored in t_items */ + size_t t_count; + /* index in primes[] that represents the current maximum size of + * t_items */ + size_t t_max_index; +}; + +struct fx_hashtable_iterator_p { + size_t i; + fx_hashtable_item *item; + + fx_hashtable *_h; + struct fx_hashtable_p *_h_p; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + +static fx_status convert_key_to_index( + const fx_value *v, + struct table_item *table, + size_t table_size, + size_t *out_index) +{ + uint64_t hash = 0; + fx_status status = fx_value_hash(v, &hash); + if (!FX_OK(status)) { + return status; + } + + size_t index = hash % table_size, i = 1; + while (table[index].i_item && i < table_size) { + const struct fx_hashtable_item_p *item = fx_object_get_private( + table[index].i_item, + FX_TYPE_HASHTABLE_ITEM); + int cmp = fx_value_compare(&item->i_key, v); + if (cmp == 0) { + break; + } + + index = (index + (i * i)) % table_size; + i++; + } + + *out_index = index; + return FX_SUCCESS; +} + +static fx_status find_key( + const fx_value *v, + struct table_item *table, + size_t table_size, + size_t *out_index) +{ + uint64_t hash = 0; + fx_status status = fx_value_hash(v, &hash); + if (!FX_OK(status)) { + return status; + } + + bool found = false; + size_t index = hash % table_size, i = 1; + while (table[index].i_item && i < table_size) { + const struct fx_hashtable_item_p *item = fx_object_get_private( + table[index].i_item, + FX_TYPE_HASHTABLE_ITEM); + int cmp = fx_value_compare(&item->i_key, v); + if (cmp == 0) { + found = true; + break; + } + + index = (index + (i * i)) % table_size; + i++; + } + + if (!found) { + return FX_ERR_NO_ENTRY; + } + + *out_index = index; + return FX_SUCCESS; +} + +static fx_status add_item_to_table( + fx_hashtable_item *item, + struct table_item *table, + size_t table_capacity) +{ + size_t index = 0; + struct fx_hashtable_item_p *item_p = fx_object_get_private( + item, + FX_TYPE_HASHTABLE_ITEM); + fx_status status = convert_key_to_index( + &item_p->i_key, + table, + table_capacity, + &index); + if (!FX_OK(status)) { + return status; + } + + if (index >= table_capacity) { + return FX_ERR_NO_SPACE; + } + + struct table_item *bucket = &table[index]; + if (bucket->i_item) { + fx_hashtable_item_unref(bucket->i_item); + } + + bucket->i_item = item; + + return FX_SUCCESS; +} + +static fx_status destroy_table( + struct table_item *table, + size_t length, + bool destroy_items) +{ + if (!destroy_items) { + free(table); + return FX_SUCCESS; + } + + for (size_t i = 0; i < length; i++) { + struct table_item *item = &table[i]; + if (!item->i_item) { + continue; + } + + fx_hashtable_item_unref(item->i_item); + } + + free(table); + return FX_SUCCESS; +} + +static fx_status migrate_table( + struct table_item *src_table, + size_t src_length, + struct table_item *dest_table, + size_t dest_capacity) +{ + for (size_t i = 0; i < src_length; i++) { + struct table_item *item = &src_table[i]; + if (!item->i_item) { + continue; + } + + add_item_to_table(item->i_item, dest_table, dest_capacity); + } + + return FX_SUCCESS; +} + +static fx_status resize_table( + struct fx_hashtable_p *hashtable, + size_t new_capacity_index) +{ + size_t capacity = primes[new_capacity_index]; + struct table_item *new_table = calloc(capacity, sizeof *new_table); + if (!new_table) { + return FX_ERR_NO_MEMORY; + } + + fx_status status = migrate_table( + hashtable->t_items, + primes[hashtable->t_max_index], + new_table, + capacity); + if (!FX_OK(status)) { + free(new_table); + return status; + } + + free(hashtable->t_items); + hashtable->t_items = new_table; + hashtable->t_max_index = new_capacity_index; + return FX_SUCCESS; +} + +static fx_status hashtable_put( + struct fx_hashtable_p *hashtable, + fx_value *key, + fx_value *value) +{ + fx_status status = FX_SUCCESS; + size_t capacity = primes[hashtable->t_max_index]; + if (hashtable->t_count + 1 > capacity) { + status = resize_table(hashtable, hashtable->t_max_index + 1); + if (!FX_OK(status)) { + return status; + } + } + + size_t index = 0; + status = convert_key_to_index( + key, + hashtable->t_items, + capacity, + &index); + if (!FX_OK(status)) { + return status; + } + + fx_hashtable_item *item = fx_object_create(FX_TYPE_HASHTABLE_ITEM); + if (!item) { + return FX_ERR_NO_MEMORY; + } + + struct fx_hashtable_item_p *item_p = fx_object_get_private( + item, + FX_TYPE_HASHTABLE_ITEM); + fx_value_copy(&item_p->i_key, key); + fx_value_copy(&item_p->i_value, value); + + hashtable->t_items[index].i_item = item; + hashtable->t_count++; + return FX_SUCCESS; +} + +static const fx_value *hashtable_get( + const struct fx_hashtable_p *hashtable, + const fx_value *key) +{ + size_t index = 0; + size_t capacity = primes[hashtable->t_max_index]; + fx_status status = convert_key_to_index( + key, + hashtable->t_items, + capacity, + &index); + if (!FX_OK(status)) { + return NULL; + } + + const fx_hashtable_item *item = hashtable->t_items[index].i_item; + if (!item) { + return NULL; + } + + const struct fx_hashtable_item_p *item_p = fx_object_get_private( + item, + FX_TYPE_HASHTABLE_ITEM); + return &item_p->i_value; +} + +static size_t hashtable_get_count(const struct fx_hashtable_p *hashtable) +{ + return hashtable->t_count; +} + +static bool hashtable_is_empty(const struct fx_hashtable_p *hashtable) +{ + return hashtable->t_count == 0; +} + +static fx_value hashtable_item_get_key(const struct fx_hashtable_item_p *item) +{ + return item->i_key; +} + +static fx_value hashtable_item_get_value(const struct fx_hashtable_item_p *item) +{ + return item->i_value; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +fx_status fx_hashtable_put( + fx_hashtable *hashtable, + fx_value *key, + fx_value *value) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_HASHTABLE, + hashtable_put, + hashtable, + key, + value); +} + +const fx_value *fx_hashtable_get( + const fx_hashtable *hashtable, + const fx_value *key) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_HASHTABLE, + hashtable_get, + hashtable, + key); +} + +size_t fx_hashtable_get_count(const fx_hashtable *hashtable) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_TYPE_HASHTABLE, + hashtable_get_count, + hashtable); +} + +bool fx_hashtable_is_empty(const fx_hashtable *hashtable) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_TYPE_HASHTABLE, + hashtable_is_empty, + hashtable); +} + +fx_value fx_hashtable_item_get_key(const fx_hashtable_item *item) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_TYPE_HASHTABLE_ITEM, + hashtable_item_get_key, + item); +} + +fx_value fx_hashtable_item_get_value(const fx_hashtable_item *item) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_TYPE_HASHTABLE_ITEM, + hashtable_item_get_value, + item); +} + +fx_iterator *fx_hashtable_begin(fx_hashtable *hashtable) +{ + fx_hashtable_iterator *it_obj = fx_object_create( + FX_TYPE_HASHTABLE_ITERATOR); + struct fx_hashtable_iterator_p *it = fx_object_get_private( + it_obj, + FX_TYPE_HASHTABLE_ITERATOR); + + it->_h = hashtable; + it->_h_p = fx_object_get_private(hashtable, FX_TYPE_HASHTABLE); + + it->i = 0; + if (fx_hashtable_is_empty(hashtable)) { + fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); + return it_obj; + } + + struct table_item *table = it->_h_p->t_items; + size_t capacity = primes[it->_h_p->t_max_index]; + + while (it->i < capacity && table[it->i].i_item == NULL) { + it->i++; + } + + return it_obj; +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void hashtable_init(fx_object *obj, void *priv) +{ + struct fx_hashtable_p *map = priv; + map->t_max_index = 0; + map->t_count = 0; + + size_t capacity = primes[map->t_max_index]; + map->t_items = calloc(capacity, sizeof *map->t_items); +} + +static void hashtable_fini(fx_object *obj, void *priv) +{ + struct fx_hashtable_p *map = priv; +} + +static fx_status to_string( + const fx_value *obj, + fx_stream *out, + const char *format) +{ + struct fx_hashtable_p *hashtable = fx_object_get_private( + obj->v_object, + FX_TYPE_HASHTABLE); + + if (!hashtable->t_count) { + fx_stream_write_cstr(out, "{}", NULL); + return FX_SUCCESS; + } + + fx_stream_write_cstr(out, "{\n", NULL); + + fx_stream_push_indent(out, 1); + + size_t nr_written = 0; + size_t capacity = primes[hashtable->t_max_index]; + for (size_t i = 0; i < capacity; i++) { + fx_hashtable_item *item = hashtable->t_items[i].i_item; + if (!item) { + continue; + } + + nr_written++; + struct fx_hashtable_item_p *item_p = fx_object_get_private( + item, + FX_TYPE_HASHTABLE_ITEM); + + const char *cstr = NULL; + fx_value_get_cstr(&item_p->i_key, &cstr); + + if (cstr) { + fx_stream_write_char(out, '"'); + } + + fx_value_to_string(&item_p->i_key, out, NULL); + + if (cstr) { + fx_stream_write_char(out, '"'); + } + + fx_stream_write_cstr(out, " = ", NULL); + + fx_value_get_cstr(&item_p->i_value, &cstr); + if (cstr) { + fx_stream_write_char(out, '"'); + } + + fx_value_to_string(&item_p->i_value, out, NULL); + + if (cstr) { + fx_stream_write_char(out, '"'); + } + + if (nr_written != hashtable->t_count) { + fx_stream_write_char(out, ','); + } + + fx_stream_write_char(out, '\n'); + } + + fx_stream_pop_indent(out); + fx_stream_write_char(out, '}'); + return FX_SUCCESS; +} + +static fx_status get_key( + const fx_value *item_value, + const fx_property *prop, + fx_value *out) +{ + fx_hashtable_item *item = NULL; + fx_value_get_object(item_value, &item); + if (!item) { + return FX_ERR_NOT_SUPPORTED; + } + + struct fx_hashtable_item_p *item_p = fx_object_get_private( + item, + FX_TYPE_HASHTABLE_ITEM); + fx_value_copy(out, &item_p->i_key); + + return FX_SUCCESS; +} + +static fx_status get_value( + const fx_value *item_value, + const fx_property *prop, + fx_value *out) +{ + fx_hashtable_item *item = NULL; + fx_value_get_object(item_value, &item); + if (!item) { + return FX_ERR_NOT_SUPPORTED; + } + + struct fx_hashtable_item_p *item_p = fx_object_get_private( + item, + FX_TYPE_HASHTABLE_ITEM); + fx_value_copy(out, &item_p->i_value); + + return FX_SUCCESS; +} + +/*** ITERATOR FUNCTIONS *******************************************************/ + +static enum fx_status iterator_move_next(const fx_iterator *obj) +{ + struct fx_hashtable_iterator_p *it = fx_object_get_private( + obj, + FX_TYPE_HASHTABLE_ITERATOR); + struct table_item *table = it->_h_p->t_items; + size_t capacity = primes[it->_h_p->t_max_index]; + + it->i++; + while (it->i < capacity && table[it->i].i_item == NULL) { + it->i++; + } + + if (it->i >= capacity || !table[it->i].i_item) { + return FX_ERR_NO_DATA; + } + + return FX_SUCCESS; +} + +static enum fx_status iterator_erase(fx_iterator *obj) +{ + struct fx_hashtable_iterator_p *it = fx_object_get_private( + obj, + FX_TYPE_HASHTABLE_ITERATOR); + + struct table_item *table = it->_h_p->t_items; + size_t capacity = primes[it->_h_p->t_max_index]; + + if (table[it->i].i_item) { + fx_hashtable_item_unref(table[it->i].i_item); + table[it->i].i_item = NULL; + } + + it->i++; + while (it->i < capacity && table[it->i].i_item == NULL) { + it->i++; + } + + if (it->i >= capacity || !table[it->i].i_item) { + return FX_ERR_NO_DATA; + } + + return FX_SUCCESS; +} + +static fx_value iterator_get_value(const fx_iterator *obj) +{ + struct fx_hashtable_iterator_p *it = fx_object_get_private( + obj, + FX_TYPE_HASHTABLE_ITERATOR); + struct table_item *table = it->_h_p->t_items; + size_t capacity = primes[it->_h_p->t_max_index]; + + if (table[it->i].i_item) { + return FX_VALUE_OBJECT(table[it->i].i_item); + } + + return FX_VALUE_EMPTY; +} + +/*** CLASS DEFINITION *********************************************************/ + +// ---- fx_hashtable DEFINITION +FX_TYPE_CLASS_BEGIN(fx_hashtable) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = to_string; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterable, FX_TYPE_ITERABLE) + FX_INTERFACE_ENTRY(it_begin) = fx_hashtable_begin; + FX_TYPE_VTABLE_INTERFACE_END(fx_iterable, FX_TYPE_ITERABLE) +FX_TYPE_CLASS_END(fx_hashtable) + +FX_TYPE_DEFINITION_BEGIN(fx_hashtable) + FX_TYPE_ID(0xc73b431c, 0x5bd5, 0x45ac, 0x888b, 0x35d352db1fe4); + FX_TYPE_NAME("fx.collections.hashtable"); + FX_TYPE_CLASS(fx_hashtable_class); + FX_TYPE_IMPLEMENTS(FX_TYPE_ITERABLE); + FX_TYPE_INSTANCE_PRIVATE(struct fx_hashtable_p); + FX_TYPE_INSTANCE_INIT(hashtable_init); + FX_TYPE_INSTANCE_FINI(hashtable_fini); +FX_TYPE_DEFINITION_END(fx_hashtable) + +// ---- fx_hashtable_iterator DEFINITION +FX_TYPE_CLASS_BEGIN(fx_hashtable_iterator) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterator, FX_TYPE_ITERATOR) + FX_INTERFACE_ENTRY(it_move_next) = iterator_move_next; + FX_INTERFACE_ENTRY(it_erase) = iterator_erase; + FX_INTERFACE_ENTRY(it_get_value) = iterator_get_value; + FX_TYPE_VTABLE_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR) +FX_TYPE_CLASS_END(fx_hashtable_iterator) + +FX_TYPE_DEFINITION_BEGIN(fx_hashtable_iterator) + FX_TYPE_ID(0x42c6836d, 0xf801, 0x49f9, 0xba58, 0xf9cd6f1cb22d); + FX_TYPE_NAME("fx.collections.hashtable.iterator"); + FX_TYPE_EXTENDS(FX_TYPE_ITERATOR); + FX_TYPE_CLASS(fx_hashtable_iterator_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_hashtable_iterator_p); +FX_TYPE_DEFINITION_END(fx_hashtable_iterator) + +// ---- fx_hashtable_item DEFINITION +FX_TYPE_CLASS_BEGIN(fx_hashtable_item) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_PROPERTY("key", get_key, NULL); + FX_TYPE_PROPERTY("value", get_value, NULL); +FX_TYPE_CLASS_END(fx_hashtable_item) + +FX_TYPE_DEFINITION_BEGIN(fx_hashtable_item) + FX_TYPE_ID(0x748f78f3, 0x53df, 0x406c, 0x8777, 0x2075bd11fb97); + FX_TYPE_NAME("fx.collections.hashtable.item"); + FX_TYPE_EXTENDS(FX_TYPE_OBJECT); + FX_TYPE_CLASS(fx_hashtable_item_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_hashtable_item_p); +FX_TYPE_DEFINITION_END(fx_hashtable_item) diff --git a/fx.collections/include/fx/collections/dict.h b/fx.collections/include/fx/collections/dict.h deleted file mode 100644 index f191eb0..0000000 --- a/fx.collections/include/fx/collections/dict.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef FX_DS_DICT_H_ -#define FX_DS_DICT_H_ - -#include -#include -#include -#include -#include -#include - -FX_DECLS_BEGIN; - -#define FX_TYPE_DICT (fx_dict_get_type()) -#define FX_TYPE_DICT_ITERATOR (fx_dict_iterator_get_type()) - -struct fx_dict_p; - -FX_DECLARE_TYPE(fx_dict); -FX_DECLARE_TYPE(fx_dict_iterator); - -FX_TYPE_CLASS_DECLARATION_BEGIN(fx_dict) -FX_TYPE_CLASS_DECLARATION_END(fx_dict) - -FX_TYPE_CLASS_DECLARATION_BEGIN(fx_dict_iterator) -FX_TYPE_CLASS_DECLARATION_END(fx_dict_iterator) - -#define FX_DICT_ITEM(k, v) {.key = (k), .value = (v)} -#define FX_DICT_ITEM_END {.key = NULL, .value = NULL} - -#define fx_dict_foreach(it, dict) \ - for (int z__fx_unique_name() = fx_dict_iterator_begin(dict, it); \ - (it)->key != NULL; \ - fx_dict_iterator_next(it)) - -typedef struct fx_dict_item { - const fx_string *key; - fx_object *value; -} fx_dict_item; - -FX_API fx_type_id fx_dict_get_type(void); -FX_API fx_type_id fx_dict_iterator_get_type(void); - -FX_TYPE_DEFAULT_CONSTRUCTOR(fx_dict, FX_TYPE_DICT); - -#if 0 -FX_API fx_dict *fx_dict_create_with_items(const fx_dict_item *items); -#endif - -FX_API fx_status fx_dict_put(fx_dict *dict, const char *key, fx_object *value); -FX_API fx_status -fx_dict_put_sk(fx_dict *dict, const fx_string *key, fx_object *value); -FX_API fx_object *fx_dict_at(const fx_dict *dict, const char *key); -FX_API fx_object *fx_dict_at_sk(const fx_dict *dict, const fx_string *key); -FX_API fx_object *fx_dict_get(fx_dict *dict, const char *key); -FX_API fx_object *fx_dict_get_sk(fx_dict *dict, const fx_string *key); - -FX_API bool fx_dict_has_key(const fx_dict *dict, const char *key); -FX_API bool fx_dict_has_skey(const fx_dict *dict, const fx_string *key); -FX_API size_t fx_dict_get_size(const fx_dict *dict); -FX_API bool fx_dict_is_empty(const fx_dict *dict); - -FX_DECLS_END; - -#endif diff --git a/fx.collections/include/fx/collections/hashtable.h b/fx.collections/include/fx/collections/hashtable.h new file mode 100644 index 0000000..a5ef4a2 --- /dev/null +++ b/fx.collections/include/fx/collections/hashtable.h @@ -0,0 +1,55 @@ +#ifndef FX_COLLECTIONS_HASHTABLE_H_ +#define FX_COLLECTIONS_HASHTABLE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +FX_DECLS_BEGIN; + +#define FX_TYPE_HASHTABLE (fx_hashtable_get_type()) +#define FX_TYPE_HASHTABLE_ITERATOR (fx_hashtable_iterator_get_type()) +#define FX_TYPE_HASHTABLE_ITEM (fx_hashtable_item_get_type()) + +FX_DECLARE_TYPE(fx_hashtable); +FX_DECLARE_TYPE(fx_hashtable_iterator); +FX_DECLARE_TYPE(fx_hashtable_item); + +FX_TYPE_CLASS_DECLARATION_BEGIN(fx_hashtable) +FX_TYPE_CLASS_DECLARATION_END(fx_hashtable) + +FX_TYPE_CLASS_DECLARATION_BEGIN(fx_hashtable_iterator) +FX_TYPE_CLASS_DECLARATION_END(fx_hashtable_iterator) + +FX_TYPE_CLASS_DECLARATION_BEGIN(fx_hashtable_item) +FX_TYPE_CLASS_DECLARATION_END(fx_hashtable_item) + +FX_API fx_type_id fx_hashtable_get_type(void); +FX_API fx_type_id fx_hashtable_iterator_get_type(void); +FX_API fx_type_id fx_hashtable_item_get_type(void); + +FX_TYPE_DEFAULT_CONSTRUCTOR(fx_hashtable, FX_TYPE_HASHTABLE); +FX_API fx_hashtable *fx_hashtable_create(void); + +FX_API fx_status +fx_hashtable_put(fx_hashtable *hashtable, fx_value *key, fx_value *value); +FX_API const fx_value *fx_hashtable_get( + const fx_hashtable *hashtable, + const fx_value *key); + +FX_API size_t fx_hashtable_get_count(const fx_hashtable *hashtable); +FX_API bool fx_hashtable_is_empty(const fx_hashtable *hashtable); + +FX_API fx_iterator *fx_hashtable_begin(fx_hashtable *hashtable); + +FX_API fx_value fx_hashtable_item_get_key(const fx_hashtable_item *item); +FX_API fx_value fx_hashtable_item_get_value(const fx_hashtable_item *item); + +FX_DECLS_END; + +#endif diff --git a/fx.collections/test/hashtables.c b/fx.collections/test/hashtables.c new file mode 100644 index 0000000..0034262 --- /dev/null +++ b/fx.collections/test/hashtables.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +int main(void) +{ + fx_hashtable *ht = fx_hashtable_create(); + fx_hashtable_put(ht, &FX_INT(32), &FX_INT(64)); + fx_hashtable_put(ht, &FX_CSTR("hello"), &FX_INT(128)); + + const fx_value *v = fx_hashtable_get(ht, &FX_INT(32)); + if (v) { + printf("32= "); + fx_value_to_string(v, fx_stdout, NULL); + printf("\n"); + } else { + printf("no value\n"); + } + + fx_iterator *it = fx_hashtable_begin(ht); + fx_foreach(val, it) + { + fx_hashtable_item *item = NULL; + fx_value_get_object(&val, &item); + printf("item %p\n", item); + fx_value key, value; + key = fx_hashtable_item_get_key(item); + value = fx_hashtable_item_get_value(item); + + fx_iterator *prop_it = fx_type_get_properties( + fx_type_get_by_id(FX_TYPE_HASHTABLE_ITEM)); + fx_foreach(prop_val, prop_it) + { + fx_property *prop; + fx_value_get_object(&prop_val, &prop); + printf("%s = ", fx_property_get_name(prop)); + fx_value value = FX_VALUE_EMPTY; + fx_property_get_value(prop, &val, &value); + fx_value_to_string(&value, fx_stdout, NULL); + printf("\n"); + } + fx_iterator_unref(prop_it); + } + fx_iterator_unref(it); + + return 0; +}