source: trunk/tcl/tclHash.c@ 18

Last change on this file since 18 was 2, checked in by Pavel Demin, 16 years ago

first commit

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