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