00001
00020 #include "util.h"
00021 #include "st.h"
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #ifndef lint
00040 static char rcsid[] UNUSED = " $Id: st.c,v 1.8 2005/04/13 05:02:20 fabio Exp $";
00041 #endif
00042
00043
00044
00045
00046
00047 #define ST_NUMCMP(x,y) ((x) != (y))
00048
00049 #define ST_NUMHASH(x,size) (ABS((long)x)%(size))
00050
00051 #if SIZEOF_VOID_P == 8
00052 #define st_shift 3
00053 #else
00054 #define st_shift 2
00055 #endif
00056
00057 #define ST_PTRHASH(x,size) ((unsigned int)((unsigned long)(x)>>st_shift)%size)
00058
00059 #define EQUAL(func, x, y) \
00060 ((((func) == st_numcmp) || ((func) == st_ptrcmp)) ?\
00061 (ST_NUMCMP((x),(y)) == 0) : ((*func)((x), (y)) == 0))
00062
00063 #define do_hash(key, table)\
00064 ((int)((table->hash == st_ptrhash) ? ST_PTRHASH((char *)(key),(table)->num_bins) :\
00065 (table->hash == st_numhash) ? ST_NUMHASH((char *)(key), (table)->num_bins) :\
00066 (*table->hash)((char *)(key), (table)->num_bins)))
00067
00068 #define PTR_NOT_EQUAL(table, ptr, user_key)\
00069 (ptr != NIL(st_table_entry) && !EQUAL(table->compare, (char *)user_key, (ptr)->key))
00070
00071 #define FIND_ENTRY(table, hash_val, key, ptr, last) \
00072 (last) = &(table)->bins[hash_val];\
00073 (ptr) = *(last);\
00074 while (PTR_NOT_EQUAL((table), (ptr), (key))) {\
00075 (last) = &(ptr)->next; (ptr) = *(last);\
00076 }\
00077 if ((ptr) != NIL(st_table_entry) && (table)->reorder_flag) {\
00078 *(last) = (ptr)->next;\
00079 (ptr)->next = (table)->bins[hash_val];\
00080 (table)->bins[hash_val] = (ptr);\
00081 }
00082
00083
00084 #define ADD_DIRECT(table, key, value, hash_val, newt)\
00085 {\
00086 if (table->num_entries/table->num_bins >= table->max_density) {\
00087 rehash(table);\
00088 hash_val = do_hash(key,table);\
00089 }\
00090 \
00091 newt = ALLOC(st_table_entry, 1);\
00092 \
00093 newt->key = (char *)key;\
00094 newt->record = value;\
00095 newt->next = table->bins[hash_val];\
00096 table->bins[hash_val] = newt;\
00097 table->num_entries++;\
00098 }
00099
00102
00103
00104
00105
00106 static int rehash (st_table *);
00107
00111
00112
00113
00114
00162 st_table *
00163 st_init_table(ST_PFICPCP compare, ST_PFICPI hash)
00164 {
00165 return st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE,
00166 ST_DEFAULT_MAX_DENSITY,
00167 ST_DEFAULT_GROW_FACTOR,
00168 ST_DEFAULT_REORDER_FLAG);
00169
00170 }
00171
00172
00198 st_table *
00199 st_init_table_with_params(
00200 ST_PFICPCP compare,
00201 ST_PFICPI hash,
00202 int size,
00203 int density,
00204 double grow_factor,
00205 int reorder_flag)
00206 {
00207 int i;
00208 st_table *newt;
00209
00210 newt = ALLOC(st_table, 1);
00211 if (newt == NIL(st_table)) {
00212 return NIL(st_table);
00213 }
00214 newt->compare = compare;
00215 newt->hash = hash;
00216 newt->num_entries = 0;
00217 newt->max_density = density;
00218 newt->grow_factor = grow_factor;
00219 newt->reorder_flag = reorder_flag;
00220 if (size <= 0) {
00221 size = 1;
00222 }
00223 newt->num_bins = size;
00224 newt->bins = ALLOC(st_table_entry *, size);
00225 if (newt->bins == NIL(st_table_entry *)) {
00226 FREE(newt);
00227 return NIL(st_table);
00228 }
00229 for(i = 0; i < size; i++) {
00230 newt->bins[i] = 0;
00231 }
00232 return newt;
00233
00234 }
00235
00236
00251 void
00252 st_free_table(st_table *table)
00253 {
00254 st_table_entry *ptr, *next;
00255 int i;
00256
00257 for(i = 0; i < table->num_bins ; i++) {
00258 ptr = table->bins[i];
00259 while (ptr != NIL(st_table_entry)) {
00260 next = ptr->next;
00261 FREE(ptr);
00262 ptr = next;
00263 }
00264 }
00265 FREE(table->bins);
00266 FREE(table);
00267
00268 }
00269
00270
00285 int
00286 st_lookup(st_table *table, void *key, void *value)
00287 {
00288 int hash_val;
00289 st_table_entry *ptr, **last;
00290
00291 hash_val = do_hash(key, table);
00292
00293 FIND_ENTRY(table, hash_val, key, ptr, last);
00294
00295 if (ptr == NIL(st_table_entry)) {
00296 return 0;
00297 } else {
00298 if (value != NIL(void)) {
00299 *(char **)value = ptr->record;
00300 }
00301 return 1;
00302 }
00303
00304 }
00305
00306
00321 int
00322 st_lookup_int(st_table *table, void *key, int *value)
00323 {
00324 int hash_val;
00325 st_table_entry *ptr, **last;
00326
00327 hash_val = do_hash(key, table);
00328
00329 FIND_ENTRY(table, hash_val, key, ptr, last);
00330
00331 if (ptr == NIL(st_table_entry)) {
00332 return 0;
00333 } else {
00334 if (value != NIL(int)) {
00335 *value = (int) (long) ptr->record;
00336 }
00337 return 1;
00338 }
00339
00340 }
00341
00342
00357 int
00358 st_insert(st_table *table, void *key, void *value)
00359 {
00360 int hash_val;
00361 st_table_entry *newt;
00362 st_table_entry *ptr, **last;
00363
00364 hash_val = do_hash(key, table);
00365
00366 FIND_ENTRY(table, hash_val, key, ptr, last);
00367
00368 if (ptr == NIL(st_table_entry)) {
00369 if (table->num_entries/table->num_bins >= table->max_density) {
00370 if (rehash(table) == ST_OUT_OF_MEM) {
00371 return ST_OUT_OF_MEM;
00372 }
00373 hash_val = do_hash(key, table);
00374 }
00375 newt = ALLOC(st_table_entry, 1);
00376 if (newt == NIL(st_table_entry)) {
00377 return ST_OUT_OF_MEM;
00378 }
00379 newt->key = (char *)key;
00380 newt->record = (char *)value;
00381 newt->next = table->bins[hash_val];
00382 table->bins[hash_val] = newt;
00383 table->num_entries++;
00384 return 0;
00385 } else {
00386 ptr->record = (char *)value;
00387 return 1;
00388 }
00389
00390 }
00391
00392
00409 int
00410 st_add_direct(st_table *table, void *key, void *value)
00411 {
00412 int hash_val;
00413 st_table_entry *newt;
00414
00415 hash_val = do_hash(key, table);
00416 if (table->num_entries / table->num_bins >= table->max_density) {
00417 if (rehash(table) == ST_OUT_OF_MEM) {
00418 return ST_OUT_OF_MEM;
00419 }
00420 }
00421 hash_val = do_hash(key, table);
00422 newt = ALLOC(st_table_entry, 1);
00423 if (newt == NIL(st_table_entry)) {
00424 return ST_OUT_OF_MEM;
00425 }
00426 newt->key = (char *)key;
00427 newt->record = (char *)value;
00428 newt->next = table->bins[hash_val];
00429 table->bins[hash_val] = newt;
00430 table->num_entries++;
00431 return 1;
00432
00433 }
00434
00435
00487 int
00488 st_find_or_add(st_table *table, void *key, void *slot)
00489 {
00490 int hash_val;
00491 st_table_entry *newt, *ptr, **last;
00492
00493 hash_val = do_hash(key, table);
00494
00495 FIND_ENTRY(table, hash_val, key, ptr, last);
00496
00497 if (ptr == NIL(st_table_entry)) {
00498 if (table->num_entries / table->num_bins >= table->max_density) {
00499 if (rehash(table) == ST_OUT_OF_MEM) {
00500 return ST_OUT_OF_MEM;
00501 }
00502 hash_val = do_hash(key, table);
00503 }
00504 newt = ALLOC(st_table_entry, 1);
00505 if (newt == NIL(st_table_entry)) {
00506 return ST_OUT_OF_MEM;
00507 }
00508 newt->key = (char *)key;
00509 newt->record = (char *) 0;
00510 newt->next = table->bins[hash_val];
00511 table->bins[hash_val] = newt;
00512 table->num_entries++;
00513 if (slot != NIL(void)) *(char ***)slot = &newt->record;
00514 return 0;
00515 } else {
00516 if (slot != NIL(void)) *(char ***)slot = &ptr->record;
00517 return 1;
00518 }
00519
00520 }
00521
00522
00535 int
00536 st_find(st_table *table, void *key, void *slot)
00537 {
00538 int hash_val;
00539 st_table_entry *ptr, **last;
00540
00541 hash_val = do_hash(key, table);
00542
00543 FIND_ENTRY(table, hash_val, key, ptr, last);
00544
00545 if (ptr == NIL(st_table_entry)) {
00546 return 0;
00547 } else {
00548 if (slot != NIL(void)) {
00549 *(char ***)slot = &ptr->record;
00550 }
00551 return 1;
00552 }
00553
00554 }
00555
00556
00570 st_table *
00571 st_copy(st_table *old_table)
00572 {
00573 st_table *new_table;
00574 st_table_entry *ptr, *newptr, *next, *newt;
00575 int i, j, num_bins = old_table->num_bins;
00576
00577 new_table = ALLOC(st_table, 1);
00578 if (new_table == NIL(st_table)) {
00579 return NIL(st_table);
00580 }
00581
00582 *new_table = *old_table;
00583 new_table->bins = ALLOC(st_table_entry *, num_bins);
00584 if (new_table->bins == NIL(st_table_entry *)) {
00585 FREE(new_table);
00586 return NIL(st_table);
00587 }
00588 for(i = 0; i < num_bins ; i++) {
00589 new_table->bins[i] = NIL(st_table_entry);
00590 ptr = old_table->bins[i];
00591 while (ptr != NIL(st_table_entry)) {
00592 newt = ALLOC(st_table_entry, 1);
00593 if (newt == NIL(st_table_entry)) {
00594 for (j = 0; j <= i; j++) {
00595 newptr = new_table->bins[j];
00596 while (newptr != NIL(st_table_entry)) {
00597 next = newptr->next;
00598 FREE(newptr);
00599 newptr = next;
00600 }
00601 }
00602 FREE(new_table->bins);
00603 FREE(new_table);
00604 return NIL(st_table);
00605 }
00606 *newt = *ptr;
00607 newt->next = new_table->bins[i];
00608 new_table->bins[i] = newt;
00609 ptr = ptr->next;
00610 }
00611 }
00612 return new_table;
00613
00614 }
00615
00616
00633 int
00634 st_delete(st_table *table, void *keyp, void *value)
00635 {
00636 int hash_val;
00637 char *key = *(char **)keyp;
00638 st_table_entry *ptr, **last;
00639
00640 hash_val = do_hash(key, table);
00641
00642 FIND_ENTRY(table, hash_val, key, ptr ,last);
00643
00644 if (ptr == NIL(st_table_entry)) {
00645 return 0;
00646 }
00647
00648 *last = ptr->next;
00649 if (value != NIL(void)) *(char **)value = ptr->record;
00650 *(char **)keyp = ptr->key;
00651 FREE(ptr);
00652 table->num_entries--;
00653 return 1;
00654
00655 }
00656
00657
00674 int
00675 st_delete_int(st_table *table, void *keyp, int *value)
00676 {
00677 int hash_val;
00678 char *key = *(char **)keyp;
00679 st_table_entry *ptr, **last;
00680
00681 hash_val = do_hash(key, table);
00682
00683 FIND_ENTRY(table, hash_val, key, ptr ,last);
00684
00685 if (ptr == NIL(st_table_entry)) {
00686 return 0;
00687 }
00688
00689 *last = ptr->next;
00690 if (value != NIL(int)) *value = (int) (long) ptr->record;
00691 *(char **)keyp = ptr->key;
00692 FREE(ptr);
00693 table->num_entries--;
00694 return 1;
00695
00696 }
00697
00698
00724 int
00725 st_foreach(st_table *table, ST_PFSR func, char *arg)
00726 {
00727 st_table_entry *ptr, **last;
00728 enum st_retval retval;
00729 int i;
00730
00731 for(i = 0; i < table->num_bins; i++) {
00732 last = &table->bins[i]; ptr = *last;
00733 while (ptr != NIL(st_table_entry)) {
00734 retval = (*func)(ptr->key, ptr->record, arg);
00735 switch (retval) {
00736 case ST_CONTINUE:
00737 last = &ptr->next; ptr = *last;
00738 break;
00739 case ST_STOP:
00740 return 0;
00741 case ST_DELETE:
00742 *last = ptr->next;
00743 table->num_entries--;
00744 FREE(ptr);
00745 ptr = *last;
00746 }
00747 }
00748 }
00749 return 1;
00750
00751 }
00752
00753
00765 int
00766 st_strhash(char *string, int modulus)
00767 {
00768 int val = 0;
00769 int c;
00770
00771 while ((c = *string++) != '\0') {
00772 val = val*997 + c;
00773 }
00774
00775 return ((val < 0) ? -val : val)%modulus;
00776
00777 }
00778
00779
00791 int
00792 st_numhash(char *x, int size)
00793 {
00794 return ST_NUMHASH(x, size);
00795
00796 }
00797
00798
00810 int
00811 st_ptrhash(char *x, int size)
00812 {
00813 return ST_PTRHASH(x, size);
00814
00815 }
00816
00817
00829 int
00830 st_numcmp(const char *x, const char *y)
00831 {
00832 return ST_NUMCMP(x, y);
00833
00834 }
00835
00836
00848 int
00849 st_ptrcmp(const char *x, const char *y)
00850 {
00851 return ST_NUMCMP(x, y);
00852
00853 }
00854
00855
00869 st_generator *
00870 st_init_gen(st_table *table)
00871 {
00872 st_generator *gen;
00873
00874 gen = ALLOC(st_generator, 1);
00875 if (gen == NIL(st_generator)) {
00876 return NIL(st_generator);
00877 }
00878 gen->table = table;
00879 gen->entry = NIL(st_table_entry);
00880 gen->index = 0;
00881 return gen;
00882
00883 }
00884
00885
00907 int
00908 st_gen(st_generator *gen, void *key_p, void *value_p)
00909 {
00910 int i;
00911
00912 if (gen->entry == NIL(st_table_entry)) {
00913
00914 for(i = gen->index; i < gen->table->num_bins; i++) {
00915 if (gen->table->bins[i] != NIL(st_table_entry)) {
00916 gen->index = i+1;
00917 gen->entry = gen->table->bins[i];
00918 break;
00919 }
00920 }
00921 if (gen->entry == NIL(st_table_entry)) {
00922 return 0;
00923 }
00924 }
00925 *(char **)key_p = gen->entry->key;
00926 if (value_p != NIL(void)) {
00927 *(char **)value_p = gen->entry->record;
00928 }
00929 gen->entry = gen->entry->next;
00930 return 1;
00931
00932 }
00933
00934
00952 int
00953 st_gen_int(st_generator *gen, void *key_p, int *value_p)
00954 {
00955 int i;
00956
00957 if (gen->entry == NIL(st_table_entry)) {
00958
00959 for(i = gen->index; i < gen->table->num_bins; i++) {
00960 if (gen->table->bins[i] != NIL(st_table_entry)) {
00961 gen->index = i+1;
00962 gen->entry = gen->table->bins[i];
00963 break;
00964 }
00965 }
00966 if (gen->entry == NIL(st_table_entry)) {
00967 return 0;
00968 }
00969 }
00970 *(char **)key_p = gen->entry->key;
00971 if (value_p != NIL(int)) {
00972 *value_p = (int) (long) gen->entry->record;
00973 }
00974 gen->entry = gen->entry->next;
00975 return 1;
00976
00977 }
00978
00979
00993 void
00994 st_free_gen(st_generator *gen)
00995 {
00996 FREE(gen);
00997
00998 }
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01020 static int
01021 rehash(st_table *table)
01022 {
01023 st_table_entry *ptr, *next, **old_bins;
01024 int i, old_num_bins, hash_val, old_num_entries;
01025
01026
01027 old_bins = table->bins;
01028 old_num_bins = table->num_bins;
01029 old_num_entries = table->num_entries;
01030
01031
01032 table->num_bins = (int) (table->grow_factor * old_num_bins);
01033 if (table->num_bins % 2 == 0) {
01034 table->num_bins += 1;
01035 }
01036 table->num_entries = 0;
01037 table->bins = ALLOC(st_table_entry *, table->num_bins);
01038 if (table->bins == NIL(st_table_entry *)) {
01039 table->bins = old_bins;
01040 table->num_bins = old_num_bins;
01041 table->num_entries = old_num_entries;
01042 return ST_OUT_OF_MEM;
01043 }
01044
01045 for (i = 0; i < table->num_bins; i++) {
01046 table->bins[i] = 0;
01047 }
01048
01049
01050 for (i = 0; i < old_num_bins; i++) {
01051 ptr = old_bins[i];
01052 while (ptr != NIL(st_table_entry)) {
01053 next = ptr->next;
01054 hash_val = do_hash(ptr->key, table);
01055 ptr->next = table->bins[hash_val];
01056 table->bins[hash_val] = ptr;
01057 table->num_entries++;
01058 ptr = next;
01059 }
01060 }
01061 FREE(old_bins);
01062
01063 return 1;
01064
01065 }