]> git.ipfire.org Git - thirdparty/squid.git/blob - test-suite/hash.c
Cleanup: un-wrap C++ header includes
[thirdparty/squid.git] / test-suite / hash.c
1
2 /*
3 * DEBUG: section 00 Hash Tables
4 * AUTHOR: Harvest Derived
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33 #include "squid.h"
34
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #if HAVE_CTYPE_H
39 #include <ctype.h>
40 #endif
41 #if HAVE_STRINGS_H
42 #include <strings.h>
43 #endif
44 #if HAVE_ASSERT_H
45 #include <assert.h>
46 #endif
47
48 #include "hash.h"
49
50 #undef free
51 extern void my_free(char *, int, void *);
52
53 #define free(a) my_free(__FILE__, __LINE__, a)
54
55 extern void print_stats();
56 /*
57 * hash_url() - Returns a well-distributed hash function for URLs.
58 * The best way is to sum up the last half of the string.
59 * Adapted from code written by Mic Bowman. -Darren
60 * Generates a standard deviation = 15.73
61 */
62 unsigned int
63 hash_url(const void *data, unsigned int size)
64 {
65 const char *s = data;
66 unsigned int i, j, n;
67 j = strlen(s);
68 for (i = j / 2, n = 0; i < j; i++)
69 n ^= 271 * (unsigned) s[i];
70 i = n ^ (j * 271);
71 return i % size;
72 }
73
74 unsigned int
75 hash_string(const void *data, unsigned int size)
76 {
77 const char *s = data;
78 unsigned int n = 0;
79 unsigned int j = 0;
80 unsigned int i = 0;
81 while (*s) {
82 j++;
83 n ^= 271 * (unsigned) *s++;
84 }
85 i = n ^ (j * 271);
86 return i % size;
87 }
88
89 /* the following 4 functions were adapted from
90 * usr/src/lib/libc/db/hash_func.c, 4.4 BSD lite */
91
92 /*
93 * HASH FUNCTIONS
94 *
95 * Assume that we've already split the bucket to which this key hashes,
96 * calculate that bucket, and check that in fact we did already split it.
97 *
98 * This came from ejb's hsearch.
99 */
100
101 #define PRIME1 37
102 #define PRIME2 1048583
103
104 /* Hash function from Chris Torek. */
105 unsigned int
106 hash4(const void *data, unsigned int size)
107 {
108 const char *key = data;
109 size_t loop;
110 unsigned int h;
111 size_t len;
112
113 #define HASH4a h = (h << 5) - h + *key++;
114 #define HASH4b h = (h << 5) + h + *key++;
115 #define HASH4 HASH4b
116
117 h = 0;
118 len = strlen(key);
119 loop = (len + 8 - 1) >> 3;
120 switch (len & (8 - 1)) {
121 case 0:
122 do {
123 HASH4;
124 /* FALLTHROUGH */
125 case 7:
126 HASH4;
127 /* FALLTHROUGH */
128 case 6:
129 HASH4;
130 /* FALLTHROUGH */
131 case 5:
132 HASH4;
133 /* FALLTHROUGH */
134 case 4:
135 HASH4;
136 /* FALLTHROUGH */
137 case 3:
138 HASH4;
139 /* FALLTHROUGH */
140 case 2:
141 HASH4;
142 /* FALLTHROUGH */
143 case 1:
144 HASH4;
145 } while (--loop);
146 }
147 return h % size;
148 }
149
150 /*
151 * hash_create - creates a new hash table, uses the cmp_func
152 * to compare keys. Returns the identification for the hash table;
153 * otherwise returns a negative number on error.
154 */
155 hash_table *
156 hash_create(HASHCMP * cmp_func, int hash_sz, HASHHASH * hash_func)
157 {
158 hash_table *hid = calloc(1, sizeof(hash_table));
159 if (!hash_sz)
160 hid->size = (unsigned int) DEFAULT_HASH_SIZE;
161 else
162 hid->size = (unsigned int) hash_sz;
163 /* allocate and null the buckets */
164 hid->buckets = calloc(hid->size, sizeof(hash_link *));
165 hid->cmp = cmp_func;
166 hid->hash = hash_func;
167 hid->current_ptr = NULL;
168 hid->current_slot = 0;
169 return hid;
170 }
171
172 /*
173 * hash_insert - inserts the given item 'item' under the given key 'k'
174 * into the hash table 'hid'. Returns non-zero on error; otherwise,
175 * returns 0 and inserts the item.
176 *
177 * It does not copy any data into the hash table, only pointers.
178 */
179 void
180 hash_insert(hash_table * hid, const char *k, void *item)
181 {
182 int i;
183 hash_link *new;
184 assert(k != NULL);
185 /* Add to the given hash table 'hid' */
186 new = calloc(1, sizeof(hash_link));
187 if (!new) {
188 fprintf(stderr, "calloc failed!\n");
189 print_stats();
190 exit(1);
191 }
192 new->item = item;
193 new->key = (char *) k;
194 i = hid->hash(k, hid->size);
195 new->next = hid->buckets[i];
196 hid->buckets[i] = new;
197 }
198
199 /*
200 * hash_join - joins a hash_link under its key lnk->key
201 * into the hash table 'hid'.
202 *
203 * It does not copy any data into the hash table, only links pointers.
204 */
205 void
206 hash_join(hash_table * hid, hash_link * lnk)
207 {
208 int i;
209 i = hid->hash(lnk->key, hid->size);
210 lnk->next = hid->buckets[i];
211 hid->buckets[i] = lnk;
212 }
213
214 /*
215 * hash_lookup - locates the item under the key 'k' in the hash table
216 * 'hid'. Returns a pointer to the hash bucket on success; otherwise
217 * returns NULL.
218 */
219 hash_link *
220 hash_lookup(hash_table * hid, const void *k)
221 {
222 hash_link *walker;
223 int b;
224 assert(k != NULL);
225 b = hid->hash(k, hid->size);
226 for (walker = hid->buckets[b]; walker != NULL; walker = walker->next) {
227 if ((hid->cmp) (k, walker->key) == 0)
228 return (walker);
229 assert(walker != walker->next);
230 }
231 return NULL;
232 }
233
234 /*
235 * hash_first - returns the first item in the hash table 'hid'.
236 * Otherwise, returns NULL on error.
237 */
238 hash_link *
239 hash_first(hash_table * hid)
240 {
241 int i;
242
243 for (i = 0; i < hid->size; i++) {
244 hid->current_slot = i;
245 if (hid->buckets[i] != NULL)
246 return (hid->current_ptr = hid->buckets[i]);
247 }
248 return NULL;
249 }
250
251 /*
252 * hash_next - returns the next item in the hash table 'hid'.
253 * Otherwise, returns NULL on error or end of list.
254 *
255 * MUST call hash_first() before hash_next().
256 */
257 hash_link *
258 hash_next(hash_table * hid)
259 {
260 int i;
261
262 if (hid->current_ptr != NULL) {
263 hid->current_ptr = hid->current_ptr->next;
264 if (hid->current_ptr != NULL)
265 return (hid->current_ptr); /* next item */
266 }
267 /* find next bucket */
268 for (i = hid->current_slot + 1; i < hid->size; i++) {
269 hid->current_slot = i;
270 if (hid->buckets[i] != NULL)
271 return (hid->current_ptr = hid->buckets[i]);
272 }
273 return NULL; /* end of list */
274 }
275
276 int
277 hash_delete(hash_table * hid, const char *key)
278 {
279 return hash_delete_link(hid, hash_lookup(hid, key));
280 }
281
282 /*
283 * hash_delete_link - deletes the given hash_link node from the
284 * hash table 'hid'. If FreeLink then free the given hash_link.
285 *
286 * On success, it returns 0 and deletes the link; otherwise,
287 * returns non-zero on error.
288 */
289 int
290 hash_unlink(hash_table * hid, hash_link * hl, int FreeLink)
291 {
292 hash_link *walker, *prev;
293 int i;
294 if (hl == NULL)
295 return -1;
296 i = hid->hash(hl->key, hid->size);
297 for (prev = NULL, walker = hid->buckets[i];
298 walker != NULL; prev = walker, walker = walker->next) {
299 if (walker == hl) {
300 if (prev == NULL) { /* it's the head */
301 hid->buckets[i] = walker->next;
302 } else {
303 prev->next = walker->next; /* skip it */
304 }
305 /* fix walker state if needed */
306 if (walker == hid->current_ptr)
307 hid->current_ptr = walker->next;
308 if (FreeLink) {
309 if (walker) {
310 free(walker);
311 }
312 }
313 return 0;
314 }
315 }
316 return 1;
317 }
318
319 /* take link off and free link node */
320 int
321 hash_delete_link(hash_table * hid, hash_link * hl)
322 {
323 return (hash_unlink(hid, hl, 1));
324 }
325
326 /* take link off only */
327 int
328 hash_remove_link(hash_table * hid, hash_link * hl)
329 {
330 return (hash_unlink(hid, hl, 0));
331 }
332
333 /*
334 * hash_get_bucket - returns the head item of the bucket
335 * in the hash table 'hid'. Otherwise, returns NULL on error.
336 */
337 hash_link *
338 hash_get_bucket(hash_table * hid, unsigned int bucket)
339 {
340 if (bucket >= hid->size)
341 return NULL;
342 return (hid->buckets[bucket]);
343 }
344
345 void
346 hashFreeMemory(hash_table * hid)
347 {
348 if (hid->buckets);
349 free(hid->buckets);
350 if (hid)
351 free(hid);
352 }
353
354 #if USE_HASH_DRIVER
355 /*
356 * hash-driver - Run with a big file as stdin to insert each line into the
357 * hash table, then prints the whole hash table, then deletes a random item,
358 * and prints the table again...
359 */
360 int
361 main(void)
362 {
363 hash_table *hid;
364 int i;
365 LOCAL_ARRAY(char, buf, BUFSIZ);
366 LOCAL_ARRAY(char, todelete, BUFSIZ);
367 hash_link *walker = NULL;
368
369 todelete[0] = '\0';
370 printf("init\n");
371
372 printf("creating hash table\n");
373 if ((hid = hash_create(strcmp, 229, hash_string)) < 0) {
374 printf("hash_create error.\n");
375 exit(1);
376 }
377 printf("done creating hash table: %d\n", hid);
378
379 while (fgets(buf, BUFSIZ, stdin)) {
380 buf[strlen(buf) - 1] = '\0';
381 printf("Inserting '%s' for item %p to hash table: %d\n",
382 buf, buf, hid);
383 hash_insert(hid, xstrdup(buf), (void *) 0x12345678);
384 if (random() % 17 == 0)
385 strcpy(todelete, buf);
386 }
387
388 printf("walking hash table...\n");
389 for (i = 0, walker = hash_first(hid); walker; walker = hash_next(hid)) {
390 printf("item %5d: key: '%s' item: %p\n", i++, walker->key,
391 walker->item);
392 }
393 printf("done walking hash table...\n");
394
395 if (todelete[0]) {
396 printf("deleting %s from %d\n", todelete, hid);
397 if (hash_delete(hid, todelete))
398 printf("hash_delete error\n");
399 }
400 printf("walking hash table...\n");
401 for (i = 0, walker = hash_first(hid); walker; walker = hash_next(hid)) {
402 printf("item %5d: key: '%s' item: %p\n", i++, walker->key,
403 walker->item);
404 }
405 printf("done walking hash table...\n");
406
407 printf("driver finished.\n");
408 exit(0);
409 }
410 #endif