Files
bluelib/object/hashmap.c

462 lines
10 KiB
C

#include "hashmap.h"
#include <blue/core/misc.h>
#include <blue/core/status.h>
#include <blue/object/hashmap.h>
#include <blue/object/string.h>
#include <blue/object/type.h>
#include <stdbool.h>
#include <stdlib.h>
#define HASH_OFFSET_BASIS 0xcbf29ce484222325
#define HASH_PRIME 0x100000001b3
/* clang-format off */
static B_BTREE_DEFINE_SIMPLE_GET(
struct b_hashmap_bucket,
uint64_t,
bk_node,
bk_hash,
get_bucket)
static B_BTREE_DEFINE_SIMPLE_INSERT(
struct b_hashmap_bucket,
bk_node,
bk_hash,
put_bucket)
static uint64_t hash_data(const void *p, size_t size)
{
/* clang-format on */
const unsigned char *s = p;
uint64_t hash = HASH_OFFSET_BASIS;
for (size_t i = 0; s[i]; i++) {
hash *= HASH_PRIME;
hash ^= s[i];
}
return hash;
}
static void hashmap_release(struct b_object *obj);
static struct b_object_type hashmap_type = {
.t_name = "corelib::hashmap",
.t_flags = B_OBJECT_FUNDAMENTAL,
.t_id = B_OBJECT_TYPE_HASHMAP,
.t_instance_size = sizeof(struct b_hashmap),
.t_release = hashmap_release,
};
struct b_hashmap *b_hashmap_create(
b_hashmap_key_destructor key_dtor, b_hashmap_value_destructor value_dtor)
{
struct b_hashmap *hashmap
= (struct b_hashmap *)b_object_type_instantiate(&hashmap_type);
if (!hashmap) {
return NULL;
}
return hashmap;
}
struct b_hashmap *b_hashmap_create_with_items(const b_hashmap_item *items)
{
struct b_hashmap *hashmap = b_hashmap_create(NULL, NULL);
if (!hashmap) {
return NULL;
}
for (size_t i = 0; items[i].key.key_data && items[i].key.key_size; i++) {
b_hashmap_put(hashmap, &items[i].key, &items[i].value);
}
return hashmap;
}
static struct b_hashmap_bucket *create_bucket(void)
{
/* clang-format on */
struct b_hashmap_bucket *bucket = malloc(sizeof *bucket);
if (!bucket) {
return NULL;
}
memset(bucket, 0x0, sizeof *bucket);
return bucket;
}
static struct b_hashmap_bucket_item *create_bucket_item(void)
{
struct b_hashmap_bucket_item *item = malloc(sizeof *item);
if (!item) {
return NULL;
}
memset(item, 0x0, sizeof *item);
return item;
}
b_status b_hashmap_put(
struct b_hashmap *hashmap, const b_hashmap_key *key,
const b_hashmap_value *value)
{
uint64_t hash = hash_data(key->key_data, key->key_size);
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
if (!bucket) {
bucket = create_bucket();
if (!bucket) {
return B_ERR_NO_MEMORY;
}
bucket->bk_hash = hash;
put_bucket(&hashmap->h_buckets, bucket);
}
b_queue_iterator it;
b_queue_foreach (&it, &bucket->bk_items) {
struct b_hashmap_bucket_item *item = b_unbox(
struct b_hashmap_bucket_item, it.entry, bi_entry);
if (item->bi_key.key_size != key->key_size) {
continue;
}
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
return B_ERR_NAME_EXISTS;
}
}
struct b_hashmap_bucket_item *item = create_bucket_item();
if (!item) {
return B_ERR_NO_MEMORY;
}
memcpy(&item->bi_key, key, sizeof *key);
memcpy(&item->bi_value, value, sizeof *value);
b_queue_push_back(&bucket->bk_items, &item->bi_entry);
return B_SUCCESS;
}
const struct b_hashmap_value *b_hashmap_get(
const struct b_hashmap *hashmap, const struct b_hashmap_key *key)
{
uint64_t hash = hash_data(key->key_data, key->key_size);
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
if (!bucket) {
return NULL;
}
b_queue_iterator it;
b_queue_foreach (&it, &bucket->bk_items) {
struct b_hashmap_bucket_item *item = b_unbox(
struct b_hashmap_bucket_item, it.entry, bi_entry);
if (item->bi_key.key_size != key->key_size) {
continue;
}
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
return &item->bi_value;
}
}
return NULL;
}
bool b_hashmap_has_key(const struct b_hashmap *hashmap, const b_hashmap_key *key)
{
uint64_t hash = hash_data(key->key_data, key->key_size);
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
if (!bucket) {
return false;
}
b_queue_iterator it;
b_queue_foreach (&it, &bucket->bk_items) {
struct b_hashmap_bucket_item *item = b_unbox(
struct b_hashmap_bucket_item, it.entry, bi_entry);
if (item->bi_key.key_size != key->key_size) {
continue;
}
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
return true;
}
}
return false;
}
size_t b_hashmap_get_size(const struct b_hashmap *hashmap)
{
size_t count = 0;
b_btree_iterator it1;
b_queue_iterator it2;
b_btree_foreach (&it1, &hashmap->h_buckets) {
struct b_hashmap_bucket *bucket
= b_unbox(struct b_hashmap_bucket, it1.node, bk_node);
b_queue_foreach (&it2, &bucket->bk_items) {
count++;
}
}
return count;
}
bool b_hashmap_is_empty(const b_hashmap *hashmap)
{
b_btree_node *first_node = b_btree_first(&hashmap->h_buckets);
struct b_hashmap_bucket *first_bucket
= b_unbox(struct b_hashmap_bucket, first_node, bk_node);
if (!first_bucket) {
return true;
}
b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items);
struct b_hashmap_bucket_item *first_item
= b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry);
if (!first_item) {
return true;
}
return false;
}
static b_status delete_item(
struct b_hashmap *hashmap, struct b_hashmap_bucket *bucket,
struct b_hashmap_bucket_item *item)
{
b_queue_delete(&bucket->bk_items, &item->bi_entry);
if (hashmap->h_key_dtor) {
hashmap->h_key_dtor((void *)item->bi_key.key_data);
}
if (hashmap->h_value_dtor) {
hashmap->h_value_dtor((void *)item->bi_value.value_data);
}
free(item);
if (b_queue_empty(&bucket->bk_items)) {
b_btree_delete(&hashmap->h_buckets, &bucket->bk_node);
free(bucket);
}
return B_SUCCESS;
}
static bool hashmap_iterator_next(struct b_iterator *it)
{
return b_hashmap_iterator_next((struct b_hashmap_iterator *)it);
}
static b_status hashmap_iterator_erase(struct b_iterator *it)
{
return b_hashmap_iterator_erase((struct b_hashmap_iterator *)it);
}
static bool hashmap_iterator_is_valid(const struct b_iterator *it)
{
return b_hashmap_iterator_is_valid((struct b_hashmap_iterator *)it);
}
static struct b_iterator_ops it_ops = {
.it_next = hashmap_iterator_next,
.it_erase = hashmap_iterator_erase,
.it_close = NULL,
.it_is_valid = hashmap_iterator_is_valid,
};
int b_hashmap_iterator_begin(
struct b_hashmap *hashmap, struct b_hashmap_iterator *it)
{
it->_h = hashmap;
it->_base.it_ops = &it_ops;
it->i = 0;
if (b_hashmap_is_empty(hashmap)) {
it->key = NULL;
it->value = NULL;
return -1;
}
struct b_btree_node *first_node = b_btree_first(&hashmap->h_buckets);
struct b_hashmap_bucket *first_bucket
= b_unbox(struct b_hashmap_bucket, first_node, bk_node);
if (!first_bucket) {
it->key = NULL;
it->value = NULL;
return -1;
}
struct b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items);
struct b_hashmap_bucket_item *first_item
= b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry);
if (!first_item) {
it->key = NULL;
it->value = NULL;
return -1;
}
it->key = &first_item->bi_key;
it->value = &first_item->bi_value;
it->_cbn = first_node;
it->_cqe = first_entry;
return 0;
}
static bool get_next_node(
struct b_btree_node *cur_node, struct b_queue_entry *cur_entry,
struct b_btree_node **out_next_node, struct b_queue_entry **out_next_entry)
{
struct b_hashmap_bucket *cur_bucket
= b_unbox(struct b_hashmap_bucket, cur_node, bk_node);
if (!cur_bucket) {
return false;
}
struct b_hashmap_bucket_item *cur_item
= b_unbox(struct b_hashmap_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_hashmap_bucket *next_bucket
= b_unbox(struct b_hashmap_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_hashmap_bucket_item *next_item
= b_unbox(struct b_hashmap_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_hashmap_iterator_next(struct b_hashmap_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 false;
}
struct b_hashmap_bucket_item *next_item
= b_unbox(struct b_hashmap_bucket_item, next_entry, bi_entry);
if (!next_item) {
it->key = NULL;
it->value = NULL;
return false;
}
it->i++;
it->key = &next_item->bi_key;
it->value = &next_item->bi_value;
it->_cbn = next_node;
it->_cqe = next_entry;
return true;
}
b_status b_hashmap_iterator_erase(struct b_hashmap_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_hashmap_bucket *cur_bucket
= b_unbox(struct b_hashmap_bucket, it->_cbn, bk_node);
struct b_hashmap_bucket_item *cur_item
= b_unbox(struct b_hashmap_bucket_item, it->_cqe, bi_entry);
struct b_hashmap_bucket_item *next_item
= b_unbox(struct b_hashmap_bucket_item, next_entry, bi_entry);
b_status status = delete_item(it->_h, cur_bucket, cur_item);
if (B_ERR(status)) {
return status;
}
if (next_item) {
it->key = &next_item->bi_key;
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_hashmap_iterator_is_valid(const struct b_hashmap_iterator *it)
{
return it->key != NULL;
}
static void hashmap_release(struct b_object *obj)
{
struct b_hashmap *map = B_HASHMAP(obj);
b_hashmap_iterator it;
b_hashmap_iterator_begin(map, &it);
while (b_hashmap_iterator_is_valid(&it)) {
b_hashmap_iterator_erase(&it);
}
}
b_object_type_id b_hashmap_type_id(void)
{
return (b_object_type_id)&hashmap_type;
}