Line data Source code
1 : /*
2 : * Generic hash table routines.
3 : * (c) Thomas Pornin 1998, 1999, 2000
4 : *
5 : * Redistribution and use in source and binary forms, with or without
6 : * modification, are permitted provided that the following conditions
7 : * are met:
8 : * 1. Redistributions of source code must retain the above copyright
9 : * notice, this list of conditions and the following disclaimer.
10 : * 2. Redistributions in binary form must reproduce the above copyright
11 : * notice, this list of conditions and the following disclaimer in the
12 : * documentation and/or other materials provided with the distribution.
13 : * 4. The name of the authors may not be used to endorse or promote
14 : * products derived from this software without specific prior written
15 : * permission.
16 : *
17 : * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
18 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 : * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
21 : * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
23 : * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 : * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 : * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 : * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : *
29 : */
30 :
31 : #include <string.h>
32 : #include "hash.h"
33 : #include "mem.h"
34 : #include "tune.h"
35 :
36 : /*
37 : * hash_string() is a sample hash function for strings
38 : */
39 0 : int hash_string(char *s)
40 : {
41 : #ifdef FAST_HASH
42 : unsigned h = 0, g;
43 :
44 : while (*s) {
45 : h = (h << 4) + *(unsigned char *)(s ++);
46 : if ((g = h & 0xF000U) != 0) h ^= (g >> 12);
47 : h &= ~g;
48 : }
49 : return (h ^ (h >> 9)) & 127U;
50 : #else
51 0 : unsigned char h = 0;
52 :
53 0 : for (; *s; s ++) h ^= (unsigned char)(*s);
54 0 : return ((int)h);
55 : #endif
56 : }
57 :
58 : /*
59 : * struct hash_item is the basic data type to internally handle hash tables
60 : */
61 : struct hash_item {
62 : void *data;
63 : struct hash_item *next;
64 : };
65 :
66 : /*
67 : * This function adds an entry to the struct hash_item list
68 : */
69 0 : static struct hash_item *add_entry(struct hash_item *blist, void *data)
70 : {
71 0 : struct hash_item *t = getmem(sizeof(struct hash_item));
72 :
73 0 : t->data = data;
74 0 : t->next = blist;
75 0 : return t;
76 : }
77 :
78 : /*
79 : * This function finds a struct hash_item in a list, using the
80 : * comparison function provided as cmpdata (*cmpdata() returns
81 : * non-zero if the two parameters are to be considered identical).
82 : *
83 : * It returns 0 if the item is not found.
84 : */
85 0 : static struct hash_item *get_entry(struct hash_item *blist, void *data,
86 : int (*cmpdata)(void *, void *))
87 : {
88 0 : while (blist) {
89 0 : if ((*cmpdata)(data, blist->data)) return blist;
90 0 : blist = blist->next;
91 : }
92 0 : return 0;
93 : }
94 :
95 : /*
96 : * This function acts like get_entry but deletes the found item, using
97 : * the provided function deldata(); it returns 0 if the given data was
98 : * not found.
99 : */
100 0 : static struct hash_item *del_entry(struct hash_item *blist, void *data,
101 : int (*cmpdata)(void *, void *), void (*deldata)(void *))
102 : {
103 0 : struct hash_item *prev = 0, *save = blist;
104 :
105 0 : while (blist) {
106 0 : if ((*cmpdata)(data, blist->data)) {
107 0 : if (deldata) (*deldata)(blist->data);
108 0 : if (prev) prev->next = blist->next;
109 0 : if (save == blist) save = blist->next;
110 0 : freemem(blist);
111 0 : return save;
112 : }
113 0 : prev = blist;
114 0 : blist = blist->next;
115 : }
116 0 : return 0;
117 : }
118 :
119 : /*
120 : * This function creates a new hashtable, with the hashing and comparison
121 : * functions given as parameters
122 : */
123 0 : struct HT *newHT(int n, int (*cmpdata)(void *, void *), int (*hash)(void *),
124 : void (*deldata)(void *))
125 : {
126 0 : struct HT *t = getmem(sizeof(struct HT));
127 : int i;
128 :
129 0 : t->lists = getmem(n * sizeof(struct hash_item *));
130 0 : for (i = 0; i < n; i ++) t->lists[i] = 0;
131 0 : t->nb_lists = n;
132 0 : t->cmpdata = cmpdata;
133 0 : t->hash = hash;
134 0 : t->deldata = deldata;
135 0 : return t;
136 : }
137 :
138 : /*
139 : * This function adds a new entry in the hashtable ht; it returns 0
140 : * on success, or a pointer to the already present item otherwise.
141 : */
142 0 : void *putHT(struct HT *ht, void *data)
143 : {
144 : int h;
145 : struct hash_item *d;
146 :
147 0 : h = ((*(ht->hash))(data));
148 : #ifndef FAST_HASH
149 0 : h %= ht->nb_lists;
150 : #endif
151 0 : if ((d = get_entry(ht->lists[h], data, ht->cmpdata)))
152 0 : return d->data;
153 0 : ht->lists[h] = add_entry(ht->lists[h], data);
154 0 : return 0;
155 : }
156 :
157 : /*
158 : * This function adds a new entry in the hashtable ht, even if an equal
159 : * entry is already there. Exercise caution !
160 : * The new entry will "hide" the old one, which means that the new will be
161 : * found upon lookup/delete, not the old one.
162 : */
163 0 : void *forceputHT(struct HT *ht, void *data)
164 : {
165 : int h;
166 :
167 0 : h = ((*(ht->hash))(data));
168 : #ifndef FAST_HASH
169 0 : h %= ht->nb_lists;
170 : #endif
171 0 : ht->lists[h] = add_entry(ht->lists[h], data);
172 0 : return 0;
173 : }
174 :
175 : /*
176 : * This function finds the entry corresponding to *data in the
177 : * hashtable ht (using the comparison function given as argument
178 : * to newHT)
179 : */
180 0 : void *getHT(struct HT *ht, void *data)
181 : {
182 : int h;
183 : struct hash_item *t;
184 :
185 0 : h = ((*(ht->hash))(data));
186 : #ifndef FAST_HASH
187 0 : h %= ht->nb_lists;
188 : #endif
189 0 : if ((t = get_entry(ht->lists[h], data, ht->cmpdata)) == 0)
190 0 : return 0;
191 0 : return (t->data);
192 : }
193 :
194 : /*
195 : * This function finds and delete the entry corresponding to *data
196 : * in the hashtable ht (using the comparison function given as
197 : * argument to newHT).
198 : */
199 :
200 0 : int delHT(struct HT *ht, void *data)
201 : {
202 : int h;
203 :
204 0 : h = ((*(ht->hash))(data));
205 : #ifndef FAST_HASH
206 0 : h %= ht->nb_lists;
207 : #endif
208 0 : ht->lists[h] = del_entry(ht->lists[h], data, ht->cmpdata, ht->deldata);
209 0 : return 1;
210 : }
211 :
212 : /*
213 : * This function completely eradicates from memory a given hash table,
214 : * releasing all objects
215 : */
216 0 : void killHT(struct HT *ht)
217 : {
218 : int i;
219 : struct hash_item *t, *n;
220 0 : void (*dd)(void *) = ht->deldata;
221 :
222 0 : for (i = 0; i < ht->nb_lists; i ++) for (t = ht->lists[i]; t;) {
223 0 : n = t->next;
224 0 : if (dd) (*dd)(t->data);
225 0 : freemem(t);
226 0 : t = n;
227 : }
228 0 : freemem(ht->lists);
229 0 : freemem(ht);
230 0 : }
231 :
232 : /*
233 : * This function stores a backup of the hash table, for context stacking.
234 : */
235 0 : void saveHT(struct HT *ht, void **buffer)
236 : {
237 0 : struct hash_item **b = (struct hash_item **)buffer;
238 :
239 0 : mmv(b, ht->lists, ht->nb_lists * sizeof(struct hash_item *));
240 0 : }
241 :
242 : /*
243 : * This function restores the saved state of the hash table.
244 : * Do NOT use if some of the entries that were present before the backup
245 : * have been removed (even temporarily).
246 : */
247 0 : void restoreHT(struct HT *ht, void **buffer)
248 : {
249 0 : struct hash_item **b = (struct hash_item **)buffer;
250 : int i;
251 :
252 0 : for (i = 0; i < ht->nb_lists; i ++) {
253 0 : struct hash_item *t = ht->lists[i], *n;
254 :
255 0 : while (t != b[i]) {
256 0 : n = t->next;
257 0 : (*(ht->deldata))(t->data);
258 0 : freemem(t);
259 0 : t = n;
260 : }
261 0 : ht->lists[i] = b[i];
262 : }
263 0 : }
264 :
265 : /*
266 : * This function is evil. It inserts a new item in a saved hash table,
267 : * tweaking the save buffer and the hash table in order to keep things
268 : * stable. There are no checks.
269 : */
270 0 : void tweakHT(struct HT *ht, void **buffer, void *data)
271 : {
272 : int h;
273 : struct hash_item *d, *e;
274 :
275 0 : h = ((*(ht->hash))(data));
276 : #ifndef FAST_HASH
277 0 : h %= ht->nb_lists;
278 : #endif
279 0 : for (d = ht->lists[h]; d != buffer[h]; d = d->next);
280 0 : d = add_entry(buffer[h], data);
281 0 : if (buffer[h] == ht->lists[h]) {
282 0 : buffer[h] = ht->lists[h] = d;
283 0 : return;
284 : }
285 0 : for (e = ht->lists[h]; e->next != buffer[h]; e = e->next);
286 0 : e->next = d;
287 0 : buffer[h] = d;
288 : }
289 :
290 : /*
291 : * This function scans the whole table and calls the given function on
292 : * each entry.
293 : */
294 0 : void scanHT(struct HT *ht, void (*action)(void *))
295 : {
296 : int i;
297 :
298 0 : for (i = 0; i < ht->nb_lists; i ++) {
299 0 : struct hash_item *t = ht->lists[i];
300 :
301 0 : while (t) {
302 0 : (*action)(t->data);
303 0 : t = t->next;
304 : }
305 : }
306 0 : }
307 :
308 : /*
309 : * The two following fonctions are generic for storing structures
310 : * uniquely identified by their name, which must be the first
311 : * field of the structure.
312 : */
313 0 : int hash_struct(void *m)
314 : {
315 0 : char *n = *(char **)m;
316 :
317 : #ifdef FAST_HASH
318 : return hash_string(n);
319 : #else
320 0 : return hash_string(n) & 127;
321 : #endif
322 : }
323 :
324 0 : int cmp_struct(void *m1, void *m2)
325 : {
326 0 : char *n1 = *(char **)m1, *n2 = *(char **)m2;
327 :
328 0 : return !strcmp(n1, n2);
329 : }
|