Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclHash.c @ 25

Last change on this file since 25 was 25, checked in by landauf, 16 years ago

added tcl to libs

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