patty/src/dict.c

207 lines
5.1 KiB
C
Raw Normal View History

2015-07-20 16:27:17 -05:00
#include <stdlib.h>
#include <string.h>
#include <patty/dict.h>
#include <patty/hash.h>
patty_dict *patty_dict_new() {
patty_dict *dict;
if ((dict = malloc(sizeof(*dict))) == NULL) {
goto error_malloc_dict;
}
memset(dict, 0x00, sizeof(*dict));
return dict;
error_malloc_dict:
return NULL;
}
patty_dict_slot *patty_dict_slot_find(patty_dict *dict, uint32_t hash) {
patty_dict_bucket *bucket = &dict->bucket;
int collisions;
for (collisions = 0; collisions < 7; collisions++) {
uint32_t mask = 0x0f << (4 * collisions);
uint8_t index = (hash & mask) >> (4 * collisions);
patty_dict_slot *slot = &bucket->slots[index];
if (slot->hash == 0x00000000) {
/*
* In this case, we have determined that there is no value in the
* dict for the given hash.
*/
return NULL;
} else if (slot->hash == hash) {
/*
* We have found the desired slot, so return that.
*/
return slot;
}
/*
* Otherwise, look for the next bucket, if present.
*/
bucket = (patty_dict_bucket *)slot->next;
/*
* If there is no next bucket available, then return null.
*/
if (bucket == NULL) {
return NULL;
}
}
return NULL;
}
static int bucket_each_slot(int level, patty_dict_bucket *bucket, patty_dict_callback callback, void *ctx) {
int i;
if (level > 7) {
return 0;
}
for (i=0; i<PATTY_DICT_BUCKET_SIZE; i++) {
patty_dict_slot *slot = &bucket->slots[i];
if (slot->key != NULL && slot->keysz > 0) {
if (callback(slot->key, slot->keysz, slot->value, ctx) < 0) {
goto error_callback;
}
}
if (slot->next) {
2020-05-23 13:44:52 -04:00
if (bucket_each_slot(level+1,
(patty_dict_bucket *)slot->next,
callback,
ctx) < 0) {
goto error_next_slot;
}
2015-07-20 16:27:17 -05:00
}
}
return 0;
error_callback:
2020-05-23 13:44:52 -04:00
error_next_slot:
2015-07-20 16:27:17 -05:00
return -1;
}
int patty_dict_each(patty_dict *dict, patty_dict_callback callback, void *ctx) {
return bucket_each_slot(0, &dict->bucket, callback, ctx);
}
void *patty_dict_get(patty_dict *dict, void *key, size_t keysz) {
patty_dict_slot *slot;
uint32_t hash = patty_hash(key, keysz);
if ((slot = patty_dict_slot_find(dict, hash)) == NULL) {
return NULL;
}
return slot->value;
}
2020-06-07 02:46:12 -04:00
void *patty_dict_set_with_hash(patty_dict *dict,
void *key,
size_t keysz,
void *value,
uint32_t hash) {
2015-07-20 16:27:17 -05:00
patty_dict_bucket *bucket = &dict->bucket;
int collisions;
for (collisions = 0; collisions < 7; collisions++) {
uint32_t mask = 0x0f << (4 * collisions);
uint8_t index = (hash & mask) >> (4 * collisions);
patty_dict_slot *slot = &bucket->slots[index];
if (slot->hash == 0x00000000 && slot->key == NULL) {
/*
* We have found an empty hash slot, so let's store the value.
*/
slot->hash = hash;
slot->key = key;
slot->keysz = keysz;
slot->value = value;
return value;
} else if (slot->hash == hash) {
/*
* Otherwise, we've found an existing slot, so let's update that
* and bail.
*/
return slot->value = value;
}
/*
* Take a look to see if there is a next bucket in the chain.
*/
bucket = (patty_dict_bucket *)slot->next;
/*
* If there is no next bucket available, then create one.
*/
if (bucket == NULL) {
if ((bucket = malloc(sizeof(*bucket))) == NULL) {
goto error_malloc_bucket;
}
memset(bucket, 0x00, sizeof(*bucket));
slot->next = (patty_dict_slot *)bucket;
}
}
return NULL;
error_malloc_bucket:
return NULL;
}
2020-06-07 02:46:12 -04:00
void *patty_dict_set(patty_dict *dict, void *key, size_t keysz, void *value) {
return patty_dict_set_with_hash(dict,
key,
keysz,
value,
patty_hash(key, keysz));
}
int patty_dict_delete_with_hash(patty_dict *dict, uint32_t hash) {
2015-07-21 23:01:34 -05:00
patty_dict_slot *slot;
if ((slot = patty_dict_slot_find(dict, hash)) == NULL) {
goto error_dict_slot_find;
2015-07-21 23:01:34 -05:00
}
memset(slot, '\0', sizeof(*slot));
return 0;
error_dict_slot_find:
return -1;
}
int patty_dict_delete(patty_dict *dict, void *key, size_t keysz) {
return patty_dict_delete_with_hash(dict, patty_hash(key, keysz));
2015-07-21 23:01:34 -05:00
}
2015-07-20 16:27:17 -05:00
static int dict_item_release(void *key, size_t keysz, void *value, void *ctx) {
free(value);
return 0;
}
void patty_dict_destroy(patty_dict *dict) {
(void)patty_dict_each(dict, dict_item_release, NULL);
free(dict);
}