]>
cvs.zerfleddert.de Git - micropolis/blob - src/tcl/tclhash.c
4 * Implementation of in-memory hash tables for Tcl and Tcl-based
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.
18 static char rcsid
[] = "$Header: /user6/ouster/tcl/RCS/tclHash.c,v 1.9 92/01/04 15:45:21 ouster Exp $ SPRITE (Berkeley)";
24 * When there are this many entries per bucket, on average, rebuild
25 * the hash table to make it larger.
28 #define REBUILD_MULTIPLIER 3
32 * The following macro takes a preliminary integer hash value and
33 * produces an index into a hash tables bucket list. The idea is
34 * to make it so that preliminary values that are arbitrarily similar
35 * will end up in different buckets. The hash function was taken
36 * from a random-number generator.
39 #define RANDOM_INDEX(tablePtr, i) \
40 (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
43 * Procedure prototypes for static procedures in this file:
46 static Tcl_HashEntry
* ArrayFind
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
48 static Tcl_HashEntry
* ArrayCreate
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
49 char *key
, int *newPtr
));
50 static Tcl_HashEntry
* BogusFind
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
52 static Tcl_HashEntry
* BogusCreate
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
53 char *key
, int *newPtr
));
54 static int HashString
_ANSI_ARGS_((char *string
));
55 static void RebuildTable
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
));
56 static Tcl_HashEntry
* StringFind
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
58 static Tcl_HashEntry
* StringCreate
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
59 char *key
, int *newPtr
));
60 static Tcl_HashEntry
* OneWordFind
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
62 static Tcl_HashEntry
* OneWordCreate
_ANSI_ARGS_((Tcl_HashTable
*tablePtr
,
63 char *key
, int *newPtr
));
66 *----------------------------------------------------------------------
68 * Tcl_InitHashTable --
70 * Given storage for a hash table, set up the fields to prepare
71 * the hash table for use.
77 * TablePtr is now ready to be passed to Tcl_FindHashEntry and
78 * Tcl_CreateHashEntry.
80 *----------------------------------------------------------------------
85 register Tcl_HashTable
*tablePtr
, /* Pointer to table record, which
86 * is supplied by the caller. */
87 int keyType
/* Type of keys to use in table:
88 * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
89 * or an integer >= 2. */
92 tablePtr
->buckets
= tablePtr
->staticBuckets
;
93 tablePtr
->staticBuckets
[0] = tablePtr
->staticBuckets
[1] = 0;
94 tablePtr
->staticBuckets
[2] = tablePtr
->staticBuckets
[3] = 0;
95 tablePtr
->numBuckets
= TCL_SMALL_HASH_TABLE
;
96 tablePtr
->numEntries
= 0;
97 tablePtr
->rebuildSize
= TCL_SMALL_HASH_TABLE
*REBUILD_MULTIPLIER
;
98 tablePtr
->downShift
= 28;
100 tablePtr
->keyType
= keyType
;
101 if (keyType
== TCL_STRING_KEYS
) {
102 tablePtr
->findProc
= StringFind
;
103 tablePtr
->createProc
= StringCreate
;
104 } else if (keyType
== TCL_ONE_WORD_KEYS
) {
105 tablePtr
->findProc
= OneWordFind
;
106 tablePtr
->createProc
= OneWordCreate
;
108 tablePtr
->findProc
= ArrayFind
;
109 tablePtr
->createProc
= ArrayCreate
;
114 *----------------------------------------------------------------------
116 * Tcl_DeleteHashEntry --
118 * Remove a single entry from a hash table.
124 * The entry given by entryPtr is deleted from its table and
125 * should never again be used by the caller. It is up to the
126 * caller to free the clientData field of the entry, if that
129 *----------------------------------------------------------------------
133 Tcl_DeleteHashEntry (Tcl_HashEntry
*entryPtr
)
135 register Tcl_HashEntry
*prevPtr
;
137 if (*entryPtr
->bucketPtr
== entryPtr
) {
138 *entryPtr
->bucketPtr
= entryPtr
->nextPtr
;
140 for (prevPtr
= *entryPtr
->bucketPtr
; ; prevPtr
= prevPtr
->nextPtr
) {
141 if (prevPtr
== NULL
) {
142 panic("malformed bucket chain in Tcl_DeleteHashEntry");
144 if (prevPtr
->nextPtr
== entryPtr
) {
145 prevPtr
->nextPtr
= entryPtr
->nextPtr
;
150 entryPtr
->tablePtr
->numEntries
--;
151 ckfree((char *) entryPtr
);
155 *----------------------------------------------------------------------
157 * Tcl_DeleteHashTable --
159 * Free up everything associated with a hash table except for
160 * the record for the table itself.
166 * The hash table is no longer useable.
168 *----------------------------------------------------------------------
172 Tcl_DeleteHashTable (
173 register Tcl_HashTable
*tablePtr
/* Table to delete. */
176 register Tcl_HashEntry
*hPtr
, *nextPtr
;
180 * Free up all the entries in the table.
183 for (i
= 0; i
< tablePtr
->numBuckets
; i
++) {
184 hPtr
= tablePtr
->buckets
[i
];
185 while (hPtr
!= NULL
) {
186 nextPtr
= hPtr
->nextPtr
;
187 ckfree((char *) hPtr
);
193 * Free up the bucket array, if it was dynamically allocated.
196 if (tablePtr
->buckets
!= tablePtr
->staticBuckets
) {
197 ckfree((char *) tablePtr
->buckets
);
201 * Arrange for panics if the table is used again without
205 tablePtr
->findProc
= BogusFind
;
206 tablePtr
->createProc
= BogusCreate
;
210 *----------------------------------------------------------------------
212 * Tcl_FirstHashEntry --
214 * Locate the first entry in a hash table and set up a record
215 * that can be used to step through all the remaining entries
219 * The return value is a pointer to the first entry in tablePtr,
220 * or NULL if tablePtr has no entries in it. The memory at
221 * *searchPtr is initialized so that subsequent calls to
222 * Tcl_NextHashEntry will return all of the entries in the table,
228 *----------------------------------------------------------------------
233 Tcl_HashTable
*tablePtr
, /* Table to search. */
234 Tcl_HashSearch
*searchPtr
/* Place to store information about
235 * progress through the table. */
238 searchPtr
->tablePtr
= tablePtr
;
239 searchPtr
->nextIndex
= 0;
240 searchPtr
->nextEntryPtr
= NULL
;
241 return Tcl_NextHashEntry(searchPtr
);
245 *----------------------------------------------------------------------
247 * Tcl_NextHashEntry --
249 * Once a hash table enumeration has been initiated by calling
250 * Tcl_FirstHashEntry, this procedure may be called to return
251 * successive elements of the table.
254 * The return value is the next entry in the hash table being
255 * enumerated, or NULL if the end of the table is reached.
260 *----------------------------------------------------------------------
265 register Tcl_HashSearch
*searchPtr
/* Place to store information about
266 * progress through the table. Must
267 * have been initialized by calling
268 * Tcl_FirstHashEntry. */
273 while (searchPtr
->nextEntryPtr
== NULL
) {
274 if (searchPtr
->nextIndex
>= searchPtr
->tablePtr
->numBuckets
) {
277 searchPtr
->nextEntryPtr
=
278 searchPtr
->tablePtr
->buckets
[searchPtr
->nextIndex
];
279 searchPtr
->nextIndex
++;
281 hPtr
= searchPtr
->nextEntryPtr
;
282 searchPtr
->nextEntryPtr
= hPtr
->nextPtr
;
287 *----------------------------------------------------------------------
291 * Return statistics describing the layout of the hash table
292 * in its hash buckets.
295 * The return value is a malloc-ed string containing information
296 * about tablePtr. It is the caller's responsibility to free
302 *----------------------------------------------------------------------
307 Tcl_HashTable
*tablePtr
/* Table for which to produce stats. */
310 #define NUM_COUNTERS 10
311 int count
[NUM_COUNTERS
], overflow
, i
, j
;
313 register Tcl_HashEntry
*hPtr
;
317 * Compute a histogram of bucket usage.
320 for (i
= 0; i
< NUM_COUNTERS
; i
++) {
325 for (i
= 0; i
< tablePtr
->numBuckets
; i
++) {
327 for (hPtr
= tablePtr
->buckets
[i
]; hPtr
!= NULL
; hPtr
= hPtr
->nextPtr
) {
330 if (j
< NUM_COUNTERS
) {
336 average
+= (tmp
+1.0)*(tmp
/tablePtr
->numEntries
)/2.0;
340 * Print out the histogram and a few other pieces of information.
343 result
= (char *) ckalloc((unsigned) ((NUM_COUNTERS
*60) + 300));
344 sprintf(result
, "%d entries in table, %d buckets\n",
345 tablePtr
->numEntries
, tablePtr
->numBuckets
);
346 p
= result
+ strlen(result
);
347 for (i
= 0; i
< NUM_COUNTERS
; i
++) {
348 sprintf(p
, "number of buckets with %d entries: %d\n",
352 sprintf(p
, "number of buckets with more %d or more entries: %d\n",
353 NUM_COUNTERS
, overflow
);
355 sprintf(p
, "average search distance for entry: %.1f", average
);
360 *----------------------------------------------------------------------
364 * Compute a one-word summary of a text string, which can be
365 * used to generate a hash index.
368 * The return value is a one-word summary of the information in
374 *----------------------------------------------------------------------
379 register char *string
/* String from which to compute hash value. */
382 register int result
, c
;
385 * I tried a zillion different hash functions and asked many other
386 * people for advice. Many people had their own favorite functions,
387 * all different, but no-one had much idea why they were good ones.
388 * I chose the one below (multiply by 9 and add new character)
389 * because of the following reasons:
391 * 1. Multiplying by 10 is perfect for keys that are decimal strings,
392 * and multiplying by 9 is just about as good.
393 * 2. Times-9 is (shift-left-3) plus (old). This means that each
394 * character's bits hang around in the low-order bits of the
395 * hash value for ever, plus they spread fairly rapidly up to
396 * the high-order bits to fill out the hash value. This seems
397 * works well both for decimal and non-decimal strings.
407 result
+= (result
<<3) + c
;
413 *----------------------------------------------------------------------
417 * Given a hash table with string keys, and a string key, find
418 * the entry with a matching key.
421 * The return value is a token for the matching entry in the
422 * hash table, or NULL if there was no matching entry.
427 *----------------------------------------------------------------------
430 static Tcl_HashEntry
*
432 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
433 char *key
/* Key to use to find matching entry. */
436 register Tcl_HashEntry
*hPtr
;
437 register char *p1
, *p2
;
440 index
= HashString(key
) & tablePtr
->mask
;
443 * Search all of the entries in the appropriate bucket.
446 for (hPtr
= tablePtr
->buckets
[index
]; hPtr
!= NULL
;
447 hPtr
= hPtr
->nextPtr
) {
448 for (p1
= key
, p2
= hPtr
->key
.string
; ; p1
++, p2
++) {
461 *----------------------------------------------------------------------
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.
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.
476 * A new entry may be added to the hash table.
478 *----------------------------------------------------------------------
481 static Tcl_HashEntry
*
483 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
484 char *key
, /* Key to use to find or create matching
486 int *newPtr
/* Store info here telling whether a new
487 * entry was created. */
490 register Tcl_HashEntry
*hPtr
;
491 register char *p1
, *p2
;
494 index
= HashString(key
) & tablePtr
->mask
;
497 * Search all of the entries in this bucket.
500 for (hPtr
= tablePtr
->buckets
[index
]; hPtr
!= NULL
;
501 hPtr
= hPtr
->nextPtr
) {
502 for (p1
= key
, p2
= hPtr
->key
.string
; ; p1
++, p2
++) {
514 * Entry not found. Add a new one to the bucket.
518 hPtr
= (Tcl_HashEntry
*) ckalloc((unsigned)
519 (sizeof(Tcl_HashEntry
) + strlen(key
) - (sizeof(hPtr
->key
) -1)));
520 hPtr
->tablePtr
= tablePtr
;
521 hPtr
->bucketPtr
= &(tablePtr
->buckets
[index
]);
522 hPtr
->nextPtr
= *hPtr
->bucketPtr
;
523 hPtr
->clientData
= 0;
524 strcpy(hPtr
->key
.string
, key
);
525 *hPtr
->bucketPtr
= hPtr
;
526 tablePtr
->numEntries
++;
529 * If the table has exceeded a decent size, rebuild it with many
533 if (tablePtr
->numEntries
>= tablePtr
->rebuildSize
) {
534 RebuildTable(tablePtr
);
540 *----------------------------------------------------------------------
544 * Given a hash table with one-word keys, and a one-word key, find
545 * the entry with a matching key.
548 * The return value is a token for the matching entry in the
549 * hash table, or NULL if there was no matching entry.
554 *----------------------------------------------------------------------
557 static Tcl_HashEntry
*
559 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
560 register char *key
/* Key to use to find matching entry. */
563 register Tcl_HashEntry
*hPtr
;
566 index
= RANDOM_INDEX(tablePtr
, key
);
569 * Search all of the entries in the appropriate bucket.
572 for (hPtr
= tablePtr
->buckets
[index
]; hPtr
!= NULL
;
573 hPtr
= hPtr
->nextPtr
) {
574 if (hPtr
->key
.oneWordValue
== key
) {
582 *----------------------------------------------------------------------
586 * Given a hash table with one-word keys, and a one-word key, find
587 * the entry with a matching key. If there is no matching entry,
588 * then create a new entry that does match.
591 * The return value is a pointer to the matching entry. If this
592 * is a newly-created entry, then *newPtr will be set to a non-zero
593 * value; otherwise *newPtr will be set to 0. If this is a new
594 * entry the value stored in the entry will initially be 0.
597 * A new entry may be added to the hash table.
599 *----------------------------------------------------------------------
602 static Tcl_HashEntry
*
604 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
605 register char *key
, /* Key to use to find or create matching
607 int *newPtr
/* Store info here telling whether a new
608 * entry was created. */
611 register Tcl_HashEntry
*hPtr
;
614 index
= RANDOM_INDEX(tablePtr
, key
);
617 * Search all of the entries in this bucket.
620 for (hPtr
= tablePtr
->buckets
[index
]; hPtr
!= NULL
;
621 hPtr
= hPtr
->nextPtr
) {
622 if (hPtr
->key
.oneWordValue
== key
) {
629 * Entry not found. Add a new one to the bucket.
633 hPtr
= (Tcl_HashEntry
*) ckalloc(sizeof(Tcl_HashEntry
));
634 hPtr
->tablePtr
= tablePtr
;
635 hPtr
->bucketPtr
= &(tablePtr
->buckets
[index
]);
636 hPtr
->nextPtr
= *hPtr
->bucketPtr
;
637 hPtr
->clientData
= 0;
638 hPtr
->key
.oneWordValue
= key
;
639 *hPtr
->bucketPtr
= hPtr
;
640 tablePtr
->numEntries
++;
643 * If the table has exceeded a decent size, rebuild it with many
647 if (tablePtr
->numEntries
>= tablePtr
->rebuildSize
) {
648 RebuildTable(tablePtr
);
654 *----------------------------------------------------------------------
658 * Given a hash table with array-of-int keys, and a key, find
659 * the entry with a matching key.
662 * The return value is a token for the matching entry in the
663 * hash table, or NULL if there was no matching entry.
668 *----------------------------------------------------------------------
671 static Tcl_HashEntry
*
673 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
674 char *key
/* Key to use to find matching entry. */
677 register Tcl_HashEntry
*hPtr
;
678 int *arrayPtr
= (int *) key
;
679 register int *iPtr1
, *iPtr2
;
682 for (index
= 0, count
= tablePtr
->keyType
, iPtr1
= arrayPtr
;
683 count
> 0; count
--, iPtr1
++) {
686 index
= RANDOM_INDEX(tablePtr
, index
);
689 * Search all of the entries in the appropriate bucket.
692 for (hPtr
= tablePtr
->buckets
[index
]; hPtr
!= NULL
;
693 hPtr
= hPtr
->nextPtr
) {
694 for (iPtr1
= arrayPtr
, iPtr2
= hPtr
->key
.words
,
695 count
= tablePtr
->keyType
; ; count
--, iPtr1
++, iPtr2
++) {
699 if (*iPtr1
!= *iPtr2
) {
708 *----------------------------------------------------------------------
712 * Given a hash table with one-word keys, and a one-word key, find
713 * the entry with a matching key. If there is no matching entry,
714 * then create a new entry that does match.
717 * The return value is a pointer to the matching entry. If this
718 * is a newly-created entry, then *newPtr will be set to a non-zero
719 * value; otherwise *newPtr will be set to 0. If this is a new
720 * entry the value stored in the entry will initially be 0.
723 * A new entry may be added to the hash table.
725 *----------------------------------------------------------------------
728 static Tcl_HashEntry
*
730 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
731 register char *key
, /* Key to use to find or create matching
733 int *newPtr
/* Store info here telling whether a new
734 * entry was created. */
737 register Tcl_HashEntry
*hPtr
;
738 int *arrayPtr
= (int *) key
;
739 register int *iPtr1
, *iPtr2
;
742 for (index
= 0, count
= tablePtr
->keyType
, iPtr1
= arrayPtr
;
743 count
> 0; count
--, iPtr1
++) {
746 index
= RANDOM_INDEX(tablePtr
, index
);
749 * Search all of the entries in the appropriate bucket.
752 for (hPtr
= tablePtr
->buckets
[index
]; hPtr
!= NULL
;
753 hPtr
= hPtr
->nextPtr
) {
754 for (iPtr1
= arrayPtr
, iPtr2
= hPtr
->key
.words
,
755 count
= tablePtr
->keyType
; ; count
--, iPtr1
++, iPtr2
++) {
760 if (*iPtr1
!= *iPtr2
) {
767 * Entry not found. Add a new one to the bucket.
771 hPtr
= (Tcl_HashEntry
*) ckalloc((unsigned) (sizeof(Tcl_HashEntry
)
772 + (tablePtr
->keyType
*sizeof(int)) - 4));
773 hPtr
->tablePtr
= tablePtr
;
774 hPtr
->bucketPtr
= &(tablePtr
->buckets
[index
]);
775 hPtr
->nextPtr
= *hPtr
->bucketPtr
;
776 hPtr
->clientData
= 0;
777 for (iPtr1
= arrayPtr
, iPtr2
= hPtr
->key
.words
, count
= tablePtr
->keyType
;
778 count
> 0; count
--, iPtr1
++, iPtr2
++) {
781 *hPtr
->bucketPtr
= hPtr
;
782 tablePtr
->numEntries
++;
785 * If the table has exceeded a decent size, rebuild it with many
789 if (tablePtr
->numEntries
>= tablePtr
->rebuildSize
) {
790 RebuildTable(tablePtr
);
796 *----------------------------------------------------------------------
800 * This procedure is invoked when an Tcl_FindHashEntry is called
801 * on a table that has been deleted.
804 * If panic returns (which it shouldn't) this procedure returns
810 *----------------------------------------------------------------------
814 static Tcl_HashEntry
*
816 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
817 char *key
/* Key to use to find matching entry. */
820 panic("called Tcl_FindHashEntry on deleted table");
825 *----------------------------------------------------------------------
829 * This procedure is invoked when an Tcl_CreateHashEntry is called
830 * on a table that has been deleted.
833 * If panic returns (which it shouldn't) this procedure returns
839 *----------------------------------------------------------------------
843 static Tcl_HashEntry
*
845 Tcl_HashTable
*tablePtr
, /* Table in which to lookup entry. */
846 char *key
, /* Key to use to find or create matching
848 int *newPtr
/* Store info here telling whether a new
849 * entry was created. */
852 panic("called Tcl_CreateHashEntry on deleted table");
857 *----------------------------------------------------------------------
861 * This procedure is invoked when the ratio of entries to hash
862 * buckets becomes too large. It creates a new table with a
863 * larger bucket array and moves all of the entries into the
870 * Memory gets reallocated and entries get re-hashed to new
873 *----------------------------------------------------------------------
878 register Tcl_HashTable
*tablePtr
/* Table to enlarge. */
881 int oldSize
, count
, index
;
882 Tcl_HashEntry
**oldBuckets
;
883 register Tcl_HashEntry
**oldChainPtr
, **newChainPtr
;
884 register Tcl_HashEntry
*hPtr
;
886 oldSize
= tablePtr
->numBuckets
;
887 oldBuckets
= tablePtr
->buckets
;
890 * Allocate and initialize the new bucket array, and set up
891 * hashing constants for new array size.
894 tablePtr
->numBuckets
*= 4;
895 tablePtr
->buckets
= (Tcl_HashEntry
**) ckalloc((unsigned)
896 (tablePtr
->numBuckets
* sizeof(Tcl_HashEntry
*)));
897 for (count
= tablePtr
->numBuckets
, newChainPtr
= tablePtr
->buckets
;
898 count
> 0; count
--, newChainPtr
++) {
901 tablePtr
->rebuildSize
*= 4;
902 tablePtr
->downShift
-= 2;
903 tablePtr
->mask
= (tablePtr
->mask
<< 2) + 3;
906 * Rehash all of the existing entries into the new bucket array.
909 for (oldChainPtr
= oldBuckets
; oldSize
> 0; oldSize
--, oldChainPtr
++) {
910 for (hPtr
= *oldChainPtr
; hPtr
!= NULL
; hPtr
= *oldChainPtr
) {
911 *oldChainPtr
= hPtr
->nextPtr
;
912 if (tablePtr
->keyType
== TCL_STRING_KEYS
) {
913 index
= HashString(hPtr
->key
.string
) & tablePtr
->mask
;
914 } else if (tablePtr
->keyType
== TCL_ONE_WORD_KEYS
) {
915 index
= RANDOM_INDEX(tablePtr
, hPtr
->key
.oneWordValue
);
920 for (index
= 0, count
= tablePtr
->keyType
,
921 iPtr
= hPtr
->key
.words
; count
> 0; count
--, iPtr
++) {
924 index
= RANDOM_INDEX(tablePtr
, index
);
926 hPtr
->bucketPtr
= &(tablePtr
->buckets
[index
]);
927 hPtr
->nextPtr
= *hPtr
->bucketPtr
;
928 *hPtr
->bucketPtr
= hPtr
;
933 * Free up the old bucket array, if it was dynamically allocated.
936 if (oldBuckets
!= tablePtr
->staticBuckets
) {
937 ckfree((char *) oldBuckets
);