hash.c revision 1.3 1 /* $NetBSD: hash.c,v 1.3 2006/09/03 07:45:40 dsl 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 <util.h>
51 #include "defs.h"
52
53 /*
54 * Interned strings are kept in a hash table. By making each string
55 * unique, the program can compare strings by comparing pointers.
56 */
57 struct hashent {
58 // XXXLUKEM: a SIMPLEQ might be more appropriate
59 TAILQ_ENTRY(hashent) h_next;
60 const char *h_name; /* the string */
61 u_int h_hash; /* its hash value */
62 void *h_value; /* other values (for name=value) */
63 };
64 struct hashtab {
65 size_t ht_size; /* size (power of 2) */
66 u_int ht_mask; /* == ht_size - 1 */
67 u_int ht_used; /* number of entries used */
68 u_int ht_lim; /* when to expand */
69 TAILQ_HEAD(hashenthead, hashent) *ht_tab;
70 };
71
72 static struct hashtab strings;
73
74 /*
75 * HASHFRACTION controls ht_lim, which in turn controls the average chain
76 * length. We allow a few entries, on average, as comparing them is usually
77 * cheap (the h_hash values prevent a strcmp).
78 */
79 #define HASHFRACTION(sz) ((sz) * 3 / 2)
80
81 static void ht_expand(struct hashtab *);
82 static void ht_init(struct hashtab *, size_t);
83 static inline u_int hash(const char *);
84 static inline struct hashent *newhashent(const char *, u_int);
85
86 /*
87 * Initialize a new hash table. The size must be a power of 2.
88 */
89 static void
90 ht_init(struct hashtab *ht, size_t sz)
91 {
92 u_int n;
93
94 ht->ht_tab = emalloc(sz * sizeof (ht->ht_tab[0]));
95 ht->ht_size = sz;
96 ht->ht_mask = sz - 1;
97 for (n = 0; n < sz; n++)
98 TAILQ_INIT(&ht->ht_tab[n]);
99 ht->ht_used = 0;
100 ht->ht_lim = HASHFRACTION(sz);
101 }
102
103 /*
104 * Expand an existing hash table.
105 */
106 static void
107 ht_expand(struct hashtab *ht)
108 {
109 struct hashenthead *h, *oldh;
110 struct hashent *p;
111 u_int n, i;
112
113 n = ht->ht_size * 2;
114 h = emalloc(n * sizeof *h);
115 for (i = 0; i < n; i++)
116 TAILQ_INIT(&h[i]);
117 oldh = ht->ht_tab;
118 n--;
119 for (i = 0; i < ht->ht_size; i++) {
120 while ((p = TAILQ_FIRST(&oldh[i])) != NULL) {
121 TAILQ_REMOVE(&oldh[i], p, h_next);
122 // XXXLUKEM: really should be TAILQ_INSERT_TAIL
123 TAILQ_INSERT_HEAD(&h[p->h_hash & n], p, h_next);
124 }
125 }
126 free(ht->ht_tab);
127 ht->ht_tab = h;
128 ht->ht_mask = n;
129 ht->ht_size = ++n;
130 ht->ht_lim = HASHFRACTION(n);
131 }
132
133 /*
134 * Make a new hash entry, setting its h_next to NULL.
135 * If the free list is not empty, use the first entry from there,
136 * otherwise allocate a new entry.
137 */
138 static inline struct hashent *
139 newhashent(const char *name, u_int h)
140 {
141 struct hashent *hp;
142
143 hp = ecalloc(1, sizeof(*hp));
144
145 hp->h_name = name;
146 hp->h_hash = h;
147 return (hp);
148 }
149
150 /*
151 * Hash a string.
152 */
153 static inline u_int
154 hash(const char *str)
155 {
156 u_int h;
157
158 for (h = 0; *str;)
159 h = (h << 5) + h + *str++;
160 return (h);
161 }
162
163 void
164 initintern(void)
165 {
166
167 ht_init(&strings, 128);
168 }
169
170 /*
171 * Generate a single unique copy of the given string. We expect this
172 * function to be used frequently, so it should be fast.
173 */
174 const char *
175 intern(const char *s)
176 {
177 struct hashtab *ht;
178 struct hashent *hp;
179 struct hashenthead *hpp;
180 u_int h;
181 char *p;
182
183 ht = &strings;
184 h = hash(s);
185 hpp = &ht->ht_tab[h & ht->ht_mask];
186 TAILQ_FOREACH(hp, hpp, h_next) {
187 if (hp->h_hash == h && strcmp(hp->h_name, s) == 0)
188 return (hp->h_name);
189 }
190 p = estrdup(s);
191 hp = newhashent(p, h);
192 TAILQ_INSERT_TAIL(hpp, hp, h_next);
193 if (++ht->ht_used > ht->ht_lim)
194 ht_expand(ht);
195 return (p);
196 }
197
198 struct hashtab *
199 ht_new(void)
200 {
201 struct hashtab *ht;
202
203 ht = ecalloc(1, sizeof *ht);
204 ht_init(ht, 8);
205 return (ht);
206 }
207
208 /*
209 * Insert and/or replace.
210 */
211 int
212 ht_insrep(struct hashtab *ht, const char *nam, void *val, int replace)
213 {
214 struct hashent *hp;
215 struct hashenthead *hpp;
216 u_int h;
217
218 h = hash(nam);
219 hpp = &ht->ht_tab[h & ht->ht_mask];
220 TAILQ_FOREACH(hp, hpp, h_next) {
221 if (hp->h_name == nam) {
222 if (replace)
223 hp->h_value = val;
224 return (1);
225 }
226 }
227 hp = newhashent(nam, h);
228 TAILQ_INSERT_TAIL(hpp, hp, h_next);
229 hp->h_value = val;
230 if (++ht->ht_used > ht->ht_lim)
231 ht_expand(ht);
232 return (0);
233 }
234
235 /*
236 * Remove.
237 */
238 int
239 ht_remove(struct hashtab *ht, const char *name)
240 {
241 struct hashent *hp;
242 struct hashenthead *hpp;
243 u_int h;
244
245 h = hash(name);
246 hpp = &ht->ht_tab[h & ht->ht_mask];
247
248 TAILQ_FOREACH(hp, hpp, h_next) {
249 if (hp->h_name != name)
250 continue;
251 TAILQ_REMOVE(hpp, hp, h_next);
252
253 free(hp);
254 ht->ht_used--;
255 return (0);
256 }
257 return (1);
258 }
259
260 void *
261 ht_lookup(struct hashtab *ht, const char *nam)
262 {
263 struct hashent *hp;
264 struct hashenthead *hpp;
265 u_int h;
266
267 h = hash(nam);
268 hpp = &ht->ht_tab[h & ht->ht_mask];
269 TAILQ_FOREACH(hp, hpp, h_next)
270 if (hp->h_name == nam)
271 return (hp->h_value);
272 return (NULL);
273 }
274
275 /*
276 * first parameter to callback is the entry name from the hash table
277 * second parameter is the value from the hash table
278 * third argument is passed through from the "arg" parameter to ht_enumerate()
279 */
280
281 int
282 ht_enumerate(struct hashtab *ht, ht_callback cbfunc, void *arg)
283 {
284 struct hashent *hp;
285 struct hashenthead *hpp;
286 u_int i;
287 int rval = 0;
288
289 for (i = 0; i < ht->ht_size; i++) {
290 hpp = &ht->ht_tab[i];
291 TAILQ_FOREACH(hp, hpp, h_next)
292 rval += (*cbfunc)(hp->h_name, hp->h_value, arg);
293 }
294 return rval;
295 }
296