Main Page   Alphabetical List   Compound List   File List   Compound Members   File Members  

hashTable.c

Go to the documentation of this file.
00001 /*
00002 ** LCLint - annotation-assisted static program checker
00003 ** Copyright (C) 1994-2000 University of Virginia,
00004 **         Massachusetts Institute of Technology
00005 **
00006 ** This program is free software; you can redistribute it and/or modify it
00007 ** under the terms of the GNU General Public License as published by the
00008 ** Free Software Foundation; either version 2 of the License, or (at your
00009 ** option) any later version.
00010 ** 
00011 ** This program is distributed in the hope that it will be useful, but
00012 ** WITHOUT ANY WARRANTY; without even the implied warranty of
00013 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 ** General Public License for more details.
00015 ** 
00016 ** The GNU General Public License is available from http://www.gnu.org/ or
00017 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00018 ** MA 02111-1307, USA.
00019 **
00020 ** For information on lclint: lclint-request@cs.virginia.edu
00021 ** To report a bug: lclint-bug@cs.virginia.edu
00022 ** For more information: http://lclint.cs.virginia.edu
00023 */
00024 /*
00025 ** hashTable.c
00026 **
00027 ** Since hsearch defined in <search.h> only allows one hash table,
00028 ** I'll implement my own.
00029 **
00030 ** Try to find a decent hash function, etc. later...
00031 **
00032 ** hashTable is from string -> unsigned
00033 **
00034 */
00035 
00036 # include "lclintMacros.nf"
00037 # include "basic.h"
00038 
00039 /*@constant null hbucket hbucket_undefined; @*/
00040 # define hbucket_undefined 0
00041 
00042 static /*@truenull@*/ bool hbucket_isNull (/*@null@*/ hbucket h) 
00043 { 
00044   return (h == hbucket_undefined); 
00045 }
00046 
00047 static hentry
00048 hentry_create (cstring key, int val)
00049 {
00050   hentry h;
00051 
00052   h.key = key;
00053   h.val = val;
00054   return (h);
00055 }
00056 
00057 static bool
00058 hbucket_isEmpty (hbucket h)
00059 {
00060   return (h == hbucket_undefined || h->size == 0);
00061 }
00062 
00063 static cstring
00064 hbucket_unparse (hbucket h)
00065 {
00066   cstring s = cstring_undefined;
00067 
00068   if (!hbucket_isNull (h))
00069     {
00070       int i;
00071       
00072       for (i = 0; i < h->size; i++)
00073         {
00074           s = message ("%q %s:%d", s, h->entries[i].key, h->entries[i].val);
00075         }
00076     }
00077 
00078   return s;
00079 }
00080 
00081 static hbucket
00082 hbucket_single (hentry e)
00083 {
00084   hbucket h = (hbucket) dmalloc (sizeof (*h));
00085   
00086   h->size = 1;
00087   h->nspace = HBUCKET_BASESIZE - 1;
00088   h->entries = (hentry *) dmalloc (HBUCKET_BASESIZE * sizeof (*h->entries));
00089   h->entries[0] = e;
00090   
00091   return (h);
00092 }
00093 
00094 static void
00095 hbucket_grow (/*@notnull@*/ hbucket h)
00096 {
00097   int i;
00098   hentry *newentries; 
00099   
00100   h->nspace += HBUCKET_BASESIZE;
00101 
00102   newentries = (hentry *) 
00103     dmalloc ((h->size + HBUCKET_BASESIZE) * sizeof (*newentries));
00104   
00105   for (i = 0; i < h->size; i++) 
00106     {
00107       newentries[i] = h->entries[i]; 
00108     }
00109   
00110   sfree (h->entries);
00111   h->entries = newentries; 
00112 }
00113 
00114 static int hbucket_lookup (hbucket p_h, cstring p_key);
00115 
00116 /*
00117 ** bizarre duplicate behaviour
00118 ** required for duplicate entries
00119 */
00120 
00121 static void
00122 hbucket_add (/*@notnull@*/ hbucket h, hentry e)
00123 {
00124   int exloc = hbucket_lookup (h, e.key);
00125 
00126   
00127   if (exloc != HBUCKET_DNE)
00128     {
00129       h->entries[exloc].key = e.key;
00130       h->entries[exloc].val = e.val;
00131       return;
00132     }
00133 
00134   if (h->nspace == 0)
00135     {
00136             hbucket_grow (h);
00137     }
00138 
00139   h->entries[h->size] = e;
00140   h->size++;
00141   h->nspace--;
00142 }
00143 
00144 static int
00145 hbucket_ncollisions (hbucket h)
00146 {
00147   if (!hbucket_isNull (h) && (h->size > 1))
00148     return (h->size - 1);
00149   else
00150     return 0;
00151 }
00152 
00153 int
00154 hbucket_lookup (hbucket h, cstring key)
00155 {
00156   if (!hbucket_isNull (h))
00157     {
00158       int i;
00159       
00160       for (i = 0; i < h->size; i++)
00161         {
00162           if (cstring_equal (h->entries[i].key, key))
00163             {
00164               return h->entries[i].val;
00165             }
00166         }
00167     }
00168 
00169   return HBUCKET_DNE;
00170 }
00171 
00172 static
00173 void hbucket_free (/*@only@*/ hbucket h)
00174 {
00175   if (!hbucket_isNull (h))
00176     {
00177       sfree (h->entries);
00178       sfree (h);
00179     }
00180 }
00181 
00182 void 
00183 hashTable_free (/*@only@*/ hashTable h)
00184 {
00185   int i;
00186 
00187   for (i = 0; i < h->size; i++)
00188     {
00189       hbucket_free (h->buckets[i]);
00190     }
00191 
00192   sfree (h->buckets);
00193   sfree (h);
00194 }
00195   
00196 static int
00197 hashTable_countCollisions (hashTable h)
00198 {
00199   int nc = 0;
00200   int i;
00201 
00202   for (i = 0; i < h->size; i++)
00203     {
00204       nc += hbucket_ncollisions (h->buckets[i]);
00205     }
00206 
00207   return (nc);
00208 }
00209 
00210 
00211 static int
00212 hashTable_countEmpty (hashTable h)
00213 {
00214   int nc = 0;
00215   int i;
00216 
00217   for (i = 0; i < h->size; i++)
00218     {
00219       if (hbucket_isEmpty (h->buckets[i]))
00220         {
00221           nc++;
00222         }
00223     }
00224 
00225   return (nc);
00226 }
00227 
00228 /*
00229 ** hash function snarfed from quake/hash.c Hash_String
00230 ** by Stephen Harrison
00231 */
00232 
00233 /*
00234  * Here are 256 random numbers for use in the hash function
00235  */
00236 
00237 /*@+ignoresigns@*/
00238 static unsigned int RandomNumbers[256] =
00239 {
00240 #include "256_random_numbers.nf"
00241 };
00242 /*@=ignoresigns@*/
00243 
00244 static unsigned int 
00245 hashTable_hashValue (hashTable h, cstring key)
00246 {
00247   char *p;
00248   unsigned int hash_value = 0;
00249 
00250   for (p = cstring_toCharsSafe (key); *p != '\0'; p++)
00251     {
00252       hash_value = (hash_value << 1) ^ RandomNumbers[*p % 256];
00253     }
00254 
00255   return (hash_value % h->size);
00256 }
00257 
00258 static /*@exposed@*/ hbucket
00259 hashTable_hash (hashTable h, cstring key)
00260 {
00261   return h->buckets[hashTable_hashValue (h, key)];
00262 }
00263 
00264 
00265 /*@only@*/ hashTable
00266 hashTable_create (int size)
00267 {
00268   int i;
00269   hashTable h = (hashTable) dmalloc (sizeof (*h));
00270   
00271   h->size = size;
00272   h->nentries = 0;
00273   h->buckets = (hbucket *) dmalloc (sizeof (*h->buckets) * size);
00274   
00275   /*@+loopexec@*/
00276   for (i = 0; i < size; i++)
00277     {
00278       h->buckets[i] = hbucket_undefined;
00279     }
00280   /*@-loopexec@*/
00281   return h;
00282 }
00283 
00284 /*@-mustfree@*/
00285 static /*@unused@*/ void
00286 hashTable_print (hashTable h)
00287 {
00288   int i;
00289 
00290   for (i = 0; i < h->size; i++)
00291     {
00292       hbucket hb = h->buckets[i];
00293 
00294       if (hb != NULL)
00295         {
00296           llmsg (message ("%d. %s\n", i, hbucket_unparse (hb)));
00297         }
00298     }
00299 
00300   llmsg (message ("size: %d, collisions: %d, empty: %d", 
00301                   h->size, 
00302                   hashTable_countCollisions (h),
00303                   hashTable_countEmpty (h)));
00304 }
00305 /*@=mustfree@*/
00306 
00307 /*@only@*/ cstring
00308 hashTable_stats (hashTable h)
00309 {
00310   return (message ("size: %d, collisions: %d, empty: %d\n", 
00311                         h->size, hashTable_countCollisions (h),
00312                         hashTable_countEmpty (h)));
00313 }
00314 
00315 static void
00316 hashTable_addEntry (hashTable h, hentry e)
00317 {
00318   unsigned int hindex = hashTable_hashValue (h, e.key);
00319   /*
00320   ** using
00321   **   hbucket hb = h->buckets[hindex];  
00322   ** instead reveals a bug I don't want to deal with right now!
00323   */
00324 
00325   h->nentries++;
00326   
00327   if (hbucket_isNull (h->buckets[hindex]))
00328     {
00329       h->buckets[hindex] = hbucket_single (e); 
00330     }
00331   else
00332     {
00333       hbucket_add (h->buckets[hindex], e);
00334     }
00335 }
00336 
00337 void
00338 hashTable_insert (hashTable h, cstring key, int value)
00339 {
00340   unsigned int hindex;
00341   hbucket hb;
00342   hentry e;  
00343 
00344   
00345   h->nentries++;
00346 
00347   /*
00348   ** rehashing based (loosely) on code by Steve Harrison
00349   */
00350 
00351   if (h->nentries * 162 > h->size * 100) 
00352     {
00353       int i;
00354       int oldsize = h->size;
00355       int newsize = 1 + ((oldsize * 26244) / 10000); /* 26244 = 162^2 */
00356       hbucket *oldbuckets = h->buckets;
00357 
00358       
00359       h->size = newsize;  
00360       h->nentries = 0;
00361       h->buckets = (hbucket *) dmalloc (sizeof (*h->buckets) * newsize);
00362 
00363       /*@+loopexec@*/
00364       for (i = 0; i < newsize; i++)
00365         {
00366           h->buckets[i] = hbucket_undefined;
00367         }
00368       /*@=loopexec@*/
00369       
00370       for (i = 0; i < oldsize; i++)
00371         {
00372           hbucket bucket = oldbuckets[i];
00373           
00374           if (!hbucket_isNull (bucket))
00375             {
00376               int j;
00377 
00378               for (j = 0; j < bucket->size; j++)
00379                 {
00380                   hashTable_addEntry (h, bucket->entries[j]);
00381                 }
00382 
00383               sfree (bucket);
00384             }
00385         }
00386 
00387       sfree (oldbuckets);
00388     }
00389 
00390   
00391   e = hentry_create (key, value);
00392   hindex = hashTable_hashValue (h, key);
00393   hb = h->buckets[hindex];
00394   
00395   if (hbucket_isNull (hb))
00396     {
00397             h->buckets[hindex] = hbucket_single (e);
00398     }
00399   else
00400     {
00401             hbucket_add (hb, e);
00402     }
00403 
00404   }
00405 
00406 int
00407 hashTable_lookup (hashTable h, cstring key)
00408 {
00409   hbucket hb = hashTable_hash (h, key);
00410 
00411   return (hbucket_lookup (hb, key));
00412 }
00413 
00414 /*
00415 ** This is needed if oldkey is going to be released.
00416 */
00417 
00418 void
00419 hashTable_replaceKey (hashTable h, cstring oldkey, /*@dependent@*/ cstring newkey)
00420 {
00421   hbucket hb = hashTable_hash (h, oldkey);
00422 
00423   llassert (cstring_equal (oldkey, newkey));
00424 
00425   if (!hbucket_isNull (hb))
00426     {
00427       int i;
00428       
00429       for (i = 0; i < hb->size; i++)
00430         {
00431           if (cstring_equal (hb->entries[i].key, oldkey))
00432             {
00433               hb->entries[i].key = newkey;
00434               return;
00435             }
00436         }
00437     }
00438 
00439   llbug (message ("hashTable_replaceKey: %s not found", oldkey));
00440 }
00441 
00442 void
00443 hashTable_remove (hashTable h, cstring key)
00444 {
00445   hbucket hb = hashTable_hash (h, key);
00446 
00447   if (!hbucket_isNull (hb))
00448     {
00449       int i;
00450       
00451       for (i = 0; i < hb->size; i++)
00452         {
00453           if (cstring_equal (hb->entries[i].key, key))
00454             {
00455               if (i < hb->size - 1)
00456                 {
00457                   hb->entries[i] = hb->entries[hb->size - 1];
00458                 }
00459               
00460               hb->size--;
00461               return;
00462             }
00463         }
00464     }
00465 
00466   llbug (message ("hashTable_removeKey: %s not found", key));
00467 }
00468 
00469 

Generated at Fri Nov 3 18:57:41 2000 for LCLint by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000