265 lines
6.2 KiB
C
265 lines
6.2 KiB
C
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <hexagram/dict.h>
|
||
|
#include <hexagram/hash.h>
|
||
|
|
||
|
hexagram_dict *hexagram_dict_new() {
|
||
|
hexagram_dict *dict;
|
||
|
|
||
|
if ((dict = malloc(sizeof(*dict))) == NULL) {
|
||
|
goto error_malloc_dict;
|
||
|
}
|
||
|
|
||
|
memset(dict, 0x00, sizeof(*dict));
|
||
|
|
||
|
if ((dict->bucket = malloc(sizeof(*dict->bucket))) == NULL) {
|
||
|
goto error_malloc_dict_bucket;
|
||
|
}
|
||
|
|
||
|
memset(dict->bucket, 0x00, sizeof(*dict->bucket));
|
||
|
|
||
|
return dict;
|
||
|
|
||
|
error_malloc_dict_bucket:
|
||
|
free(dict);
|
||
|
|
||
|
error_malloc_dict:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void bucket_destroy(hexagram_dict *dict, hexagram_dict_bucket *bucket) {
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i<HEXAGRAM_DICT_BUCKET_SLOTS; i++) {
|
||
|
hexagram_dict_slot *slot = &bucket->slots[i];
|
||
|
|
||
|
if (slot->next_bucket) {
|
||
|
bucket_destroy(dict, (hexagram_dict_bucket *)slot->next_bucket);
|
||
|
}
|
||
|
|
||
|
if (slot->key) {
|
||
|
slot->key = NULL;
|
||
|
}
|
||
|
|
||
|
if (slot->value) {
|
||
|
slot->value = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(bucket);
|
||
|
}
|
||
|
|
||
|
void hexagram_dict_destroy(hexagram_dict *dict) {
|
||
|
bucket_destroy(dict, dict->bucket);
|
||
|
|
||
|
free(dict);
|
||
|
}
|
||
|
|
||
|
static hexagram_dict_slot *slot_find_readable(hexagram_dict *dict, uint32_t hash) {
|
||
|
hexagram_dict_bucket *bucket = dict->bucket;
|
||
|
|
||
|
int collisions;
|
||
|
|
||
|
for (collisions = 0; collisions < HEXAGRAM_DICT_BUCKET_DEPTH; collisions++) {
|
||
|
uint32_t mask = 0x0f << (4 * collisions);
|
||
|
uint8_t index = (hash & mask) >> (4 * collisions);
|
||
|
|
||
|
hexagram_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 = (hexagram_dict_bucket *)slot->next_bucket;
|
||
|
|
||
|
/*
|
||
|
* If there is no next bucket available, then return null.
|
||
|
*/
|
||
|
if (bucket == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static hexagram_dict_slot *slot_find_writable(hexagram_dict *dict, uint32_t hash) {
|
||
|
hexagram_dict_bucket *bucket = dict->bucket;
|
||
|
|
||
|
int collisions;
|
||
|
|
||
|
for (collisions = 0; collisions < HEXAGRAM_DICT_BUCKET_DEPTH; collisions++) {
|
||
|
uint32_t mask = 0x0f << (4 * collisions);
|
||
|
uint8_t index = (hash & mask) >> (4 * collisions);
|
||
|
|
||
|
hexagram_dict_slot *slot = &bucket->slots[index];
|
||
|
|
||
|
if ((slot->hash == 0x00 && slot->key == NULL) || slot->hash == hash) {
|
||
|
/*
|
||
|
* We have found a suitable slot, so let's return that.
|
||
|
*/
|
||
|
return slot;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Take a look to see if there is a next bucket in the chain.
|
||
|
*/
|
||
|
bucket = (hexagram_dict_bucket *)slot->next_bucket;
|
||
|
|
||
|
/*
|
||
|
* 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_bucket = (hexagram_dict_slot *)bucket;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
|
||
|
error_malloc_bucket:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int bucket_each_slot(hexagram_dict *dict,
|
||
|
int level,
|
||
|
hexagram_dict_bucket *bucket,
|
||
|
hexagram_dict_callback *callback,
|
||
|
void *ctx) {
|
||
|
int i;
|
||
|
|
||
|
if (level > HEXAGRAM_DICT_BUCKET_DEPTH) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
for (i=0; i<HEXAGRAM_DICT_BUCKET_SLOTS; i++) {
|
||
|
hexagram_dict_slot *slot = &bucket->slots[i];
|
||
|
|
||
|
if (slot->next_bucket) {
|
||
|
return bucket_each_slot(dict,
|
||
|
level+1,
|
||
|
(hexagram_dict_bucket *)slot->next_bucket,
|
||
|
callback,
|
||
|
ctx);
|
||
|
}
|
||
|
|
||
|
if (slot->key) {
|
||
|
if (callback(dict, slot, ctx) < 0) {
|
||
|
goto error_callback;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error_callback:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int hexagram_dict_each(hexagram_dict *dict,
|
||
|
hexagram_dict_callback *callback,
|
||
|
void *ctx) {
|
||
|
return bucket_each_slot(dict, 0, dict->bucket, callback, ctx);
|
||
|
}
|
||
|
|
||
|
void *hexagram_dict_get(hexagram_dict *dict,
|
||
|
void *key,
|
||
|
size_t keysz) {
|
||
|
hexagram_dict_slot *slot;
|
||
|
uint32_t hash;
|
||
|
|
||
|
if (hexagram_hash(key, keysz, &hash) < 0) {
|
||
|
goto error_hash;
|
||
|
}
|
||
|
|
||
|
if ((slot = slot_find_readable(dict, hash)) == NULL) {
|
||
|
goto error_slot_find_readable;
|
||
|
}
|
||
|
|
||
|
return slot->value;
|
||
|
|
||
|
error_slot_find_readable:
|
||
|
error_hash:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
int hexagram_dict_delete(hexagram_dict *dict, void *key, size_t keysz) {
|
||
|
hexagram_dict_slot *slot;
|
||
|
uint32_t hash;
|
||
|
|
||
|
if (hexagram_hash(key, keysz, &hash) < 0) {
|
||
|
goto error_hash;
|
||
|
}
|
||
|
|
||
|
if ((slot = slot_find_readable(dict, hash)) == NULL) {
|
||
|
goto error_slot_find_readable;
|
||
|
}
|
||
|
|
||
|
if (slot->key) {
|
||
|
slot->key = NULL;
|
||
|
}
|
||
|
|
||
|
if (slot->value) {
|
||
|
slot->value = NULL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error_hash:
|
||
|
error_slot_find_readable:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void *hexagram_dict_set_with_hash(hexagram_dict *dict,
|
||
|
void *key,
|
||
|
void *value,
|
||
|
uint32_t hash) {
|
||
|
hexagram_dict_slot *slot;
|
||
|
|
||
|
if ((slot = slot_find_writable(dict, hash)) == NULL) {
|
||
|
goto error_slot_find_writable;
|
||
|
}
|
||
|
|
||
|
slot->hash = hash;
|
||
|
slot->key = key;
|
||
|
|
||
|
return slot->value = value;
|
||
|
|
||
|
error_slot_find_writable:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void *hexagram_dict_set(hexagram_dict *dict,
|
||
|
void *key,
|
||
|
size_t keysz,
|
||
|
void *value) {
|
||
|
uint32_t hash;
|
||
|
|
||
|
if (hexagram_hash(key, keysz, &hash) < 0) {
|
||
|
goto error_hash;
|
||
|
}
|
||
|
|
||
|
return hexagram_dict_set_with_hash(dict, key, value, hash);
|
||
|
|
||
|
error_hash:
|
||
|
return NULL;
|
||
|
}
|