|
|
1.1 root 1: /*
2: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
3: * Copyright (c) 1988, 1989 by Adam de Boor
4: * Copyright (c) 1989 by Berkeley Softworks
5: * All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Adam de Boor.
9: *
10: * Redistribution and use in source and binary forms are permitted
11: * provided that: (1) source distributions retain this entire copyright
12: * notice and comment, and (2) distributions including binaries display
13: * the following acknowledgement: ``This product includes software
14: * developed by the University of California, Berkeley and its contributors''
15: * in the documentation or other materials provided with the distribution
16: * and in all advertising materials mentioning features or use of this
17: * software. Neither the name of the University nor the names of its
18: * contributors may be used to endorse or promote products derived
19: * from this software without specific prior written permission.
20: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23: */
24:
25: #ifndef lint
26: static char sccsid[] = "@(#)hash.c 5.4 (Berkeley) 6/1/90";
27: #endif /* not lint */
28:
29: /* hash.c --
30: *
31: * This module contains routines to manipulate a hash table.
32: * See hash.h for a definition of the structure of the hash
33: * table. Hash tables grow automatically as the amount of
34: * information increases.
35: */
36:
37: #include "sprite.h"
38: #include "hash.h"
39: #include "list.h"
40:
41: /*
42: * Forward references to local procedures that are used before they're
43: * defined:
44: */
45:
46: static Hash_Entry * ChainSearch();
47: static int Hash();
48: static void RebuildTable();
49:
50: /*
51: * The following defines the ratio of # entries to # buckets
52: * at which we rebuild the table to make it larger.
53: */
54:
55: static rebuildLimit = 3;
56:
57: /*
58: *---------------------------------------------------------
59: *
60: * Hash_InitTable --
61: *
62: * This routine just sets up the hash table.
63: *
64: * Results:
65: * None.
66: *
67: * Side Effects:
68: * Memory is allocated for the initial bucket area.
69: *
70: *---------------------------------------------------------
71: */
72:
73: void
74: Hash_InitTable(tablePtr, numBuckets, keyType)
75: register Hash_Table *tablePtr; /* Structure to use to hold table. */
76: int numBuckets; /* How many buckets to create for
77: * starters. This number is rounded
78: * up to a power of two. If <= 0,
79: * a reasonable default is chosen.
80: * The table will grow in size later
81: * as needed. */
82: int keyType; /* HASH_STRING_KEYS means that key
83: * values passed to HashFind will be
84: * strings, passed via a (char *)
85: * pointer. HASH_ONE_WORD_KEYS means
86: * that key values will be any
87: * one-word value passed as Address.
88: * > 1 means that key values will be
89: * multi-word values whose address is
90: * passed as Address. In this last
91: * case, keyType is the number of
92: * words in the key, not the number
93: * of bytes. */
94: {
95: register int i;
96: register List_Links *bucketPtr;
97:
98: /*
99: * Round up the size to a power of two.
100: */
101:
102: if (numBuckets <= 0) {
103: numBuckets = 16;
104: }
105: tablePtr->numEntries = 0;
106: tablePtr->keyType = keyType;
107: tablePtr->size = 2;
108: tablePtr->mask = 1;
109: tablePtr->downShift = 29;
110: while (tablePtr->size < numBuckets) {
111: tablePtr->size <<= 1;
112: tablePtr->mask = (tablePtr->mask << 1) + 1;
113: tablePtr->downShift--;
114: }
115:
116: tablePtr->bucketPtr = (List_Links *) emalloc(sizeof(List_Links)
117: * tablePtr->size);
118: for (i=0, bucketPtr = tablePtr->bucketPtr; i < tablePtr->size;
119: i++, bucketPtr++) {
120: List_Init(bucketPtr);
121: }
122: }
123:
124: /*
125: *---------------------------------------------------------
126: *
127: * Hash_DeleteTable --
128: *
129: * This routine removes everything from a hash table
130: * and frees up the memory space it occupied (except for
131: * the space in the Hash_Table structure).
132: *
133: * Results:
134: * None.
135: *
136: * Side Effects:
137: * Lots of memory is freed up.
138: *
139: *---------------------------------------------------------
140: */
141:
142: void
143: Hash_DeleteTable(tablePtr)
144: Hash_Table *tablePtr; /* Hash table whose entries are all to
145: * be freed. */
146: {
147: register List_Links *hashTableEnd;
148: register Hash_Entry *hashEntryPtr;
149: register List_Links *bucketPtr;
150:
151: bucketPtr = tablePtr->bucketPtr;
152: hashTableEnd = &(bucketPtr[tablePtr->size]);
153: for (; bucketPtr < hashTableEnd; bucketPtr++) {
154: while (!List_IsEmpty(bucketPtr)) {
155: hashEntryPtr = (Hash_Entry *) List_First(bucketPtr);
156: List_Remove((List_Links *) hashEntryPtr);
157: free((Address) hashEntryPtr);
158: }
159: }
160: free((Address) tablePtr->bucketPtr);
161:
162: /*
163: * Set up the hash table to cause memory faults on any future
164: * access attempts until re-initialization.
165: */
166:
167: tablePtr->bucketPtr = (List_Links *) NIL;
168: }
169:
170: /*
171: *---------------------------------------------------------
172: *
173: * Hash_FindEntry --
174: *
175: * Searches a hash table for an entry corresponding to key.
176: *
177: * Results:
178: * The return value is a pointer to the entry for key,
179: * if key was present in the table. If key was not
180: * present, NULL is returned.
181: *
182: * Side Effects:
183: * None.
184: *
185: *---------------------------------------------------------
186: */
187:
188: Hash_Entry *
189: Hash_FindEntry(tablePtr, key)
190: Hash_Table *tablePtr; /* Hash table to search. */
191: Address key; /* A hash key. */
192: {
193: return(ChainSearch(tablePtr, key,
194: &(tablePtr->bucketPtr[Hash(tablePtr, key)])));
195: }
196:
197: /*
198: *---------------------------------------------------------
199: *
200: * Hash_CreateEntry --
201: *
202: * Searches a hash table for an entry corresponding to
203: * key. If no entry is found, then one is created.
204: *
205: * Results:
206: * The return value is a pointer to the entry. If *newPtr
207: * isn't NULL, then *newPtr is filled in with TRUE if a
208: * new entry was created, and FALSE if an entry already existed
209: * with the given key.
210: *
211: * Side Effects:
212: * Memory may be allocated, and the hash buckets may be modified.
213: *---------------------------------------------------------
214: */
215:
216: Hash_Entry *
217: Hash_CreateEntry(tablePtr, key, newPtr)
218: register Hash_Table *tablePtr; /* Hash table to search. */
219: Address key; /* A hash key. */
220: Boolean *newPtr; /* Filled in with TRUE if new entry
221: * created, FALSE otherwise. */
222: {
223: register Hash_Entry *hashEntryPtr;
224: register int *hashKeyPtr;
225: register int *keyPtr;
226: List_Links *hashList;
227:
228: keyPtr = (int *) key;
229:
230: hashList = &(tablePtr->bucketPtr[Hash(tablePtr, (Address) keyPtr)]);
231: hashEntryPtr = ChainSearch(tablePtr, (Address) keyPtr, hashList);
232:
233: if (hashEntryPtr != (Hash_Entry *) NULL) {
234: if (newPtr != NULL) {
235: *newPtr = FALSE;
236: }
237: return hashEntryPtr;
238: }
239:
240: /*
241: * The desired entry isn't there. Before allocating a new entry,
242: * see if we're overloading the buckets. If so, then make a
243: * bigger table.
244: */
245:
246: if (tablePtr->numEntries >= rebuildLimit * tablePtr->size) {
247: RebuildTable(tablePtr);
248: hashList = &(tablePtr->bucketPtr[Hash(tablePtr, (Address) keyPtr)]);
249: }
250: tablePtr->numEntries += 1;
251:
252: /*
253: * Not there, we have to allocate. If the string is longer
254: * than 3 bytes, then we have to allocate extra space in the
255: * entry.
256: */
257:
258: switch (tablePtr->keyType) {
259: case HASH_STRING_KEYS:
260: hashEntryPtr = (Hash_Entry *) emalloc(sizeof(Hash_Entry) +
261: strlen((char *) keyPtr) - 3);
262: strcpy(hashEntryPtr->key.name, (char *) keyPtr);
263: break;
264: case HASH_ONE_WORD_KEYS:
265: hashEntryPtr = (Hash_Entry *) emalloc(sizeof(Hash_Entry));
266: hashEntryPtr->key.ptr = (Address) keyPtr;
267: break;
268: case 2:
269: hashEntryPtr =
270: (Hash_Entry *) emalloc(sizeof(Hash_Entry) + sizeof(int));
271: hashKeyPtr = hashEntryPtr->key.words;
272: *hashKeyPtr++ = *keyPtr++;
273: *hashKeyPtr = *keyPtr;
274: break;
275: default: {
276: register n;
277:
278: n = tablePtr->keyType;
279: hashEntryPtr = (Hash_Entry *)
280: emalloc(sizeof(Hash_Entry) + (n - 1) * sizeof(int));
281: hashKeyPtr = hashEntryPtr->key.words;
282: do {
283: *hashKeyPtr++ = *keyPtr++;
284: } while (--n);
285: break;
286: }
287: }
288:
289: hashEntryPtr->clientData = (ClientData) NULL;
290: List_Insert((List_Links *) hashEntryPtr, LIST_ATFRONT(hashList));
291:
292: if (newPtr != NULL) {
293: *newPtr = TRUE;
294: }
295: return hashEntryPtr;
296: }
297:
298: /*
299: *---------------------------------------------------------
300: *
301: * Hash_DeleteEntry --
302: *
303: * Delete the given hash table entry and free memory associated with
304: * it.
305: *
306: * Results:
307: * None.
308: *
309: * Side Effects:
310: * Hash chain that entry lives in is modified and memory is freed.
311: *
312: *---------------------------------------------------------
313: */
314:
315: void
316: Hash_DeleteEntry(tablePtr, hashEntryPtr)
317: Hash_Table *tablePtr;
318: register Hash_Entry *hashEntryPtr;
319: {
320: if (hashEntryPtr != (Hash_Entry *) NULL) {
321: List_Remove((List_Links *) hashEntryPtr);
322: free((Address) hashEntryPtr);
323: tablePtr->numEntries--;
324: }
325: }
326:
327: /*
328: *---------------------------------------------------------
329: *
330: * Hash_EnumFirst --
331: * This procedure sets things up for a complete search
332: * of all entries recorded in the hash table.
333: *
334: * Results:
335: * The return value is the address of the first entry in
336: * the hash table, or NULL if the table is empty.
337: *
338: * Side Effects:
339: * The information in hashSearchPtr is initialized so that successive
340: * calls to Hash_Next will return successive HashEntry's
341: * from the table.
342: *
343: *---------------------------------------------------------
344: */
345:
346: Hash_Entry *
347: Hash_EnumFirst(tablePtr, hashSearchPtr)
348: Hash_Table *tablePtr; /* Table to be searched. */
349: register Hash_Search *hashSearchPtr; /* Area in which to keep state
350: * about search.*/
351: {
352: hashSearchPtr->tablePtr = tablePtr;
353: hashSearchPtr->nextIndex = 0;
354: hashSearchPtr->hashEntryPtr = (Hash_Entry *) NULL;
355: return Hash_EnumNext(hashSearchPtr);
356: }
357:
358: /*
359: *---------------------------------------------------------
360: *
361: * Hash_EnumNext --
362: * This procedure returns successive entries in the hash table.
363: *
364: * Results:
365: * The return value is a pointer to the next HashEntry
366: * in the table, or NULL when the end of the table is
367: * reached.
368: *
369: * Side Effects:
370: * The information in hashSearchPtr is modified to advance to the
371: * next entry.
372: *
373: *---------------------------------------------------------
374: */
375:
376: Hash_Entry *
377: Hash_EnumNext(hashSearchPtr)
378: register Hash_Search *hashSearchPtr; /* Area used to keep state about
379: search. */
380: {
381: register List_Links *hashList;
382: register Hash_Entry *hashEntryPtr;
383:
384: hashEntryPtr = hashSearchPtr->hashEntryPtr;
385: while (hashEntryPtr == (Hash_Entry *) NULL ||
386: List_IsAtEnd(hashSearchPtr->hashList,
387: (List_Links *) hashEntryPtr)) {
388: if (hashSearchPtr->nextIndex >= hashSearchPtr->tablePtr->size) {
389: return((Hash_Entry *) NULL);
390: }
391: hashList = &(hashSearchPtr->tablePtr->bucketPtr[
392: hashSearchPtr->nextIndex]);
393: hashSearchPtr->nextIndex++;
394: if (!List_IsEmpty(hashList)) {
395: hashEntryPtr = (Hash_Entry *) List_First(hashList);
396: hashSearchPtr->hashList = hashList;
397: break;
398: }
399: }
400:
401: hashSearchPtr->hashEntryPtr =
402: (Hash_Entry *) List_Next((List_Links *) hashEntryPtr);
403:
404: return(hashEntryPtr);
405: }
406:
407: /*
408: *---------------------------------------------------------
409: *
410: * Hash_PrintStats --
411: *
412: * This routine calls a caller-supplied procedure to print
413: * statistics about the current bucket situation.
414: *
415: * Results:
416: * None.
417: *
418: * Side Effects:
419: * Proc gets called (potentially many times) to output information
420: * about the hash table. It must have the following calling sequence:
421: *
422: * void
423: * proc(clientData, string)
424: * ClientData clientData;
425: * char *string;
426: * {
427: * }
428: *
429: * In each call, clientData is the same as the clientData argument
430: * to this procedure, and string is a null-terminated string of
431: * characters to output.
432: *
433: *---------------------------------------------------------
434: */
435:
436: void
437: Hash_PrintStats(tablePtr, proc, clientData)
438: Hash_Table *tablePtr; /* Table for which to print info. */
439: void (*proc)(); /* Procedure to call to do actual
440: * I/O. */
441: {
442: int count[10], overflow, i, j;
443: char msg[100];
444: Hash_Entry *hashEntryPtr;
445: List_Links *hashList;
446:
447: for (i=0; i<10; i++) {
448: count[i] = 0;
449: }
450: overflow = 0;
451: for (i = 0; i < tablePtr->size; i++) {
452: j = 0;
453: hashList = &(tablePtr->bucketPtr[i]);
454: LIST_FORALL(hashList, (List_Links *) hashEntryPtr) {
455: j++;
456: }
457: if (j < 10) {
458: count[j]++;
459: } else {
460: overflow++;
461: }
462: }
463:
464: sprintf(msg, "Entries in table %d number of buckets %d\n",
465: tablePtr->numEntries, tablePtr->size);
466: (*proc)(clientData, msg);
467: for (i = 0; i < 10; i++) {
468: sprintf(msg, "Number of buckets with %d entries: %d.\n",
469: i, count[i]);
470: (*proc)(clientData, msg);
471: }
472: sprintf(msg, "Number of buckets with > 9 entries: %d.\n",
473: overflow);
474: (*proc)(clientData, msg);
475: }
476:
477: /*
478: *---------------------------------------------------------
479: *
480: * Hash --
481: * This is a local procedure to compute a hash table
482: * bucket address based on a string value.
483: *
484: * Results:
485: * The return value is an integer between 0 and size - 1.
486: *
487: * Side Effects:
488: * None.
489: *
490: * Design:
491: * It is expected that most keys are decimal numbers,
492: * so the algorithm behaves accordingly. The randomizing
493: * code is stolen straight from the rand library routine.
494: *
495: *---------------------------------------------------------
496: */
497:
498: static int
499: Hash(tablePtr, key)
500: register Hash_Table *tablePtr;
501: register char *key;
502: {
503: register int i = 0;
504: register int j;
505: register int *intPtr;
506:
507: switch (tablePtr->keyType) {
508: case HASH_STRING_KEYS:
509: while (*key != 0) {
510: i = (i * 10) + (*key++ - '0');
511: }
512: break;
513: case HASH_ONE_WORD_KEYS:
514: i = (int) key;
515: break;
516: case 2:
517: i = ((int *) key)[0] + ((int *) key)[1];
518: break;
519: default:
520: j = tablePtr->keyType;
521: intPtr = (int *) key;
522: do {
523: i += *intPtr++;
524: j--;
525: } while (j > 0);
526: break;
527: }
528:
529:
530: return(((i*1103515245 + 12345) >> tablePtr->downShift) & tablePtr->mask);
531: }
532:
533: /*
534: *---------------------------------------------------------
535: *
536: * ChainSearch --
537: *
538: * Search the hash table for the entry in the hash chain.
539: *
540: * Results:
541: * Pointer to entry in hash chain, NULL if none found.
542: *
543: * Side Effects:
544: * None.
545: *
546: *---------------------------------------------------------
547: */
548:
549: static Hash_Entry *
550: ChainSearch(tablePtr, key, hashList)
551: Hash_Table *tablePtr; /* Hash table to search. */
552: register Address key; /* A hash key. */
553: register List_Links *hashList;
554: {
555: register Hash_Entry *hashEntryPtr;
556: register int *hashKeyPtr;
557: int *keyPtr;
558: register int numKeys;
559:
560: numKeys = tablePtr->keyType;
561: LIST_FORALL(hashList, (List_Links *) hashEntryPtr) {
562: switch (numKeys) {
563: case 0:
564: if (strcmp(hashEntryPtr->key.name, key) == 0) {
565: return(hashEntryPtr);
566: }
567: break;
568: case 1:
569: if (hashEntryPtr->key.ptr == key) {
570: return(hashEntryPtr);
571: }
572: break;
573: case 2:
574: hashKeyPtr = hashEntryPtr->key.words;
575: keyPtr = (int *) key;
576: if (*hashKeyPtr++ == *keyPtr++ && *hashKeyPtr == *keyPtr) {
577: return(hashEntryPtr);
578: }
579: break;
580: default:
581: if (bcmp((Address) hashEntryPtr->key.words,
582: (Address) key, numKeys * sizeof(int))) {
583: return(hashEntryPtr);
584: }
585: break;
586: }
587: }
588:
589: /*
590: * The desired entry isn't there
591: */
592:
593: return ((Hash_Entry *) NULL);
594: }
595:
596: /*
597: *---------------------------------------------------------
598: *
599: * RebuildTable --
600: * This local routine makes a new hash table that
601: * is larger than the old one.
602: *
603: * Results:
604: * None.
605: *
606: * Side Effects:
607: * The entire hash table is moved, so any bucket numbers
608: * from the old table are invalid.
609: *
610: *---------------------------------------------------------
611: */
612:
613: static void
614: RebuildTable(tablePtr)
615: Hash_Table *tablePtr; /* Table to be enlarged. */
616: {
617: int oldSize;
618: int bucket;
619: List_Links *oldBucketPtr;
620: register Hash_Entry *hashEntryPtr;
621: register List_Links *oldHashList;
622:
623: oldBucketPtr = tablePtr->bucketPtr;
624: oldSize = tablePtr->size;
625:
626: /*
627: * Build a new table 4 times as large as the old one.
628: */
629:
630: Hash_InitTable(tablePtr, tablePtr->size * 4, tablePtr->keyType);
631:
632: for (oldHashList = oldBucketPtr; oldSize > 0; oldSize--, oldHashList++) {
633: while (!List_IsEmpty(oldHashList)) {
634: hashEntryPtr = (Hash_Entry *) List_First(oldHashList);
635: List_Remove((List_Links *) hashEntryPtr);
636: switch (tablePtr->keyType) {
637: case HASH_STRING_KEYS:
638: bucket = Hash(tablePtr, (Address) hashEntryPtr->key.name);
639: break;
640: case HASH_ONE_WORD_KEYS:
641: bucket = Hash(tablePtr, (Address) hashEntryPtr->key.ptr);
642: break;
643: default:
644: bucket = Hash(tablePtr, (Address) hashEntryPtr->key.words);
645: break;
646: }
647: List_Insert((List_Links *) hashEntryPtr,
648: LIST_ATFRONT(&(tablePtr->bucketPtr[bucket])));
649: tablePtr->numEntries++;
650: }
651: }
652:
653: free((Address) oldBucketPtr);
654: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.