root/src/hashtable.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. FT_DECLARE
  2. hash
  3. hashtable_expand
  4. FT_DECLARE
  5. FT_DECLARE
  6. FT_DECLARE
  7. FT_DECLARE
  8. FT_DECLARE
  9. FT_DECLARE
  10. FT_DECLARE
  11. FT_DECLARE

   1 /*
   2  * Copyright (c) 2002, Christopher Clark
   3  * All rights reserved.
   4  * 
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 
   9  * * Redistributions of source code must retain the above copyright
  10  * notice, this list of conditions and the following disclaimer.
  11  * 
  12  * * Redistributions in binary form must reproduce the above copyright
  13  * notice, this list of conditions and the following disclaimer in the
  14  * documentation and/or other materials provided with the distribution.
  15  * 
  16  * * Neither the name of the original author; nor the names of any contributors
  17  * may be used to endorse or promote products derived from this software
  18  * without specific prior written permission.
  19  * 
  20  * 
  21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
  25  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 
  34 #include "private/ftdm_core.h"
  35 #include "hashtable.h"
  36 #include "hashtable_private.h"
  37 #include <stdlib.h>
  38 #include <stdio.h>
  39 #include <string.h>
  40 #include <math.h>
  41 
  42 /*
  43   Credit for primes table: Aaron Krowne
  44   http://br.endernet.org/~akrowne/
  45   http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
  46 */
  47 static const unsigned int primes[] = {
  48         53, 97, 193, 389,
  49         769, 1543, 3079, 6151,
  50         12289, 24593, 49157, 98317,
  51         196613, 393241, 786433, 1572869,
  52         3145739, 6291469, 12582917, 25165843,
  53         50331653, 100663319, 201326611, 402653189,
  54         805306457, 1610612741
  55 };
  56 const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
  57 const float max_load_factor = 0.65f;
  58 
  59 /*****************************************************************************/
  60 FT_DECLARE(struct hashtable *)
  61 create_hashtable(unsigned int minsize,
  62                  unsigned int (*hashf) (void*),
  63                  int (*eqf) (void*,void*))
  64 {
  65     struct hashtable *h;
  66     unsigned int pindex, size = primes[0];
  67     /* Check requested hashtable isn't too large */
  68     if (minsize > (1u << 30)) return NULL;
  69     /* Enforce size as prime */
  70     for (pindex=0; pindex < prime_table_length; pindex++) {
  71         if (primes[pindex] > minsize) { size = primes[pindex]; break; }
  72     }
  73     h = (struct hashtable *)ftdm_malloc(sizeof(struct hashtable));
  74     if (NULL == h) return NULL; /*oom*/
  75     h->table = (struct entry **)ftdm_malloc(sizeof(struct entry*) * size);
  76     if (NULL == h->table) { ftdm_safe_free(h); return NULL; } /*oom*/
  77     memset(h->table, 0, size * sizeof(struct entry *));
  78     h->tablelength  = size;
  79     h->primeindex   = pindex;
  80     h->entrycount   = 0;
  81     h->hashfn       = hashf;
  82     h->eqfn         = eqf;
  83     h->loadlimit    = (unsigned int) ceil(size * max_load_factor);
  84     return h;
  85 }
  86 
  87 /*****************************************************************************/
  88 unsigned int
  89 hash(struct hashtable *h, void *k)
  90 {
  91     /* Aim to protect against poor hash functions by adding logic here
  92      * - logic taken from java 1.4 hashtable source */
  93     unsigned int i = h->hashfn(k);
  94     i += ~(i << 9);
  95     i ^=  ((i >> 14) | (i << 18)); /* >>> */
  96     i +=  (i << 4);
  97     i ^=  ((i >> 10) | (i << 22)); /* >>> */
  98     return i;
  99 }
 100 
 101 /*****************************************************************************/
 102 static int
 103 hashtable_expand(struct hashtable *h)
 104 {
 105     /* Double the size of the table to accomodate more entries */
 106     struct entry **newtable;
 107     struct entry *e;
 108     struct entry **pE;
 109     unsigned int newsize, i, index;
 110     /* Check we're not hitting max capacity */
 111     if (h->primeindex == (prime_table_length - 1)) return 0;
 112     newsize = primes[++(h->primeindex)];
 113 
 114     newtable = (struct entry **)ftdm_malloc(sizeof(struct entry*) * newsize);
 115     if (NULL != newtable)
 116                 {
 117                         memset(newtable, 0, newsize * sizeof(struct entry *));
 118                         /* This algorithm is not 'stable'. ie. it reverses the list
 119                          * when it transfers entries between the tables */
 120                         for (i = 0; i < h->tablelength; i++) {
 121                                 while (NULL != (e = h->table[i])) {
 122                                         h->table[i] = e->next;
 123                                         index = indexFor(newsize,e->h);
 124                                         e->next = newtable[index];
 125                                         newtable[index] = e;
 126                                 }
 127                         }
 128                         ftdm_safe_free(h->table);
 129                         h->table = newtable;
 130                 }
 131     /* Plan B: realloc instead */
 132     else 
 133                 {
 134                         newtable = (struct entry **)
 135                                 realloc(h->table, newsize * sizeof(struct entry *));
 136                         if (NULL == newtable) { (h->primeindex)--; return 0; }
 137                         h->table = newtable;
 138                         memset(newtable[h->tablelength], 0, newsize - h->tablelength);
 139                         for (i = 0; i < h->tablelength; i++) {
 140                                 for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
 141                                         index = indexFor(newsize,e->h);
 142                                         if (index == i)
 143                                                 {
 144                                                         pE = &(e->next);
 145                                                 }
 146                                         else
 147                                                 {
 148                                                         *pE = e->next;
 149                                                         e->next = newtable[index];
 150                                                         newtable[index] = e;
 151                                                 }
 152                                 }
 153                         }
 154                 }
 155     h->tablelength = newsize;
 156     h->loadlimit   = (unsigned int) ceil(newsize * max_load_factor);
 157     return -1;
 158 }
 159 
 160 /*****************************************************************************/
 161 FT_DECLARE(unsigned int)
 162 hashtable_count(struct hashtable *h)
 163 {
 164     return h->entrycount;
 165 }
 166 
 167 /*****************************************************************************/
 168 FT_DECLARE(int)
 169 hashtable_insert(struct hashtable *h, void *k, void *v, hashtable_flag_t flags)
 170 {
 171     /* This method allows duplicate keys - but they shouldn't be used */
 172     unsigned int index;
 173     struct entry *e;
 174     if (++(h->entrycount) > h->loadlimit)
 175                 {
 176                         /* Ignore the return value. If expand fails, we should
 177                          * still try cramming just this value into the existing table
 178                          * -- we may not have memory for a larger table, but one more
 179                          * element may be ok. Next time we insert, we'll try expanding again.*/
 180                         hashtable_expand(h);
 181                 }
 182     e = (struct entry *)ftdm_malloc(sizeof(struct entry));
 183     if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
 184     e->h = hash(h,k);
 185     index = indexFor(h->tablelength,e->h);
 186     e->k = k;
 187     e->v = v;
 188         e->flags = flags;
 189     e->next = h->table[index];
 190     h->table[index] = e;
 191     return -1;
 192 }
 193 
 194 /*****************************************************************************/
 195 FT_DECLARE(void *) /* returns value associated with key */
 196 hashtable_search(struct hashtable *h, void *k)
 197 {
 198     struct entry *e;
 199     unsigned int hashvalue, index;
 200     hashvalue = hash(h,k);
 201     index = indexFor(h->tablelength,hashvalue);
 202     e = h->table[index];
 203     while (NULL != e)
 204                 {
 205                         /* Check hash value to short circuit heavier comparison */
 206                         if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
 207                         e = e->next;
 208                 }
 209     return NULL;
 210 }
 211 
 212 /*****************************************************************************/
 213 FT_DECLARE(void *) /* returns value associated with key */
 214 hashtable_remove(struct hashtable *h, void *k)
 215 {
 216     /* TODO: consider compacting the table when the load factor drops enough,
 217      *       or provide a 'compact' method. */
 218 
 219     struct entry *e;
 220     struct entry **pE;
 221     void *v;
 222     unsigned int hashvalue, index;
 223 
 224     hashvalue = hash(h,k);
 225     index = indexFor(h->tablelength,hash(h,k));
 226     pE = &(h->table[index]);
 227     e = *pE;
 228     while (NULL != e)
 229                 {
 230                         /* Check hash value to short circuit heavier comparison */
 231                         if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
 232                                 {
 233                                         *pE = e->next;
 234                                         h->entrycount--;
 235                                         v = e->v;
 236                                         if (e->flags & HASHTABLE_FLAG_FREE_KEY) {
 237                                                 freekey(e->k);
 238                                         }
 239                                         ftdm_safe_free(e);
 240                                         return v;
 241                                 }
 242                         pE = &(e->next);
 243                         e = e->next;
 244                 }
 245     return NULL;
 246 }
 247 
 248 /*****************************************************************************/
 249 /* destroy */
 250 FT_DECLARE(void)
 251 hashtable_destroy(struct hashtable *h)
 252 {
 253     unsigned int i;
 254     struct entry *e, *f;
 255     struct entry **table = h->table;
 256 
 257         for (i = 0; i < h->tablelength; i++)
 258         {
 259             e = table[i];
 260             while (NULL != e)
 261                                 { f = e; e = e->next; if (f->flags & HASHTABLE_FLAG_FREE_KEY) freekey(f->k); if (f->flags & HASHTABLE_FLAG_FREE_VALUE) ftdm_safe_free(f->v); ftdm_safe_free(f); }
 262         }
 263     
 264     ftdm_safe_free(h->table);
 265     ftdm_safe_free(h);
 266 }
 267 
 268 FT_DECLARE(struct hashtable_iterator *) hashtable_next(struct hashtable_iterator *i)
 269 {
 270 
 271         if (i->e) {
 272                 if ((i->e = i->e->next) != 0) { 
 273                         return i;
 274                 } else {
 275                         i->pos++;
 276                 }
 277         }
 278         
 279         while(i->pos < i->h->tablelength && !i->h->table[i->pos]) {
 280                 i->pos++;
 281         }
 282         
 283         if (i->pos >= i->h->tablelength) {
 284                 return NULL;
 285         }
 286         
 287         if ((i->e = i->h->table[i->pos]) != 0) { 
 288                 return i;
 289         }
 290 
 291         return NULL;
 292 }
 293 
 294 FT_DECLARE(struct hashtable_iterator *) hashtable_first(struct hashtable *h)
 295 {
 296         h->iterator.pos = 0;
 297         h->iterator.e = NULL;
 298         h->iterator.h = h;
 299         return hashtable_next(&h->iterator);
 300 }
 301 
 302 
 303 
 304 FT_DECLARE(void) hashtable_this(struct hashtable_iterator *i, const void **key, int *klen, void **val)
 305 {
 306         if (i->e) {
 307                 if (key) {
 308                         *key = i->e->k;
 309                 }
 310                 if (klen) {
 311                         *klen = (int)strlen(i->e->k);
 312                 }
 313                 if (val) {
 314                         *val = i->e->v;
 315                 }
 316         } else {
 317                 if (key) {
 318                         *key = NULL;
 319                 }
 320                 if (klen) {
 321                         *klen = 0;
 322                 }
 323                 if (val) {
 324                         *val = NULL;
 325                 }
 326         }
 327 }
 328 
 329 
 330 /* For Emacs:
 331  * Local Variables:
 332  * mode:c
 333  * indent-tabs-mode:t
 334  * tab-width:4
 335  * c-basic-offset:4
 336  * End:
 337  * For VIM:
 338  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 339  */
 340 

/* [<][>][^][v][top][bottom][index][help] */