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