00001
00006
00007
00008
00009
00010 #ifdef _MSC_VER
00011 # pragma warning(disable:4131)
00012 # pragma warning(disable:4127)
00013 # pragma warning(disable:4100)
00014 #endif
00015
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include "st.h"
00019
00020
00021 #include <malloc.h>
00022
00023
00024
00025
00026
00027 typedef struct st_table_entry st_table_entry;
00028
00029 struct st_table_entry {
00030 unsigned int hash;
00031 char *key;
00032 char *record;
00033 st_table_entry *next;
00034 };
00035
00036 #define ST_DEFAULT_MAX_DENSITY 5
00037 #define ST_DEFAULT_INIT_TABLE_SIZE 11
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 static int numcmp();
00049 static int numhash();
00050 static struct st_hash_type type_numhash = {
00051 numcmp,
00052 numhash,
00053 };
00054
00055 extern int strcmp();
00056 static int strhash();
00057 static struct st_hash_type type_strhash = {
00058 strcmp,
00059 strhash,
00060 };
00061
00062
00063 static void rehash();
00064
00065
00066
00067
00068 #define alloc(type) (type*)malloc((unsigned)sizeof(type))
00069 #define Calloc(n,s) (char*)calloc((n),(s))
00070
00071
00072 #define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y)) == 0)
00073
00074 #define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key))
00075 #define do_hash_bin(key,table) (do_hash(key, table)%(table)->num_bins)
00076
00077
00078
00079
00080
00081 #define MINSIZE 8
00082
00083
00084
00085
00086 static long primes[] = {
00087 8 + 3,
00088 16 + 3,
00089 32 + 5,
00090 64 + 3,
00091 128 + 3,
00092 256 + 27,
00093 512 + 9,
00094 1024 + 9,
00095 2048 + 5,
00096 4096 + 3,
00097 8192 + 27,
00098 16384 + 43,
00099 32768 + 3,
00100 65536 + 45,
00101 131072 + 29,
00102 262144 + 3,
00103 524288 + 21,
00104 1048576 + 7,
00105 2097152 + 17,
00106 4194304 + 15,
00107 8388608 + 9,
00108 16777216 + 43,
00109 33554432 + 35,
00110 67108864 + 15,
00111 134217728 + 29,
00112 268435456 + 3,
00113 536870912 + 11,
00114 1073741824 + 85,
00115 0
00116 };
00117
00118 static int
00119 new_size(size)
00120 int size;
00121 {
00122 int i;
00123
00124 #if 0
00125 for (i=3; i<31; i++) {
00126 if ((1<<i) > size) return 1<<i;
00127 }
00128 return -1;
00129 #else
00130 int newsize;
00131
00132 for (i = 0, newsize = MINSIZE;
00133 i < sizeof(primes)/sizeof(primes[0]);
00134 i++, newsize <<= 1)
00135 {
00136 if (newsize > size) return primes[i];
00137 }
00138
00139 return -1;
00140 #endif
00141 }
00142
00143 #ifdef HASH_LOG
00144 static int collision = 0;
00145 static int init_st = 0;
00146
00147 static void stat_col()
00148 {
00149 FILE *f = fopen("/tmp/col", "w");
00150 fprintf(f, "collision: %d\n", collision);
00151 fclose(f);
00152 }
00153 #endif
00154
00155 st_table* st_init_table_with_size( struct st_hash_type *type, 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);
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()
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()
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 char *key;
00254 char **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 char *key;
00293 char *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 char *key;
00315 char *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 char **key;
00395 char **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 char **key;
00437 char **value;
00438 char *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 char *key, *value, *never;
00467 {
00468 if (value == never) return ST_DELETE;
00469 return ST_CONTINUE;
00470 }
00471
00472 void
00473 st_cleanup_safe( st_table *table, char *never)
00474 {
00475 int num_entries = table->num_entries;
00476
00477 st_foreach(table, delete_never, never);
00478 table->num_entries = num_entries;
00479 }
00480
00481
00482 void st_foreach(st_table *table,int (*func)(),char *arg)
00483 {
00484 st_table_entry *ptr, *last, *tmp;
00485 enum st_retval retval;
00486 int i;
00487
00488 for(i = 0; i < table->num_bins; i++) {
00489 last = 0;
00490 for(ptr = table->bins[i]; ptr != 0;) {
00491 retval = (*func)(ptr->key, ptr->record, arg);
00492 switch (retval) {
00493 case ST_CONTINUE:
00494 last = ptr;
00495 ptr = ptr->next;
00496 break;
00497 case ST_STOP:
00498 return;
00499 case ST_DELETE:
00500 tmp = ptr;
00501 if (last == 0) {
00502 table->bins[i] = ptr->next;
00503 }
00504 else {
00505 last->next = ptr->next;
00506 }
00507 ptr = ptr->next;
00508 free(tmp);
00509 table->num_entries--;
00510 }
00511 }
00512 }
00513 }
00514
00515 static int
00516 strhash(string)
00517 register char *string;
00518 {
00519 register int c;
00520
00521 #ifdef HASH_ELFHASH
00522 register unsigned int h = 0, g;
00523
00524 while ((c = *string++) != '\0') {
00525 h = ( h << 4 ) + c;
00526 if ( g = h & 0xF0000000 )
00527 h ^= g >> 24;
00528 h &= ~g;
00529 }
00530 return h;
00531 #elif HASH_PERL
00532 register int val = 0;
00533
00534 while ((c = *string++) != '\0') {
00535 val = val*33 + c;
00536 }
00537
00538 return val + (val>>5);
00539 #else
00540 register int val = 0;
00541
00542 while ((c = *string++) != '\0') {
00543 val = val*997 + c;
00544 }
00545
00546 return val + (val>>5);
00547 #endif
00548 }
00549
00550 static int
00551 numcmp(x, y)
00552 long x, y;
00553 {
00554 return x != y;
00555 }
00556
00557 static int
00558 numhash(n)
00559 long n;
00560 {
00561 return n;
00562 }
00563
00564 #ifdef _MSC_VER
00565 # pragma warning(default:4131)
00566 # pragma warning(default:4127)
00567 # pragma warning(default:4100)
00568 #endif