|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.