00001
00002
00003
00004
00005 #ifdef NOT_RUBY
00006 #include "regint.h"
00007 #include "st.h"
00008 #else
00009 #include "ruby/ruby.h"
00010 #endif
00011
00012 #include <stdio.h>
00013 #ifdef HAVE_STDLIB_H
00014 #include <stdlib.h>
00015 #endif
00016 #include <string.h>
00017
00018 typedef struct st_table_entry st_table_entry;
00019
00020 struct st_table_entry {
00021 st_index_t hash;
00022 st_data_t key;
00023 st_data_t record;
00024 st_table_entry *next;
00025 st_table_entry *fore, *back;
00026 };
00027
00028 typedef struct st_packed_entry {
00029 st_index_t hash;
00030 st_data_t key, val;
00031 } st_packed_entry;
00032
00033 #define STATIC_ASSERT(name, expr) typedef int static_assert_##name##_check[(expr) ? 1 : -1];
00034
00035 #define ST_DEFAULT_MAX_DENSITY 5
00036 #define ST_DEFAULT_INIT_TABLE_SIZE 11
00037 #define ST_DEFAULT_SECOND_TABLE_SIZE 19
00038 #define ST_DEFAULT_PACKED_TABLE_SIZE 18
00039 #define PACKED_UNIT (int)(sizeof(st_packed_entry) / sizeof(st_table_entry*))
00040 #define MAX_PACKED_HASH (int)(ST_DEFAULT_PACKED_TABLE_SIZE * sizeof(st_table_entry*) / sizeof(st_packed_entry))
00041
00042 STATIC_ASSERT(st_packed_entry, sizeof(st_packed_entry) == sizeof(st_table_entry*[PACKED_UNIT]))
00043 STATIC_ASSERT(st_packed_bins, sizeof(st_packed_entry[MAX_PACKED_HASH]) <= sizeof(st_table_entry*[ST_DEFAULT_PACKED_TABLE_SIZE]))
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055 #define type_numhash st_hashtype_num
00056 const struct st_hash_type st_hashtype_num = {
00057 st_numcmp,
00058 st_numhash,
00059 };
00060
00061
00062 static st_index_t strhash(st_data_t);
00063 static const struct st_hash_type type_strhash = {
00064 strcmp,
00065 strhash,
00066 };
00067
00068 static st_index_t strcasehash(st_data_t);
00069 static const struct st_hash_type type_strcasehash = {
00070 st_strcasecmp,
00071 strcasehash,
00072 };
00073
00074 static void rehash(st_table *);
00075
00076 #ifdef RUBY
00077 #define malloc xmalloc
00078 #define calloc xcalloc
00079 #define realloc xrealloc
00080 #define free(x) xfree(x)
00081 #endif
00082
00083 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
00084
00085 #define EQUAL(table,x,y) ((x)==(y) || (*(table)->type->compare)((x),(y)) == 0)
00086
00087 #define do_hash(key,table) (st_index_t)(*(table)->type->hash)((key))
00088 #define do_hash_bin(key,table) (do_hash((key), (table))%(table)->num_bins)
00089
00090
00091 #define st_alloc_entry() (st_table_entry *)malloc(sizeof(st_table_entry))
00092 #define st_free_entry(entry) free(entry)
00093 #define st_alloc_table() (st_table *)malloc(sizeof(st_table))
00094 #define st_dealloc_table(table) free(table)
00095 #define st_alloc_bins(size) (st_table_entry **)calloc(size, sizeof(st_table_entry *))
00096 #define st_free_bins(bins, size) free(bins)
00097 static inline st_table_entry**
00098 st_realloc_bins(st_table_entry **bins, st_index_t newsize, st_index_t oldsize)
00099 {
00100 bins = (st_table_entry **)realloc(bins, newsize * sizeof(st_table_entry *));
00101 MEMZERO(bins, st_table_entry*, newsize);
00102 return bins;
00103 }
00104
00105
00106 #define bins as.big.bins
00107 #define head as.big.head
00108 #define tail as.big.tail
00109 #define real_entries as.packed.real_entries
00110
00111
00112 #define PACKED_BINS(table) ((table)->as.packed.entries)
00113 #define PACKED_ENT(table, i) PACKED_BINS(table)[i]
00114 #define PKEY(table, i) PACKED_ENT((table), (i)).key
00115 #define PVAL(table, i) PACKED_ENT((table), (i)).val
00116 #define PHASH(table, i) PACKED_ENT((table), (i)).hash
00117 #define PKEY_SET(table, i, v) (PKEY((table), (i)) = (v))
00118 #define PVAL_SET(table, i, v) (PVAL((table), (i)) = (v))
00119 #define PHASH_SET(table, i, v) (PHASH((table), (i)) = (v))
00120
00121
00122 static inline void
00123 remove_packed_entry(st_table *table, st_index_t i)
00124 {
00125 table->real_entries--;
00126 table->num_entries--;
00127 if (i < table->real_entries) {
00128 MEMMOVE(&PACKED_ENT(table, i), &PACKED_ENT(table, i+1),
00129 st_packed_entry, table->real_entries - i);
00130 }
00131 }
00132
00133 static inline void
00134 remove_safe_packed_entry(st_table *table, st_index_t i, st_data_t never)
00135 {
00136 table->num_entries--;
00137 PKEY_SET(table, i, never);
00138 PVAL_SET(table, i, never);
00139 PHASH_SET(table, i, 0);
00140 }
00141
00142
00143
00144
00145
00146 #define MINSIZE 8
00147
00148
00149
00150
00151 static const unsigned int primes[] = {
00152 ST_DEFAULT_INIT_TABLE_SIZE,
00153 ST_DEFAULT_SECOND_TABLE_SIZE,
00154 32 + 5,
00155 64 + 3,
00156 128 + 3,
00157 256 + 27,
00158 512 + 9,
00159 1024 + 9,
00160 2048 + 5,
00161 4096 + 3,
00162 8192 + 27,
00163 16384 + 43,
00164 32768 + 3,
00165 65536 + 45,
00166 131072 + 29,
00167 262144 + 3,
00168 524288 + 21,
00169 1048576 + 7,
00170 2097152 + 17,
00171 4194304 + 15,
00172 8388608 + 9,
00173 16777216 + 43,
00174 33554432 + 35,
00175 67108864 + 15,
00176 134217728 + 29,
00177 268435456 + 3,
00178 536870912 + 11,
00179 1073741824 + 85,
00180 0
00181 };
00182
00183 static st_index_t
00184 new_size(st_index_t size)
00185 {
00186 int i;
00187
00188 #if 0
00189 for (i=3; i<31; i++) {
00190 if ((1<<i) > size) return 1<<i;
00191 }
00192 return -1;
00193 #else
00194 st_index_t newsize;
00195
00196 for (i = 0, newsize = MINSIZE; i < numberof(primes); i++, newsize <<= 1) {
00197 if (newsize > size) return primes[i];
00198 }
00199
00200 #ifndef NOT_RUBY
00201 rb_raise(rb_eRuntimeError, "st_table too big");
00202 #endif
00203 return -1;
00204 #endif
00205 }
00206
00207 #ifdef HASH_LOG
00208 #ifdef HAVE_UNISTD_H
00209 #include <unistd.h>
00210 #endif
00211 static struct {
00212 int all, total, num, str, strcase;
00213 } collision;
00214 static int init_st = 0;
00215
00216 static void
00217 stat_col(void)
00218 {
00219 char fname[10+sizeof(long)*3];
00220 FILE *f = fopen((snprintf(fname, sizeof(fname), "/tmp/col%ld", (long)getpid()), fname), "w");
00221 fprintf(f, "collision: %d / %d (%6.2f)\n", collision.all, collision.total,
00222 ((double)collision.all / (collision.total)) * 100);
00223 fprintf(f, "num: %d, str: %d, strcase: %d\n", collision.num, collision.str, collision.strcase);
00224 fclose(f);
00225 }
00226 #endif
00227
00228 st_table*
00229 st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
00230 {
00231 st_table *tbl;
00232
00233 #ifdef HASH_LOG
00234 # if HASH_LOG+0 < 0
00235 {
00236 const char *e = getenv("ST_HASH_LOG");
00237 if (!e || !*e) init_st = 1;
00238 }
00239 # endif
00240 if (init_st == 0) {
00241 init_st = 1;
00242 atexit(stat_col);
00243 }
00244 #endif
00245
00246
00247 tbl = st_alloc_table();
00248 tbl->type = type;
00249 tbl->num_entries = 0;
00250 tbl->entries_packed = size <= MAX_PACKED_HASH;
00251 if (tbl->entries_packed) {
00252 size = ST_DEFAULT_PACKED_TABLE_SIZE;
00253 }
00254 else {
00255 size = new_size(size);
00256 }
00257 tbl->num_bins = size;
00258 tbl->bins = st_alloc_bins(size);
00259 tbl->head = 0;
00260 tbl->tail = 0;
00261
00262 return tbl;
00263 }
00264
00265 st_table*
00266 st_init_table(const struct st_hash_type *type)
00267 {
00268 return st_init_table_with_size(type, 0);
00269 }
00270
00271 st_table*
00272 st_init_numtable(void)
00273 {
00274 return st_init_table(&type_numhash);
00275 }
00276
00277 st_table*
00278 st_init_numtable_with_size(st_index_t size)
00279 {
00280 return st_init_table_with_size(&type_numhash, size);
00281 }
00282
00283 st_table*
00284 st_init_strtable(void)
00285 {
00286 return st_init_table(&type_strhash);
00287 }
00288
00289 st_table*
00290 st_init_strtable_with_size(st_index_t size)
00291 {
00292 return st_init_table_with_size(&type_strhash, size);
00293 }
00294
00295 st_table*
00296 st_init_strcasetable(void)
00297 {
00298 return st_init_table(&type_strcasehash);
00299 }
00300
00301 st_table*
00302 st_init_strcasetable_with_size(st_index_t size)
00303 {
00304 return st_init_table_with_size(&type_strcasehash, size);
00305 }
00306
00307 void
00308 st_clear(st_table *table)
00309 {
00310 register st_table_entry *ptr, *next;
00311 st_index_t i;
00312
00313 if (table->entries_packed) {
00314 table->num_entries = 0;
00315 table->real_entries = 0;
00316 return;
00317 }
00318
00319 for (i = 0; i < table->num_bins; i++) {
00320 ptr = table->bins[i];
00321 table->bins[i] = 0;
00322 while (ptr != 0) {
00323 next = ptr->next;
00324 st_free_entry(ptr);
00325 ptr = next;
00326 }
00327 }
00328 table->num_entries = 0;
00329 table->head = 0;
00330 table->tail = 0;
00331 }
00332
00333 void
00334 st_free_table(st_table *table)
00335 {
00336 st_clear(table);
00337 st_free_bins(table->bins, table->num_bins);
00338 st_dealloc_table(table);
00339 }
00340
00341 size_t
00342 st_memsize(const st_table *table)
00343 {
00344 if (table->entries_packed) {
00345 return table->num_bins * sizeof (void *) + sizeof(st_table);
00346 }
00347 else {
00348 return table->num_entries * sizeof(struct st_table_entry) + table->num_bins * sizeof (void *) + sizeof(st_table);
00349 }
00350 }
00351
00352 #define PTR_NOT_EQUAL(table, ptr, hash_val, key) \
00353 ((ptr) != 0 && ((ptr)->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key)))
00354
00355 #ifdef HASH_LOG
00356 static void
00357 count_collision(const struct st_hash_type *type)
00358 {
00359 collision.all++;
00360 if (type == &type_numhash) {
00361 collision.num++;
00362 }
00363 else if (type == &type_strhash) {
00364 collision.strcase++;
00365 }
00366 else if (type == &type_strcasehash) {
00367 collision.str++;
00368 }
00369 }
00370 #define COLLISION (collision_check ? count_collision(table->type) : (void)0)
00371 #define FOUND_ENTRY (collision_check ? collision.total++ : (void)0)
00372 #else
00373 #define COLLISION
00374 #define FOUND_ENTRY
00375 #endif
00376
00377 #define FIND_ENTRY(table, ptr, hash_val, bin_pos) \
00378 ((ptr) = find_entry((table), key, (hash_val), ((bin_pos) = (hash_val)%(table)->num_bins)))
00379
00380 static st_table_entry *
00381 find_entry(st_table *table, st_data_t key, st_index_t hash_val, st_index_t bin_pos)
00382 {
00383 register st_table_entry *ptr = table->bins[bin_pos];
00384 FOUND_ENTRY;
00385 if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {
00386 COLLISION;
00387 while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {
00388 ptr = ptr->next;
00389 }
00390 ptr = ptr->next;
00391 }
00392 return ptr;
00393 }
00394
00395 static inline st_index_t
00396 find_packed_index_from(st_table *table, st_index_t hash_val, st_data_t key, st_index_t i)
00397 {
00398 while (i < table->real_entries &&
00399 (PHASH(table, i) != hash_val || !EQUAL(table, key, PKEY(table, i)))) {
00400 i++;
00401 }
00402 return i;
00403 }
00404
00405 static inline st_index_t
00406 find_packed_index(st_table *table, st_index_t hash_val, st_data_t key)
00407 {
00408 return find_packed_index_from(table, hash_val, key, 0);
00409 }
00410
00411 #define collision_check 0
00412
00413 int
00414 st_lookup(st_table *table, register st_data_t key, st_data_t *value)
00415 {
00416 st_index_t hash_val;
00417 register st_table_entry *ptr;
00418
00419 hash_val = do_hash(key, table);
00420
00421 if (table->entries_packed) {
00422 st_index_t i = find_packed_index(table, hash_val, key);
00423 if (i < table->real_entries) {
00424 if (value != 0) *value = PVAL(table, i);
00425 return 1;
00426 }
00427 return 0;
00428 }
00429
00430 ptr = find_entry(table, key, hash_val, hash_val % table->num_bins);
00431
00432 if (ptr == 0) {
00433 return 0;
00434 }
00435 else {
00436 if (value != 0) *value = ptr->record;
00437 return 1;
00438 }
00439 }
00440
00441 int
00442 st_get_key(st_table *table, register st_data_t key, st_data_t *result)
00443 {
00444 st_index_t hash_val;
00445 register st_table_entry *ptr;
00446
00447 hash_val = do_hash(key, table);
00448
00449 if (table->entries_packed) {
00450 st_index_t i = find_packed_index(table, hash_val, key);
00451 if (i < table->real_entries) {
00452 if (result != 0) *result = PKEY(table, i);
00453 return 1;
00454 }
00455 return 0;
00456 }
00457
00458 ptr = find_entry(table, key, hash_val, hash_val % table->num_bins);
00459
00460 if (ptr == 0) {
00461 return 0;
00462 }
00463 else {
00464 if (result != 0) *result = ptr->key;
00465 return 1;
00466 }
00467 }
00468
00469 #undef collision_check
00470 #define collision_check 1
00471
00472 static inline st_table_entry *
00473 new_entry(st_table * table, st_data_t key, st_data_t value,
00474 st_index_t hash_val, register st_index_t bin_pos)
00475 {
00476 register st_table_entry *entry = st_alloc_entry();
00477
00478 entry->next = table->bins[bin_pos];
00479 table->bins[bin_pos] = entry;
00480 entry->hash = hash_val;
00481 entry->key = key;
00482 entry->record = value;
00483
00484 return entry;
00485 }
00486
00487 static inline void
00488 add_direct(st_table *table, st_data_t key, st_data_t value,
00489 st_index_t hash_val, register st_index_t bin_pos)
00490 {
00491 register st_table_entry *entry;
00492 if (table->num_entries > ST_DEFAULT_MAX_DENSITY * table->num_bins) {
00493 rehash(table);
00494 bin_pos = hash_val % table->num_bins;
00495 }
00496
00497 entry = new_entry(table, key, value, hash_val, bin_pos);
00498
00499 if (table->head != 0) {
00500 entry->fore = 0;
00501 (entry->back = table->tail)->fore = entry;
00502 table->tail = entry;
00503 }
00504 else {
00505 table->head = table->tail = entry;
00506 entry->fore = entry->back = 0;
00507 }
00508 table->num_entries++;
00509 }
00510
00511 static void
00512 unpack_entries(register st_table *table)
00513 {
00514 st_index_t i;
00515 st_packed_entry packed_bins[MAX_PACKED_HASH];
00516 register st_table_entry *entry, *preventry = 0, **chain;
00517 st_table tmp_table = *table;
00518
00519 MEMCPY(packed_bins, PACKED_BINS(table), st_packed_entry, MAX_PACKED_HASH);
00520 table->as.packed.entries = packed_bins;
00521 tmp_table.entries_packed = 0;
00522 #if ST_DEFAULT_INIT_TABLE_SIZE == ST_DEFAULT_PACKED_TABLE_SIZE
00523 MEMZERO(tmp_table.bins, st_table_entry*, tmp_table.num_bins);
00524 #else
00525 tmp_table.bins = st_realloc_bins(tmp_table.bins, ST_DEFAULT_INIT_TABLE_SIZE, tmp_table.num_bins);
00526 tmp_table.num_bins = ST_DEFAULT_INIT_TABLE_SIZE;
00527 #endif
00528 i = 0;
00529 chain = &tmp_table.head;
00530 do {
00531 st_data_t key = packed_bins[i].key;
00532 st_data_t val = packed_bins[i].val;
00533 st_index_t hash = packed_bins[i].hash;
00534 entry = new_entry(&tmp_table, key, val, hash,
00535 hash % ST_DEFAULT_INIT_TABLE_SIZE);
00536 *chain = entry;
00537 entry->back = preventry;
00538 preventry = entry;
00539 chain = &entry->fore;
00540 } while (++i < MAX_PACKED_HASH);
00541 *chain = NULL;
00542 tmp_table.tail = entry;
00543 *table = tmp_table;
00544 }
00545
00546 static void
00547 add_packed_direct(st_table *table, st_data_t key, st_data_t value, st_index_t hash_val)
00548 {
00549 if (table->real_entries < MAX_PACKED_HASH) {
00550 st_index_t i = table->real_entries++;
00551 PKEY_SET(table, i, key);
00552 PVAL_SET(table, i, value);
00553 PHASH_SET(table, i, hash_val);
00554 table->num_entries++;
00555 }
00556 else {
00557 unpack_entries(table);
00558 add_direct(table, key, value, hash_val, hash_val % table->num_bins);
00559 }
00560 }
00561
00562
00563 int
00564 st_insert(register st_table *table, register st_data_t key, st_data_t value)
00565 {
00566 st_index_t hash_val;
00567 register st_index_t bin_pos;
00568 register st_table_entry *ptr;
00569
00570 hash_val = do_hash(key, table);
00571
00572 if (table->entries_packed) {
00573 st_index_t i = find_packed_index(table, hash_val, key);
00574 if (i < table->real_entries) {
00575 PVAL_SET(table, i, value);
00576 return 1;
00577 }
00578 add_packed_direct(table, key, value, hash_val);
00579 return 0;
00580 }
00581
00582 FIND_ENTRY(table, ptr, hash_val, bin_pos);
00583
00584 if (ptr == 0) {
00585 add_direct(table, key, value, hash_val, bin_pos);
00586 return 0;
00587 }
00588 else {
00589 ptr->record = value;
00590 return 1;
00591 }
00592 }
00593
00594 int
00595 st_insert2(register st_table *table, register st_data_t key, st_data_t value,
00596 st_data_t (*func)(st_data_t))
00597 {
00598 st_index_t hash_val;
00599 register st_index_t bin_pos;
00600 register st_table_entry *ptr;
00601
00602 hash_val = do_hash(key, table);
00603
00604 if (table->entries_packed) {
00605 st_index_t i = find_packed_index(table, hash_val, key);
00606 if (i < table->real_entries) {
00607 PVAL_SET(table, i, value);
00608 return 1;
00609 }
00610 key = (*func)(key);
00611 add_packed_direct(table, key, value, hash_val);
00612 return 0;
00613 }
00614
00615 FIND_ENTRY(table, ptr, hash_val, bin_pos);
00616
00617 if (ptr == 0) {
00618 key = (*func)(key);
00619 add_direct(table, key, value, hash_val, bin_pos);
00620 return 0;
00621 }
00622 else {
00623 ptr->record = value;
00624 return 1;
00625 }
00626 }
00627
00628 void
00629 st_add_direct(st_table *table, st_data_t key, st_data_t value)
00630 {
00631 st_index_t hash_val;
00632
00633 hash_val = do_hash(key, table);
00634 if (table->entries_packed) {
00635 add_packed_direct(table, key, value, hash_val);
00636 return;
00637 }
00638
00639 add_direct(table, key, value, hash_val, hash_val % table->num_bins);
00640 }
00641
00642 static void
00643 rehash(register st_table *table)
00644 {
00645 register st_table_entry *ptr, **new_bins;
00646 st_index_t new_num_bins, hash_val;
00647
00648 new_num_bins = new_size(table->num_bins+1);
00649 new_bins = st_realloc_bins(table->bins, new_num_bins, table->num_bins);
00650 table->num_bins = new_num_bins;
00651 table->bins = new_bins;
00652
00653 if ((ptr = table->head) != 0) {
00654 do {
00655 hash_val = ptr->hash % new_num_bins;
00656 ptr->next = new_bins[hash_val];
00657 new_bins[hash_val] = ptr;
00658 } while ((ptr = ptr->fore) != 0);
00659 }
00660 }
00661
00662 st_table*
00663 st_copy(st_table *old_table)
00664 {
00665 st_table *new_table;
00666 st_table_entry *ptr, *entry, *prev, **tailp;
00667 st_index_t num_bins = old_table->num_bins;
00668 st_index_t hash_val;
00669
00670 new_table = st_alloc_table();
00671 if (new_table == 0) {
00672 return 0;
00673 }
00674
00675 *new_table = *old_table;
00676 new_table->bins = st_alloc_bins(num_bins);
00677
00678 if (new_table->bins == 0) {
00679 st_dealloc_table(new_table);
00680 return 0;
00681 }
00682
00683 if (old_table->entries_packed) {
00684 MEMCPY(new_table->bins, old_table->bins, st_table_entry*, old_table->num_bins);
00685 return new_table;
00686 }
00687
00688 if ((ptr = old_table->head) != 0) {
00689 prev = 0;
00690 tailp = &new_table->head;
00691 do {
00692 entry = st_alloc_entry();
00693 if (entry == 0) {
00694 st_free_table(new_table);
00695 return 0;
00696 }
00697 *entry = *ptr;
00698 hash_val = entry->hash % num_bins;
00699 entry->next = new_table->bins[hash_val];
00700 new_table->bins[hash_val] = entry;
00701 entry->back = prev;
00702 *tailp = prev = entry;
00703 tailp = &entry->fore;
00704 } while ((ptr = ptr->fore) != 0);
00705 new_table->tail = prev;
00706 }
00707
00708 return new_table;
00709 }
00710
00711 static inline void
00712 remove_entry(st_table *table, st_table_entry *ptr)
00713 {
00714 if (ptr->fore == 0 && ptr->back == 0) {
00715 table->head = 0;
00716 table->tail = 0;
00717 }
00718 else {
00719 st_table_entry *fore = ptr->fore, *back = ptr->back;
00720 if (fore) fore->back = back;
00721 if (back) back->fore = fore;
00722 if (ptr == table->head) table->head = fore;
00723 if (ptr == table->tail) table->tail = back;
00724 }
00725 table->num_entries--;
00726 }
00727
00728 int
00729 st_delete(register st_table *table, register st_data_t *key, st_data_t *value)
00730 {
00731 st_index_t hash_val;
00732 st_table_entry **prev;
00733 register st_table_entry *ptr;
00734
00735 hash_val = do_hash(*key, table);
00736
00737 if (table->entries_packed) {
00738 st_index_t i = find_packed_index(table, hash_val, *key);
00739 if (i < table->real_entries) {
00740 if (value != 0) *value = PVAL(table, i);
00741 *key = PKEY(table, i);
00742 remove_packed_entry(table, i);
00743 return 1;
00744 }
00745 if (value != 0) *value = 0;
00746 return 0;
00747 }
00748
00749 prev = &table->bins[hash_val % table->num_bins];
00750 for (;(ptr = *prev) != 0; prev = &ptr->next) {
00751 if (EQUAL(table, *key, ptr->key)) {
00752 *prev = ptr->next;
00753 remove_entry(table, ptr);
00754 if (value != 0) *value = ptr->record;
00755 *key = ptr->key;
00756 st_free_entry(ptr);
00757 return 1;
00758 }
00759 }
00760
00761 if (value != 0) *value = 0;
00762 return 0;
00763 }
00764
00765 int
00766 st_delete_safe(register st_table *table, register st_data_t *key, st_data_t *value, st_data_t never)
00767 {
00768 st_index_t hash_val;
00769 register st_table_entry *ptr;
00770
00771 hash_val = do_hash(*key, table);
00772
00773 if (table->entries_packed) {
00774 st_index_t i = find_packed_index(table, hash_val, *key);
00775 if (i < table->real_entries) {
00776 if (value != 0) *value = PVAL(table, i);
00777 *key = PKEY(table, i);
00778 remove_safe_packed_entry(table, i, never);
00779 return 1;
00780 }
00781 if (value != 0) *value = 0;
00782 return 0;
00783 }
00784
00785 ptr = table->bins[hash_val % table->num_bins];
00786
00787 for (; ptr != 0; ptr = ptr->next) {
00788 if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) {
00789 remove_entry(table, ptr);
00790 *key = ptr->key;
00791 if (value != 0) *value = ptr->record;
00792 ptr->key = ptr->record = never;
00793 return 1;
00794 }
00795 }
00796
00797 if (value != 0) *value = 0;
00798 return 0;
00799 }
00800
00801 void
00802 st_cleanup_safe(st_table *table, st_data_t never)
00803 {
00804 st_table_entry *ptr, **last, *tmp;
00805 st_index_t i;
00806
00807 if (table->entries_packed) {
00808 st_index_t i = 0, j = 0;
00809 while (PKEY(table, i) != never) {
00810 if (i++ == table->real_entries) return;
00811 }
00812 for (j = i; ++i < table->real_entries;) {
00813 if (PKEY(table, i) == never) continue;
00814 PACKED_ENT(table, j) = PACKED_ENT(table, i);
00815 j++;
00816 }
00817 table->real_entries = j;
00818
00819 table->num_entries = j;
00820 return;
00821 }
00822
00823 for (i = 0; i < table->num_bins; i++) {
00824 ptr = *(last = &table->bins[i]);
00825 while (ptr != 0) {
00826 if (ptr->key == never) {
00827 tmp = ptr;
00828 *last = ptr = ptr->next;
00829 st_free_entry(tmp);
00830 }
00831 else {
00832 ptr = *(last = &ptr->next);
00833 }
00834 }
00835 }
00836 }
00837
00838 int
00839 st_update(st_table *table, st_data_t key, st_update_callback_func *func, st_data_t arg)
00840 {
00841 st_index_t hash_val, bin_pos;
00842 register st_table_entry *ptr, **last, *tmp;
00843 st_data_t value = 0;
00844 int retval, existing = 0;
00845
00846 hash_val = do_hash(key, table);
00847
00848 if (table->entries_packed) {
00849 st_index_t i = find_packed_index(table, hash_val, key);
00850 if (i < table->real_entries) {
00851 key = PKEY(table, i);
00852 value = PVAL(table, i);
00853 existing = 1;
00854 }
00855 {
00856 retval = (*func)(&key, &value, arg, existing);
00857 if (!table->entries_packed) {
00858 FIND_ENTRY(table, ptr, hash_val, bin_pos);
00859 goto unpacked;
00860 }
00861 switch (retval) {
00862 case ST_CONTINUE:
00863 if (!existing) {
00864 add_packed_direct(table, key, value, hash_val);
00865 break;
00866 }
00867 PVAL_SET(table, i, value);
00868 break;
00869 case ST_DELETE:
00870 if (!existing) break;
00871 remove_packed_entry(table, i);
00872 }
00873 }
00874 return existing;
00875 }
00876
00877 FIND_ENTRY(table, ptr, hash_val, bin_pos);
00878
00879 if (ptr != 0) {
00880 key = ptr->key;
00881 value = ptr->record;
00882 existing = 1;
00883 }
00884 {
00885 retval = (*func)(&key, &value, arg, existing);
00886 unpacked:
00887 switch (retval) {
00888 case ST_CONTINUE:
00889 if (!existing) {
00890 add_direct(table, key, value, hash_val, hash_val % table->num_bins);
00891 break;
00892 }
00893 ptr->record = value;
00894 break;
00895 case ST_DELETE:
00896 if (!existing) break;
00897 last = &table->bins[bin_pos];
00898 for (; (tmp = *last) != 0; last = &tmp->next) {
00899 if (ptr == tmp) {
00900 tmp = ptr->fore;
00901 *last = ptr->next;
00902 remove_entry(table, ptr);
00903 st_free_entry(ptr);
00904 break;
00905 }
00906 }
00907 break;
00908 }
00909 return existing;
00910 }
00911 }
00912
00913 int
00914 st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never)
00915 {
00916 st_table_entry *ptr, **last, *tmp;
00917 enum st_retval retval;
00918 st_index_t i;
00919
00920 if (table->entries_packed) {
00921 for (i = 0; i < table->real_entries; i++) {
00922 st_data_t key, val;
00923 st_index_t hash;
00924 key = PKEY(table, i);
00925 val = PVAL(table, i);
00926 hash = PHASH(table, i);
00927 if (key == never) continue;
00928 retval = (*func)(key, val, arg);
00929 if (!table->entries_packed) {
00930 FIND_ENTRY(table, ptr, hash, i);
00931 if (retval == ST_CHECK) {
00932 if (!ptr) goto deleted;
00933 goto unpacked_continue;
00934 }
00935 goto unpacked;
00936 }
00937 switch (retval) {
00938 case ST_CHECK:
00939 if (PHASH(table, i) == 0 && PKEY(table, i) == never) {
00940 break;
00941 }
00942 i = find_packed_index_from(table, hash, key, i);
00943 if (i >= table->real_entries) {
00944 i = find_packed_index(table, hash, key);
00945 if (i >= table->real_entries) goto deleted;
00946 }
00947
00948 case ST_CONTINUE:
00949 break;
00950 case ST_STOP:
00951 return 0;
00952 case ST_DELETE:
00953 remove_safe_packed_entry(table, i, never);
00954 break;
00955 }
00956 }
00957 return 0;
00958 }
00959 else {
00960 ptr = table->head;
00961 }
00962
00963 if (ptr != 0) {
00964 do {
00965 if (ptr->key == never)
00966 goto unpacked_continue;
00967 i = ptr->hash % table->num_bins;
00968 retval = (*func)(ptr->key, ptr->record, arg);
00969 unpacked:
00970 switch (retval) {
00971 case ST_CHECK:
00972 for (tmp = table->bins[i]; tmp != ptr; tmp = tmp->next) {
00973 if (!tmp) {
00974 deleted:
00975
00976 retval = (*func)(0, 0, arg, 1);
00977 return 1;
00978 }
00979 }
00980
00981 case ST_CONTINUE:
00982 unpacked_continue:
00983 ptr = ptr->fore;
00984 break;
00985 case ST_STOP:
00986 return 0;
00987 case ST_DELETE:
00988 last = &table->bins[ptr->hash % table->num_bins];
00989 for (; (tmp = *last) != 0; last = &tmp->next) {
00990 if (ptr == tmp) {
00991 tmp = ptr->fore;
00992 remove_entry(table, ptr);
00993 ptr->key = ptr->record = never;
00994 ptr->hash = 0;
00995 ptr = tmp;
00996 break;
00997 }
00998 }
00999 }
01000 } while (ptr && table->head);
01001 }
01002 return 0;
01003 }
01004
01005 int
01006 st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
01007 {
01008 st_table_entry *ptr, **last, *tmp;
01009 enum st_retval retval;
01010 st_index_t i;
01011
01012 if (table->entries_packed) {
01013 for (i = 0; i < table->real_entries; i++) {
01014 st_data_t key, val, hash;
01015 key = PKEY(table, i);
01016 val = PVAL(table, i);
01017 hash = PHASH(table, i);
01018 retval = (*func)(key, val, arg);
01019 if (!table->entries_packed) {
01020 FIND_ENTRY(table, ptr, hash, i);
01021 if (!ptr) return 0;
01022 goto unpacked;
01023 }
01024 switch (retval) {
01025 case ST_CONTINUE:
01026 break;
01027 case ST_CHECK:
01028 case ST_STOP:
01029 return 0;
01030 case ST_DELETE:
01031 remove_packed_entry(table, i);
01032 i--;
01033 break;
01034 }
01035 }
01036 return 0;
01037 }
01038 else {
01039 ptr = table->head;
01040 }
01041
01042 if (ptr != 0) {
01043 do {
01044 i = ptr->hash % table->num_bins;
01045 retval = (*func)(ptr->key, ptr->record, arg);
01046 unpacked:
01047 switch (retval) {
01048 case ST_CONTINUE:
01049 ptr = ptr->fore;
01050 break;
01051 case ST_CHECK:
01052 case ST_STOP:
01053 return 0;
01054 case ST_DELETE:
01055 last = &table->bins[ptr->hash % table->num_bins];
01056 for (; (tmp = *last) != 0; last = &tmp->next) {
01057 if (ptr == tmp) {
01058 tmp = ptr->fore;
01059 *last = ptr->next;
01060 remove_entry(table, ptr);
01061 st_free_entry(ptr);
01062 ptr = tmp;
01063 break;
01064 }
01065 }
01066 }
01067 } while (ptr && table->head);
01068 }
01069 return 0;
01070 }
01071
01072 #if 0
01073 int
01074 st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
01075 {
01076 st_table_entry *ptr, **last, *tmp;
01077 enum st_retval retval;
01078 int i;
01079
01080 if (table->entries_packed) {
01081 for (i = table->num_entries-1; 0 <= i; i--) {
01082 int j;
01083 st_data_t key, val;
01084 key = PKEY(table, i);
01085 val = PVAL(table, i);
01086 retval = (*func)(key, val, arg);
01087 switch (retval) {
01088 case ST_CHECK:
01089 for (j = 0; j < table->num_entries; j++) {
01090 if (PKEY(table, j) == key)
01091 break;
01092 }
01093 if (j == table->num_entries) {
01094
01095 retval = (*func)(0, 0, arg, 1);
01096 return 1;
01097 }
01098
01099 case ST_CONTINUE:
01100 break;
01101 case ST_STOP:
01102 return 0;
01103 case ST_DELETE:
01104 remove_packed_entry(table, i);
01105 break;
01106 }
01107 }
01108 return 0;
01109 }
01110
01111 if ((ptr = table->head) != 0) {
01112 ptr = ptr->back;
01113 do {
01114 retval = (*func)(ptr->key, ptr->record, arg, 0);
01115 switch (retval) {
01116 case ST_CHECK:
01117 i = ptr->hash % table->num_bins;
01118 for (tmp = table->bins[i]; tmp != ptr; tmp = tmp->next) {
01119 if (!tmp) {
01120
01121 retval = (*func)(0, 0, arg, 1);
01122 return 1;
01123 }
01124 }
01125
01126 case ST_CONTINUE:
01127 ptr = ptr->back;
01128 break;
01129 case ST_STOP:
01130 return 0;
01131 case ST_DELETE:
01132 last = &table->bins[ptr->hash % table->num_bins];
01133 for (; (tmp = *last) != 0; last = &tmp->next) {
01134 if (ptr == tmp) {
01135 tmp = ptr->back;
01136 *last = ptr->next;
01137 remove_entry(table, ptr);
01138 st_free_entry(ptr);
01139 ptr = tmp;
01140 break;
01141 }
01142 }
01143 ptr = ptr->next;
01144 free(tmp);
01145 table->num_entries--;
01146 }
01147 } while (ptr && table->head);
01148 }
01149 return 0;
01150 }
01151 #endif
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221 #define FNV1_32A_INIT 0x811c9dc5
01222
01223
01224
01225
01226 #define FNV_32_PRIME 0x01000193
01227
01228 #ifdef ST_USE_FNV1
01229 static st_index_t
01230 strhash(st_data_t arg)
01231 {
01232 register const char *string = (const char *)arg;
01233 register st_index_t hval = FNV1_32A_INIT;
01234
01235
01236
01237
01238 while (*string) {
01239
01240 hval ^= (unsigned int)*string++;
01241
01242
01243 hval *= FNV_32_PRIME;
01244 }
01245 return hval;
01246 }
01247 #else
01248
01249 #ifndef UNALIGNED_WORD_ACCESS
01250 # if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
01251 defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD86) || \
01252 defined(__mc68020__)
01253 # define UNALIGNED_WORD_ACCESS 1
01254 # endif
01255 #endif
01256 #ifndef UNALIGNED_WORD_ACCESS
01257 # define UNALIGNED_WORD_ACCESS 0
01258 #endif
01259
01260
01261 #ifndef MURMUR
01262 #define MURMUR 2
01263 #endif
01264
01265 #define MurmurMagic_1 (st_index_t)0xc6a4a793
01266 #define MurmurMagic_2 (st_index_t)0x5bd1e995
01267 #if MURMUR == 1
01268 #define MurmurMagic MurmurMagic_1
01269 #elif MURMUR == 2
01270 #if SIZEOF_ST_INDEX_T > 4
01271 #define MurmurMagic ((MurmurMagic_1 << 32) | MurmurMagic_2)
01272 #else
01273 #define MurmurMagic MurmurMagic_2
01274 #endif
01275 #endif
01276
01277 static inline st_index_t
01278 murmur(st_index_t h, st_index_t k, int r)
01279 {
01280 const st_index_t m = MurmurMagic;
01281 #if MURMUR == 1
01282 h += k;
01283 h *= m;
01284 h ^= h >> r;
01285 #elif MURMUR == 2
01286 k *= m;
01287 k ^= k >> r;
01288 k *= m;
01289
01290 h *= m;
01291 h ^= k;
01292 #endif
01293 return h;
01294 }
01295
01296 static inline st_index_t
01297 murmur_finish(st_index_t h)
01298 {
01299 #if MURMUR == 1
01300 h = murmur(h, 0, 10);
01301 h = murmur(h, 0, 17);
01302 #elif MURMUR == 2
01303 h ^= h >> 13;
01304 h *= MurmurMagic;
01305 h ^= h >> 15;
01306 #endif
01307 return h;
01308 }
01309
01310 #define murmur_step(h, k) murmur((h), (k), 16)
01311
01312 #if MURMUR == 1
01313 #define murmur1(h) murmur_step((h), 16)
01314 #else
01315 #define murmur1(h) murmur_step((h), 24)
01316 #endif
01317
01318 st_index_t
01319 st_hash(const void *ptr, size_t len, st_index_t h)
01320 {
01321 const char *data = ptr;
01322 st_index_t t = 0;
01323
01324 h += 0xdeadbeef;
01325
01326 #define data_at(n) (st_index_t)((unsigned char)data[(n)])
01327 #define UNALIGNED_ADD_4 UNALIGNED_ADD(2); UNALIGNED_ADD(1); UNALIGNED_ADD(0)
01328 #if SIZEOF_ST_INDEX_T > 4
01329 #define UNALIGNED_ADD_8 UNALIGNED_ADD(6); UNALIGNED_ADD(5); UNALIGNED_ADD(4); UNALIGNED_ADD(3); UNALIGNED_ADD_4
01330 #if SIZEOF_ST_INDEX_T > 8
01331 #define UNALIGNED_ADD_16 UNALIGNED_ADD(14); UNALIGNED_ADD(13); UNALIGNED_ADD(12); UNALIGNED_ADD(11); \
01332 UNALIGNED_ADD(10); UNALIGNED_ADD(9); UNALIGNED_ADD(8); UNALIGNED_ADD(7); UNALIGNED_ADD_8
01333 #define UNALIGNED_ADD_ALL UNALIGNED_ADD_16
01334 #endif
01335 #define UNALIGNED_ADD_ALL UNALIGNED_ADD_8
01336 #else
01337 #define UNALIGNED_ADD_ALL UNALIGNED_ADD_4
01338 #endif
01339 if (len >= sizeof(st_index_t)) {
01340 #if !UNALIGNED_WORD_ACCESS
01341 int align = (int)((st_data_t)data % sizeof(st_index_t));
01342 if (align) {
01343 st_index_t d = 0;
01344 int sl, sr, pack;
01345
01346 switch (align) {
01347 #ifdef WORDS_BIGENDIAN
01348 # define UNALIGNED_ADD(n) case SIZEOF_ST_INDEX_T - (n) - 1: \
01349 t |= data_at(n) << CHAR_BIT*(SIZEOF_ST_INDEX_T - (n) - 2)
01350 #else
01351 # define UNALIGNED_ADD(n) case SIZEOF_ST_INDEX_T - (n) - 1: \
01352 t |= data_at(n) << CHAR_BIT*(n)
01353 #endif
01354 UNALIGNED_ADD_ALL;
01355 #undef UNALIGNED_ADD
01356 }
01357
01358 #ifdef WORDS_BIGENDIAN
01359 t >>= (CHAR_BIT * align) - CHAR_BIT;
01360 #else
01361 t <<= (CHAR_BIT * align);
01362 #endif
01363
01364 data += sizeof(st_index_t)-align;
01365 len -= sizeof(st_index_t)-align;
01366
01367 sl = CHAR_BIT * (SIZEOF_ST_INDEX_T-align);
01368 sr = CHAR_BIT * align;
01369
01370 while (len >= sizeof(st_index_t)) {
01371 d = *(st_index_t *)data;
01372 #ifdef WORDS_BIGENDIAN
01373 t = (t << sr) | (d >> sl);
01374 #else
01375 t = (t >> sr) | (d << sl);
01376 #endif
01377 h = murmur_step(h, t);
01378 t = d;
01379 data += sizeof(st_index_t);
01380 len -= sizeof(st_index_t);
01381 }
01382
01383 pack = len < (size_t)align ? (int)len : align;
01384 d = 0;
01385 switch (pack) {
01386 #ifdef WORDS_BIGENDIAN
01387 # define UNALIGNED_ADD(n) case (n) + 1: \
01388 d |= data_at(n) << CHAR_BIT*(SIZEOF_ST_INDEX_T - (n) - 1)
01389 #else
01390 # define UNALIGNED_ADD(n) case (n) + 1: \
01391 d |= data_at(n) << CHAR_BIT*(n)
01392 #endif
01393 UNALIGNED_ADD_ALL;
01394 #undef UNALIGNED_ADD
01395 }
01396 #ifdef WORDS_BIGENDIAN
01397 t = (t << sr) | (d >> sl);
01398 #else
01399 t = (t >> sr) | (d << sl);
01400 #endif
01401
01402 #if MURMUR == 2
01403 if (len < (size_t)align) goto skip_tail;
01404 #endif
01405 h = murmur_step(h, t);
01406 data += pack;
01407 len -= pack;
01408 }
01409 else
01410 #endif
01411 {
01412 do {
01413 h = murmur_step(h, *(st_index_t *)data);
01414 data += sizeof(st_index_t);
01415 len -= sizeof(st_index_t);
01416 } while (len >= sizeof(st_index_t));
01417 }
01418 }
01419
01420 t = 0;
01421 switch (len) {
01422 #ifdef WORDS_BIGENDIAN
01423 # define UNALIGNED_ADD(n) case (n) + 1: \
01424 t |= data_at(n) << CHAR_BIT*(SIZEOF_ST_INDEX_T - (n) - 1)
01425 #else
01426 # define UNALIGNED_ADD(n) case (n) + 1: \
01427 t |= data_at(n) << CHAR_BIT*(n)
01428 #endif
01429 UNALIGNED_ADD_ALL;
01430 #undef UNALIGNED_ADD
01431 #if MURMUR == 1
01432 h = murmur_step(h, t);
01433 #elif MURMUR == 2
01434 # if !UNALIGNED_WORD_ACCESS
01435 skip_tail:
01436 # endif
01437 h ^= t;
01438 h *= MurmurMagic;
01439 #endif
01440 }
01441
01442 return murmur_finish(h);
01443 }
01444
01445 st_index_t
01446 st_hash_uint32(st_index_t h, uint32_t i)
01447 {
01448 return murmur_step(h + i, 16);
01449 }
01450
01451 st_index_t
01452 st_hash_uint(st_index_t h, st_index_t i)
01453 {
01454 st_index_t v = 0;
01455 h += i;
01456 #ifdef WORDS_BIGENDIAN
01457 #if SIZEOF_ST_INDEX_T*CHAR_BIT > 12*8
01458 v = murmur1(v + (h >> 12*8));
01459 #endif
01460 #if SIZEOF_ST_INDEX_T*CHAR_BIT > 8*8
01461 v = murmur1(v + (h >> 8*8));
01462 #endif
01463 #if SIZEOF_ST_INDEX_T*CHAR_BIT > 4*8
01464 v = murmur1(v + (h >> 4*8));
01465 #endif
01466 #endif
01467 v = murmur1(v + h);
01468 #ifndef WORDS_BIGENDIAN
01469 #if SIZEOF_ST_INDEX_T*CHAR_BIT > 4*8
01470 v = murmur1(v + (h >> 4*8));
01471 #endif
01472 #if SIZEOF_ST_INDEX_T*CHAR_BIT > 8*8
01473 v = murmur1(v + (h >> 8*8));
01474 #endif
01475 #if SIZEOF_ST_INDEX_T*CHAR_BIT > 12*8
01476 v = murmur1(v + (h >> 12*8));
01477 #endif
01478 #endif
01479 return v;
01480 }
01481
01482 st_index_t
01483 st_hash_end(st_index_t h)
01484 {
01485 h = murmur_step(h, 10);
01486 h = murmur_step(h, 17);
01487 return h;
01488 }
01489
01490 #undef st_hash_start
01491 st_index_t
01492 st_hash_start(st_index_t h)
01493 {
01494 return h;
01495 }
01496
01497 static st_index_t
01498 strhash(st_data_t arg)
01499 {
01500 register const char *string = (const char *)arg;
01501 return st_hash(string, strlen(string), FNV1_32A_INIT);
01502 }
01503 #endif
01504
01505 int
01506 st_strcasecmp(const char *s1, const char *s2)
01507 {
01508 unsigned int c1, c2;
01509
01510 while (1) {
01511 c1 = (unsigned char)*s1++;
01512 c2 = (unsigned char)*s2++;
01513 if (c1 == '\0' || c2 == '\0') {
01514 if (c1 != '\0') return 1;
01515 if (c2 != '\0') return -1;
01516 return 0;
01517 }
01518 if ((unsigned int)(c1 - 'A') <= ('Z' - 'A')) c1 += 'a' - 'A';
01519 if ((unsigned int)(c2 - 'A') <= ('Z' - 'A')) c2 += 'a' - 'A';
01520 if (c1 != c2) {
01521 if (c1 > c2)
01522 return 1;
01523 else
01524 return -1;
01525 }
01526 }
01527 }
01528
01529 int
01530 st_strncasecmp(const char *s1, const char *s2, size_t n)
01531 {
01532 unsigned int c1, c2;
01533
01534 while (n--) {
01535 c1 = (unsigned char)*s1++;
01536 c2 = (unsigned char)*s2++;
01537 if (c1 == '\0' || c2 == '\0') {
01538 if (c1 != '\0') return 1;
01539 if (c2 != '\0') return -1;
01540 return 0;
01541 }
01542 if ((unsigned int)(c1 - 'A') <= ('Z' - 'A')) c1 += 'a' - 'A';
01543 if ((unsigned int)(c2 - 'A') <= ('Z' - 'A')) c2 += 'a' - 'A';
01544 if (c1 != c2) {
01545 if (c1 > c2)
01546 return 1;
01547 else
01548 return -1;
01549 }
01550 }
01551 return 0;
01552 }
01553
01554 static st_index_t
01555 strcasehash(st_data_t arg)
01556 {
01557 register const char *string = (const char *)arg;
01558 register st_index_t hval = FNV1_32A_INIT;
01559
01560
01561
01562
01563 while (*string) {
01564 unsigned int c = (unsigned char)*string++;
01565 if ((unsigned int)(c - 'A') <= ('Z' - 'A')) c += 'a' - 'A';
01566 hval ^= c;
01567
01568
01569 hval *= FNV_32_PRIME;
01570 }
01571 return hval;
01572 }
01573
01574 int
01575 st_numcmp(st_data_t x, st_data_t y)
01576 {
01577 return x != y;
01578 }
01579
01580 st_index_t
01581 st_numhash(st_data_t n)
01582 {
01583 return (st_index_t)n;
01584 }
01585