fx.collections: replace fx_dict with fx_hashtable
This commit is contained in:
@@ -1,732 +0,0 @@
|
||||
#include <fx/collections/dict.h>
|
||||
#include <fx/hash.h>
|
||||
#include <fx/status.h>
|
||||
#include <fx/stream.h>
|
||||
#include <fx/string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*** 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)
|
||||
@@ -0,0 +1,640 @@
|
||||
#include <fx/collections/hashtable.h>
|
||||
#include <fx/misc.h>
|
||||
#include <fx/reflection/property.h>
|
||||
#include <fx/status.h>
|
||||
#include <fx/string.h>
|
||||
#include <fx/vector.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*** 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)
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef FX_DS_DICT_H_
|
||||
#define FX_DS_DICT_H_
|
||||
|
||||
#include <fx/bst.h>
|
||||
#include <fx/macros.h>
|
||||
#include <fx/misc.h>
|
||||
#include <fx/queue.h>
|
||||
#include <fx/status.h>
|
||||
#include <fx/string.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef FX_COLLECTIONS_HASHTABLE_H_
|
||||
#define FX_COLLECTIONS_HASHTABLE_H_
|
||||
|
||||
#include <fx/bst.h>
|
||||
#include <fx/iterator.h>
|
||||
#include <fx/macros.h>
|
||||
#include <fx/misc.h>
|
||||
#include <fx/queue.h>
|
||||
#include <fx/status.h>
|
||||
#include <fx/value.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <fx/collections/hashtable.h>
|
||||
#include <fx/int.h>
|
||||
#include <fx/reflection/type.h>
|
||||
#include <fx/stream.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user