tclHash.cGo to the documentation of this file.00001 /* 00002 * tclHash.c -- 00003 * 00004 * Implementation of in-memory hash tables for Tcl and Tcl-based 00005 * applications. 00006 * 00007 * Copyright (c) 1991-1993 The Regents of the University of California. 00008 * Copyright (c) 1994 Sun Microsystems, Inc. 00009 * 00010 * See the file "license.terms" for information on usage and redistribution of 00011 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * RCS: @(#) $Id: tclHash.c,v 1.33 2007/12/13 15:23:17 dgp Exp $ 00014 */ 00015 00016 #include "tclInt.h" 00017 00018 /* 00019 * Prevent macros from clashing with function definitions. 00020 */ 00021 00022 #undef Tcl_FindHashEntry 00023 #undef Tcl_CreateHashEntry 00024 00025 /* 00026 * When there are this many entries per bucket, on average, rebuild the hash 00027 * table to make it larger. 00028 */ 00029 00030 #define REBUILD_MULTIPLIER 3 00031 00032 /* 00033 * The following macro takes a preliminary integer hash value and produces an 00034 * index into a hash tables bucket list. The idea is to make it so that 00035 * preliminary values that are arbitrarily similar will end up in different 00036 * buckets. The hash function was taken from a random-number generator. 00037 */ 00038 00039 #define RANDOM_INDEX(tablePtr, i) \ 00040 (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask) 00041 00042 /* 00043 * Prototypes for the array hash key methods. 00044 */ 00045 00046 static Tcl_HashEntry * AllocArrayEntry(Tcl_HashTable *tablePtr, VOID *keyPtr); 00047 static int CompareArrayKeys(VOID *keyPtr, Tcl_HashEntry *hPtr); 00048 static unsigned int HashArrayKey(Tcl_HashTable *tablePtr, VOID *keyPtr); 00049 00050 /* 00051 * Prototypes for the one word hash key methods. 00052 */ 00053 00054 #if 0 00055 static Tcl_HashEntry * AllocOneWordEntry(Tcl_HashTable *tablePtr, 00056 VOID *keyPtr); 00057 static int CompareOneWordKeys(VOID *keyPtr, Tcl_HashEntry *hPtr); 00058 static unsigned int HashOneWordKey(Tcl_HashTable *tablePtr, VOID *keyPtr); 00059 #endif 00060 00061 /* 00062 * Prototypes for the string hash key methods. 00063 */ 00064 00065 static Tcl_HashEntry * AllocStringEntry(Tcl_HashTable *tablePtr, 00066 VOID *keyPtr); 00067 static int CompareStringKeys(VOID *keyPtr, Tcl_HashEntry *hPtr); 00068 static unsigned int HashStringKey(Tcl_HashTable *tablePtr, VOID *keyPtr); 00069 00070 /* 00071 * Function prototypes for static functions in this file: 00072 */ 00073 00074 static Tcl_HashEntry * BogusFind(Tcl_HashTable *tablePtr, const char *key); 00075 static Tcl_HashEntry * BogusCreate(Tcl_HashTable *tablePtr, const char *key, 00076 int *newPtr); 00077 static void RebuildTable(Tcl_HashTable *tablePtr); 00078 00079 Tcl_HashKeyType tclArrayHashKeyType = { 00080 TCL_HASH_KEY_TYPE_VERSION, /* version */ 00081 TCL_HASH_KEY_RANDOMIZE_HASH, /* flags */ 00082 HashArrayKey, /* hashKeyProc */ 00083 CompareArrayKeys, /* compareKeysProc */ 00084 AllocArrayEntry, /* allocEntryProc */ 00085 NULL /* freeEntryProc */ 00086 }; 00087 00088 Tcl_HashKeyType tclOneWordHashKeyType = { 00089 TCL_HASH_KEY_TYPE_VERSION, /* version */ 00090 0, /* flags */ 00091 NULL, /* HashOneWordKey, */ /* hashProc */ 00092 NULL, /* CompareOneWordKey, */ /* compareProc */ 00093 NULL, /* AllocOneWordKey, */ /* allocEntryProc */ 00094 NULL /* FreeOneWordKey, */ /* freeEntryProc */ 00095 }; 00096 00097 Tcl_HashKeyType tclStringHashKeyType = { 00098 TCL_HASH_KEY_TYPE_VERSION, /* version */ 00099 0, /* flags */ 00100 HashStringKey, /* hashKeyProc */ 00101 CompareStringKeys, /* compareKeysProc */ 00102 AllocStringEntry, /* allocEntryProc */ 00103 NULL /* freeEntryProc */ 00104 }; 00105 00106 /* 00107 *---------------------------------------------------------------------- 00108 * 00109 * Tcl_InitHashTable -- 00110 * 00111 * Given storage for a hash table, set up the fields to prepare the hash 00112 * table for use. 00113 * 00114 * Results: 00115 * None. 00116 * 00117 * Side effects: 00118 * TablePtr is now ready to be passed to Tcl_FindHashEntry and 00119 * Tcl_CreateHashEntry. 00120 * 00121 *---------------------------------------------------------------------- 00122 */ 00123 00124 #undef Tcl_InitHashTable 00125 void 00126 Tcl_InitHashTable( 00127 register Tcl_HashTable *tablePtr, 00128 /* Pointer to table record, which is supplied 00129 * by the caller. */ 00130 int keyType) /* Type of keys to use in table: 00131 * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS, or an 00132 * integer >= 2. */ 00133 { 00134 /* 00135 * Use a special value to inform the extended version that it must not 00136 * access any of the new fields in the Tcl_HashTable. If an extension is 00137 * rebuilt then any calls to this function will be redirected to the 00138 * extended version by a macro. 00139 */ 00140 00141 Tcl_InitCustomHashTable(tablePtr, keyType, (Tcl_HashKeyType *) -1); 00142 } 00143 00144 /* 00145 *---------------------------------------------------------------------- 00146 * 00147 * Tcl_InitCustomHashTable -- 00148 * 00149 * Given storage for a hash table, set up the fields to prepare the hash 00150 * table for use. This is an extended version of Tcl_InitHashTable which 00151 * supports user defined keys. 00152 * 00153 * Results: 00154 * None. 00155 * 00156 * Side effects: 00157 * TablePtr is now ready to be passed to Tcl_FindHashEntry and 00158 * Tcl_CreateHashEntry. 00159 * 00160 *---------------------------------------------------------------------- 00161 */ 00162 00163 void 00164 Tcl_InitCustomHashTable( 00165 register Tcl_HashTable *tablePtr, 00166 /* Pointer to table record, which is supplied 00167 * by the caller. */ 00168 int keyType, /* Type of keys to use in table: 00169 * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS, 00170 * TCL_CUSTOM_TYPE_KEYS, TCL_CUSTOM_PTR_KEYS, 00171 * or an integer >= 2. */ 00172 Tcl_HashKeyType *typePtr) /* Pointer to structure which defines the 00173 * behaviour of this table. */ 00174 { 00175 #if (TCL_SMALL_HASH_TABLE != 4) 00176 Tcl_Panic("Tcl_InitCustomHashTable: TCL_SMALL_HASH_TABLE is %d, not 4", 00177 TCL_SMALL_HASH_TABLE); 00178 #endif 00179 00180 tablePtr->buckets = tablePtr->staticBuckets; 00181 tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0; 00182 tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0; 00183 tablePtr->numBuckets = TCL_SMALL_HASH_TABLE; 00184 tablePtr->numEntries = 0; 00185 tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER; 00186 tablePtr->downShift = 28; 00187 tablePtr->mask = 3; 00188 tablePtr->keyType = keyType; 00189 tablePtr->findProc = Tcl_FindHashEntry; 00190 tablePtr->createProc = Tcl_CreateHashEntry; 00191 00192 if (typePtr == NULL) { 00193 /* 00194 * The caller has been rebuilt so the hash table is an extended 00195 * version. 00196 */ 00197 } else if (typePtr != (Tcl_HashKeyType *) -1) { 00198 /* 00199 * The caller is requesting a customized hash table so it must be an 00200 * extended version. 00201 */ 00202 00203 tablePtr->typePtr = typePtr; 00204 } else { 00205 /* 00206 * The caller has not been rebuilt so the hash table is not extended. 00207 */ 00208 } 00209 } 00210 00211 /* 00212 *---------------------------------------------------------------------- 00213 * 00214 * Tcl_FindHashEntry -- 00215 * 00216 * Given a hash table find the entry with a matching key. 00217 * 00218 * Results: 00219 * The return value is a token for the matching entry in the hash table, 00220 * or NULL if there was no matching entry. 00221 * 00222 * Side effects: 00223 * None. 00224 * 00225 *---------------------------------------------------------------------- 00226 */ 00227 00228 Tcl_HashEntry * 00229 Tcl_FindHashEntry( 00230 Tcl_HashTable *tablePtr, /* Table in which to lookup entry. */ 00231 const char *key) /* Key to use to find matching entry. */ 00232 { 00233 00234 return Tcl_CreateHashEntry(tablePtr, key, NULL); 00235 } 00236 00237 00238 /* 00239 *---------------------------------------------------------------------- 00240 * 00241 * Tcl_CreateHashEntry -- 00242 * 00243 * Given a hash table with string keys, and a string key, find the entry 00244 * with a matching key. If there is no matching entry, then create a new 00245 * entry that does match. 00246 * 00247 * Results: 00248 * The return value is a pointer to the matching entry. If this is a 00249 * newly-created entry, then *newPtr will be set to a non-zero value; 00250 * otherwise *newPtr will be set to 0. If this is a new entry the value 00251 * stored in the entry will initially be 0. 00252 * 00253 * Side effects: 00254 * A new entry may be added to the hash table. 00255 * 00256 *---------------------------------------------------------------------- 00257 */ 00258 00259 Tcl_HashEntry * 00260 Tcl_CreateHashEntry( 00261 Tcl_HashTable *tablePtr, /* Table in which to lookup entry. */ 00262 const char *key, /* Key to use to find or create matching 00263 * entry. */ 00264 int *newPtr) /* Store info here telling whether a new entry 00265 * was created. */ 00266 { 00267 register Tcl_HashEntry *hPtr; 00268 const Tcl_HashKeyType *typePtr; 00269 unsigned int hash; 00270 int index; 00271 00272 if (tablePtr->keyType == TCL_STRING_KEYS) { 00273 typePtr = &tclStringHashKeyType; 00274 } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) { 00275 typePtr = &tclOneWordHashKeyType; 00276 } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS 00277 || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) { 00278 typePtr = tablePtr->typePtr; 00279 } else { 00280 typePtr = &tclArrayHashKeyType; 00281 } 00282 00283 if (typePtr->hashKeyProc) { 00284 hash = typePtr->hashKeyProc(tablePtr, (VOID *) key); 00285 if (typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) { 00286 index = RANDOM_INDEX (tablePtr, hash); 00287 } else { 00288 index = hash & tablePtr->mask; 00289 } 00290 } else { 00291 hash = PTR2UINT(key); 00292 index = RANDOM_INDEX (tablePtr, hash); 00293 } 00294 00295 /* 00296 * Search all of the entries in the appropriate bucket. 00297 */ 00298 00299 if (typePtr->compareKeysProc) { 00300 Tcl_CompareHashKeysProc *compareKeysProc = typePtr->compareKeysProc; 00301 for (hPtr = tablePtr->buckets[index]; hPtr != NULL; 00302 hPtr = hPtr->nextPtr) { 00303 #if TCL_HASH_KEY_STORE_HASH 00304 if (hash != PTR2UINT(hPtr->hash)) { 00305 continue; 00306 } 00307 #endif 00308 if (compareKeysProc((VOID *) key, hPtr)) { 00309 if (newPtr) { 00310 *newPtr = 0; 00311 } 00312 return hPtr; 00313 } 00314 } 00315 } else { 00316 for (hPtr = tablePtr->buckets[index]; hPtr != NULL; 00317 hPtr = hPtr->nextPtr) { 00318 #if TCL_HASH_KEY_STORE_HASH 00319 if (hash != PTR2UINT(hPtr->hash)) { 00320 continue; 00321 } 00322 #endif 00323 if (key == hPtr->key.oneWordValue) { 00324 if (newPtr) { 00325 *newPtr = 0; 00326 } 00327 return hPtr; 00328 } 00329 } 00330 } 00331 00332 if (!newPtr) { 00333 return NULL; 00334 } 00335 00336 /* 00337 * Entry not found. Add a new one to the bucket. 00338 */ 00339 00340 *newPtr = 1; 00341 if (typePtr->allocEntryProc) { 00342 hPtr = typePtr->allocEntryProc(tablePtr, (VOID *) key); 00343 } else { 00344 hPtr = (Tcl_HashEntry *) ckalloc((unsigned) sizeof(Tcl_HashEntry)); 00345 hPtr->key.oneWordValue = (char *) key; 00346 hPtr->clientData = 0; 00347 } 00348 00349 hPtr->tablePtr = tablePtr; 00350 #if TCL_HASH_KEY_STORE_HASH 00351 hPtr->hash = UINT2PTR(hash); 00352 hPtr->nextPtr = tablePtr->buckets[index]; 00353 tablePtr->buckets[index] = hPtr; 00354 #else 00355 hPtr->bucketPtr = &(tablePtr->buckets[index]); 00356 hPtr->nextPtr = *hPtr->bucketPtr; 00357 *hPtr->bucketPtr = hPtr; 00358 #endif 00359 tablePtr->numEntries++; 00360 00361 /* 00362 * If the table has exceeded a decent size, rebuild it with many more 00363 * buckets. 00364 */ 00365 00366 if (tablePtr->numEntries >= tablePtr->rebuildSize) { 00367 RebuildTable(tablePtr); 00368 } 00369 return hPtr; 00370 } 00371 00372 /* 00373 *---------------------------------------------------------------------- 00374 * 00375 * Tcl_DeleteHashEntry -- 00376 * 00377 * Remove a single entry from a hash table. 00378 * 00379 * Results: 00380 * None. 00381 * 00382 * Side effects: 00383 * The entry given by entryPtr is deleted from its table and should never 00384 * again be used by the caller. It is up to the caller to free the 00385 * clientData field of the entry, if that is relevant. 00386 * 00387 *---------------------------------------------------------------------- 00388 */ 00389 00390 void 00391 Tcl_DeleteHashEntry( 00392 Tcl_HashEntry *entryPtr) 00393 { 00394 register Tcl_HashEntry *prevPtr; 00395 const Tcl_HashKeyType *typePtr; 00396 Tcl_HashTable *tablePtr; 00397 Tcl_HashEntry **bucketPtr; 00398 #if TCL_HASH_KEY_STORE_HASH 00399 int index; 00400 #endif 00401 00402 tablePtr = entryPtr->tablePtr; 00403 00404 if (tablePtr->keyType == TCL_STRING_KEYS) { 00405 typePtr = &tclStringHashKeyType; 00406 } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) { 00407 typePtr = &tclOneWordHashKeyType; 00408 } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS 00409 || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) { 00410 typePtr = tablePtr->typePtr; 00411 } else { 00412 typePtr = &tclArrayHashKeyType; 00413 } 00414 00415 #if TCL_HASH_KEY_STORE_HASH 00416 if (typePtr->hashKeyProc == NULL 00417 || typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) { 00418 index = RANDOM_INDEX (tablePtr, entryPtr->hash); 00419 } else { 00420 index = PTR2UINT(entryPtr->hash) & tablePtr->mask; 00421 } 00422 00423 bucketPtr = &(tablePtr->buckets[index]); 00424 #else 00425 bucketPtr = entryPtr->bucketPtr; 00426 #endif 00427 00428 if (*bucketPtr == entryPtr) { 00429 *bucketPtr = entryPtr->nextPtr; 00430 } else { 00431 for (prevPtr = *bucketPtr; ; prevPtr = prevPtr->nextPtr) { 00432 if (prevPtr == NULL) { 00433 Tcl_Panic("malformed bucket chain in Tcl_DeleteHashEntry"); 00434 } 00435 if (prevPtr->nextPtr == entryPtr) { 00436 prevPtr->nextPtr = entryPtr->nextPtr; 00437 break; 00438 } 00439 } 00440 } 00441 00442 tablePtr->numEntries--; 00443 if (typePtr->freeEntryProc) { 00444 typePtr->freeEntryProc (entryPtr); 00445 } else { 00446 ckfree((char *) entryPtr); 00447 } 00448 } 00449 00450 /* 00451 *---------------------------------------------------------------------- 00452 * 00453 * Tcl_DeleteHashTable -- 00454 * 00455 * Free up everything associated with a hash table except for the record 00456 * for the table itself. 00457 * 00458 * Results: 00459 * None. 00460 * 00461 * Side effects: 00462 * The hash table is no longer useable. 00463 * 00464 *---------------------------------------------------------------------- 00465 */ 00466 00467 void 00468 Tcl_DeleteHashTable( 00469 register Tcl_HashTable *tablePtr) /* Table to delete. */ 00470 { 00471 register Tcl_HashEntry *hPtr, *nextPtr; 00472 const Tcl_HashKeyType *typePtr; 00473 int i; 00474 00475 if (tablePtr->keyType == TCL_STRING_KEYS) { 00476 typePtr = &tclStringHashKeyType; 00477 } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) { 00478 typePtr = &tclOneWordHashKeyType; 00479 } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS 00480 || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) { 00481 typePtr = tablePtr->typePtr; 00482 } else { 00483 typePtr = &tclArrayHashKeyType; 00484 } 00485 00486 /* 00487 * Free up all the entries in the table. 00488 */ 00489 00490 for (i = 0; i < tablePtr->numBuckets; i++) { 00491 hPtr = tablePtr->buckets[i]; 00492 while (hPtr != NULL) { 00493 nextPtr = hPtr->nextPtr; 00494 if (typePtr->freeEntryProc) { 00495 typePtr->freeEntryProc (hPtr); 00496 } else { 00497 ckfree((char *) hPtr); 00498 } 00499 hPtr = nextPtr; 00500 } 00501 } 00502 00503 /* 00504 * Free up the bucket array, if it was dynamically allocated. 00505 */ 00506 00507 if (tablePtr->buckets != tablePtr->staticBuckets) { 00508 if (typePtr->flags & TCL_HASH_KEY_SYSTEM_HASH) { 00509 TclpSysFree((char *) tablePtr->buckets); 00510 } else { 00511 ckfree((char *) tablePtr->buckets); 00512 } 00513 } 00514 00515 /* 00516 * Arrange for panics if the table is used again without 00517 * re-initialization. 00518 */ 00519 00520 tablePtr->findProc = BogusFind; 00521 tablePtr->createProc = BogusCreate; 00522 } 00523 00524 /* 00525 *---------------------------------------------------------------------- 00526 * 00527 * Tcl_FirstHashEntry -- 00528 * 00529 * Locate the first entry in a hash table and set up a record that can be 00530 * used to step through all the remaining entries of the table. 00531 * 00532 * Results: 00533 * The return value is a pointer to the first entry in tablePtr, or NULL 00534 * if tablePtr has no entries in it. The memory at *searchPtr is 00535 * initialized so that subsequent calls to Tcl_NextHashEntry will return 00536 * all of the entries in the table, one at a time. 00537 * 00538 * Side effects: 00539 * None. 00540 * 00541 *---------------------------------------------------------------------- 00542 */ 00543 00544 Tcl_HashEntry * 00545 Tcl_FirstHashEntry( 00546 Tcl_HashTable *tablePtr, /* Table to search. */ 00547 Tcl_HashSearch *searchPtr) /* Place to store information about progress 00548 * through the table. */ 00549 { 00550 searchPtr->tablePtr = tablePtr; 00551 searchPtr->nextIndex = 0; 00552 searchPtr->nextEntryPtr = NULL; 00553 return Tcl_NextHashEntry(searchPtr); 00554 } 00555 00556 /* 00557 *---------------------------------------------------------------------- 00558 * 00559 * Tcl_NextHashEntry -- 00560 * 00561 * Once a hash table enumeration has been initiated by calling 00562 * Tcl_FirstHashEntry, this function may be called to return successive 00563 * elements of the table. 00564 * 00565 * Results: 00566 * The return value is the next entry in the hash table being enumerated, 00567 * or NULL if the end of the table is reached. 00568 * 00569 * Side effects: 00570 * None. 00571 * 00572 *---------------------------------------------------------------------- 00573 */ 00574 00575 Tcl_HashEntry * 00576 Tcl_NextHashEntry( 00577 register Tcl_HashSearch *searchPtr) 00578 /* Place to store information about progress 00579 * through the table. Must have been 00580 * initialized by calling 00581 * Tcl_FirstHashEntry. */ 00582 { 00583 Tcl_HashEntry *hPtr; 00584 Tcl_HashTable *tablePtr = searchPtr->tablePtr; 00585 00586 while (searchPtr->nextEntryPtr == NULL) { 00587 if (searchPtr->nextIndex >= tablePtr->numBuckets) { 00588 return NULL; 00589 } 00590 searchPtr->nextEntryPtr = 00591 tablePtr->buckets[searchPtr->nextIndex]; 00592 searchPtr->nextIndex++; 00593 } 00594 hPtr = searchPtr->nextEntryPtr; 00595 searchPtr->nextEntryPtr = hPtr->nextPtr; 00596 return hPtr; 00597 } 00598 00599 /* 00600 *---------------------------------------------------------------------- 00601 * 00602 * Tcl_HashStats -- 00603 * 00604 * Return statistics describing the layout of the hash table in its hash 00605 * buckets. 00606 * 00607 * Results: 00608 * The return value is a malloc-ed string containing information about 00609 * tablePtr. It is the caller's responsibility to free this string. 00610 * 00611 * Side effects: 00612 * None. 00613 * 00614 *---------------------------------------------------------------------- 00615 */ 00616 00617 const char * 00618 Tcl_HashStats( 00619 Tcl_HashTable *tablePtr) /* Table for which to produce stats. */ 00620 { 00621 #define NUM_COUNTERS 10 00622 int count[NUM_COUNTERS], overflow, i, j; 00623 double average, tmp; 00624 register Tcl_HashEntry *hPtr; 00625 char *result, *p; 00626 const Tcl_HashKeyType *typePtr; 00627 00628 if (tablePtr->keyType == TCL_STRING_KEYS) { 00629 typePtr = &tclStringHashKeyType; 00630 } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) { 00631 typePtr = &tclOneWordHashKeyType; 00632 } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS 00633 || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) { 00634 typePtr = tablePtr->typePtr; 00635 } else { 00636 typePtr = &tclArrayHashKeyType; 00637 } 00638 00639 /* 00640 * Compute a histogram of bucket usage. 00641 */ 00642 00643 for (i = 0; i < NUM_COUNTERS; i++) { 00644 count[i] = 0; 00645 } 00646 overflow = 0; 00647 average = 0.0; 00648 for (i = 0; i < tablePtr->numBuckets; i++) { 00649 j = 0; 00650 for (hPtr = tablePtr->buckets[i]; hPtr != NULL; hPtr = hPtr->nextPtr) { 00651 j++; 00652 } 00653 if (j < NUM_COUNTERS) { 00654 count[j]++; 00655 } else { 00656 overflow++; 00657 } 00658 tmp = j; 00659 if (tablePtr->numEntries != 0) { 00660 average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0; 00661 } 00662 } 00663 00664 /* 00665 * Print out the histogram and a few other pieces of information. 00666 */ 00667 00668 if (typePtr->flags & TCL_HASH_KEY_SYSTEM_HASH) { 00669 result = (char *) TclpSysAlloc((unsigned) (NUM_COUNTERS*60) + 300, 0); 00670 } else { 00671 result = (char *) ckalloc((unsigned) (NUM_COUNTERS*60) + 300); 00672 } 00673 sprintf(result, "%d entries in table, %d buckets\n", 00674 tablePtr->numEntries, tablePtr->numBuckets); 00675 p = result + strlen(result); 00676 for (i = 0; i < NUM_COUNTERS; i++) { 00677 sprintf(p, "number of buckets with %d entries: %d\n", 00678 i, count[i]); 00679 p += strlen(p); 00680 } 00681 sprintf(p, "number of buckets with %d or more entries: %d\n", 00682 NUM_COUNTERS, overflow); 00683 p += strlen(p); 00684 sprintf(p, "average search distance for entry: %.1f", average); 00685 return result; 00686 } 00687 00688 /* 00689 *---------------------------------------------------------------------- 00690 * 00691 * AllocArrayEntry -- 00692 * 00693 * Allocate space for a Tcl_HashEntry containing the array key. 00694 * 00695 * Results: 00696 * The return value is a pointer to the created entry. 00697 * 00698 * Side effects: 00699 * None. 00700 * 00701 *---------------------------------------------------------------------- 00702 */ 00703 00704 static Tcl_HashEntry * 00705 AllocArrayEntry( 00706 Tcl_HashTable *tablePtr, /* Hash table. */ 00707 VOID *keyPtr) /* Key to store in the hash table entry. */ 00708 { 00709 int *array = (int *) keyPtr; 00710 register int *iPtr1, *iPtr2; 00711 Tcl_HashEntry *hPtr; 00712 int count; 00713 unsigned int size; 00714 00715 count = tablePtr->keyType; 00716 00717 size = sizeof(Tcl_HashEntry) + (count*sizeof(int)) - sizeof(hPtr->key); 00718 if (size < sizeof(Tcl_HashEntry)) { 00719 size = sizeof(Tcl_HashEntry); 00720 } 00721 hPtr = (Tcl_HashEntry *) ckalloc(size); 00722 00723 for (iPtr1 = array, iPtr2 = hPtr->key.words; 00724 count > 0; count--, iPtr1++, iPtr2++) { 00725 *iPtr2 = *iPtr1; 00726 } 00727 hPtr->clientData = 0; 00728 00729 return hPtr; 00730 } 00731 00732 /* 00733 *---------------------------------------------------------------------- 00734 * 00735 * CompareArrayKeys -- 00736 * 00737 * Compares two array keys. 00738 * 00739 * Results: 00740 * The return value is 0 if they are different and 1 if they are the 00741 * same. 00742 * 00743 * Side effects: 00744 * None. 00745 * 00746 *---------------------------------------------------------------------- 00747 */ 00748 00749 static int 00750 CompareArrayKeys( 00751 VOID *keyPtr, /* New key to compare. */ 00752 Tcl_HashEntry *hPtr) /* Existing key to compare. */ 00753 { 00754 register const int *iPtr1 = (const int *) keyPtr; 00755 register const int *iPtr2 = (const int *) hPtr->key.words; 00756 Tcl_HashTable *tablePtr = hPtr->tablePtr; 00757 int count; 00758 00759 for (count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) { 00760 if (count == 0) { 00761 return 1; 00762 } 00763 if (*iPtr1 != *iPtr2) { 00764 break; 00765 } 00766 } 00767 return 0; 00768 } 00769 00770 /* 00771 *---------------------------------------------------------------------- 00772 * 00773 * HashArrayKey -- 00774 * 00775 * Compute a one-word summary of an array, which can be used to generate 00776 * a hash index. 00777 * 00778 * Results: 00779 * The return value is a one-word summary of the information in 00780 * string. 00781 * 00782 * Side effects: 00783 * None. 00784 * 00785 *---------------------------------------------------------------------- 00786 */ 00787 00788 static unsigned int 00789 HashArrayKey( 00790 Tcl_HashTable *tablePtr, /* Hash table. */ 00791 VOID *keyPtr) /* Key from which to compute hash value. */ 00792 { 00793 register const int *array = (const int *) keyPtr; 00794 register unsigned int result; 00795 int count; 00796 00797 for (result = 0, count = tablePtr->keyType; count > 0; 00798 count--, array++) { 00799 result += *array; 00800 } 00801 return result; 00802 } 00803 00804 /* 00805 *---------------------------------------------------------------------- 00806 * 00807 * AllocStringEntry -- 00808 * 00809 * Allocate space for a Tcl_HashEntry containing the string key. 00810 * 00811 * Results: 00812 * The return value is a pointer to the created entry. 00813 * 00814 * Side effects: 00815 * None. 00816 * 00817 *---------------------------------------------------------------------- 00818 */ 00819 00820 static Tcl_HashEntry * 00821 AllocStringEntry( 00822 Tcl_HashTable *tablePtr, /* Hash table. */ 00823 VOID *keyPtr) /* Key to store in the hash table entry. */ 00824 { 00825 const char *string = (const char *) keyPtr; 00826 Tcl_HashEntry *hPtr; 00827 unsigned int size; 00828 00829 size = sizeof(Tcl_HashEntry) + strlen(string) + 1 - sizeof(hPtr->key); 00830 if (size < sizeof(Tcl_HashEntry)) { 00831 size = sizeof(Tcl_HashEntry); 00832 } 00833 hPtr = (Tcl_HashEntry *) ckalloc(size); 00834 strcpy(hPtr->key.string, string); 00835 hPtr->clientData = 0; 00836 return hPtr; 00837 } 00838 00839 /* 00840 *---------------------------------------------------------------------- 00841 * 00842 * CompareStringKeys -- 00843 * 00844 * Compares two string keys. 00845 * 00846 * Results: 00847 * The return value is 0 if they are different and 1 if they are the 00848 * same. 00849 * 00850 * Side effects: 00851 * None. 00852 * 00853 *---------------------------------------------------------------------- 00854 */ 00855 00856 static int 00857 CompareStringKeys( 00858 VOID *keyPtr, /* New key to compare. */ 00859 Tcl_HashEntry *hPtr) /* Existing key to compare. */ 00860 { 00861 register const char *p1 = (const char *) keyPtr; 00862 register const char *p2 = (const char *) hPtr->key.string; 00863 00864 return !strcmp(p1, p2); 00865 } 00866 00867 /* 00868 *---------------------------------------------------------------------- 00869 * 00870 * HashStringKey -- 00871 * 00872 * Compute a one-word summary of a text string, which can be used to 00873 * generate a hash index. 00874 * 00875 * Results: 00876 * The return value is a one-word summary of the information in string. 00877 * 00878 * Side effects: 00879 * None. 00880 * 00881 *---------------------------------------------------------------------- 00882 */ 00883 00884 static unsigned int 00885 HashStringKey( 00886 Tcl_HashTable *tablePtr, /* Hash table. */ 00887 VOID *keyPtr) /* Key from which to compute hash value. */ 00888 { 00889 register const char *string = (const char *) keyPtr; 00890 register unsigned int result; 00891 register int c; 00892 00893 /* 00894 * I tried a zillion different hash functions and asked many other people 00895 * for advice. Many people had their own favorite functions, all 00896 * different, but no-one had much idea why they were good ones. I chose 00897 * the one below (multiply by 9 and add new character) because of the 00898 * following reasons: 00899 * 00900 * 1. Multiplying by 10 is perfect for keys that are decimal strings, and 00901 * multiplying by 9 is just about as good. 00902 * 2. Times-9 is (shift-left-3) plus (old). This means that each 00903 * character's bits hang around in the low-order bits of the hash value 00904 * for ever, plus they spread fairly rapidly up to the high-order bits 00905 * to fill out the hash value. This seems works well both for decimal 00906 * and non-decimal strings, but isn't strong against maliciously-chosen 00907 * keys. 00908 */ 00909 00910 result = 0; 00911 00912 for (c=*string++ ; c ; c=*string++) { 00913 result += (result<<3) + c; 00914 } 00915 return result; 00916 } 00917 00918 /* 00919 *---------------------------------------------------------------------- 00920 * 00921 * BogusFind -- 00922 * 00923 * This function is invoked when an Tcl_FindHashEntry is called on a 00924 * table that has been deleted. 00925 * 00926 * Results: 00927 * If Tcl_Panic returns (which it shouldn't) this function returns NULL. 00928 * 00929 * Side effects: 00930 * Generates a panic. 00931 * 00932 *---------------------------------------------------------------------- 00933 */ 00934 00935 /* ARGSUSED */ 00936 static Tcl_HashEntry * 00937 BogusFind( 00938 Tcl_HashTable *tablePtr, /* Table in which to lookup entry. */ 00939 const char *key) /* Key to use to find matching entry. */ 00940 { 00941 Tcl_Panic("called %s on deleted table", "Tcl_FindHashEntry"); 00942 return NULL; 00943 } 00944 00945 /* 00946 *---------------------------------------------------------------------- 00947 * 00948 * BogusCreate -- 00949 * 00950 * This function is invoked when an Tcl_CreateHashEntry is called on a 00951 * table that has been deleted. 00952 * 00953 * Results: 00954 * If panic returns (which it shouldn't) this function returns NULL. 00955 * 00956 * Side effects: 00957 * Generates a panic. 00958 * 00959 *---------------------------------------------------------------------- 00960 */ 00961 00962 /* ARGSUSED */ 00963 static Tcl_HashEntry * 00964 BogusCreate( 00965 Tcl_HashTable *tablePtr, /* Table in which to lookup entry. */ 00966 const char *key, /* Key to use to find or create matching 00967 * entry. */ 00968 int *newPtr) /* Store info here telling whether a new entry 00969 * was created. */ 00970 { 00971 Tcl_Panic("called %s on deleted table", "Tcl_CreateHashEntry"); 00972 return NULL; 00973 } 00974 00975 /* 00976 *---------------------------------------------------------------------- 00977 * 00978 * RebuildTable -- 00979 * 00980 * This function is invoked when the ratio of entries to hash buckets 00981 * becomes too large. It creates a new table with a larger bucket array 00982 * and moves all of the entries into the new table. 00983 * 00984 * Results: 00985 * None. 00986 * 00987 * Side effects: 00988 * Memory gets reallocated and entries get re-hashed to new buckets. 00989 * 00990 *---------------------------------------------------------------------- 00991 */ 00992 00993 static void 00994 RebuildTable( 00995 register Tcl_HashTable *tablePtr) /* Table to enlarge. */ 00996 { 00997 int oldSize, count, index; 00998 Tcl_HashEntry **oldBuckets; 00999 register Tcl_HashEntry **oldChainPtr, **newChainPtr; 01000 register Tcl_HashEntry *hPtr; 01001 const Tcl_HashKeyType *typePtr; 01002 01003 if (tablePtr->keyType == TCL_STRING_KEYS) { 01004 typePtr = &tclStringHashKeyType; 01005 } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) { 01006 typePtr = &tclOneWordHashKeyType; 01007 } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS 01008 || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) { 01009 typePtr = tablePtr->typePtr; 01010 } else { 01011 typePtr = &tclArrayHashKeyType; 01012 } 01013 01014 oldSize = tablePtr->numBuckets; 01015 oldBuckets = tablePtr->buckets; 01016 01017 /* 01018 * Allocate and initialize the new bucket array, and set up hashing 01019 * constants for new array size. 01020 */ 01021 01022 tablePtr->numBuckets *= 4; 01023 if (typePtr->flags & TCL_HASH_KEY_SYSTEM_HASH) { 01024 tablePtr->buckets = (Tcl_HashEntry **) TclpSysAlloc((unsigned) 01025 (tablePtr->numBuckets * sizeof(Tcl_HashEntry *)), 0); 01026 } else { 01027 tablePtr->buckets = (Tcl_HashEntry **) ckalloc((unsigned) 01028 (tablePtr->numBuckets * sizeof(Tcl_HashEntry *))); 01029 } 01030 for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets; 01031 count > 0; count--, newChainPtr++) { 01032 *newChainPtr = NULL; 01033 } 01034 tablePtr->rebuildSize *= 4; 01035 tablePtr->downShift -= 2; 01036 tablePtr->mask = (tablePtr->mask << 2) + 3; 01037 01038 /* 01039 * Rehash all of the existing entries into the new bucket array. 01040 */ 01041 01042 for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) { 01043 for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) { 01044 *oldChainPtr = hPtr->nextPtr; 01045 #if TCL_HASH_KEY_STORE_HASH 01046 if (typePtr->hashKeyProc == NULL 01047 || typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) { 01048 index = RANDOM_INDEX (tablePtr, hPtr->hash); 01049 } else { 01050 index = PTR2UINT(hPtr->hash) & tablePtr->mask; 01051 } 01052 hPtr->nextPtr = tablePtr->buckets[index]; 01053 tablePtr->buckets[index] = hPtr; 01054 #else 01055 VOID *key = (VOID *) Tcl_GetHashKey(tablePtr, hPtr); 01056 01057 if (typePtr->hashKeyProc) { 01058 unsigned int hash; 01059 01060 hash = typePtr->hashKeyProc(tablePtr, key); 01061 if (typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) { 01062 index = RANDOM_INDEX (tablePtr, hash); 01063 } else { 01064 index = hash & tablePtr->mask; 01065 } 01066 } else { 01067 index = RANDOM_INDEX (tablePtr, key); 01068 } 01069 01070 hPtr->bucketPtr = &(tablePtr->buckets[index]); 01071 hPtr->nextPtr = *hPtr->bucketPtr; 01072 *hPtr->bucketPtr = hPtr; 01073 #endif 01074 } 01075 } 01076 01077 /* 01078 * Free up the old bucket array, if it was dynamically allocated. 01079 */ 01080 01081 if (oldBuckets != tablePtr->staticBuckets) { 01082 if (typePtr->flags & TCL_HASH_KEY_SYSTEM_HASH) { 01083 TclpSysFree((char *) oldBuckets); 01084 } else { 01085 ckfree((char *) oldBuckets); 01086 } 01087 } 01088 } 01089 01090 /* 01091 * Local Variables: 01092 * mode: c 01093 * c-basic-offset: 4 01094 * fill-column: 78 01095 * End: 01096 */
Generated on Wed Mar 12 12:18:16 2008 by 1.5.1 |