xref: /onnv-gate/usr/src/cmd/fs.d/nfs/mountd/hashset.c (revision 11211:a6230133d60c)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*11211SThomas.Haynes@Sun.COM  * Common Development and Distribution License (the "License").
6*11211SThomas.Haynes@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*11211SThomas.Haynes@Sun.COM 
220Sstevel@tonic-gate /*
23*11211SThomas.Haynes@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*11211SThomas.Haynes@Sun.COM  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <stdio.h>
280Sstevel@tonic-gate #include <stdlib.h>
290Sstevel@tonic-gate #include <string.h>
300Sstevel@tonic-gate #include "hashset.h"
310Sstevel@tonic-gate #include "mountd.h"
32*11211SThomas.Haynes@Sun.COM #include <sys/sdt.h>
330Sstevel@tonic-gate 
340Sstevel@tonic-gate /*
350Sstevel@tonic-gate  * HASHSET is hash table managing pointers to a set of keys
360Sstevel@tonic-gate  * (set is a collection without duplicates). The public interface
370Sstevel@tonic-gate  * of the HASHSET is similar to the java.util.Set interface.
380Sstevel@tonic-gate  * Unlike the libc `hsearch' based hash table, this implementation
390Sstevel@tonic-gate  * does allow multiple instances of HASHSET within a single application,
400Sstevel@tonic-gate  * and the HASHSET_ITERATOR allows to iterate through the entire set
410Sstevel@tonic-gate  * using h_next().
420Sstevel@tonic-gate  *
430Sstevel@tonic-gate  * HASHSET does not store actual keys but only pointers to keys. Hence the
440Sstevel@tonic-gate  * data remains intact when HASHSET grows (resizes itself). HASHSET accesses
450Sstevel@tonic-gate  * the actual key data only through the hash and equal functions given
460Sstevel@tonic-gate  * as arguments to h_create.
470Sstevel@tonic-gate  *
480Sstevel@tonic-gate  * Hash collisions are resolved with linked lists.
490Sstevel@tonic-gate  */
500Sstevel@tonic-gate 
510Sstevel@tonic-gate typedef struct HashSetEntry {
520Sstevel@tonic-gate 	uint_t e_hash;		/* Hash value */
530Sstevel@tonic-gate 	const void *e_key;	/* Pointer to a key */
540Sstevel@tonic-gate 	struct HashSetEntry *e_next;
550Sstevel@tonic-gate } ENTRY;
560Sstevel@tonic-gate 
570Sstevel@tonic-gate struct HashSet {
580Sstevel@tonic-gate 	ENTRY **h_table;	/* Pointer to an array of ENTRY'ies */
590Sstevel@tonic-gate 	uint_t h_tableSize;	/* Size of the array */
600Sstevel@tonic-gate 	uint_t h_count;		/* Current count */
610Sstevel@tonic-gate 	uint_t h_threshold;	/* loadFactor threshold */
620Sstevel@tonic-gate 	float  h_loadFactor;	/* Current loadFactor (h_count/h_tableSize( */
630Sstevel@tonic-gate 	uint_t (*h_hash) (const void *);
640Sstevel@tonic-gate 	int    (*h_equal) (const void *, const void *);
650Sstevel@tonic-gate };
660Sstevel@tonic-gate 
670Sstevel@tonic-gate struct HashSetIterator {
680Sstevel@tonic-gate 	HASHSET i_h;
690Sstevel@tonic-gate 	uint_t i_indx;
700Sstevel@tonic-gate 	ENTRY *i_e;
710Sstevel@tonic-gate 	uint_t i_coll;
720Sstevel@tonic-gate };
730Sstevel@tonic-gate 
740Sstevel@tonic-gate static void rehash(HASHSET h);
750Sstevel@tonic-gate 
760Sstevel@tonic-gate #define	DEFAULT_INITIALCAPACITY	1
770Sstevel@tonic-gate #define	DEFAULT_LOADFACTOR	0.75
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate  *  Create a HASHSET
810Sstevel@tonic-gate  *  - HASHSET is a hash table of pointers to keys
820Sstevel@tonic-gate  *  - duplicate keys are not allowed
830Sstevel@tonic-gate  *  - the HASHSET is opaque and can be accessed only through the h_ functions
840Sstevel@tonic-gate  *  - two keys k1 and k2 are considered equal if the result of equal(k1, k2)
850Sstevel@tonic-gate  *    is non-zero
860Sstevel@tonic-gate  *  - the function hash(key) is used to compute hash values for keys; if
870Sstevel@tonic-gate  *    keys are "equal" the values returned by the hash function must be
880Sstevel@tonic-gate  *    identical.
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate 
910Sstevel@tonic-gate HASHSET
h_create(uint_t (* hash)(const void *),int (* equal)(const void *,const void *),uint_t initialCapacity,float loadFactor)920Sstevel@tonic-gate h_create(uint_t (*hash) (const void *),
930Sstevel@tonic-gate     int    (*equal) (const void *, const void *),
940Sstevel@tonic-gate     uint_t initialCapacity,
950Sstevel@tonic-gate     float loadFactor)
960Sstevel@tonic-gate {
970Sstevel@tonic-gate 	HASHSET h;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	if (initialCapacity == 0)
1000Sstevel@tonic-gate 		initialCapacity = DEFAULT_INITIALCAPACITY;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	if (loadFactor < 0.0)
1030Sstevel@tonic-gate 		loadFactor = DEFAULT_LOADFACTOR;
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	h = exmalloc(sizeof (*h));
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	if (h == NULL)
1080Sstevel@tonic-gate 		return (NULL);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	h->h_table = exmalloc(initialCapacity * sizeof (ENTRY *));
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	(void) memset(h->h_table, 0, initialCapacity * sizeof (ENTRY *));
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate 	if (h->h_table == NULL) {
1150Sstevel@tonic-gate 		free(h);
1160Sstevel@tonic-gate 		return (NULL);
1170Sstevel@tonic-gate 	}
1180Sstevel@tonic-gate 	h->h_tableSize = initialCapacity;
1190Sstevel@tonic-gate 	h->h_hash = hash;
1200Sstevel@tonic-gate 	h->h_equal = equal;
1210Sstevel@tonic-gate 	h->h_loadFactor = loadFactor;
1220Sstevel@tonic-gate 	h->h_threshold = (uint_t)(initialCapacity * loadFactor);
1230Sstevel@tonic-gate 	h->h_count = 0;
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	return (h);
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate /*
1290Sstevel@tonic-gate  *  Return a pointer to a matching key
1300Sstevel@tonic-gate  */
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate const void *
h_get(const HASHSET h,void * key)1330Sstevel@tonic-gate h_get(const HASHSET h, void *key)
1340Sstevel@tonic-gate {
1350Sstevel@tonic-gate 	uint_t hash = h->h_hash(key);
1360Sstevel@tonic-gate 	uint_t i = hash % h->h_tableSize;
1370Sstevel@tonic-gate 	ENTRY *e;
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	for (e = h->h_table[i]; e; e = e->e_next)
1400Sstevel@tonic-gate 		if (e->e_hash == hash && h->h_equal(e->e_key, key))
1410Sstevel@tonic-gate 			return (e->e_key);
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	return (NULL);
1440Sstevel@tonic-gate }
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate /*
1470Sstevel@tonic-gate  *  Rehash (grow) HASHSET
1480Sstevel@tonic-gate  *  - called when loadFactor exceeds threshold
1490Sstevel@tonic-gate  *  - new size is 2*old_size+1
1500Sstevel@tonic-gate  */
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate static void
rehash(HASHSET h)1530Sstevel@tonic-gate rehash(HASHSET h)
1540Sstevel@tonic-gate {
1550Sstevel@tonic-gate 	uint_t i = h->h_tableSize;
1560Sstevel@tonic-gate 	uint_t newtabSize = 2 * i + 1;
1570Sstevel@tonic-gate 	ENTRY **newtab = exmalloc(newtabSize * sizeof (ENTRY *));
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	(void) memset(newtab, 0, newtabSize * sizeof (ENTRY *));
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	while (i--) {
1620Sstevel@tonic-gate 		ENTRY *e, *next;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		for (e = h->h_table[i]; e; e = next) {
1650Sstevel@tonic-gate 			uint_t k = e->e_hash % newtabSize;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 			next = (ENTRY *) e->e_next;
1680Sstevel@tonic-gate 			e->e_next = (ENTRY *) newtab[k];
1690Sstevel@tonic-gate 			newtab[k] = e;
1700Sstevel@tonic-gate 		}
1710Sstevel@tonic-gate 	}
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	h->h_threshold = (uint_t)(newtabSize * h->h_loadFactor);
1740Sstevel@tonic-gate 	h->h_tableSize = newtabSize;
1750Sstevel@tonic-gate 	free(h->h_table);
1760Sstevel@tonic-gate 	h->h_table = newtab;
1770Sstevel@tonic-gate }
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate  *  Store a key into a HASHSET
1810Sstevel@tonic-gate  *  - if there is already an "equal" key then the new key will not
1820Sstevel@tonic-gate  *    be stored and the function returns a pointer to an existing key
1830Sstevel@tonic-gate  *  - otherwise the function stores pointer to the new key and return NULL
1840Sstevel@tonic-gate  */
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate const void *
h_put(HASHSET h,const void * key)1870Sstevel@tonic-gate h_put(HASHSET h, const void *key)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate 	uint_t hash = h->h_hash(key);
1900Sstevel@tonic-gate 	uint_t indx = hash % h->h_tableSize;
1910Sstevel@tonic-gate 	ENTRY *e;
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	for (e = h->h_table[indx]; e; e = e->e_next)
1940Sstevel@tonic-gate 		if (e->e_hash == hash && h->h_equal(e->e_key, key))
1950Sstevel@tonic-gate 			return (key);
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	if (h->h_count >= h->h_threshold) {
1980Sstevel@tonic-gate 		rehash(h);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 		indx = hash % h->h_tableSize;
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	e = exmalloc(sizeof (ENTRY));
2040Sstevel@tonic-gate 	e->e_hash = hash;
2050Sstevel@tonic-gate 	e->e_key = (void *) key;
2060Sstevel@tonic-gate 	e->e_next = h->h_table[indx];
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	h->h_table[indx] = e;
2090Sstevel@tonic-gate 	h->h_count++;
2100Sstevel@tonic-gate 
211*11211SThomas.Haynes@Sun.COM 	DTRACE_PROBE2(mountd, hashset, h->h_count, h->h_loadFactor);
212*11211SThomas.Haynes@Sun.COM 
2130Sstevel@tonic-gate 	return (NULL);
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate /*
2170Sstevel@tonic-gate  *  Delete a key
2180Sstevel@tonic-gate  *  - if there is no "equal" key in the HASHSET the fuction returns NULL
2190Sstevel@tonic-gate  *  - otherwise the function returns a pointer to the deleted key
2200Sstevel@tonic-gate  */
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate const void *
h_delete(HASHSET h,const void * key)2230Sstevel@tonic-gate h_delete(HASHSET h, const void *key)
2240Sstevel@tonic-gate {
2250Sstevel@tonic-gate 	uint_t hash = h->h_hash(key);
2260Sstevel@tonic-gate 	uint_t indx = hash % h->h_tableSize;
2270Sstevel@tonic-gate 	ENTRY *e, *prev;
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	for (e = h->h_table[indx], prev = NULL; e; prev = e, e = e->e_next) {
2300Sstevel@tonic-gate 		if (e->e_hash == hash && h->h_equal(e->e_key, key)) {
2310Sstevel@tonic-gate 			key = e->e_key;
2320Sstevel@tonic-gate 			if (prev)
2330Sstevel@tonic-gate 				prev->e_next = e->e_next;
2340Sstevel@tonic-gate 			else
2350Sstevel@tonic-gate 				h->h_table[indx] = e->e_next;
2360Sstevel@tonic-gate 			free(e);
2370Sstevel@tonic-gate 			return (key);
2380Sstevel@tonic-gate 		}
2390Sstevel@tonic-gate 	}
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	return (NULL);
2420Sstevel@tonic-gate }
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate /*
2450Sstevel@tonic-gate  *  Return an opaque HASHSET_ITERATOR (to be used in h_next())
2460Sstevel@tonic-gate  */
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate HASHSET_ITERATOR
h_iterator(HASHSET h)2490Sstevel@tonic-gate h_iterator(HASHSET h)
2500Sstevel@tonic-gate {
2510Sstevel@tonic-gate 	HASHSET_ITERATOR i = exmalloc(sizeof (*i));
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	i->i_h = h;
2540Sstevel@tonic-gate 	i->i_indx = h->h_tableSize;
2550Sstevel@tonic-gate 	i->i_e = NULL;
2560Sstevel@tonic-gate 	i->i_coll = 0;
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	return (i);
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate /*
2620Sstevel@tonic-gate  * Return a pointer to a next key
2630Sstevel@tonic-gate  */
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate const void *
h_next(HASHSET_ITERATOR i)2660Sstevel@tonic-gate h_next(HASHSET_ITERATOR i)
2670Sstevel@tonic-gate {
2680Sstevel@tonic-gate 	const void *key;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	while (i->i_e == NULL) {
2710Sstevel@tonic-gate 		if (i->i_indx-- == 0)
2720Sstevel@tonic-gate 			return (NULL);
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 		i->i_e = i->i_h->h_table[i->i_indx];
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	key = i->i_e->e_key;
2780Sstevel@tonic-gate 	i->i_e = i->i_e->e_next;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (i->i_e)
2810Sstevel@tonic-gate 		i->i_coll++;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	return (key);
2840Sstevel@tonic-gate }
285