tclHash.c

Go 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  doxygen 1.5.1