/*1* tclHash.c --2*3* Implementation of in-memory hash tables for Tcl and Tcl-based4* applications.5*6* Copyright (c) 1991-1993 The Regents of the University of California.7* Copyright (c) 1994 Sun Microsystems, Inc.8*9* See the file "license.terms" for information on usage and redistribution10* of this file, and for a DISCLAIMER OF ALL WARRANTIES.11*12* SCCS: @(#) tclHash.c 1.15 96/02/15 11:50:2313*/1415#include "tclInt.h"1617/*18* When there are this many entries per bucket, on average, rebuild19* the hash table to make it larger.20*/2122#define REBUILD_MULTIPLIER 3232425/*26* The following macro takes a preliminary integer hash value and27* produces an index into a hash tables bucket list. The idea is28* to make it so that preliminary values that are arbitrarily similar29* will end up in different buckets. The hash function was taken30* from a random-number generator.31*/3233#define RANDOM_INDEX(tablePtr, i) \34(((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)3536/*37* Procedure prototypes for static procedures in this file:38*/3940static Tcl_HashEntry * ArrayFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,41char *key));42static Tcl_HashEntry * ArrayCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,43char *key, int *newPtr));44static Tcl_HashEntry * BogusFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,45char *key));46static Tcl_HashEntry * BogusCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,47char *key, int *newPtr));48static unsigned int HashString _ANSI_ARGS_((char *string));49static void RebuildTable _ANSI_ARGS_((Tcl_HashTable *tablePtr));50static Tcl_HashEntry * StringFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,51char *key));52static Tcl_HashEntry * StringCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,53char *key, int *newPtr));54static Tcl_HashEntry * OneWordFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,55char *key));56static Tcl_HashEntry * OneWordCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,57char *key, int *newPtr));5859/*60*----------------------------------------------------------------------61*62* Tcl_InitHashTable --63*64* Given storage for a hash table, set up the fields to prepare65* the hash table for use.66*67* Results:68* None.69*70* Side effects:71* TablePtr is now ready to be passed to Tcl_FindHashEntry and72* Tcl_CreateHashEntry.73*74*----------------------------------------------------------------------75*/7677void78Tcl_InitHashTable(tablePtr, keyType)79register Tcl_HashTable *tablePtr; /* Pointer to table record, which80* is supplied by the caller. */81int keyType; /* Type of keys to use in table:82* TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,83* or an integer >= 2. */84{85tablePtr->buckets = tablePtr->staticBuckets;86tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;87tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;88tablePtr->numBuckets = TCL_SMALL_HASH_TABLE;89tablePtr->numEntries = 0;90tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER;91tablePtr->downShift = 28;92tablePtr->mask = 3;93tablePtr->keyType = keyType;94if (keyType == TCL_STRING_KEYS) {95tablePtr->findProc = StringFind;96tablePtr->createProc = StringCreate;97} else if (keyType == TCL_ONE_WORD_KEYS) {98tablePtr->findProc = OneWordFind;99tablePtr->createProc = OneWordCreate;100} else {101tablePtr->findProc = ArrayFind;102tablePtr->createProc = ArrayCreate;103};104}105106/*107*----------------------------------------------------------------------108*109* Tcl_DeleteHashEntry --110*111* Remove a single entry from a hash table.112*113* Results:114* None.115*116* Side effects:117* The entry given by entryPtr is deleted from its table and118* should never again be used by the caller. It is up to the119* caller to free the clientData field of the entry, if that120* is relevant.121*122*----------------------------------------------------------------------123*/124125void126Tcl_DeleteHashEntry(entryPtr)127Tcl_HashEntry *entryPtr;128{129register Tcl_HashEntry *prevPtr;130131if (*entryPtr->bucketPtr == entryPtr) {132*entryPtr->bucketPtr = entryPtr->nextPtr;133} else {134for (prevPtr = *entryPtr->bucketPtr; ; prevPtr = prevPtr->nextPtr) {135if (prevPtr == NULL) {136panic("malformed bucket chain in Tcl_DeleteHashEntry");137}138if (prevPtr->nextPtr == entryPtr) {139prevPtr->nextPtr = entryPtr->nextPtr;140break;141}142}143}144entryPtr->tablePtr->numEntries--;145ckfree((char *) entryPtr);146}147148/*149*----------------------------------------------------------------------150*151* Tcl_DeleteHashTable --152*153* Free up everything associated with a hash table except for154* the record for the table itself.155*156* Results:157* None.158*159* Side effects:160* The hash table is no longer useable.161*162*----------------------------------------------------------------------163*/164165void166Tcl_DeleteHashTable(tablePtr)167register Tcl_HashTable *tablePtr; /* Table to delete. */168{169register Tcl_HashEntry *hPtr, *nextPtr;170int i;171172/*173* Free up all the entries in the table.174*/175176for (i = 0; i < tablePtr->numBuckets; i++) {177hPtr = tablePtr->buckets[i];178while (hPtr != NULL) {179nextPtr = hPtr->nextPtr;180ckfree((char *) hPtr);181hPtr = nextPtr;182}183}184185/*186* Free up the bucket array, if it was dynamically allocated.187*/188189if (tablePtr->buckets != tablePtr->staticBuckets) {190ckfree((char *) tablePtr->buckets);191}192193/*194* Arrange for panics if the table is used again without195* re-initialization.196*/197198tablePtr->findProc = BogusFind;199tablePtr->createProc = BogusCreate;200}201202/*203*----------------------------------------------------------------------204*205* Tcl_FirstHashEntry --206*207* Locate the first entry in a hash table and set up a record208* that can be used to step through all the remaining entries209* of the table.210*211* Results:212* The return value is a pointer to the first entry in tablePtr,213* or NULL if tablePtr has no entries in it. The memory at214* *searchPtr is initialized so that subsequent calls to215* Tcl_NextHashEntry will return all of the entries in the table,216* one at a time.217*218* Side effects:219* None.220*221*----------------------------------------------------------------------222*/223224Tcl_HashEntry *225Tcl_FirstHashEntry(tablePtr, searchPtr)226Tcl_HashTable *tablePtr; /* Table to search. */227Tcl_HashSearch *searchPtr; /* Place to store information about228* progress through the table. */229{230searchPtr->tablePtr = tablePtr;231searchPtr->nextIndex = 0;232searchPtr->nextEntryPtr = NULL;233return Tcl_NextHashEntry(searchPtr);234}235236/*237*----------------------------------------------------------------------238*239* Tcl_NextHashEntry --240*241* Once a hash table enumeration has been initiated by calling242* Tcl_FirstHashEntry, this procedure may be called to return243* successive elements of the table.244*245* Results:246* The return value is the next entry in the hash table being247* enumerated, or NULL if the end of the table is reached.248*249* Side effects:250* None.251*252*----------------------------------------------------------------------253*/254255Tcl_HashEntry *256Tcl_NextHashEntry(searchPtr)257register Tcl_HashSearch *searchPtr; /* Place to store information about258* progress through the table. Must259* have been initialized by calling260* Tcl_FirstHashEntry. */261{262Tcl_HashEntry *hPtr;263264while (searchPtr->nextEntryPtr == NULL) {265if (searchPtr->nextIndex >= searchPtr->tablePtr->numBuckets) {266return NULL;267}268searchPtr->nextEntryPtr =269searchPtr->tablePtr->buckets[searchPtr->nextIndex];270searchPtr->nextIndex++;271}272hPtr = searchPtr->nextEntryPtr;273searchPtr->nextEntryPtr = hPtr->nextPtr;274return hPtr;275}276277/*278*----------------------------------------------------------------------279*280* Tcl_HashStats --281*282* Return statistics describing the layout of the hash table283* in its hash buckets.284*285* Results:286* The return value is a malloc-ed string containing information287* about tablePtr. It is the caller's responsibility to free288* this string.289*290* Side effects:291* None.292*293*----------------------------------------------------------------------294*/295296char *297Tcl_HashStats(tablePtr)298Tcl_HashTable *tablePtr; /* Table for which to produce stats. */299{300#define NUM_COUNTERS 10301int count[NUM_COUNTERS], overflow, i, j;302double average, tmp;303register Tcl_HashEntry *hPtr;304char *result, *p;305306/*307* Compute a histogram of bucket usage.308*/309310for (i = 0; i < NUM_COUNTERS; i++) {311count[i] = 0;312}313overflow = 0;314average = 0.0;315for (i = 0; i < tablePtr->numBuckets; i++) {316j = 0;317for (hPtr = tablePtr->buckets[i]; hPtr != NULL; hPtr = hPtr->nextPtr) {318j++;319}320if (j < NUM_COUNTERS) {321count[j]++;322} else {323overflow++;324}325tmp = j;326average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;327}328329/*330* Print out the histogram and a few other pieces of information.331*/332333result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300));334sprintf(result, "%d entries in table, %d buckets\n",335tablePtr->numEntries, tablePtr->numBuckets);336p = result + strlen(result);337for (i = 0; i < NUM_COUNTERS; i++) {338sprintf(p, "number of buckets with %d entries: %d\n",339i, count[i]);340p += strlen(p);341}342sprintf(p, "number of buckets with %d or more entries: %d\n",343NUM_COUNTERS, overflow);344p += strlen(p);345sprintf(p, "average search distance for entry: %.1f", average);346return result;347}348349/*350*----------------------------------------------------------------------351*352* HashString --353*354* Compute a one-word summary of a text string, which can be355* used to generate a hash index.356*357* Results:358* The return value is a one-word summary of the information in359* string.360*361* Side effects:362* None.363*364*----------------------------------------------------------------------365*/366367static unsigned int368HashString(string)369register char *string; /* String from which to compute hash value. */370{371register unsigned int result;372register int c;373374/*375* I tried a zillion different hash functions and asked many other376* people for advice. Many people had their own favorite functions,377* all different, but no-one had much idea why they were good ones.378* I chose the one below (multiply by 9 and add new character)379* because of the following reasons:380*381* 1. Multiplying by 10 is perfect for keys that are decimal strings,382* and multiplying by 9 is just about as good.383* 2. Times-9 is (shift-left-3) plus (old). This means that each384* character's bits hang around in the low-order bits of the385* hash value for ever, plus they spread fairly rapidly up to386* the high-order bits to fill out the hash value. This seems387* works well both for decimal and non-decimal strings.388*/389390result = 0;391while (1) {392c = *string;393string++;394if (c == 0) {395break;396}397result += (result<<3) + c;398}399return result;400}401402/*403*----------------------------------------------------------------------404*405* StringFind --406*407* Given a hash table with string keys, and a string key, find408* the entry with a matching key.409*410* Results:411* The return value is a token for the matching entry in the412* hash table, or NULL if there was no matching entry.413*414* Side effects:415* None.416*417*----------------------------------------------------------------------418*/419420static Tcl_HashEntry *421StringFind(tablePtr, key)422Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */423char *key; /* Key to use to find matching entry. */424{425register Tcl_HashEntry *hPtr;426register char *p1, *p2;427int index;428429index = HashString(key) & tablePtr->mask;430431/*432* Search all of the entries in the appropriate bucket.433*/434435for (hPtr = tablePtr->buckets[index]; hPtr != NULL;436hPtr = hPtr->nextPtr) {437for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {438if (*p1 != *p2) {439break;440}441if (*p1 == '\0') {442return hPtr;443}444}445}446return NULL;447}448449/*450*----------------------------------------------------------------------451*452* StringCreate --453*454* Given a hash table with string keys, and a string key, find455* the entry with a matching key. If there is no matching entry,456* then create a new entry that does match.457*458* Results:459* The return value is a pointer to the matching entry. If this460* is a newly-created entry, then *newPtr will be set to a non-zero461* value; otherwise *newPtr will be set to 0. If this is a new462* entry the value stored in the entry will initially be 0.463*464* Side effects:465* A new entry may be added to the hash table.466*467*----------------------------------------------------------------------468*/469470static Tcl_HashEntry *471StringCreate(tablePtr, key, newPtr)472Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */473char *key; /* Key to use to find or create matching474* entry. */475int *newPtr; /* Store info here telling whether a new476* entry was created. */477{478register Tcl_HashEntry *hPtr;479register char *p1, *p2;480int index;481482index = HashString(key) & tablePtr->mask;483484/*485* Search all of the entries in this bucket.486*/487488for (hPtr = tablePtr->buckets[index]; hPtr != NULL;489hPtr = hPtr->nextPtr) {490for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {491if (*p1 != *p2) {492break;493}494if (*p1 == '\0') {495*newPtr = 0;496return hPtr;497}498}499}500501/*502* Entry not found. Add a new one to the bucket.503*/504505*newPtr = 1;506hPtr = (Tcl_HashEntry *) ckalloc((unsigned)507(sizeof(Tcl_HashEntry) + strlen(key) - (sizeof(hPtr->key) -1)));508hPtr->tablePtr = tablePtr;509hPtr->bucketPtr = &(tablePtr->buckets[index]);510hPtr->nextPtr = *hPtr->bucketPtr;511hPtr->clientData = 0;512strcpy(hPtr->key.string, key);513*hPtr->bucketPtr = hPtr;514tablePtr->numEntries++;515516/*517* If the table has exceeded a decent size, rebuild it with many518* more buckets.519*/520521if (tablePtr->numEntries >= tablePtr->rebuildSize) {522RebuildTable(tablePtr);523}524return hPtr;525}526527/*528*----------------------------------------------------------------------529*530* OneWordFind --531*532* Given a hash table with one-word keys, and a one-word key, find533* the entry with a matching key.534*535* Results:536* The return value is a token for the matching entry in the537* hash table, or NULL if there was no matching entry.538*539* Side effects:540* None.541*542*----------------------------------------------------------------------543*/544545static Tcl_HashEntry *546OneWordFind(tablePtr, key)547Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */548register char *key; /* Key to use to find matching entry. */549{550register Tcl_HashEntry *hPtr;551int index;552553index = RANDOM_INDEX(tablePtr, key);554555/*556* Search all of the entries in the appropriate bucket.557*/558559for (hPtr = tablePtr->buckets[index]; hPtr != NULL;560hPtr = hPtr->nextPtr) {561if (hPtr->key.oneWordValue == key) {562return hPtr;563}564}565return NULL;566}567568/*569*----------------------------------------------------------------------570*571* OneWordCreate --572*573* Given a hash table with one-word keys, and a one-word key, find574* the entry with a matching key. If there is no matching entry,575* then create a new entry that does match.576*577* Results:578* The return value is a pointer to the matching entry. If this579* is a newly-created entry, then *newPtr will be set to a non-zero580* value; otherwise *newPtr will be set to 0. If this is a new581* entry the value stored in the entry will initially be 0.582*583* Side effects:584* A new entry may be added to the hash table.585*586*----------------------------------------------------------------------587*/588589static Tcl_HashEntry *590OneWordCreate(tablePtr, key, newPtr)591Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */592register char *key; /* Key to use to find or create matching593* entry. */594int *newPtr; /* Store info here telling whether a new595* entry was created. */596{597register Tcl_HashEntry *hPtr;598int index;599600index = RANDOM_INDEX(tablePtr, key);601602/*603* Search all of the entries in this bucket.604*/605606for (hPtr = tablePtr->buckets[index]; hPtr != NULL;607hPtr = hPtr->nextPtr) {608if (hPtr->key.oneWordValue == key) {609*newPtr = 0;610return hPtr;611}612}613614/*615* Entry not found. Add a new one to the bucket.616*/617618*newPtr = 1;619hPtr = (Tcl_HashEntry *) ckalloc(sizeof(Tcl_HashEntry));620hPtr->tablePtr = tablePtr;621hPtr->bucketPtr = &(tablePtr->buckets[index]);622hPtr->nextPtr = *hPtr->bucketPtr;623hPtr->clientData = 0;624hPtr->key.oneWordValue = key;625*hPtr->bucketPtr = hPtr;626tablePtr->numEntries++;627628/*629* If the table has exceeded a decent size, rebuild it with many630* more buckets.631*/632633if (tablePtr->numEntries >= tablePtr->rebuildSize) {634RebuildTable(tablePtr);635}636return hPtr;637}638639/*640*----------------------------------------------------------------------641*642* ArrayFind --643*644* Given a hash table with array-of-int keys, and a key, find645* the entry with a matching key.646*647* Results:648* The return value is a token for the matching entry in the649* hash table, or NULL if there was no matching entry.650*651* Side effects:652* None.653*654*----------------------------------------------------------------------655*/656657static Tcl_HashEntry *658ArrayFind(tablePtr, key)659Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */660char *key; /* Key to use to find matching entry. */661{662register Tcl_HashEntry *hPtr;663int *arrayPtr = (int *) key;664register int *iPtr1, *iPtr2;665int index, count;666667for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;668count > 0; count--, iPtr1++) {669index += *iPtr1;670}671index = RANDOM_INDEX(tablePtr, index);672673/*674* Search all of the entries in the appropriate bucket.675*/676677for (hPtr = tablePtr->buckets[index]; hPtr != NULL;678hPtr = hPtr->nextPtr) {679for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,680count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {681if (count == 0) {682return hPtr;683}684if (*iPtr1 != *iPtr2) {685break;686}687}688}689return NULL;690}691692/*693*----------------------------------------------------------------------694*695* ArrayCreate --696*697* Given a hash table with one-word keys, and a one-word key, find698* the entry with a matching key. If there is no matching entry,699* then create a new entry that does match.700*701* Results:702* The return value is a pointer to the matching entry. If this703* is a newly-created entry, then *newPtr will be set to a non-zero704* value; otherwise *newPtr will be set to 0. If this is a new705* entry the value stored in the entry will initially be 0.706*707* Side effects:708* A new entry may be added to the hash table.709*710*----------------------------------------------------------------------711*/712713static Tcl_HashEntry *714ArrayCreate(tablePtr, key, newPtr)715Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */716register char *key; /* Key to use to find or create matching717* entry. */718int *newPtr; /* Store info here telling whether a new719* entry was created. */720{721register Tcl_HashEntry *hPtr;722int *arrayPtr = (int *) key;723register int *iPtr1, *iPtr2;724int index, count;725726for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;727count > 0; count--, iPtr1++) {728index += *iPtr1;729}730index = RANDOM_INDEX(tablePtr, index);731732/*733* Search all of the entries in the appropriate bucket.734*/735736for (hPtr = tablePtr->buckets[index]; hPtr != NULL;737hPtr = hPtr->nextPtr) {738for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,739count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {740if (count == 0) {741*newPtr = 0;742return hPtr;743}744if (*iPtr1 != *iPtr2) {745break;746}747}748}749750/*751* Entry not found. Add a new one to the bucket.752*/753754*newPtr = 1;755hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry)756+ (tablePtr->keyType*sizeof(int)) - 4));757hPtr->tablePtr = tablePtr;758hPtr->bucketPtr = &(tablePtr->buckets[index]);759hPtr->nextPtr = *hPtr->bucketPtr;760hPtr->clientData = 0;761for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words, count = tablePtr->keyType;762count > 0; count--, iPtr1++, iPtr2++) {763*iPtr2 = *iPtr1;764}765*hPtr->bucketPtr = hPtr;766tablePtr->numEntries++;767768/*769* If the table has exceeded a decent size, rebuild it with many770* more buckets.771*/772773if (tablePtr->numEntries >= tablePtr->rebuildSize) {774RebuildTable(tablePtr);775}776return hPtr;777}778779/*780*----------------------------------------------------------------------781*782* BogusFind --783*784* This procedure is invoked when an Tcl_FindHashEntry is called785* on a table that has been deleted.786*787* Results:788* If panic returns (which it shouldn't) this procedure returns789* NULL.790*791* Side effects:792* Generates a panic.793*794*----------------------------------------------------------------------795*/796797/* ARGSUSED */798static Tcl_HashEntry *799BogusFind(tablePtr, key)800Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */801char *key; /* Key to use to find matching entry. */802{803panic("called Tcl_FindHashEntry on deleted table");804return NULL;805}806807/*808*----------------------------------------------------------------------809*810* BogusCreate --811*812* This procedure is invoked when an Tcl_CreateHashEntry is called813* on a table that has been deleted.814*815* Results:816* If panic returns (which it shouldn't) this procedure returns817* NULL.818*819* Side effects:820* Generates a panic.821*822*----------------------------------------------------------------------823*/824825/* ARGSUSED */826static Tcl_HashEntry *827BogusCreate(tablePtr, key, newPtr)828Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */829char *key; /* Key to use to find or create matching830* entry. */831int *newPtr; /* Store info here telling whether a new832* entry was created. */833{834panic("called Tcl_CreateHashEntry on deleted table");835return NULL;836}837838/*839*----------------------------------------------------------------------840*841* RebuildTable --842*843* This procedure is invoked when the ratio of entries to hash844* buckets becomes too large. It creates a new table with a845* larger bucket array and moves all of the entries into the846* new table.847*848* Results:849* None.850*851* Side effects:852* Memory gets reallocated and entries get re-hashed to new853* buckets.854*855*----------------------------------------------------------------------856*/857858static void859RebuildTable(tablePtr)860register Tcl_HashTable *tablePtr; /* Table to enlarge. */861{862int oldSize, count, index;863Tcl_HashEntry **oldBuckets;864register Tcl_HashEntry **oldChainPtr, **newChainPtr;865register Tcl_HashEntry *hPtr;866867oldSize = tablePtr->numBuckets;868oldBuckets = tablePtr->buckets;869870/*871* Allocate and initialize the new bucket array, and set up872* hashing constants for new array size.873*/874875tablePtr->numBuckets *= 4;876tablePtr->buckets = (Tcl_HashEntry **) ckalloc((unsigned)877(tablePtr->numBuckets * sizeof(Tcl_HashEntry *)));878for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets;879count > 0; count--, newChainPtr++) {880*newChainPtr = NULL;881}882tablePtr->rebuildSize *= 4;883tablePtr->downShift -= 2;884tablePtr->mask = (tablePtr->mask << 2) + 3;885886/*887* Rehash all of the existing entries into the new bucket array.888*/889890for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) {891for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) {892*oldChainPtr = hPtr->nextPtr;893if (tablePtr->keyType == TCL_STRING_KEYS) {894index = HashString(hPtr->key.string) & tablePtr->mask;895} else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {896index = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);897} else {898register int *iPtr;899int count;900901for (index = 0, count = tablePtr->keyType,902iPtr = hPtr->key.words; count > 0; count--, iPtr++) {903index += *iPtr;904}905index = RANDOM_INDEX(tablePtr, index);906}907hPtr->bucketPtr = &(tablePtr->buckets[index]);908hPtr->nextPtr = *hPtr->bucketPtr;909*hPtr->bucketPtr = hPtr;910}911}912913/*914* Free up the old bucket array, if it was dynamically allocated.915*/916917if (oldBuckets != tablePtr->staticBuckets) {918ckfree((char *) oldBuckets);919}920}921922923