rizin/shlr/sdb/src/ht.c

309 lines
7.6 KiB
C

/* radare2 - BSD 3 Clause License - crowell, pancake 2016 */
#include "ht.h"
#include "sdb.h"
// Sizes of the ht.
const int ht_primes_sizes[] = {
#if GROWABLE
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131,
163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861,
5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023,
25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523,
108631, 130363, 156437, 187751, 225307, 270371, 324449,
389357, 467237, 560689, 672827, 807403, 968897, 1162687,
1395263, 1674319, 2009191, 2411033, 2893249, 3471899,
4166287, 4999559, 5999471, 7199369
#else
1024,
#endif
};
// Create a new hashtable and return a pointer to it.
// size - number of buckets in the hashtable
// hashfunction - the function that does the hashing, must not be null.
// comparator - the function to check if values are equal, if NULL, just checks
// == (for storing ints).
// keydup - function to duplicate to key (eg strdup), if NULL just does strup.
// valdup - same as keydup, but for values but if NULL just assign
// pair_free - function for freeing a keyvaluepair - if NULL just does free.
// calcsize - function to calculate the size of a value. if NULL, just stores 0.
static SdbHash* internal_ht_new(ut32 size, HashFunction hashfunction,
ListComparator comparator, DupKey keydup,
DupValue valdup, HtKvFreeFunc pair_free,
CalcSize calcsizeK, CalcSize calcsizeV) {
SdbHash* ht = calloc (1, sizeof (*ht));
if (!ht) {
return NULL;
}
ht->size = size;
ht->count = 0;
ht->prime_idx = 0;
ht->load_factor = 1;
ht->hashfn = hashfunction;
ht->cmp = (ListComparator)strcmp;
ht->dupkey = keydup? keydup: (DupKey)strdup;
ht->dupvalue = valdup? valdup: NULL;
ht->table = calloc (ht->size, sizeof (SdbList*));
ht->calcsizeK = calcsizeK? calcsizeK: (CalcSize)strlen;
ht->calcsizeV = calcsizeV? calcsizeV: NULL;
ht->freefn = pair_free;
ht->deleted = ls_newf (free);
#if INSERTORDER
ht->list = ls_newf (NULL);
#endif
// Because we use calloc, each listptr will be NULL until used */
return ht;
}
bool ht_delete_internal(SdbHash* ht, const char* key, ut32* hash) {
HtKv* kv;
SdbListIter* iter;
ut32 computed_hash = hash ? *hash : ht->hashfn (key);
#if USE_KEYLEN
ut32 key_len = ht->calcsizeK ((void *)key);
#endif
ut32 bucket = computed_hash % ht->size;
#if INSERTORDER
ls_foreach (ht->list, iter, kv) {
#if USE_KEYLEN
if (key_len != kv->key_len) {
continue;
}
#endif
if (key == kv->key || !ht->cmp (key, kv->key)) {
ls_delete (ht->list, iter);
ht->count--;
break;
}
}
#endif
SdbList* list = ht->table[bucket];
ls_foreach (list, iter, kv) {
#if USE_KEYLEN
if (key_len != kv->key_len) {
continue;
}
#endif
if (key == kv->key || !ht->cmp (key, kv->key)) {
#if EXCHANGE
ls_split_iter (list, iter);
ls_append (ht->deleted, iter);
if (list->free) {
list->free (iter->data);
}
iter->data = NULL;
#else
ls_delete (list, iter);
#endif
ht->count--;
return true;
}
}
return false;
}
SdbHash* ht_new(DupValue valdup, HtKvFreeFunc pair_free, CalcSize calcsizeV) {
return internal_ht_new (ht_primes_sizes[0], (HashFunction)sdb_hash,
(ListComparator)strcmp, (DupKey)strdup,
valdup, pair_free, (CalcSize)strlen, calcsizeV);
}
void ht_free(SdbHash* ht) {
if (ht) {
ut32 i;
for (i = 0; i < ht->size; i++) {
ls_free (ht->table[i]);
}
free (ht->table);
ls_free (ht->deleted);
#if INSERTORDER
ls_free (ht->list);
#endif
free (ht);
}
}
void ht_free_deleted(SdbHash* ht) {
if (!ls_empty (ht->deleted)) {
ls_free (ht->deleted);
ht->deleted = ls_newf (free);
}
}
// Increases the size of the hashtable by 2.
#if GROWABLE
static void internal_ht_grow(SdbHash* ht) {
SdbHash* ht2;
SdbHash swap;
HtKv* kv;
SdbListIter* iter;
ut32 i, sz = ht_primes_sizes[ht->prime_idx];
ht2 = internal_ht_new (sz, ht->hashfn, ht->cmp, ht->dupkey,
ht->dupvalue, (HtKvFreeFunc)ht->freefn, ht->calcsize);
ht2->prime_idx = ht->prime_idx;
for (i = 0; i < ht->size; i++) {
ls_foreach (ht->table[i], iter, kv) {
(void)ht_insert (ht2, kv->key, kv->value);
}
}
// And now swap the internals.
swap = *ht;
*ht = *ht2;
*ht2 = swap;
ht_free (ht2);
}
#endif
static bool internal_ht_insert_kv(SdbHash *ht, HtKv *kv, bool update) {
bool found = false;
if (!ht || !kv) {
return false;
}
ut32 bucket, hash = ht->hashfn (kv->key);
if (update) {
(void)ht_delete_internal (ht, kv->key, &hash);
} else {
(void)ht_find (ht, kv->key, &found);
}
if (update || !found) {
bucket = hash % ht->size;
if (!ht->table[bucket]) {
ht->table[bucket] = ls_newf ((SdbListFree)ht->freefn);
}
ls_prepend (ht->table[bucket], kv);
#if INSERTORDER
ls_append (ht->list, kv);
#endif
ht->count++;
#if GROWABLE
// Check if we need to grow the table.
if (ht->count >= ht->load_factor * ht_primes_sizes[ht->prime_idx]) {
ht->prime_idx++;
internal_ht_grow (ht);
}
#endif
return true;
}
return false;
}
static bool internal_ht_insert(SdbHash* ht, bool update, const char* key,
void* value) {
if (!ht || !key || !value) {
return false;
}
HtKv* kv = calloc (1, sizeof (HtKv));
if (kv) {
kv->key = ht->dupkey ((void *)key);
if (ht->dupvalue) {
kv->value = ht->dupvalue ((void *)value);
} else {
kv->value = (void *)value;
}
kv->key_len = ht->calcsizeK ((void *)kv->key);
if (ht->calcsizeV) {
kv->value_len = ht->calcsizeV ((void *)kv->value);
} else {
kv->value_len = 0;
}
if (!internal_ht_insert_kv (ht, kv, update)) {
if (ht->freefn) {
ht->freefn (kv);
}
return false;
}
return true;
}
return false;
}
bool ht_insert_kv(SdbHash *ht, HtKv *kv, bool update) {
return internal_ht_insert_kv (ht, kv, update);
}
// Inserts the key value pair key, value into the hashtable.
// Doesn't allow for "update" of the value.
bool ht_insert(SdbHash* ht, const char* key, void* value) {
return internal_ht_insert (ht, false, key, value);
}
// Inserts the key value pair key, value into the hashtable.
// Does allow for "update" of the value.
bool ht_update(SdbHash* ht, const char* key, void* value) {
return internal_ht_insert (ht, true, key, value);
}
// Returns the corresponding SdbKv entry from the key.
// If `found` is not NULL, it will be set to true if the entry was found, false
// otherwise.
HtKv* ht_find_kv(SdbHash* ht, const char* key, bool* found) {
if (!ht) {
return NULL;
}
ut32 hash, bucket;
SdbListIter* iter;
HtKv* kv;
#if USE_KEYLEN
if (!key) {
return NULL;
}
ut32 key_len = ht->calcsizeK ((void *)key);
#endif
hash = ht->hashfn (key);
bucket = hash % ht->size;
ls_foreach (ht->table[bucket], iter, kv) {
#if USE_KEYLEN
if (key_len != kv->key_len) {
continue;
}
#endif
bool match = !ht->cmp (key, kv->key);
if (match) {
if (found) {
*found = true;
}
return kv;
}
}
if (found) {
*found = false;
}
return NULL;
}
// Looks up the corresponding value from the key.
// If `found` is not NULL, it will be set to true if the entry was found, false
// otherwise.
void* ht_find(SdbHash* ht, const char* key, bool* found) {
bool _found = false;
if (!found) {
found = &_found;
}
HtKv* kv = ht_find_kv (ht, key, found);
return (kv && *found)? kv->value : NULL;
}
// Deletes a entry from the hash table from the key, if the pair exists.
bool ht_delete(SdbHash* ht, const char* key) {
return ht_delete_internal (ht, key, NULL);
}
void ht_foreach(SdbHash *ht, HtForeachCallback cb, void *user) {
if (!ht) {
return;
}
ut32 i = 0;
HtKv *kv;
SdbListIter *iter;
for (i = 0; i < ht->size; i++) {
ls_foreach (ht->table[i], iter, kv) {
if (!kv || !kv->key || !kv->value) {
continue;
}
if (!cb (user, kv->key, kv->value)) {
return;
}
}
}
}