xref: /illumos-gate/usr/src/lib/libc/port/gen/hsearch.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * Compile time switches:
32  *
33  *  MULT - use a multiplicative hashing function.
34  *  DIV - use the remainder mod table size as a hashing function.
35  *  CHAINED - use a linked list to resolve collisions.
36  *  OPEN - use open addressing to resolve collisions.
37  *  BRENT - use Brent's modification to improve the OPEN algorithm.
38  *  SORTUP - CHAINED list is sorted in increasing order.
39  *  SORTDOWN - CHAINED list is sorted in decreasing order.
40  *  START - CHAINED list with entries appended at front.
41  *  DRIVER - compile in a main program to drive the tests.
42  *  HSEARCH_DEBUG - compile some debugging printout statements.
43  *  USCR - user supplied comparison routine.
44  */
45 
46 #pragma weak _hcreate = hcreate
47 #pragma weak _hdestroy = hdestroy
48 #pragma weak _hsearch = hsearch
49 
50 #include "lint.h"
51 #include <mtlib.h>
52 #include <limits.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <thread.h>
57 #include <synch.h>
58 #include <search.h>
59 
60 typedef char *POINTER;
61 
62 #define	SUCCEED		0
63 #define	FAIL		1
64 #define	TRUE		1
65 #define	FALSE		0
66 #define	repeat		for (;;)
67 #define	until(A)	if (A) break;
68 
69 #ifdef OPEN
70 #undef CHAINED
71 #else
72 #ifndef CHAINED
73 #define	OPEN
74 #endif
75 #endif
76 
77 #ifdef MULT
78 #undef DIV
79 #else
80 #ifndef DIV
81 #define	MULT
82 #endif
83 #endif
84 
85 #ifdef START
86 #undef SORTUP
87 #undef SORTDOWN
88 #else
89 #ifdef SORTUP
90 #undef SORTDOWN
91 #endif
92 #endif
93 
94 #ifdef USCR
95 #define	COMPARE(A, B) (* hcompar)((A), (B))
96 extern int (* hcompar)();
97 #else
98 #define	COMPARE(A, B) strcmp((A), (B))
99 #endif
100 
101 #ifdef MULT
102 #define	SHIFT ((CHAR_BIT * sizeof (int)) - m) /* Shift factor */
103 #define	FACTOR 035761254233	/* Magic multiplication factor */
104 #define	HASH hashm		/* Multiplicative hash function */
105 #define	HASH2 hash2m	/* Secondary hash function */
106 static unsigned int hashm(POINTER);
107 static unsigned int hash2m(POINTER);
108 #else
109 #ifdef DIV
110 #define	HASH hashd		/* Division hashing routine */
111 #define	HASH2(A) 1		/* Secondary hash function */
112 static unsigned int hashd();
113 #endif
114 #endif
115 
116 #ifdef CHAINED
117 typedef struct node {	/* Part of the linked list of entries */
118 	ENTRY item;
119 	struct node *next;
120 } NODE;
121 typedef NODE *TABELEM;
122 static NODE **table;	/* The address of the hash table */
123 static ENTRY *build();
124 #else
125 #ifdef OPEN
126 typedef ENTRY TABELEM;	/* What the table contains (TABle ELEMents) */
127 static TABELEM *table;	/* The address of the hash table */
128 static unsigned int count = 0;	/* Number of entries in hash table */
129 #endif
130 #endif
131 
132 static unsigned int length;	/* Size of the hash table */
133 static unsigned int m;		/* Log base 2 of length */
134 static unsigned int prcnt;	/* Number of probes this item */
135 static mutex_t table_lock = DEFAULTMUTEX;
136 #define	RETURN(n)    { lmutex_unlock(&table_lock); return (n); }
137 
138 /*
139  * forward declarations
140  */
141 
142 static unsigned int crunch(POINTER);
143 
144 #ifdef DRIVER
145 static void hdump();
146 
147 main()
148 {
149 	char line[80];	/* Room for the input line */
150 	int i = 0;		/* Data generator */
151 	ENTRY *res;		/* Result of hsearch */
152 	ENTRY *new;		/* Test entry */
153 
154 start:
155 	if (hcreate(5))
156 		printf("Length = %u, m = %u\n", length, m);
157 	else {
158 		fprintf(stderr, "Out of core\n");
159 		exit(FAIL);
160 	}
161 	repeat {
162 	hdump();
163 	printf("Enter a probe: ");
164 	until(EOF == scanf("%s", line) || strcmp(line, "quit") == 0);
165 #ifdef HSEARCH_DEBUG
166 	printf("%s, ", line);
167 	printf("division: %d, ", hashd(line));
168 	printf("multiplication: %d\n", hashm(line));
169 #endif
170 	new = (ENTRY *) malloc(sizeof (ENTRY));
171 	if (new == NULL) {
172 		fprintf(stderr, "Out of core \n");
173 		exit(FAIL);
174 	} else {
175 		new->key = malloc((unsigned)strlen(line) + 1);
176 		if (new->key == NULL) {
177 			fprintf(stderr, "Out of core \n");
178 			exit(FAIL);
179 		}
180 		(void) strcpy(new->key, line);
181 		new->data = malloc(sizeof (int));
182 		if (new->data == NULL) {
183 			fprintf(stderr, "Out of core \n");
184 			exit(FAIL);
185 		}
186 		*new->data = i++;
187 	}
188 	res = hsearch(*new, ENTER);
189 	printf("The number of probes required was %d\n", prcnt);
190 	if (res == (ENTRY *) 0)
191 		printf("Table is full\n");
192 	else {
193 		printf("Success: ");
194 		printf("Key = %s, Value = %d\n", res->key, *res->data);
195 	}
196 	}
197 	printf("Do you wish to start another hash table (yes/no?)");
198 	if (EOF == scanf("%s", line) || strcmp(line, "no") == 0)
199 		exit(SUCCEED);
200 	hdestroy();
201 	goto start;
202 }
203 #endif
204 
205 int
206 hcreate(size_t size)	/* Create a hash table no smaller than size */
207 	/* Minimum "size" for hash table */
208 {
209 	size_t unsize;	/* Holds the shifted size */
210 	TABELEM *local_table;
211 	TABELEM *old_table;
212 	unsigned int local_length;
213 	unsigned int local_m;
214 
215 	if (size == 0)
216 		return (FALSE);
217 
218 	unsize = size;	/* +1 for empty table slot; -1 for ceiling */
219 	local_length = 1;	/* Maximum entries in table */
220 	local_m = 0;		/* Log2 length */
221 	while (unsize) {
222 		unsize >>= 1;
223 		local_length <<= 1;
224 		local_m++;
225 	}
226 
227 	local_table = (TABELEM *) calloc(local_length, sizeof (TABELEM));
228 
229 	lmutex_lock(&table_lock);
230 	old_table = table;
231 	table = local_table;
232 	length = local_length;
233 	m = local_m;
234 	lmutex_unlock(&table_lock);
235 	if (old_table != NULL)
236 		free(old_table);
237 	return (local_table != NULL);
238 }
239 
240 void
241 hdestroy(void)	/* Reset the module to its initial state */
242 {
243 	POINTER local_table;
244 
245 	lmutex_lock(&table_lock);
246 #ifdef CHAINED
247 	int i;
248 	NODE *p, *oldp;
249 	for (i = 0; i < length; i++) {
250 		if (table[i] != (NODE *)NULL) {
251 			p = table[i];
252 			while (p != (NODE *)NULL) {
253 				oldp = p;
254 				p = p -> next;
255 				/*
256 				 * This is a locking vs malloc() violation.
257 				 * Fortunately, it is not actually compiled.
258 				 */
259 				free(oldp);
260 			}
261 		}
262 	}
263 #endif
264 	local_table = (POINTER)table;
265 	table = 0;
266 #ifdef OPEN
267 	count = 0;
268 #endif
269 	lmutex_unlock(&table_lock);
270 	free(local_table);
271 }
272 
273 #ifdef OPEN
274 /*
275  * Hash search of a fixed-capacity table.  Open addressing used to
276  *  resolve collisions.  Algorithm modified from Knuth, Volume 3,
277  *  section 6.4, algorithm D.  Labels flag corresponding actions.
278  */
279 
280 /* Find or insert the item into the table */
281 ENTRY
282 *hsearch(ENTRY item, ACTION action)
283 	/* "item" to be inserted or found */
284 	/* action: FIND or ENTER */
285 {
286 	unsigned int i;	/* Insertion index */
287 	unsigned int c;	/* Secondary probe displacement */
288 
289 	lmutex_lock(&table_lock);
290 	prcnt = 1;
291 
292 /* D1: */
293 	i = HASH(item.key);	/* Primary hash on key */
294 #ifdef HSEARCH_DEBUG
295 	if (action == ENTER)
296 		printf("hash = %o\n", i);
297 #endif
298 
299 /* D2: */
300 	if (table[i].key == NULL)	/* Empty slot? */
301 		goto D6;
302 	else if (COMPARE(table[i].key, item.key) == 0)	/* Match? */
303 		RETURN(&table[i]);
304 
305 /* D3: */
306 	c = HASH2(item.key);	/* No match => compute secondary hash */
307 #ifdef HSEARCH_DEBUG
308 	if (action == ENTER)
309 		printf("hash2 = %o\n", c);
310 #endif
311 
312 D4:
313 	i = (i + c) % length;	/* Advance to next slot */
314 	prcnt++;
315 
316 /* D5: */
317 	if (table[i].key == NULL)	/* Empty slot? */
318 		goto D6;
319 	else if (COMPARE(table[i].key, item.key) == 0)	/* Match? */
320 		RETURN(&table[i])
321 	else
322 		goto D4;
323 
324 D6:	if (action == FIND)		/* Insert if requested */
325 		RETURN((ENTRY *) NULL);
326 	if (count == (length - 1))	/* Table full? */
327 		RETURN((ENTRY *) 0);
328 
329 #ifdef BRENT
330 /*
331  * Brent's variation of the open addressing algorithm.  Do extra
332  * work during insertion to speed retrieval.  May require switching
333  * of previously placed items.  Adapted from Knuth, Volume 3, section
334  * 4.6 and Brent's article in CACM, volume 10, #2, February 1973.
335  */
336 
337 	{
338 	unsigned int p0 = HASH(item.key);   /* First probe index */
339 	unsigned int c0 = HASH2(item.key);  /* Main branch increment */
340 	unsigned int r = prcnt - 1; /* Current minimum distance */
341 	unsigned int j;		/* Counts along main branch */
342 	unsigned int k;		/* Counts along secondary branch */
343 	unsigned int curj;	/* Current best main branch site */
344 	unsigned int curpos;	/* Current best table index */
345 	unsigned int pj;	/* Main branch indices */
346 	unsigned int cj;	/* Secondary branch increment distance */
347 	unsigned int pjk;	/* Secondary branch probe indices */
348 
349 	if (prcnt >= 3) {
350 		for (j = 0; j < prcnt; j++) {   /* Count along main branch */
351 			pj = (p0 + j * c0) % length; /* New main branch index */
352 			cj = HASH2(table[pj].key); /* Secondary branch incr. */
353 			for (k = 1; j+k <= r; k++) {
354 					/* Count on secondary branch */
355 				pjk = (pj + k * cj) % length;
356 					/* Secondary probe */
357 				if (table[pjk].key == NULL) {
358 					/* Improvement found */
359 					r = j + k; /* Decrement upper bound */
360 					curj = pj; /* Save main probe index */
361 					curpos = pjk;
362 						/* Save secondeary index */
363 				}
364 			}
365 		}
366 		if (r != prcnt - 1) {	/* If an improvement occurred */
367 			table[curpos] = table[curj]; /* Old key to new site */
368 #ifdef HSEARCH_DEBUG
369 			printf("Switch curpos = %o, curj = %o, oldi = %o\n",
370 				curj, curpos, i);
371 #endif
372 			i = curj;
373 		}
374 	}
375 	}
376 #endif
377 	count++;			/* Increment table occupancy count */
378 	table[i] = item;		/* Save item */
379 
380 	lmutex_unlock(&table_lock);
381 	return (&table[i]);		/* Address of item is returned */
382 }
383 #endif
384 
385 #ifdef USCR
386 #ifdef DRIVER
387 static int
388 compare(a, b)
389 POINTER a;
390 POINTER b;
391 {
392     return (strcmp(a, b));
393 }
394 
395 int (* hcompar)() = compare;
396 #endif
397 #endif
398 
399 #ifdef CHAINED
400 #ifdef SORTUP
401 #define	STRCMP(A, B) (COMPARE((A), (B)) > 0)
402 #else
403 #ifdef SORTDOWN
404 #define	STRCMP(A, B) (COMPARE((A), (B)) < 0)
405 #else
406 #define	STRCMP(A, B) (COMPARE((A), (B)) != 0)
407 #endif
408 #endif
409 
410 ENTRY
411 *hsearch(item, action)	/* Chained search with sorted lists */
412 ENTRY item;		/* Item to be inserted or found */
413 ACTION action;		/* FIND or ENTER */
414 {
415 	NODE *p;		/* Searches through the linked list */
416 	NODE **q;		/* Where to store the pointer to a new NODE */
417 	unsigned int i;		/* Result of hash */
418 	int res;		/* Result of string comparison */
419 
420 	lmutex_lock(&table_lock);
421 	prcnt = 1;
422 
423 	i = HASH(item.key);	/* Table[i] contains list head */
424 
425 	if (table[i] == (NODE*)NULL) { /* List has not yet been begun */
426 		if (action == FIND)
427 			RETURN((ENTRY *) NULL);
428 		else
429 			RETURN(build(&table[i], (NODE *) NULL, item));
430 	} else {		/* List is not empty */
431 		q = &table[i];
432 		p = table[i];
433 		while (p != NULL && (res = STRCMP(item.key, p->item.key))) {
434 			prcnt++;
435 			q = &(p->next);
436 			p = p->next;
437 		}
438 
439 		if (p != NULL && res == 0)	/* Item has been found */
440 			RETURN(&(p->item));
441 		else {			/* Item is not yet on list */
442 			if (action == FIND)
443 				RETURN((ENTRY *) NULL);
444 			else
445 #ifdef START
446 				RETURN(build(&table[i], table[i], item));
447 #else
448 				RETURN(build(q, p, item));
449 #endif
450 		}
451 	}
452 }
453 
454 static ENTRY
455 *build(last, next, item)
456 NODE **last;		/* Where to store in last list item */
457 NODE *next;		/* Link to next list item */
458 ENTRY item;		/* Item to be kept in node */
459 {
460 	/*
461 	 * This is a locking vs malloc() violation.
462 	 * Fortunately, it is not actually compiled.
463 	 */
464 	NODE *p = (NODE *) malloc(sizeof (NODE));
465 
466 	if (p != NULL) {
467 		p->item = item;
468 		*last = p;
469 		p->next = next;
470 		return (&(p->item));
471 	} else
472 		return (NULL);
473 }
474 #endif
475 
476 #ifdef DIV
477 static unsigned int
478 hashd(key)		/* Division hashing scheme */
479 POINTER key;		/* Key to be hashed */
480 {
481     return (crunch(key) % length);
482 }
483 #else
484 #ifdef MULT
485 /*
486  *  NOTE: The following algorithm only works on machines where
487  *  the results of multiplying two integers is the least
488  *  significant part of the double word integer required to hold
489  *  the result.  It is adapted from Knuth, Volume 3, section 6.4.
490  */
491 
492 static unsigned int
493 hashm(POINTER key)	/* Multiplication hashing scheme */
494 	/* "key" to be hashed */
495 {
496 	return ((int)(((unsigned)(crunch(key) * FACTOR)) >> SHIFT));
497 }
498 
499 /*
500  * Secondary hashing, for use with multiplicitive hashing scheme.
501  * Adapted from Knuth, Volume 3, section 6.4.
502  */
503 
504 static unsigned int
505 hash2m(POINTER key)	/* Secondary hashing routine */
506 	/* "key" is the string to be hashed */
507 {
508     return (((unsigned int)((crunch(key) * FACTOR) << m) >> SHIFT) | 1);
509 }
510 #endif
511 #endif
512 
513 /* PJ Weinberger's hash function */
514 static unsigned int
515 crunch(POINTER key)	/* Convert multicharacter key to unsigned int */
516 {
517 	unsigned int h = 0;
518 	unsigned int g;
519 	unsigned char *p = (unsigned char *)key;
520 
521 	for (; *p; p++) {
522 		h = (h << 4) + *p;
523 		g = h & 0xf0000000;
524 		if (g != 0) {
525 			h ^= (g >> 24);
526 			h ^= g;
527 		}
528 	}
529 	return (h);
530 }
531 
532 #ifdef DRIVER
533 static void
534 hdump()			/* Dumps loc, data, probe count, key */
535 {
536 	unsigned int i;	/* Counts table slots */
537 #ifdef OPEN
538 	unsigned int sum = 0;	/* Counts probes */
539 #else
540 #ifdef CHAINED
541 	NODE *a;		/* Current Node on list */
542 #endif
543 #endif
544 
545 	for (i = 0; i < length; i++)
546 #ifdef OPEN
547 		if (table[i].key == NULL)
548 			printf("%o.\t-,\t-,\t(NULL)\n", i);
549 		else {
550 			unsigned int oldpr = prcnt;
551 				/* Save current probe count */
552 
553 			hsearch(table[i], FIND);
554 			sum += prcnt;
555 			printf("%o.\t%d,\t%d,\t%s\n", i,
556 			    *table[i].data, prcnt, table[i].key);
557 			prcnt = oldpr;
558 		}
559 	printf("Total probes = %d\n", sum);
560 #else
561 #ifdef CHAINED
562 	if (table[i] == NULL)
563 		printf("%o.\t-,\t-,\t(NULL)\n", i);
564 	else {
565 		printf("%o.", i);
566 		for (a = table[i]; a != NULL; a = a->next)
567 			printf("\t%d,\t%#0.4x,\t%s\n",
568 			    *a->item.data, a, a->item.key);
569 	}
570 #endif
571 #endif
572 }
573 #endif
574