diff --git a/src/include/switch_hashtable.h b/src/include/switch_hashtable.h index dddc89f286..1fdbeb9736 100644 --- a/src/include/switch_hashtable.h +++ b/src/include/switch_hashtable.h @@ -119,7 +119,8 @@ switch_create_hashtable(switch_hashtable_t **hp, unsigned int minsize, typedef enum { HASHTABLE_FLAG_NONE = 0, HASHTABLE_FLAG_FREE_KEY = (1 << 0), - HASHTABLE_FLAG_FREE_VALUE = (1 << 1) + HASHTABLE_FLAG_FREE_VALUE = (1 << 1), + HASHTABLE_DUP_CHECK = (1 << 2) } hashtable_flag_t; SWITCH_DECLARE(int) diff --git a/src/switch_core_hash.c b/src/switch_core_hash.c index e997ac9bcc..ddf08c0c6c 100644 --- a/src/switch_core_hash.c +++ b/src/switch_core_hash.c @@ -57,7 +57,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_hash_destroy(switch_hash_t **hash) SWITCH_DECLARE(switch_status_t) switch_core_hash_insert_destructor(switch_hash_t *hash, const char *key, const void *data, hashtable_destructor_t destructor) { - switch_hashtable_insert_destructor(hash, strdup(key), (void *)data, HASHTABLE_FLAG_FREE_KEY, destructor); + switch_hashtable_insert_destructor(hash, strdup(key), (void *)data, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_DUP_CHECK, destructor); return SWITCH_STATUS_SUCCESS; } diff --git a/src/switch_hashtable.c b/src/switch_hashtable.c index 7f8ba15de0..597312b805 100644 --- a/src/switch_hashtable.c +++ b/src/switch_hashtable.c @@ -153,13 +153,54 @@ switch_hashtable_count(switch_hashtable_t *h) return h->entrycount; } +static void * _switch_hashtable_remove(switch_hashtable_t *h, void *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) && (h->eqfn(k, e->k))) { + *pE = e->next; + h->entrycount--; + v = e->v; + if (e->flags & HASHTABLE_FLAG_FREE_KEY) { + freekey(e->k); + } + if (e->flags & HASHTABLE_FLAG_FREE_VALUE) { + switch_safe_free(e->v); + v = NULL; + } else if (e->destructor) { + e->destructor(e->v); + v = e->v = NULL; + } + switch_safe_free(e); + return v; + } + pE = &(e->next); + e = e->next; + } + return NULL; +} + /*****************************************************************************/ SWITCH_DECLARE(int) switch_hashtable_insert_destructor(switch_hashtable_t *h, void *k, void *v, hashtable_flag_t flags, hashtable_destructor_t destructor) { - /* This method allows duplicate keys - but they shouldn't be used */ - unsigned int index; struct entry *e; + unsigned int hashvalue = hash(h, k); + unsigned index = indexFor(h->tablelength, hashvalue); + + if (flags & HASHTABLE_DUP_CHECK) { + _switch_hashtable_remove(h, k, hashvalue, index); + } + if (++(h->entrycount) > h->loadlimit) { /* Ignore the return value. If expand fails, we should @@ -167,11 +208,11 @@ switch_hashtable_insert_destructor(switch_hashtable_t *h, void *k, void *v, hash * -- 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.*/ hashtable_expand(h); + index = indexFor(h->tablelength, hashvalue); } e = (struct entry *)malloc(sizeof(struct entry)); if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ - e->h = hash(h,k); - index = indexFor(h->tablelength,e->h); + e->h = hashvalue; e->k = k; e->v = v; e->flags = flags; @@ -202,40 +243,8 @@ switch_hashtable_search(switch_hashtable_t *h, void *k) SWITCH_DECLARE(void *) /* returns value associated with key */ switch_hashtable_remove(switch_hashtable_t *h, void *k) { - /* TODO: consider compacting the table when the load factor drops enough, - * or provide a 'compact' method. */ - - struct entry *e; - struct entry **pE; - void *v; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - pE = &(h->table[index]); - e = *pE; - while (NULL != e) { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { - *pE = e->next; - h->entrycount--; - v = e->v; - if (e->flags & HASHTABLE_FLAG_FREE_KEY) { - freekey(e->k); - } - if (e->flags & HASHTABLE_FLAG_FREE_VALUE) { - switch_safe_free(e->v); - } else if (e->destructor) { - e->destructor(e->v); - e->v = NULL; - } - switch_safe_free(e); - return v; - } - pE = &(e->next); - e = e->next; - } - return NULL; + unsigned int hashvalue = hash(h,k); + return _switch_hashtable_remove(h, k, hashvalue, indexFor(h->tablelength,hashvalue)); } /*****************************************************************************/