hashtable.c

Go to the documentation of this file.
00001 /* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
00002 /*
00003 * Copyright (c) 2002, Christopher Clark
00004 * All rights reserved.
00005 * 
00006 * Redistribution and use in source and binary forms, with or without
00007 * modification, are permitted provided that the following conditions
00008 * are met:
00009 * 
00010 * * Redistributions of source code must retain the above copyright
00011 * notice, this list of conditions and the following disclaimer.
00012 * 
00013 * * Redistributions in binary form must reproduce the above copyright
00014 * notice, this list of conditions and the following disclaimer in the
00015 * documentation and/or other materials provided with the distribution.
00016 * 
00017 * * Neither the name of the original author; nor the names of any contributors
00018 * may be used to endorse or promote products derived from this software
00019 * without specific prior written permission.
00020 * 
00021 * 
00022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00025 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
00026 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00027 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00028 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00029 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00030 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00031 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 */
00034 
00035 #include "hashtable.h"
00036 #include "hashtable_private.h"
00037 #include <stdlib.h>
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <math.h>
00041 #include "../MALLOC/includes/MALLOC.h"
00042 
00043 /*
00044 Credit for primes table: Aaron Krowne
00045  http://br.endernet.org/~akrowne/
00046  http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
00047 */
00048 static const unsigned int primes[] = {
00049 53, 97, 193, 389,
00050 769, 1543, 3079, 6151,
00051 12289, 24593, 49157, 98317,
00052 196613, 393241, 786433, 1572869,
00053 3145739, 6291469, 12582917, 25165843,
00054 50331653, 100663319, 201326611, 402653189,
00055 805306457, 1610612741
00056 };
00057 const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
00058 const float max_load_factor = 0.65f;
00059 
00060 /*****************************************************************************/
00061 struct hashtable *
00062 create_hashtable(unsigned int minsize,
00063                  unsigned int (*hashf) (void*),
00064                  int (*eqf) (void*,void*))
00065 {
00066     struct hashtable *h;
00067     unsigned int pindex, size = primes[0];
00068     /* Check requested hashtable isn't too large */
00069     if (minsize > (1u << 30)) return NULL;
00070     /* Enforce size as prime */
00071     for (pindex=0; pindex < prime_table_length; pindex++) {
00072         if (primes[pindex] > minsize) { size = primes[pindex]; break; }
00073     }
00074     h = (struct hashtable *)MALLOC(sizeof(struct hashtable));
00075     if (NULL == h) return NULL; /*oom*/
00076     h->table = (struct entry **)MALLOC(sizeof(struct entry*) * size);
00077     if (NULL == h->table) { FREE(h); return NULL; } /*oom*/
00078     memset(h->table, 0, size * sizeof(struct entry *));
00079     h->tablelength  = size;
00080     h->primeindex   = pindex;
00081     h->entrycount   = 0;
00082     h->hashfn       = hashf;
00083     h->eqfn         = eqf;
00084     h->loadlimit    = (unsigned int) ceil(size * max_load_factor);
00085     return h;
00086 }
00087 
00088 /*****************************************************************************/
00089 unsigned int
00090 hash(struct hashtable *h, void *k)
00091 {
00092     /* Aim to protect against poor hash functions by adding logic here
00093      * - logic taken from java 1.4 hashtable source */
00094     unsigned int i = h->hashfn(k);
00095     i += ~(i << 9);
00096     i ^=  ((i >> 14) | (i << 18)); /* >>> */
00097     i +=  (i << 4);
00098     i ^=  ((i >> 10) | (i << 22)); /* >>> */
00099     return i;
00100 }
00101 
00102 /*****************************************************************************/
00103 static int
00104 hashtable_expand(struct hashtable *h)
00105 {
00106     /* Double the size of the table to accomodate more entries */
00107     struct entry **newtable;
00108     struct entry *e;
00109     struct entry **pE;
00110     unsigned int newsize, i, index_;
00111     /* Check we're not hitting max capacity */
00112     if (h->primeindex == (prime_table_length - 1)) return 0;
00113     newsize = primes[++(h->primeindex)];
00114 
00115     newtable = (struct entry **)MALLOC(sizeof(struct entry*) * newsize);
00116     if (NULL != newtable)
00117     {
00118         memset(newtable, 0, newsize * sizeof(struct entry *));
00119         /* This algorithm is not 'stable'. ie. it reverses the list
00120          * when it transfers entries between the tables */
00121         for (i = 0; i < h->tablelength; i++) {
00122             while (NULL != (e = h->table[i])) {
00123                 h->table[i] = e->next;
00124                 index_ = indexFor(newsize,e->h);
00125                 e->next = newtable[index_];
00126                 newtable[index_] = e;
00127             }
00128         }
00129         FREE(h->table);
00130         h->table = newtable;
00131     }
00132     /* Plan B: realloc instead */
00133     else 
00134     {
00135         newtable = (struct entry **) REALLOC(h->table, newsize * sizeof(struct entry *));
00136         if (NULL == newtable) { (h->primeindex)--; return 0; }
00137         h->table = newtable;
00138         memset(newtable[h->tablelength], 0, newsize - h->tablelength);
00139         for (i = 0; i < h->tablelength; i++) {
00140             for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
00141                 index_ = indexFor(newsize,e->h);
00142                 if (index_ == i)
00143                 {
00144                     pE = &(e->next);
00145                 }
00146                 else
00147                 {
00148                     *pE = e->next;
00149                     e->next = newtable[index_];
00150                     newtable[index_] = e;
00151                 }
00152             }
00153         }
00154     }
00155     h->tablelength = newsize;
00156     h->loadlimit   = (unsigned int) ceil(newsize * max_load_factor);
00157     return -1;
00158 }
00159 
00160 /*****************************************************************************/
00161 unsigned int
00162 hashtable_count(struct hashtable *h)
00163 {
00164     return h->entrycount;
00165 }
00166 
00167 /*****************************************************************************/
00168 int
00169 hashtable_insert(struct hashtable *h, void *k, void *v)
00170 {
00171     /* This method allows duplicate keys - but they shouldn't be used */
00172     unsigned int index_;
00173     struct entry *e;
00174     if (++(h->entrycount) > h->loadlimit)
00175     {
00176         /* Ignore the return value. If expand fails, we should
00177          * still try cramming just this value into the existing table
00178          * -- we may not have memory for a larger table, but one more
00179          * element may be ok. Next time we insert, we'll try expanding again.*/
00180         hashtable_expand(h);
00181     }
00182     e = (struct entry *)MALLOC(sizeof(struct entry));
00183     if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
00184     e->h = hash(h,k);
00185     index_ = indexFor(h->tablelength,e->h);
00186     e->k = k;
00187     e->v = v;
00188     e->next = h->table[index_];
00189     h->table[index_] = e;
00190     return -1;
00191 }
00192 
00193 /*****************************************************************************/
00194 void * /* returns value associated with key */
00195 hashtable_search(struct hashtable *h, void *k)
00196 {
00197     struct entry *e;
00198     unsigned int hashvalue, index_;
00199     hashvalue = hash(h,k);
00200     index_ = indexFor(h->tablelength,hashvalue);
00201     e = h->table[index_];
00202     while (NULL != e)
00203     {
00204         /* Check hash value to short circuit heavier comparison */
00205         if ((hashvalue == e->h) && (h->eqfn(k, e->k))) 
00206                 {
00207                         return e->v;
00208                 }
00209                         
00210         e = e->next;
00211     }
00212     return NULL;
00213 }
00214 
00215 /*****************************************************************************/
00216 void * /* returns value associated with key */
00217 hashtable_remove(struct hashtable *h, void *k)
00218 {
00219     /* TODO: consider compacting the table when the load factor drops enough,
00220      *       or provide a 'compact' method. */
00221 
00222     struct entry *e;
00223     struct entry **pE;
00224     void *v;
00225     unsigned int hashvalue, index_;
00226 
00227     hashvalue = hash(h,k);
00228     index_ = indexFor(h->tablelength,hash(h,k));
00229     pE = &(h->table[index_]);
00230     e = *pE;
00231     while (NULL != e)
00232     {
00233         /* Check hash value to short circuit heavier comparison */
00234         if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
00235         {
00236             *pE = e->next;
00237             h->entrycount--;
00238             v = e->v;
00239             freekey(e->k);
00240             FREE(e);
00241             return v;
00242         }
00243         pE = &(e->next);
00244         e = e->next;
00245     }
00246     return NULL;
00247 }
00248 
00249 /*****************************************************************************/
00250 /* destroy */
00251 void hashtable_destroy(struct hashtable *h, int free_values)
00252 {
00253     unsigned int i;
00254     struct entry *e, *f;
00255     struct entry **table = h->table;
00256     if (free_values)
00257     {
00258         for (i = 0; i < h->tablelength; i++)
00259         {
00260             e = table[i];
00261             while (NULL != e)
00262             { f = e; e = e->next; freekey(f->k); FREE(f->v); FREE(f); }
00263         }
00264     }
00265     else
00266     {
00267         for (i = 0; i < h->tablelength; i++)
00268         {
00269             e = table[i];
00270             while (NULL != e)
00271             { f = e; e = e->next; freekey(f->k); FREE(f); }
00272         }
00273     }
00274     FREE(h->table);
00275     FREE(h);
00276 }
00277 

Generated on Sun Mar 4 15:03:44 2007 for Scilab [trunk] by  doxygen 1.5.1