Annotation of micropolis/src/tcl/tclhash.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tclHash.c --
                      3:  *
                      4:  *     Implementation of in-memory hash tables for Tcl and Tcl-based
                      5:  *     applications.
                      6:  *
                      7:  * Copyright 1991 Regents of the University of California
                      8:  * Permission to use, copy, modify, and distribute this
                      9:  * software and its documentation for any purpose and without
                     10:  * fee is hereby granted, provided that this copyright
                     11:  * notice appears in all copies.  The University of California
                     12:  * makes no representations about the suitability of this
                     13:  * software for any purpose.  It is provided "as is" without
                     14:  * express or implied warranty.
                     15:  */
                     16: 
                     17: #ifndef lint
                     18: static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclHash.c,v 1.9 92/01/04 15:45:21 ouster Exp $ SPRITE (Berkeley)";
                     19: #endif /* not lint */
                     20: 
                     21: #include "tclint.h"
                     22: 
                     23: /*
                     24:  * Imported library procedures for which there are no header files:
                     25:  */
                     26: 
                     27: extern void panic();
                     28: 
                     29: /*
                     30:  * When there are this many entries per bucket, on average, rebuild
                     31:  * the hash table to make it larger.
                     32:  */
                     33: 
                     34: #define REBUILD_MULTIPLIER     3
                     35: 
                     36: 
                     37: /*
                     38:  * The following macro takes a preliminary integer hash value and
                     39:  * produces an index into a hash tables bucket list.  The idea is
                     40:  * to make it so that preliminary values that are arbitrarily similar
                     41:  * will end up in different buckets.  The hash function was taken
                     42:  * from a random-number generator.
                     43:  */
                     44: 
                     45: #define RANDOM_INDEX(tablePtr, i) \
                     46:     (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
                     47: 
                     48: /*
                     49:  * Procedure prototypes for static procedures in this file:
                     50:  */
                     51: 
                     52: static Tcl_HashEntry * ArrayFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     53:                            char *key));
                     54: static Tcl_HashEntry * ArrayCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     55:                            char *key, int *newPtr));
                     56: static Tcl_HashEntry * BogusFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     57:                            char *key));
                     58: static Tcl_HashEntry * BogusCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     59:                            char *key, int *newPtr));
                     60: static int             HashString _ANSI_ARGS_((char *string));
                     61: static void            RebuildTable _ANSI_ARGS_((Tcl_HashTable *tablePtr));
                     62: static Tcl_HashEntry * StringFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     63:                            char *key));
                     64: static Tcl_HashEntry * StringCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     65:                            char *key, int *newPtr));
                     66: static Tcl_HashEntry * OneWordFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     67:                            char *key));
                     68: static Tcl_HashEntry * OneWordCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
                     69:                            char *key, int *newPtr));
                     70: 
                     71: /*
                     72:  *----------------------------------------------------------------------
                     73:  *
                     74:  * Tcl_InitHashTable --
                     75:  *
                     76:  *     Given storage for a hash table, set up the fields to prepare
                     77:  *     the hash table for use.
                     78:  *
                     79:  * Results:
                     80:  *     None.
                     81:  *
                     82:  * Side effects:
                     83:  *     TablePtr is now ready to be passed to Tcl_FindHashEntry and
                     84:  *     Tcl_CreateHashEntry.
                     85:  *
                     86:  *----------------------------------------------------------------------
                     87:  */
                     88: 
                     89: void
                     90: Tcl_InitHashTable(tablePtr, keyType)
                     91:     register Tcl_HashTable *tablePtr;  /* Pointer to table record, which
                     92:                                         * is supplied by the caller. */
                     93:     int keyType;                       /* Type of keys to use in table:
                     94:                                         * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
                     95:                                         * or an integer >= 2. */
                     96: {
                     97:     tablePtr->buckets = tablePtr->staticBuckets;
                     98:     tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
                     99:     tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
                    100:     tablePtr->numBuckets = TCL_SMALL_HASH_TABLE;
                    101:     tablePtr->numEntries = 0;
                    102:     tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER;
                    103:     tablePtr->downShift = 28;
                    104:     tablePtr->mask = 3;
                    105:     tablePtr->keyType = keyType;
                    106:     if (keyType == TCL_STRING_KEYS) {
                    107:        tablePtr->findProc = StringFind;
                    108:        tablePtr->createProc = StringCreate;
                    109:     } else if (keyType == TCL_ONE_WORD_KEYS) {
                    110:        tablePtr->findProc = OneWordFind;
                    111:        tablePtr->createProc = OneWordCreate;
                    112:     } else {
                    113:        tablePtr->findProc = ArrayFind;
                    114:        tablePtr->createProc = ArrayCreate;
                    115:     };
                    116: }
                    117: 
                    118: /*
                    119:  *----------------------------------------------------------------------
                    120:  *
                    121:  * Tcl_DeleteHashEntry --
                    122:  *
                    123:  *     Remove a single entry from a hash table.
                    124:  *
                    125:  * Results:
                    126:  *     None.
                    127:  *
                    128:  * Side effects:
                    129:  *     The entry given by entryPtr is deleted from its table and
                    130:  *     should never again be used by the caller.  It is up to the
                    131:  *     caller to free the clientData field of the entry, if that
                    132:  *     is relevant.
                    133:  *
                    134:  *----------------------------------------------------------------------
                    135:  */
                    136: 
                    137: void
                    138: Tcl_DeleteHashEntry(entryPtr)
                    139:     Tcl_HashEntry *entryPtr;
                    140: {
                    141:     register Tcl_HashEntry *prevPtr;
                    142: 
                    143:     if (*entryPtr->bucketPtr == entryPtr) {
                    144:        *entryPtr->bucketPtr = entryPtr->nextPtr;
                    145:     } else {
                    146:        for (prevPtr = *entryPtr->bucketPtr; ; prevPtr = prevPtr->nextPtr) {
                    147:            if (prevPtr == NULL) {
                    148:                panic("malformed bucket chain in Tcl_DeleteHashEntry");
                    149:            }
                    150:            if (prevPtr->nextPtr == entryPtr) {
                    151:                prevPtr->nextPtr = entryPtr->nextPtr;
                    152:                break;
                    153:            }
                    154:        }
                    155:     }
                    156:     entryPtr->tablePtr->numEntries--;
                    157:     ckfree((char *) entryPtr);
                    158: }
                    159: 
                    160: /*
                    161:  *----------------------------------------------------------------------
                    162:  *
                    163:  * Tcl_DeleteHashTable --
                    164:  *
                    165:  *     Free up everything associated with a hash table except for
                    166:  *     the record for the table itself.
                    167:  *
                    168:  * Results:
                    169:  *     None.
                    170:  *
                    171:  * Side effects:
                    172:  *     The hash table is no longer useable.
                    173:  *
                    174:  *----------------------------------------------------------------------
                    175:  */
                    176: 
                    177: void
                    178: Tcl_DeleteHashTable(tablePtr)
                    179:     register Tcl_HashTable *tablePtr;          /* Table to delete. */
                    180: {
                    181:     register Tcl_HashEntry *hPtr, *nextPtr;
                    182:     int i;
                    183: 
                    184:     /*
                    185:      * Free up all the entries in the table.
                    186:      */
                    187: 
                    188:     for (i = 0; i < tablePtr->numBuckets; i++) {
                    189:        hPtr = tablePtr->buckets[i];
                    190:        while (hPtr != NULL) {
                    191:            nextPtr = hPtr->nextPtr;
                    192:            ckfree((char *) hPtr);
                    193:            hPtr = nextPtr;
                    194:        }
                    195:     }
                    196: 
                    197:     /*
                    198:      * Free up the bucket array, if it was dynamically allocated.
                    199:      */
                    200: 
                    201:     if (tablePtr->buckets != tablePtr->staticBuckets) {
                    202:        ckfree((char *) tablePtr->buckets);
                    203:     }
                    204: 
                    205:     /*
                    206:      * Arrange for panics if the table is used again without
                    207:      * re-initialization.
                    208:      */
                    209: 
                    210:     tablePtr->findProc = BogusFind;
                    211:     tablePtr->createProc = BogusCreate;
                    212: }
                    213: 
                    214: /*
                    215:  *----------------------------------------------------------------------
                    216:  *
                    217:  * Tcl_FirstHashEntry --
                    218:  *
                    219:  *     Locate the first entry in a hash table and set up a record
                    220:  *     that can be used to step through all the remaining entries
                    221:  *     of the table.
                    222:  *
                    223:  * Results:
                    224:  *     The return value is a pointer to the first entry in tablePtr,
                    225:  *     or NULL if tablePtr has no entries in it.  The memory at
                    226:  *     *searchPtr is initialized so that subsequent calls to
                    227:  *     Tcl_NextHashEntry will return all of the entries in the table,
                    228:  *     one at a time.
                    229:  *
                    230:  * Side effects:
                    231:  *     None.
                    232:  *
                    233:  *----------------------------------------------------------------------
                    234:  */
                    235: 
                    236: Tcl_HashEntry *
                    237: Tcl_FirstHashEntry(tablePtr, searchPtr)
                    238:     Tcl_HashTable *tablePtr;           /* Table to search. */
                    239:     Tcl_HashSearch *searchPtr;         /* Place to store information about
                    240:                                         * progress through the table. */
                    241: {
                    242:     searchPtr->tablePtr = tablePtr;
                    243:     searchPtr->nextIndex = 0;
                    244:     searchPtr->nextEntryPtr = NULL;
                    245:     return Tcl_NextHashEntry(searchPtr);
                    246: }
                    247: 
                    248: /*
                    249:  *----------------------------------------------------------------------
                    250:  *
                    251:  * Tcl_NextHashEntry --
                    252:  *
                    253:  *     Once a hash table enumeration has been initiated by calling
                    254:  *     Tcl_FirstHashEntry, this procedure may be called to return
                    255:  *     successive elements of the table.
                    256:  *
                    257:  * Results:
                    258:  *     The return value is the next entry in the hash table being
                    259:  *     enumerated, or NULL if the end of the table is reached.
                    260:  *
                    261:  * Side effects:
                    262:  *     None.
                    263:  *
                    264:  *----------------------------------------------------------------------
                    265:  */
                    266: 
                    267: Tcl_HashEntry *
                    268: Tcl_NextHashEntry(searchPtr)
                    269:     register Tcl_HashSearch *searchPtr;        /* Place to store information about
                    270:                                         * progress through the table.  Must
                    271:                                         * have been initialized by calling
                    272:                                         * Tcl_FirstHashEntry. */
                    273: {
                    274:     Tcl_HashEntry *hPtr;
                    275: 
                    276:     while (searchPtr->nextEntryPtr == NULL) {
                    277:        if (searchPtr->nextIndex >= searchPtr->tablePtr->numBuckets) {
                    278:            return NULL;
                    279:        }
                    280:        searchPtr->nextEntryPtr =
                    281:                searchPtr->tablePtr->buckets[searchPtr->nextIndex];
                    282:        searchPtr->nextIndex++;
                    283:     }
                    284:     hPtr = searchPtr->nextEntryPtr;
                    285:     searchPtr->nextEntryPtr = hPtr->nextPtr;
                    286:     return hPtr;
                    287: }
                    288: 
                    289: /*
                    290:  *----------------------------------------------------------------------
                    291:  *
                    292:  * Tcl_HashStats --
                    293:  *
                    294:  *     Return statistics describing the layout of the hash table
                    295:  *     in its hash buckets.
                    296:  *
                    297:  * Results:
                    298:  *     The return value is a malloc-ed string containing information
                    299:  *     about tablePtr.  It is the caller's responsibility to free
                    300:  *     this string.
                    301:  *
                    302:  * Side effects:
                    303:  *     None.
                    304:  *
                    305:  *----------------------------------------------------------------------
                    306:  */
                    307: 
                    308: char *
                    309: Tcl_HashStats(tablePtr)
                    310:     Tcl_HashTable *tablePtr;           /* Table for which to produce stats. */
                    311: {
                    312: #define NUM_COUNTERS 10
                    313:     int count[NUM_COUNTERS], overflow, i, j;
                    314:     double average, tmp;
                    315:     register Tcl_HashEntry *hPtr;
                    316:     char *result, *p;
                    317: 
                    318:     /*
                    319:      * Compute a histogram of bucket usage.
                    320:      */
                    321: 
                    322:     for (i = 0; i < NUM_COUNTERS; i++) {
                    323:        count[i] = 0;
                    324:     }
                    325:     overflow = 0;
                    326:     average = 0.0;
                    327:     for (i = 0; i < tablePtr->numBuckets; i++) {
                    328:        j = 0;
                    329:        for (hPtr = tablePtr->buckets[i]; hPtr != NULL; hPtr = hPtr->nextPtr) {
                    330:            j++;
                    331:        }
                    332:        if (j < NUM_COUNTERS) {
                    333:            count[j]++;
                    334:        } else {
                    335:            overflow++;
                    336:        }
                    337:        tmp = j;
                    338:        average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
                    339:     }
                    340: 
                    341:     /*
                    342:      * Print out the histogram and a few other pieces of information.
                    343:      */
                    344: 
                    345:     result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300));
                    346:     sprintf(result, "%d entries in table, %d buckets\n",
                    347:            tablePtr->numEntries, tablePtr->numBuckets);
                    348:     p = result + strlen(result);
                    349:     for (i = 0; i < NUM_COUNTERS; i++) {
                    350:        sprintf(p, "number of buckets with %d entries: %d\n",
                    351:                i, count[i]);
                    352:        p += strlen(p);
                    353:     }
                    354:     sprintf(p, "number of buckets with more %d or more entries: %d\n",
                    355:            NUM_COUNTERS, overflow);
                    356:     p += strlen(p);
                    357:     sprintf(p, "average search distance for entry: %.1f", average);
                    358:     return result;
                    359: }
                    360: 
                    361: /*
                    362:  *----------------------------------------------------------------------
                    363:  *
                    364:  * HashString --
                    365:  *
                    366:  *     Compute a one-word summary of a text string, which can be
                    367:  *     used to generate a hash index.
                    368:  *
                    369:  * Results:
                    370:  *     The return value is a one-word summary of the information in
                    371:  *     string.
                    372:  *
                    373:  * Side effects:
                    374:  *     None.
                    375:  *
                    376:  *----------------------------------------------------------------------
                    377:  */
                    378: 
                    379: static int
                    380: HashString(string)
                    381:     register char *string;     /* String from which to compute hash value. */
                    382: {
                    383:     register int result, c;
                    384: 
                    385:     /*
                    386:      * I tried a zillion different hash functions and asked many other
                    387:      * people for advice.  Many people had their own favorite functions,
                    388:      * all different, but no-one had much idea why they were good ones.
                    389:      * I chose the one below (multiply by 9 and add new character)
                    390:      * because of the following reasons:
                    391:      *
                    392:      * 1. Multiplying by 10 is perfect for keys that are decimal strings,
                    393:      *    and multiplying by 9 is just about as good.
                    394:      * 2. Times-9 is (shift-left-3) plus (old).  This means that each
                    395:      *    character's bits hang around in the low-order bits of the
                    396:      *    hash value for ever, plus they spread fairly rapidly up to
                    397:      *    the high-order bits to fill out the hash value.  This seems
                    398:      *    works well both for decimal and non-decimal strings.
                    399:      */
                    400: 
                    401:     result = 0;
                    402:     while (1) {
                    403:        c = *string;
                    404:        string++;
                    405:        if (c == 0) {
                    406:            break;
                    407:        }
                    408:        result += (result<<3) + c;
                    409:     }
                    410:     return result;
                    411: }
                    412: 
                    413: /*
                    414:  *----------------------------------------------------------------------
                    415:  *
                    416:  * StringFind --
                    417:  *
                    418:  *     Given a hash table with string keys, and a string key, find
                    419:  *     the entry with a matching key.
                    420:  *
                    421:  * Results:
                    422:  *     The return value is a token for the matching entry in the
                    423:  *     hash table, or NULL if there was no matching entry.
                    424:  *
                    425:  * Side effects:
                    426:  *     None.
                    427:  *
                    428:  *----------------------------------------------------------------------
                    429:  */
                    430: 
                    431: static Tcl_HashEntry *
                    432: StringFind(tablePtr, key)
                    433:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    434:     char *key;                 /* Key to use to find matching entry. */
                    435: {
                    436:     register Tcl_HashEntry *hPtr;
                    437:     register char *p1, *p2;
                    438:     int index;
                    439: 
                    440:     index = HashString(key) & tablePtr->mask;
                    441: 
                    442:     /*
                    443:      * Search all of the entries in the appropriate bucket.
                    444:      */
                    445: 
                    446:     for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
                    447:            hPtr = hPtr->nextPtr) {
                    448:        for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
                    449:            if (*p1 != *p2) {
                    450:                break;
                    451:            }
                    452:            if (*p1 == '\0') {
                    453:                return hPtr;
                    454:            }
                    455:        }
                    456:     }
                    457:     return NULL;
                    458: }
                    459: 
                    460: /*
                    461:  *----------------------------------------------------------------------
                    462:  *
                    463:  * StringCreate --
                    464:  *
                    465:  *     Given a hash table with string keys, and a string key, find
                    466:  *     the entry with a matching key.  If there is no matching entry,
                    467:  *     then create a new entry that does match.
                    468:  *
                    469:  * Results:
                    470:  *     The return value is a pointer to the matching entry.  If this
                    471:  *     is a newly-created entry, then *newPtr will be set to a non-zero
                    472:  *     value;  otherwise *newPtr will be set to 0.  If this is a new
                    473:  *     entry the value stored in the entry will initially be 0.
                    474:  *
                    475:  * Side effects:
                    476:  *     A new entry may be added to the hash table.
                    477:  *
                    478:  *----------------------------------------------------------------------
                    479:  */
                    480: 
                    481: static Tcl_HashEntry *
                    482: StringCreate(tablePtr, key, newPtr)
                    483:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    484:     char *key;                 /* Key to use to find or create matching
                    485:                                 * entry. */
                    486:     int *newPtr;               /* Store info here telling whether a new
                    487:                                 * entry was created. */
                    488: {
                    489:     register Tcl_HashEntry *hPtr;
                    490:     register char *p1, *p2;
                    491:     int index;
                    492: 
                    493:     index = HashString(key) & tablePtr->mask;
                    494: 
                    495:     /*
                    496:      * Search all of the entries in this bucket.
                    497:      */
                    498: 
                    499:     for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
                    500:            hPtr = hPtr->nextPtr) {
                    501:        for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
                    502:            if (*p1 != *p2) {
                    503:                break;
                    504:            }
                    505:            if (*p1 == '\0') {
                    506:                *newPtr = 0;
                    507:                return hPtr;
                    508:            }
                    509:        }
                    510:     }
                    511: 
                    512:     /*
                    513:      * Entry not found.  Add a new one to the bucket.
                    514:      */
                    515: 
                    516:     *newPtr = 1;
                    517:     hPtr = (Tcl_HashEntry *) ckalloc((unsigned)
                    518:            (sizeof(Tcl_HashEntry) + strlen(key) - (sizeof(hPtr->key) -1)));
                    519:     hPtr->tablePtr = tablePtr;
                    520:     hPtr->bucketPtr = &(tablePtr->buckets[index]);
                    521:     hPtr->nextPtr = *hPtr->bucketPtr;
                    522:     hPtr->clientData = 0;
                    523:     strcpy(hPtr->key.string, key);
                    524:     *hPtr->bucketPtr = hPtr;
                    525:     tablePtr->numEntries++;
                    526: 
                    527:     /*
                    528:      * If the table has exceeded a decent size, rebuild it with many
                    529:      * more buckets.
                    530:      */
                    531: 
                    532:     if (tablePtr->numEntries >= tablePtr->rebuildSize) {
                    533:        RebuildTable(tablePtr);
                    534:     }
                    535:     return hPtr;
                    536: }
                    537: 
                    538: /*
                    539:  *----------------------------------------------------------------------
                    540:  *
                    541:  * OneWordFind --
                    542:  *
                    543:  *     Given a hash table with one-word keys, and a one-word key, find
                    544:  *     the entry with a matching key.
                    545:  *
                    546:  * Results:
                    547:  *     The return value is a token for the matching entry in the
                    548:  *     hash table, or NULL if there was no matching entry.
                    549:  *
                    550:  * Side effects:
                    551:  *     None.
                    552:  *
                    553:  *----------------------------------------------------------------------
                    554:  */
                    555: 
                    556: static Tcl_HashEntry *
                    557: OneWordFind(tablePtr, key)
                    558:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    559:     register char *key;                /* Key to use to find matching entry. */
                    560: {
                    561:     register Tcl_HashEntry *hPtr;
                    562:     int index;
                    563: 
                    564:     index = RANDOM_INDEX(tablePtr, key);
                    565: 
                    566:     /*
                    567:      * Search all of the entries in the appropriate bucket.
                    568:      */
                    569: 
                    570:     for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
                    571:            hPtr = hPtr->nextPtr) {
                    572:        if (hPtr->key.oneWordValue == key) {
                    573:            return hPtr;
                    574:        }
                    575:     }
                    576:     return NULL;
                    577: }
                    578: 
                    579: /*
                    580:  *----------------------------------------------------------------------
                    581:  *
                    582:  * OneWordCreate --
                    583:  *
                    584:  *     Given a hash table with one-word keys, and a one-word key, find
                    585:  *     the entry with a matching key.  If there is no matching entry,
                    586:  *     then create a new entry that does match.
                    587:  *
                    588:  * Results:
                    589:  *     The return value is a pointer to the matching entry.  If this
                    590:  *     is a newly-created entry, then *newPtr will be set to a non-zero
                    591:  *     value;  otherwise *newPtr will be set to 0.  If this is a new
                    592:  *     entry the value stored in the entry will initially be 0.
                    593:  *
                    594:  * Side effects:
                    595:  *     A new entry may be added to the hash table.
                    596:  *
                    597:  *----------------------------------------------------------------------
                    598:  */
                    599: 
                    600: static Tcl_HashEntry *
                    601: OneWordCreate(tablePtr, key, newPtr)
                    602:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    603:     register char *key;                /* Key to use to find or create matching
                    604:                                 * entry. */
                    605:     int *newPtr;               /* Store info here telling whether a new
                    606:                                 * entry was created. */
                    607: {
                    608:     register Tcl_HashEntry *hPtr;
                    609:     int index;
                    610: 
                    611:     index = RANDOM_INDEX(tablePtr, key);
                    612: 
                    613:     /*
                    614:      * Search all of the entries in this bucket.
                    615:      */
                    616: 
                    617:     for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
                    618:            hPtr = hPtr->nextPtr) {
                    619:        if (hPtr->key.oneWordValue == key) {
                    620:            *newPtr = 0;
                    621:            return hPtr;
                    622:        }
                    623:     }
                    624: 
                    625:     /*
                    626:      * Entry not found.  Add a new one to the bucket.
                    627:      */
                    628: 
                    629:     *newPtr = 1;
                    630:     hPtr = (Tcl_HashEntry *) ckalloc(sizeof(Tcl_HashEntry));
                    631:     hPtr->tablePtr = tablePtr;
                    632:     hPtr->bucketPtr = &(tablePtr->buckets[index]);
                    633:     hPtr->nextPtr = *hPtr->bucketPtr;
                    634:     hPtr->clientData = 0;
                    635:     hPtr->key.oneWordValue = key;
                    636:     *hPtr->bucketPtr = hPtr;
                    637:     tablePtr->numEntries++;
                    638: 
                    639:     /*
                    640:      * If the table has exceeded a decent size, rebuild it with many
                    641:      * more buckets.
                    642:      */
                    643: 
                    644:     if (tablePtr->numEntries >= tablePtr->rebuildSize) {
                    645:        RebuildTable(tablePtr);
                    646:     }
                    647:     return hPtr;
                    648: }
                    649: 
                    650: /*
                    651:  *----------------------------------------------------------------------
                    652:  *
                    653:  * ArrayFind --
                    654:  *
                    655:  *     Given a hash table with array-of-int keys, and a key, find
                    656:  *     the entry with a matching key.
                    657:  *
                    658:  * Results:
                    659:  *     The return value is a token for the matching entry in the
                    660:  *     hash table, or NULL if there was no matching entry.
                    661:  *
                    662:  * Side effects:
                    663:  *     None.
                    664:  *
                    665:  *----------------------------------------------------------------------
                    666:  */
                    667: 
                    668: static Tcl_HashEntry *
                    669: ArrayFind(tablePtr, key)
                    670:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    671:     char *key;                 /* Key to use to find matching entry. */
                    672: {
                    673:     register Tcl_HashEntry *hPtr;
                    674:     int *arrayPtr = (int *) key;
                    675:     register int *iPtr1, *iPtr2;
                    676:     int index, count;
                    677: 
                    678:     for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
                    679:            count > 0; count--, iPtr1++) {
                    680:        index += *iPtr1;
                    681:     }
                    682:     index = RANDOM_INDEX(tablePtr, index);
                    683: 
                    684:     /*
                    685:      * Search all of the entries in the appropriate bucket.
                    686:      */
                    687: 
                    688:     for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
                    689:            hPtr = hPtr->nextPtr) {
                    690:        for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
                    691:                count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
                    692:            if (count == 0) {
                    693:                return hPtr;
                    694:            }
                    695:            if (*iPtr1 != *iPtr2) {
                    696:                break;
                    697:            }
                    698:        }
                    699:     }
                    700:     return NULL;
                    701: }
                    702: 
                    703: /*
                    704:  *----------------------------------------------------------------------
                    705:  *
                    706:  * ArrayCreate --
                    707:  *
                    708:  *     Given a hash table with one-word keys, and a one-word key, find
                    709:  *     the entry with a matching key.  If there is no matching entry,
                    710:  *     then create a new entry that does match.
                    711:  *
                    712:  * Results:
                    713:  *     The return value is a pointer to the matching entry.  If this
                    714:  *     is a newly-created entry, then *newPtr will be set to a non-zero
                    715:  *     value;  otherwise *newPtr will be set to 0.  If this is a new
                    716:  *     entry the value stored in the entry will initially be 0.
                    717:  *
                    718:  * Side effects:
                    719:  *     A new entry may be added to the hash table.
                    720:  *
                    721:  *----------------------------------------------------------------------
                    722:  */
                    723: 
                    724: static Tcl_HashEntry *
                    725: ArrayCreate(tablePtr, key, newPtr)
                    726:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    727:     register char *key;                /* Key to use to find or create matching
                    728:                                 * entry. */
                    729:     int *newPtr;               /* Store info here telling whether a new
                    730:                                 * entry was created. */
                    731: {
                    732:     register Tcl_HashEntry *hPtr;
                    733:     int *arrayPtr = (int *) key;
                    734:     register int *iPtr1, *iPtr2;
                    735:     int index, count;
                    736: 
                    737:     for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
                    738:            count > 0; count--, iPtr1++) {
                    739:        index += *iPtr1;
                    740:     }
                    741:     index = RANDOM_INDEX(tablePtr, index);
                    742: 
                    743:     /*
                    744:      * Search all of the entries in the appropriate bucket.
                    745:      */
                    746: 
                    747:     for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
                    748:            hPtr = hPtr->nextPtr) {
                    749:        for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
                    750:                count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
                    751:            if (count == 0) {
                    752:                *newPtr = 0;
                    753:                return hPtr;
                    754:            }
                    755:            if (*iPtr1 != *iPtr2) {
                    756:                break;
                    757:            }
                    758:        }
                    759:     }
                    760: 
                    761:     /*
                    762:      * Entry not found.  Add a new one to the bucket.
                    763:      */
                    764: 
                    765:     *newPtr = 1;
                    766:     hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry)
                    767:            + (tablePtr->keyType*sizeof(int)) - 4));
                    768:     hPtr->tablePtr = tablePtr;
                    769:     hPtr->bucketPtr = &(tablePtr->buckets[index]);
                    770:     hPtr->nextPtr = *hPtr->bucketPtr;
                    771:     hPtr->clientData = 0;
                    772:     for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words, count = tablePtr->keyType;
                    773:            count > 0; count--, iPtr1++, iPtr2++) {
                    774:        *iPtr2 = *iPtr1;
                    775:     }
                    776:     *hPtr->bucketPtr = hPtr;
                    777:     tablePtr->numEntries++;
                    778: 
                    779:     /*
                    780:      * If the table has exceeded a decent size, rebuild it with many
                    781:      * more buckets.
                    782:      */
                    783: 
                    784:     if (tablePtr->numEntries >= tablePtr->rebuildSize) {
                    785:        RebuildTable(tablePtr);
                    786:     }
                    787:     return hPtr;
                    788: }
                    789: 
                    790: /*
                    791:  *----------------------------------------------------------------------
                    792:  *
                    793:  * BogusFind --
                    794:  *
                    795:  *     This procedure is invoked when an Tcl_FindHashEntry is called
                    796:  *     on a table that has been deleted.
                    797:  *
                    798:  * Results:
                    799:  *     If panic returns (which it shouldn't) this procedure returns
                    800:  *     NULL.
                    801:  *
                    802:  * Side effects:
                    803:  *     Generates a panic.
                    804:  *
                    805:  *----------------------------------------------------------------------
                    806:  */
                    807: 
                    808:        /* ARGSUSED */
                    809: static Tcl_HashEntry *
                    810: BogusFind(tablePtr, key)
                    811:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    812:     char *key;                 /* Key to use to find matching entry. */
                    813: {
                    814:     panic("called Tcl_FindHashEntry on deleted table");
                    815:     return NULL;
                    816: }
                    817: 
                    818: /*
                    819:  *----------------------------------------------------------------------
                    820:  *
                    821:  * BogusCreate --
                    822:  *
                    823:  *     This procedure is invoked when an Tcl_CreateHashEntry is called
                    824:  *     on a table that has been deleted.
                    825:  *
                    826:  * Results:
                    827:  *     If panic returns (which it shouldn't) this procedure returns
                    828:  *     NULL.
                    829:  *
                    830:  * Side effects:
                    831:  *     Generates a panic.
                    832:  *
                    833:  *----------------------------------------------------------------------
                    834:  */
                    835: 
                    836:        /* ARGSUSED */
                    837: static Tcl_HashEntry *
                    838: BogusCreate(tablePtr, key, newPtr)
                    839:     Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
                    840:     char *key;                 /* Key to use to find or create matching
                    841:                                 * entry. */
                    842:     int *newPtr;               /* Store info here telling whether a new
                    843:                                 * entry was created. */
                    844: {
                    845:     panic("called Tcl_CreateHashEntry on deleted table");
                    846:     return NULL;
                    847: }
                    848: 
                    849: /*
                    850:  *----------------------------------------------------------------------
                    851:  *
                    852:  * RebuildTable --
                    853:  *
                    854:  *     This procedure is invoked when the ratio of entries to hash
                    855:  *     buckets becomes too large.  It creates a new table with a
                    856:  *     larger bucket array and moves all of the entries into the
                    857:  *     new table.
                    858:  *
                    859:  * Results:
                    860:  *     None.
                    861:  *
                    862:  * Side effects:
                    863:  *     Memory gets reallocated and entries get re-hashed to new
                    864:  *     buckets.
                    865:  *
                    866:  *----------------------------------------------------------------------
                    867:  */
                    868: 
                    869: static void
                    870: RebuildTable(tablePtr)
                    871:     register Tcl_HashTable *tablePtr;  /* Table to enlarge. */
                    872: {
                    873:     int oldSize, count, index;
                    874:     Tcl_HashEntry **oldBuckets;
                    875:     register Tcl_HashEntry **oldChainPtr, **newChainPtr;
                    876:     register Tcl_HashEntry *hPtr;
                    877: 
                    878:     oldSize = tablePtr->numBuckets;
                    879:     oldBuckets = tablePtr->buckets;
                    880: 
                    881:     /*
                    882:      * Allocate and initialize the new bucket array, and set up
                    883:      * hashing constants for new array size.
                    884:      */
                    885: 
                    886:     tablePtr->numBuckets *= 4;
                    887:     tablePtr->buckets = (Tcl_HashEntry **) ckalloc((unsigned)
                    888:            (tablePtr->numBuckets * sizeof(Tcl_HashEntry *)));
                    889:     for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets;
                    890:            count > 0; count--, newChainPtr++) {
                    891:        *newChainPtr = NULL;
                    892:     }
                    893:     tablePtr->rebuildSize *= 4;
                    894:     tablePtr->downShift -= 2;
                    895:     tablePtr->mask = (tablePtr->mask << 2) + 3;
                    896: 
                    897:     /*
                    898:      * Rehash all of the existing entries into the new bucket array.
                    899:      */
                    900: 
                    901:     for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) {
                    902:        for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) {
                    903:            *oldChainPtr = hPtr->nextPtr;
                    904:            if (tablePtr->keyType == TCL_STRING_KEYS) {
                    905:                index = HashString(hPtr->key.string) & tablePtr->mask;
                    906:            } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
                    907:                index = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
                    908:            } else {
                    909:                register int *iPtr;
                    910:                int count;
                    911: 
                    912:                for (index = 0, count = tablePtr->keyType,
                    913:                        iPtr = hPtr->key.words; count > 0; count--, iPtr++) {
                    914:                    index += *iPtr;
                    915:                }
                    916:                index = RANDOM_INDEX(tablePtr, index);
                    917:            }
                    918:            hPtr->bucketPtr = &(tablePtr->buckets[index]);
                    919:            hPtr->nextPtr = *hPtr->bucketPtr;
                    920:            *hPtr->bucketPtr = hPtr;
                    921:        }
                    922:     }
                    923: 
                    924:     /*
                    925:      * Free up the old bucket array, if it was dynamically allocated.
                    926:      */
                    927: 
                    928:     if (oldBuckets != tablePtr->staticBuckets) {
                    929:        ckfree((char *) oldBuckets);
                    930:     }
                    931: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.