123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- /*
- * Copyright (c) 2002, Christopher Clark
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of the original author; nor the names of any contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "libks/ks.h"
- #include "libks/ks_hash.h"
- struct entry
- {
- void *k, *v;
- unsigned int h;
- ks_hash_flag_t flags;
- ks_hash_destructor_t destructor;
- struct entry *next;
- };
- struct ks_hash_iterator {
- unsigned int pos;
- ks_locked_t locked;
- struct entry *e;
- struct ks_hash *h;
- };
- struct ks_hash {
- unsigned int tablelength;
- struct entry **table;
- unsigned int entrycount;
- unsigned int loadlimit;
- unsigned int primeindex;
- unsigned int (*hashfn) (void *k);
- int (*eqfn) (void *k1, void *k2);
- ks_hash_flag_t flags;
- ks_hash_destructor_t destructor;
- ks_rwl_t *rwl;
- ks_mutex_t *mutex;
- uint32_t readers;
- ks_size_t keysize;
- ks_hash_mode_t mode;
- };
- /*****************************************************************************/
- /*****************************************************************************/
- static inline unsigned int hash(ks_hash_t *h, const void *k)
- {
- unsigned int i;
- switch (h->mode)
- {
- case KS_HASH_MODE_ARBITRARY:
- i = ks_hash_default_arbitrary(k, h->keysize, 13);
- break;
- case KS_HASH_MODE_INT:
- case KS_HASH_MODE_INT64:
- case KS_HASH_MODE_PTR:
- i = h->hashfn((void *)&k);
- break;
- case KS_HASH_MODE_UUID:
- default:
- i = h->hashfn((void *)k);
- break;
- }
- /* Aim to protect against poor hash functions by adding logic here
- * - logic taken from java 1.4 hash source */
- i += ~(i << 9);
- i ^= ((i >> 14) | (i << 18)); /* >>> */
- i += (i << 4);
- i ^= ((i >> 10) | (i << 22)); /* >>> */
- return i;
- }
- /*****************************************************************************/
- /* indexFor */
- static __inline__ unsigned int
- indexFor(unsigned int tablelength, unsigned int hashvalue) {
- return (hashvalue % tablelength);
- }
- /* Only works if tablelength == 2^N */
- /*static inline unsigned int
- indexFor(unsigned int tablelength, unsigned int hashvalue)
- {
- return (hashvalue & (tablelength - 1u));
- }
- */
- /*****************************************************************************/
- //#define freekey(X) free(X)
- /*
- Credit for primes table: Aaron Krowne
- http://br.endernet.org/~akrowne/
- http://planetmath.org/encyclopedia/GoodKs_HashPrimes.html
- */
- static const unsigned int primes[] = {
- 53, 97, 193, 389,
- 769, 1543, 3079, 6151,
- 12289, 24593, 49157, 98317,
- 196613, 393241, 786433, 1572869,
- 3145739, 6291469, 12582917, 25165843,
- 50331653, 100663319, 201326611, 402653189,
- 805306457, 1610612741
- };
- const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
- const float max_load_factor = 0.65f;
- /*****************************************************************************/
- static void ks_hash_cleanup(void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type)
- {
- //ks_hash_t *hash = (ks_hash_t *) ptr;
- switch(action) {
- case KS_MPCL_ANNOUNCE:
- break;
- case KS_MPCL_TEARDOWN:
- break;
- case KS_MPCL_DESTROY:
- //ks_hash_destroy(&hash);
- break;
- }
- }
- KS_DECLARE(ks_status_t) ks_hash_create(ks_hash_t **hp, ks_hash_mode_t mode, ks_hash_flag_t flags, ks_pool_t *pool)
- {
- return ks_hash_create_ex(hp, 16, NULL, NULL, mode, flags, NULL, pool);
- }
- KS_DECLARE(void) ks_hash_set_flags(ks_hash_t *h, ks_hash_flag_t flags)
- {
- h->flags = flags;
- }
- KS_DECLARE(void) ks_hash_set_keysize(ks_hash_t *h, ks_size_t keysize)
- {
- h->keysize = keysize;
- }
- KS_DECLARE(void) ks_hash_set_destructor(ks_hash_t *h, ks_hash_destructor_t destructor)
- {
- h->destructor = destructor;
- }
- KS_DECLARE(ks_status_t)
- ks_hash_create_ex(
- ks_hash_t **hp,
- unsigned int minsize,
- unsigned int (*hashf) (void*),
- int (*eqf) (void*,void*),
- ks_hash_mode_t mode,
- ks_hash_flag_t flags,
- ks_hash_destructor_t destructor,
- ks_pool_t *pool)
- {
- ks_hash_t *h;
- unsigned int pindex, size = primes[0];
- ks_size_t keysize = 0;
- switch(mode) {
- case KS_HASH_MODE_CASE_INSENSITIVE:
- ks_assert(hashf == NULL);
- hashf = ks_hash_default_ci;
- break;
- case KS_HASH_MODE_INT:
- ks_assert(hashf == NULL);
- ks_assert(eqf == NULL);
- hashf = ks_hash_default_int;
- eqf = ks_hash_equalkeys_int;
- keysize = 4;
- break;
- case KS_HASH_MODE_INT64:
- ks_assert(hashf == NULL);
- ks_assert(eqf == NULL);
- hashf = ks_hash_default_int64;
- eqf = ks_hash_equalkeys_int64;
- keysize = 8;
- break;
- case KS_HASH_MODE_UUID:
- ks_assert(hashf == NULL);
- ks_assert(eqf == NULL);
- hashf = ks_hash_default_uuid;
- eqf = ks_hash_equalkeys_uuid;
- keysize = sizeof(uuid_t);
- break;
- case KS_HASH_MODE_PTR:
- ks_assert(hashf == NULL);
- ks_assert(eqf == NULL);
- hashf = ks_hash_default_ptr;
- eqf = ks_hash_equalkeys_ptr;
- keysize = sizeof(void *);
- break;
- case KS_HASH_MODE_ARBITRARY:
- keysize = sizeof(void *);
- break;
- default:
- break;
- }
- if ((flags & KS_HASH_FLAG_NOLOCK)) {
- flags &= ~KS_HASH_FLAG_RWLOCK;
- }
- ks_assert(pool);
- if (!hashf) hashf = ks_hash_default;
- if (!eqf) eqf = ks_hash_equalkeys;
- if (!minsize) minsize = 16;
- /* Check requested ks_hash isn't too large */
- if (minsize > (1u << 30)) {*hp = NULL; return KS_STATUS_FAIL;}
- /* Enforce size as prime */
- for (pindex=0; pindex < prime_table_length; pindex++) {
- if (primes[pindex] > minsize) {
- size = primes[pindex];
- break;
- }
- }
- h = (ks_hash_t *) ks_pool_alloc(pool, sizeof(ks_hash_t));
- h->flags = flags;
- h->destructor = destructor;
- h->keysize = keysize;
- h->mode = mode;
- if ((flags & KS_HASH_FLAG_RWLOCK)) {
- ks_rwl_create(&h->rwl, pool);
- }
- if (!(flags & KS_HASH_FLAG_NOLOCK)) {
- ks_mutex_create(&h->mutex, KS_MUTEX_FLAG_DEFAULT, pool);
- }
- if (NULL == h) abort(); /*oom*/
- h->table = (struct entry **)ks_pool_alloc(pool, sizeof(struct entry*) * size);
- if (NULL == h->table) abort(); /*oom*/
- //memset(h->table, 0, size * sizeof(struct entry *));
- h->tablelength = size;
- h->primeindex = pindex;
- h->entrycount = 0;
- h->hashfn = hashf;
- h->eqfn = eqf;
- h->loadlimit = (unsigned int) ceil(size * max_load_factor);
- *hp = h;
- ks_pool_set_cleanup(h, NULL, ks_hash_cleanup);
- return KS_STATUS_SUCCESS;
- }
- /*****************************************************************************/
- static int
- ks_hash_expand(ks_hash_t *h)
- {
- /* Double the size of the table to accomodate more entries */
- struct entry **newtable;
- struct entry *e;
- struct entry **pE;
- unsigned int newsize, i, index;
- /* Check we're not hitting max capacity */
- if (h->primeindex == (prime_table_length - 1)) return 0;
- newsize = primes[++(h->primeindex)];
- newtable = (struct entry **)ks_pool_alloc(ks_pool_get(h), sizeof(struct entry*) * newsize);
- if (NULL != newtable)
- {
- memset(newtable, 0, newsize * sizeof(struct entry *));
- /* This algorithm is not 'stable'. ie. it reverses the list
- * when it transfers entries between the tables */
- for (i = 0; i < h->tablelength; i++) {
- while (NULL != (e = h->table[i])) {
- h->table[i] = e->next;
- index = indexFor(newsize,e->h);
- e->next = newtable[index];
- newtable[index] = e;
- }
- }
- ks_pool_free(&h->table);
- h->table = newtable;
- }
- /* Plan B: realloc instead */
- else
- {
- newtable = (struct entry **)
- ks_pool_resize(h->table, newsize * sizeof(struct entry *));
- if (NULL == newtable) { (h->primeindex)--; return 0; }
- h->table = newtable;
- memset(newtable[h->tablelength], 0, newsize - h->tablelength);
- for (i = 0; i < h->tablelength; i++) {
- for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
- index = indexFor(newsize,e->h);
- if (index == i) {
- pE = &(e->next);
- } else {
- *pE = e->next;
- e->next = newtable[index];
- newtable[index] = e;
- }
- }
- }
- }
- h->tablelength = newsize;
- h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
- return -1;
- }
- /*****************************************************************************/
- KS_DECLARE(unsigned int)
- ks_hash_count(ks_hash_t *h)
- {
- return h->entrycount;
- }
- static int key_equals(ks_hash_t *h, const void * const k1, const void * const k2)
- {
- switch (h->mode)
- {
- case KS_HASH_MODE_ARBITRARY:
- return !memcmp(k1, k2, h->keysize);
- case KS_HASH_MODE_INT:
- case KS_HASH_MODE_INT64:
- case KS_HASH_MODE_PTR:
- return h->eqfn((void *)&k1, (void *)&k2);
- case KS_HASH_MODE_UUID:
- return h->eqfn((void *)k1, (void *)k2);
- default: break;
- }
- return h->eqfn((void *)k1, (void *)k2);
- }
- static void * _ks_hash_remove(ks_hash_t *h, const void * const k, unsigned int hashvalue, unsigned int index) {
- /* TODO: consider compacting the table when the load factor drops enough,
- * or provide a 'compact' method. */
- struct entry *e;
- struct entry **pE;
- void *v;
- pE = &(h->table[index]);
- e = *pE;
- while (NULL != e) {
- /* Check hash value to short circuit heavier comparison */
- if ((hashvalue == e->h) && (key_equals(h, k, e->k))) {
- *pE = e->next;
- h->entrycount--;
- v = e->v;
- if (e->flags & KS_HASH_FLAG_FREE_KEY) {
- ks_pool_free(&e->k);
- }
- if (e->flags & KS_HASH_FLAG_FREE_VALUE) {
- ks_pool_free(&e->v);
- v = NULL;
- } else if (e->destructor) {
- e->destructor(e->v);
- v = e->v = NULL;
- } else if (h->destructor) {
- h->destructor(e->v);
- v = e->v = NULL;
- }
- ks_pool_free(&e);
- return v;
- }
- pE = &(e->next);
- e = e->next;
- }
- return NULL;
- }
- /*****************************************************************************/
- KS_DECLARE(ks_status_t) ks_hash_insert_ex(ks_hash_t *h, const void * const k, const void * const v, ks_hash_flag_t flags, ks_hash_destructor_t destructor)
- {
- struct entry *e;
- unsigned int hashvalue = hash(h, k);
- unsigned int index = indexFor(h->tablelength, hashvalue);
- ks_hash_write_lock(h);
- if (!flags) {
- flags = h->flags;
- }
- if (flags & KS_HASH_FLAG_DUP_CHECK) {
- _ks_hash_remove(h, k, hashvalue, index);
- }
- if (++(h->entrycount) > h->loadlimit)
- {
- /* Ignore the return value. If expand fails, we should
- * still try cramming just this value into the existing table
- * -- we may not have memory for a larger table, but one more
- * element may be ok. Next time we insert, we'll try expanding again.*/
- ks_hash_expand(h);
- index = indexFor(h->tablelength, hashvalue);
- }
- e = (struct entry *)ks_pool_alloc(ks_pool_get(h), sizeof(struct entry));
- e->h = hashvalue;
- e->k = (void *)k;
- e->v = (void *)v;
- e->flags = flags;
- e->destructor = destructor;
- e->next = h->table[index];
- h->table[index] = e;
- ks_hash_write_unlock(h);
- return KS_STATUS_SUCCESS;
- }
- KS_DECLARE(void) ks_hash_write_lock(ks_hash_t *h)
- {
- if ((h->flags & KS_HASH_FLAG_NOLOCK)) {
- return;
- } else if ((h->flags & KS_HASH_FLAG_RWLOCK)) {
- ks_rwl_write_lock(h->rwl);
- } else {
- ks_mutex_lock(h->mutex);
- }
- }
- KS_DECLARE(void) ks_hash_write_unlock(ks_hash_t *h)
- {
- if ((h->flags & KS_HASH_FLAG_NOLOCK)) {
- return;
- } else if ((h->flags & KS_HASH_FLAG_RWLOCK)) {
- ks_rwl_write_unlock(h->rwl);
- } else {
- ks_mutex_unlock(h->mutex);
- }
- }
- KS_DECLARE(ks_status_t) ks_hash_read_lock(ks_hash_t *h)
- {
- if (!(h->flags & KS_HASH_FLAG_RWLOCK)) {
- return KS_STATUS_INACTIVE;
- }
- ks_rwl_read_lock(h->rwl);
- ks_mutex_lock(h->mutex);
- h->readers++;
- ks_mutex_unlock(h->mutex);
- return KS_STATUS_SUCCESS;
- }
- KS_DECLARE(ks_status_t) ks_hash_read_unlock(ks_hash_t *h)
- {
- if (!(h->flags & KS_HASH_FLAG_RWLOCK)) {
- return KS_STATUS_INACTIVE;
- }
- ks_mutex_lock(h->mutex);
- ks_assert(h->readers != 0);
- h->readers--;
- ks_mutex_unlock(h->mutex);
- ks_rwl_read_unlock(h->rwl);
- return KS_STATUS_SUCCESS;
- }
- /*****************************************************************************/
- KS_DECLARE(void *) /* returns value associated with key */
- ks_hash_search(ks_hash_t *h, const void *k, ks_locked_t locked)
- {
- struct entry *e;
- unsigned int hashvalue, index;
- void *v = NULL;
- ks_assert(locked != KS_READLOCKED || (h->flags & KS_HASH_FLAG_RWLOCK));
- hashvalue = hash(h,k);
- index = indexFor(h->tablelength,hashvalue);
- if (locked == KS_READLOCKED) {
- ks_rwl_read_lock(h->rwl);
- ks_mutex_lock(h->mutex);
- h->readers++;
- ks_mutex_unlock(h->mutex);
- }
- e = h->table[index];
- while (NULL != e) {
- /* Check hash value to short circuit heavier comparison */
- if ((hashvalue == e->h) && (key_equals(h, (void *)k, e->k))) {
- v = e->v;
- break;
- }
- e = e->next;
- }
- return v;
- }
- /*****************************************************************************/
- /* returns value associated with key */
- KS_DECLARE(void *) ks_hash_remove(ks_hash_t *h, const void * const k)
- {
- void *v;
- unsigned int hashvalue = hash(h,k);
- ks_hash_write_lock(h);
- v = _ks_hash_remove(h, k, hashvalue, indexFor(h->tablelength,hashvalue));
- ks_hash_write_unlock(h);
- return v;
- }
- /*****************************************************************************/
- /* destroy */
- KS_DECLARE(void) ks_hash_destroy(ks_hash_t **h)
- {
- unsigned int i;
- struct entry *e, *f;
- struct entry **table;
- if (!h || !*h)
- return;
- table = (*h)->table;
- ks_hash_write_lock(*h);
- for (i = 0; i < (*h)->tablelength; i++) {
- e = table[i];
- while (NULL != e) {
- f = e; e = e->next;
- if (f->flags & KS_HASH_FLAG_FREE_KEY) {
- ks_pool_free(&f->k);
- }
- if (f->flags & KS_HASH_FLAG_FREE_VALUE) {
- ks_pool_free(&f->v);
- } else if (f->destructor) {
- f->destructor(f->v);
- f->v = NULL;
- } else if ((*h)->destructor) {
- (*h)->destructor(f->v);
- f->v = NULL;
- }
- ks_pool_free(&f);
- }
- }
- ks_pool_free(&(*h)->table);
- ks_hash_write_unlock(*h);
- if ((*h)->rwl) ks_pool_free(&(*h)->rwl);
- if ((*h)->mutex) {
- ks_pool_free(&(*h)->mutex);
- }
- ks_pool_free(&(*h));
- *h = NULL;
- }
- KS_DECLARE(void) ks_hash_last(ks_hash_iterator_t **iP)
- {
- ks_hash_iterator_t *i = *iP;
- if (i->locked == KS_READLOCKED) {
- ks_mutex_lock(i->h->mutex);
- i->h->readers--;
- ks_mutex_unlock(i->h->mutex);
- ks_rwl_read_unlock(i->h->rwl);
- }
- ks_pool_free(&i);
- *iP = NULL;
- }
- KS_DECLARE(ks_hash_iterator_t *) ks_hash_next(ks_hash_iterator_t **iP)
- {
- ks_hash_iterator_t *i = *iP;
- if (i->e) {
- if ((i->e = i->e->next) != 0) {
- return i;
- } else {
- i->pos++;
- }
- }
- while(i->pos < i->h->tablelength && !i->h->table[i->pos]) {
- i->pos++;
- }
- if (i->pos >= i->h->tablelength) {
- goto end;
- }
- if ((i->e = i->h->table[i->pos]) != 0) {
- return i;
- }
- end:
- ks_hash_last(iP);
- return NULL;
- }
- KS_DECLARE(ks_hash_iterator_t *) ks_hash_first(ks_hash_t *h, ks_locked_t locked)
- {
- ks_hash_iterator_t *iterator;
- ks_assert(locked != KS_READLOCKED || (h->flags & KS_HASH_FLAG_RWLOCK));
- iterator = ks_pool_alloc(ks_pool_get(h), sizeof(*iterator));
- ks_assert(iterator);
- iterator->pos = 0;
- iterator->e = NULL;
- iterator->h = h;
- if (locked == KS_READLOCKED) {
- ks_rwl_read_lock(h->rwl);
- iterator->locked = locked;
- ks_mutex_lock(h->mutex);
- h->readers++;
- ks_mutex_unlock(h->mutex);
- }
- return ks_hash_next(&iterator);
- }
- KS_DECLARE(void) ks_hash_this_val(ks_hash_iterator_t *i, void *val)
- {
- if (i->e) {
- i->e->v = val;
- }
- }
- KS_DECLARE(void) ks_hash_this(ks_hash_iterator_t *i, const void **key, ks_ssize_t *klen, void **val)
- {
- if (i->e) {
- if (key) {
- *key = i->e->k;
- }
- if (klen) {
- *klen = (int)strlen(i->e->k);
- }
- if (val) {
- *val = i->e->v;
- }
- } else {
- if (key) {
- *key = NULL;
- }
- if (klen) {
- *klen = 0;
- }
- if (val) {
- *val = NULL;
- }
- }
- }
- /* For Emacs:
- * Local Variables:
- * mode:c
- * indent-tabs-mode:t
- * tab-width:4
- * c-basic-offset:4
- * End:
- * For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
- */
|