Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals  

st.c

Go to the documentation of this file.
00001 /* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */
00002 
00003 /* static       char    sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; */
00004 
00005 #include "config.h"
00006 #include "defines.h"
00007 #include <stdio.h>
00008 #include <stdlib.h>
00009 #include <string.h>
00010 #include "st.h"
00011 
00012 #ifdef _WIN32
00013 #include <malloc.h>
00014 #endif
00015 
00016 typedef struct st_table_entry st_table_entry;
00017 
00018 struct st_table_entry {
00019     unsigned int hash;
00020     st_data_t key;
00021     st_data_t record;
00022     st_table_entry *next;
00023 };
00024 
00025 #define ST_DEFAULT_MAX_DENSITY 5
00026 #define ST_DEFAULT_INIT_TABLE_SIZE 11
00027 
00028     /*
00029      * DEFAULT_MAX_DENSITY is the default for the largest we allow the
00030      * average number of items per bin before increasing the number of
00031      * bins
00032      *
00033      * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins
00034      * allocated initially
00035      *
00036      */
00037 static int numcmp(long, long);
00038 static int numhash(long);
00039 static struct st_hash_type type_numhash = {
00040     numcmp,
00041     numhash,
00042 };
00043 
00044 /* extern int strcmp(const char *, const char *); */
00045 static int strhash(const char *);
00046 static struct st_hash_type type_strhash = {
00047     strcmp,
00048     strhash,
00049 };
00050 
00051 #ifdef RUBY_PLATFORM
00052 #define xmalloc ruby_xmalloc
00053 #define xcalloc ruby_xcalloc
00054 #define xrealloc ruby_xrealloc
00055 #define xfree ruby_xfree
00056 
00057 void *xmalloc(long);
00058 void *xcalloc(long, long);
00059 void *xrealloc(void *, long);
00060 void xfree(void *);
00061 #endif
00062 
00063 static void rehash(st_table *);
00064 
00065 #define alloc(type) (type*)xmalloc((unsigned)sizeof(type))
00066 #define Calloc(n,s) (char*)xcalloc((n),(s))
00067 
00068 #define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y)) == 0)
00069 
00070 #define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key))
00071 #define do_hash_bin(key,table) (do_hash(key, table)%(table)->num_bins)
00072 
00073 /*
00074  * MINSIZE is the minimum size of a dictionary.
00075  */
00076 
00077 #define MINSIZE 8
00078 
00079 /*
00080 Table of prime numbers 2^n+a, 2<=n<=30.
00081 */
00082 static long primes[] = {
00083         8 + 3,
00084         16 + 3,
00085         32 + 5,
00086         64 + 3,
00087         128 + 3,
00088         256 + 27,
00089         512 + 9,
00090         1024 + 9,
00091         2048 + 5,
00092         4096 + 3,
00093         8192 + 27,
00094         16384 + 43,
00095         32768 + 3,
00096         65536 + 45,
00097         131072 + 29,
00098         262144 + 3,
00099         524288 + 21,
00100         1048576 + 7,
00101         2097152 + 17,
00102         4194304 + 15,
00103         8388608 + 9,
00104         16777216 + 43,
00105         33554432 + 35,
00106         67108864 + 15,
00107         134217728 + 29,
00108         268435456 + 3,
00109         536870912 + 11,
00110         1073741824 + 85,
00111         0
00112 };
00113 
00114 static int
00115 new_size(size)
00116     int size;
00117 {
00118     int i;
00119 
00120 #if 0
00121     for (i=3; i<31; i++) {
00122         if ((1<<i) > size) return 1<<i;
00123     }
00124     return -1;
00125 #else
00126     int newsize;
00127 
00128     for (i = 0, newsize = MINSIZE;
00129          i < sizeof(primes)/sizeof(primes[0]);
00130          i++, newsize <<= 1)
00131     {
00132         if (newsize > size) return primes[i];
00133     }
00134     /* Ran out of polynomials */
00135     return -1;                  /* should raise exception */
00136 #endif
00137 }
00138 
00139 #ifdef HASH_LOG
00140 static int collision = 0;
00141 static int init_st = 0;
00142 
00143 static void
00144 stat_col()
00145 {
00146     FILE *f = fopen("/tmp/col", "w");
00147     fprintf(f, "collision: %d\n", collision);
00148     fclose(f);
00149 }
00150 #endif
00151 
00152 st_table*
00153 st_init_table_with_size(type, size)
00154     struct st_hash_type *type;
00155     int size;
00156 {
00157     st_table *tbl;
00158 
00159 #ifdef HASH_LOG
00160     if (init_st == 0) {
00161         init_st = 1;
00162         atexit(stat_col);
00163     }
00164 #endif
00165 
00166     size = new_size(size);      /* round up to prime number */
00167 
00168     tbl = alloc(st_table);
00169     tbl->type = type;
00170     tbl->num_entries = 0;
00171     tbl->num_bins = size;
00172     tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*));
00173 
00174     return tbl;
00175 }
00176 
00177 st_table*
00178 st_init_table(type)
00179     struct st_hash_type *type;
00180 {
00181     return st_init_table_with_size(type, 0);
00182 }
00183 
00184 st_table*
00185 st_init_numtable(void)
00186 {
00187     return st_init_table(&type_numhash);
00188 }
00189 
00190 st_table*
00191 st_init_numtable_with_size(size)
00192     int size;
00193 {
00194     return st_init_table_with_size(&type_numhash, size);
00195 }
00196 
00197 st_table*
00198 st_init_strtable(void)
00199 {
00200     return st_init_table(&type_strhash);
00201 }
00202 
00203 st_table*
00204 st_init_strtable_with_size(size)
00205     int size;
00206 {
00207     return st_init_table_with_size(&type_strhash, size);
00208 }
00209 
00210 void
00211 st_free_table(table)
00212     st_table *table;
00213 {
00214     register st_table_entry *ptr, *next;
00215     int i;
00216 
00217     for(i = 0; i < table->num_bins; i++) {
00218         ptr = table->bins[i];
00219         while (ptr != 0) {
00220             next = ptr->next;
00221             free(ptr);
00222             ptr = next;
00223         }
00224     }
00225     free(table->bins);
00226     free(table);
00227 }
00228 
00229 #define PTR_NOT_EQUAL(table, ptr, hash_val, key) \
00230 ((ptr) != 0 && (ptr->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key)))
00231 
00232 #ifdef HASH_LOG
00233 #define COLLISION collision++
00234 #else
00235 #define COLLISION
00236 #endif
00237 
00238 #define FIND_ENTRY(table, ptr, hash_val, bin_pos) do {\
00239     bin_pos = hash_val%(table)->num_bins;\
00240     ptr = (table)->bins[bin_pos];\
00241     if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {\
00242         COLLISION;\
00243         while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {\
00244             ptr = ptr->next;\
00245         }\
00246         ptr = ptr->next;\
00247     }\
00248 } while (0)
00249 
00250 int
00251 st_lookup(table, key, value)
00252     st_table *table;
00253     register st_data_t key;
00254     st_data_t *value;
00255 {
00256     unsigned int hash_val, bin_pos;
00257     register st_table_entry *ptr;
00258 
00259     hash_val = do_hash(key, table);
00260     FIND_ENTRY(table, ptr, hash_val, bin_pos);
00261 
00262     if (ptr == 0) {
00263         return 0;
00264     }
00265     else {
00266         if (value != 0)  *value = ptr->record;
00267         return 1;
00268     }
00269 }
00270 
00271 #define ADD_DIRECT(table, key, value, hash_val, bin_pos)\
00272 do {\
00273     st_table_entry *entry;\
00274     if (table->num_entries/(table->num_bins) > ST_DEFAULT_MAX_DENSITY) {\
00275         rehash(table);\
00276         bin_pos = hash_val % table->num_bins;\
00277     }\
00278     \
00279     entry = alloc(st_table_entry);\
00280     \
00281     entry->hash = hash_val;\
00282     entry->key = key;\
00283     entry->record = value;\
00284     entry->next = table->bins[bin_pos];\
00285     table->bins[bin_pos] = entry;\
00286     table->num_entries++;\
00287 } while (0)
00288 
00289 int
00290 st_insert(table, key, value)
00291     register st_table *table;
00292     register st_data_t key;
00293     st_data_t value;
00294 {
00295     unsigned int hash_val, bin_pos;
00296     register st_table_entry *ptr;
00297 
00298     hash_val = do_hash(key, table);
00299     FIND_ENTRY(table, ptr, hash_val, bin_pos);
00300 
00301     if (ptr == 0) {
00302         ADD_DIRECT(table, key, value, hash_val, bin_pos);
00303         return 0;
00304     }
00305     else {
00306         ptr->record = value;
00307         return 1;
00308     }
00309 }
00310 
00311 void
00312 st_add_direct(table, key, value)
00313     st_table *table;
00314     st_data_t key;
00315     st_data_t value;
00316 {
00317     unsigned int hash_val, bin_pos;
00318 
00319     hash_val = do_hash(key, table);
00320     bin_pos = hash_val % table->num_bins;
00321     ADD_DIRECT(table, key, value, hash_val, bin_pos);
00322 }
00323 
00324 static void
00325 rehash(table)
00326     register st_table *table;
00327 {
00328     register st_table_entry *ptr, *next, **new_bins;
00329     int i, old_num_bins = table->num_bins, new_num_bins;
00330     unsigned int hash_val;
00331 
00332     new_num_bins = new_size(old_num_bins+1);
00333     new_bins = (st_table_entry**)Calloc(new_num_bins, sizeof(st_table_entry*));
00334 
00335     for(i = 0; i < old_num_bins; i++) {
00336         ptr = table->bins[i];
00337         while (ptr != 0) {
00338             next = ptr->next;
00339             hash_val = ptr->hash % new_num_bins;
00340             ptr->next = new_bins[hash_val];
00341             new_bins[hash_val] = ptr;
00342             ptr = next;
00343         }
00344     }
00345     free(table->bins);
00346     table->num_bins = new_num_bins;
00347     table->bins = new_bins;
00348 }
00349 
00350 st_table*
00351 st_copy(old_table)
00352     st_table *old_table;
00353 {
00354     st_table *new_table;
00355     st_table_entry *ptr, *entry;
00356     int i, num_bins = old_table->num_bins;
00357 
00358     new_table = alloc(st_table);
00359     if (new_table == 0) {
00360         return 0;
00361     }
00362 
00363     *new_table = *old_table;
00364     new_table->bins = (st_table_entry**)
00365         Calloc((unsigned)num_bins, sizeof(st_table_entry*));
00366 
00367     if (new_table->bins == 0) {
00368         free(new_table);
00369         return 0;
00370     }
00371 
00372     for(i = 0; i < num_bins; i++) {
00373         new_table->bins[i] = 0;
00374         ptr = old_table->bins[i];
00375         while (ptr != 0) {
00376             entry = alloc(st_table_entry);
00377             if (entry == 0) {
00378                 free(new_table->bins);
00379                 free(new_table);
00380                 return 0;
00381             }
00382             *entry = *ptr;
00383             entry->next = new_table->bins[i];
00384             new_table->bins[i] = entry;
00385             ptr = ptr->next;
00386         }
00387     }
00388     return new_table;
00389 }
00390 
00391 int
00392 st_delete(table, key, value)
00393     register st_table *table;
00394     register st_data_t *key;
00395     st_data_t *value;
00396 {
00397     unsigned int hash_val;
00398     st_table_entry *tmp;
00399     register st_table_entry *ptr;
00400 
00401     hash_val = do_hash_bin(*key, table);
00402     ptr = table->bins[hash_val];
00403 
00404     if (ptr == 0) {
00405         if (value != 0) *value = 0;
00406         return 0;
00407     }
00408 
00409     if (EQUAL(table, *key, ptr->key)) {
00410         table->bins[hash_val] = ptr->next;
00411         table->num_entries--;
00412         if (value != 0) *value = ptr->record;
00413         *key = ptr->key;
00414         free(ptr);
00415         return 1;
00416     }
00417 
00418     for(; ptr->next != 0; ptr = ptr->next) {
00419         if (EQUAL(table, ptr->next->key, *key)) {
00420             tmp = ptr->next;
00421             ptr->next = ptr->next->next;
00422             table->num_entries--;
00423             if (value != 0) *value = tmp->record;
00424             *key = tmp->key;
00425             free(tmp);
00426             return 1;
00427         }
00428     }
00429 
00430     return 0;
00431 }
00432 
00433 int
00434 st_delete_safe(table, key, value, never)
00435     register st_table *table;
00436     register st_data_t *key;
00437     st_data_t *value;
00438     st_data_t never;
00439 {
00440     unsigned int hash_val;
00441     register st_table_entry *ptr;
00442 
00443     hash_val = do_hash_bin(*key, table);
00444     ptr = table->bins[hash_val];
00445 
00446     if (ptr == 0) {
00447         if (value != 0) *value = 0;
00448         return 0;
00449     }
00450 
00451     for(; ptr != 0; ptr = ptr->next) {
00452         if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) {
00453             table->num_entries--;
00454             *key = ptr->key;
00455             if (value != 0) *value = ptr->record;
00456             ptr->key = ptr->record = never;
00457             return 1;
00458         }
00459     }
00460 
00461     return 0;
00462 }
00463 
00464 static int
00465 delete_never(key, value, never)
00466     st_data_t key, value, never;
00467 {
00468     if (value == never) return ST_DELETE;
00469     return ST_CONTINUE;
00470 }
00471 
00472 void
00473 st_cleanup_safe(table, never)
00474     st_table *table;
00475     st_data_t never;
00476 {
00477     int num_entries = table->num_entries;
00478 
00479     st_foreach(table, delete_never, never);
00480     table->num_entries = num_entries;
00481 }
00482 
00483 void
00484 st_foreach(table, func, arg)
00485     st_table *table;
00486     int (*func)();
00487     st_data_t arg;
00488 {
00489     st_table_entry *ptr, *last, *tmp;
00490     enum st_retval retval;
00491     int i;
00492 
00493     for(i = 0; i < table->num_bins; i++) {
00494         last = 0;
00495         for(ptr = table->bins[i]; ptr != 0;) {
00496             retval = (*func)(ptr->key, ptr->record, arg, 0);
00497             switch (retval) {
00498             case ST_CHECK:      /* check if hash is modified during iteration */
00499                 tmp = 0;
00500                 if (i < table->num_bins) {
00501                     for (tmp = table->bins[i]; tmp; tmp=tmp->next) {
00502                         if (tmp == ptr) break;
00503                     }
00504                 }
00505                 if (!tmp) {
00506                     /* call func with error notice */
00507                     retval = (*func)(0, 0, arg, 1);
00508                     return;
00509                 }
00510                 /* fall through */
00511             case ST_CONTINUE:
00512                 last = ptr;
00513                 ptr = ptr->next;
00514                 break;
00515             case ST_STOP:
00516                 return;
00517             case ST_DELETE:
00518                 tmp = ptr;
00519                 if (last == 0) {
00520                     table->bins[i] = ptr->next;
00521                 }
00522                 else {
00523                     last->next = ptr->next;
00524                 }
00525                 ptr = ptr->next;
00526                 free(tmp);
00527                 table->num_entries--;
00528             }
00529         }
00530     }
00531 }
00532 
00533 static int
00534 strhash(string)
00535     register const char *string;
00536 {
00537     register int c;
00538 
00539 #ifdef HASH_ELFHASH
00540     register unsigned int h = 0, g;
00541 
00542     while ((c = *string++) != '\0') {
00543         h = ( h << 4 ) + c;
00544         if ( g = h & 0xF0000000 )
00545             h ^= g >> 24;
00546         h &= ~g;
00547     }
00548     return h;
00549 #elif HASH_PERL
00550     register int val = 0;
00551 
00552     while ((c = *string++) != '\0') {
00553         val += c;
00554         val += (val << 10);
00555         val ^= (val >> 6);
00556     }
00557     val += (val << 3);
00558     val ^= (val >> 11);
00559 
00560     return val + (val << 15);
00561 #else
00562     register int val = 0;
00563 
00564     while ((c = *string++) != '\0') {
00565         val = val*997 + c;
00566     }
00567 
00568     return val + (val>>5);
00569 #endif
00570 }
00571 
00572 static int
00573 numcmp(x, y)
00574     long x, y;
00575 {
00576     return x != y;
00577 }
00578 
00579 static int
00580 numhash(n)
00581     long n;
00582 {
00583     return n;
00584 }
00585 

Generated on Sat Jan 22 14:29:39 2005 for Ruby by doxygen1.2.18