00001
00002
00003
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
00030
00031
00032
00033
00034
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
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
00075
00076
00077 #define MINSIZE 8
00078
00079
00080
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
00135 return -1;
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);
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:
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
00507 retval = (*func)(0, 0, arg, 1);
00508 return;
00509 }
00510
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