xref: /netbsd-src/usr.bin/config/hash.c (revision e5548b402ae4c44fb816de42c7bba9581ce23ef5)
1 /*	$NetBSD: hash.c,v 1.1 2005/06/05 18:19:53 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratories.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	from: @(#)hash.c	8.1 (Berkeley) 6/6/93
41  */
42 
43 #if HAVE_NBTOOL_CONFIG_H
44 #include "nbtool_config.h"
45 #endif
46 
47 #include <sys/param.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include "defs.h"
51 
52 /*
53  * Interned strings are kept in a hash table.  By making each string
54  * unique, the program can compare strings by comparing pointers.
55  */
56 struct hashent {
57 	// XXXLUKEM: a SIMPLEQ might be more appropriate
58 	TAILQ_ENTRY(hashent) h_next;
59 	const char *h_name;		/* the string */
60 	u_int	h_hash;			/* its hash value */
61 	void	*h_value;		/* other values (for name=value) */
62 };
63 struct hashtab {
64 	size_t	ht_size;		/* size (power of 2) */
65 	u_int	ht_mask;		/* == ht_size - 1 */
66 	u_int	ht_used;		/* number of entries used */
67 	u_int	ht_lim;			/* when to expand */
68 	TAILQ_HEAD(hashenthead, hashent) *ht_tab;
69 };
70 
71 static struct hashtab strings;
72 
73 static struct hashenthead hefreelist = TAILQ_HEAD_INITIALIZER(hefreelist);
74 
75 /*
76  * HASHFRACTION controls ht_lim, which in turn controls the average chain
77  * length.  We allow a few entries, on average, as comparing them is usually
78  * cheap (the h_hash values prevent a strcmp).
79  */
80 #define	HASHFRACTION(sz) ((sz) * 3 / 2)
81 
82 static void			ht_expand(struct hashtab *);
83 static void			ht_init(struct hashtab *, size_t);
84 static inline u_int		hash(const char *);
85 static inline struct hashent   *newhashent(const char *, u_int);
86 
87 /*
88  * Initialize a new hash table.  The size must be a power of 2.
89  */
90 static void
91 ht_init(struct hashtab *ht, size_t sz)
92 {
93 	u_int n;
94 
95 	ht->ht_tab = emalloc(sz * sizeof (ht->ht_tab[0]));
96 	ht->ht_size = sz;
97 	ht->ht_mask = sz - 1;
98 	for (n = 0; n < sz; n++)
99 		TAILQ_INIT(&ht->ht_tab[n]);
100 	ht->ht_used = 0;
101 	ht->ht_lim = HASHFRACTION(sz);
102 }
103 
104 /*
105  * Expand an existing hash table.
106  */
107 static void
108 ht_expand(struct hashtab *ht)
109 {
110 	struct hashenthead *h, *oldh;
111 	struct hashent *p;
112 	u_int n, i;
113 
114 	n = ht->ht_size * 2;
115 	h = emalloc(n * sizeof *h);
116 	for (i = 0; i < n; i++)
117 		TAILQ_INIT(&h[i]);
118 	oldh = ht->ht_tab;
119 	n--;
120 	for (i = 0; i < ht->ht_size; i++) {
121 		while ((p = TAILQ_FIRST(&oldh[i])) != NULL) {
122 			TAILQ_REMOVE(&oldh[i], p, h_next);
123 				// XXXLUKEM: really should be TAILQ_INSERT_TAIL
124 			TAILQ_INSERT_HEAD(&h[p->h_hash & n], p, h_next);
125 		}
126 	}
127 	free(ht->ht_tab);
128 	ht->ht_tab = h;
129 	ht->ht_mask = n;
130 	ht->ht_size = ++n;
131 	ht->ht_lim = HASHFRACTION(n);
132 }
133 
134 /*
135  * Make a new hash entry, setting its h_next to NULL.
136  * If the free list is not empty, use the first entry from there,
137  * otherwise allocate a new entry.
138  */
139 static inline struct hashent *
140 newhashent(const char *name, u_int h)
141 {
142 	struct hashent *hp;
143 
144 	if (TAILQ_EMPTY(&hefreelist))
145 		hp = ecalloc(1, sizeof(*hp));
146 	else {
147 		hp = TAILQ_FIRST(&hefreelist);
148 		TAILQ_REMOVE(&hefreelist, hp, h_next);
149 	}
150 
151 	hp->h_name = name;
152 	hp->h_hash = h;
153 	return (hp);
154 }
155 
156 /*
157  * Hash a string.
158  */
159 static inline u_int
160 hash(const char *str)
161 {
162 	u_int h;
163 
164 	for (h = 0; *str;)
165 		h = (h << 5) + h + *str++;
166 	return (h);
167 }
168 
169 void
170 initintern(void)
171 {
172 
173 	ht_init(&strings, 128);
174 }
175 
176 /*
177  * Generate a single unique copy of the given string.  We expect this
178  * function to be used frequently, so it should be fast.
179  */
180 const char *
181 intern(const char *s)
182 {
183 	struct hashtab *ht;
184 	struct hashent *hp;
185 	struct hashenthead *hpp;
186 	u_int h;
187 	char *p;
188 
189 	ht = &strings;
190 	h = hash(s);
191 	hpp = &ht->ht_tab[h & ht->ht_mask];
192 	TAILQ_FOREACH(hp, hpp, h_next) {
193 		if (hp->h_hash == h && strcmp(hp->h_name, s) == 0)
194 			return (hp->h_name);
195 	}
196 	p = estrdup(s);
197 	hp = newhashent(p, h);
198 	TAILQ_INSERT_TAIL(hpp, hp, h_next);
199 	if (++ht->ht_used > ht->ht_lim)
200 		ht_expand(ht);
201 	return (p);
202 }
203 
204 struct hashtab *
205 ht_new(void)
206 {
207 	struct hashtab *ht;
208 
209 	ht = ecalloc(1, sizeof *ht);
210 	ht_init(ht, 8);
211 	return (ht);
212 }
213 
214 /*
215  * Insert and/or replace.
216  */
217 int
218 ht_insrep(struct hashtab *ht, const char *nam, void *val, int replace)
219 {
220 	struct hashent *hp;
221 	struct hashenthead *hpp;
222 	u_int h;
223 
224 	h = hash(nam);
225 	hpp = &ht->ht_tab[h & ht->ht_mask];
226 	TAILQ_FOREACH(hp, hpp, h_next) {
227 		if (hp->h_name == nam) {
228 			if (replace)
229 				hp->h_value = val;
230 			return (1);
231 		}
232 	}
233 	hp = newhashent(nam, h);
234 	TAILQ_INSERT_TAIL(hpp, hp, h_next);
235 	hp->h_value = val;
236 	if (++ht->ht_used > ht->ht_lim)
237 		ht_expand(ht);
238 	return (0);
239 }
240 
241 /*
242  * Remove.
243  */
244 int
245 ht_remove(struct hashtab *ht, const char *name)
246 {
247 	struct hashent *hp;
248 	struct hashenthead *hpp;
249 	u_int h;
250 
251 	h = hash(name);
252 	hpp = &ht->ht_tab[h & ht->ht_mask];
253 
254 	TAILQ_FOREACH(hp, hpp, h_next) {
255 		if (hp->h_name != name)
256 			continue;
257 		TAILQ_REMOVE(hpp, hp, h_next);
258 
259 		memset(hp, 0, sizeof(*hp));
260 		TAILQ_INSERT_TAIL(&hefreelist, hp, h_next);
261 		ht->ht_used--;
262 		return (0);
263 	}
264 	return (1);
265 }
266 
267 void *
268 ht_lookup(struct hashtab *ht, const char *nam)
269 {
270 	struct hashent *hp;
271 	struct hashenthead *hpp;
272 	u_int h;
273 
274 	h = hash(nam);
275 	hpp = &ht->ht_tab[h & ht->ht_mask];
276 	TAILQ_FOREACH(hp, hpp, h_next)
277 		if (hp->h_name == nam)
278 			return (hp->h_value);
279 	return (NULL);
280 }
281 
282 /*
283  * first parameter to callback is the entry name from the hash table
284  * second parameter is the value from the hash table
285  * third argument is passed through from the "arg" parameter to ht_enumerate()
286  */
287 
288 int
289 ht_enumerate(struct hashtab *ht, ht_callback cbfunc, void *arg)
290 {
291 	struct hashent *hp;
292 	struct hashenthead *hpp;
293 	u_int i;
294 	int rval = 0;
295 
296 	for (i = 0; i < ht->ht_size; i++) {
297 		hpp = &ht->ht_tab[i];
298 		TAILQ_FOREACH(hp, hpp, h_next)
299 			rval += (*cbfunc)(hp->h_name, hp->h_value, arg);
300 	}
301 	return rval;
302 }
303