VirtualBox

source: kBuild/trunk/src/kmk/hash.c@ 1983

Last change on this file since 1983 was 1925, checked in by bird, 16 years ago

kmk: some stats adjustments.

  • Property svn:eol-style set to native
File size: 13.4 KB
Line 
1/* hash.c -- hash table maintenance
2 Copyright (C) 1995, 1999, 2002 Free Software Foundation, Inc.
3 Written by Greg McGary <[email protected]> <[email protected]>
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2, or (at your option)
8any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16this program; see the file COPYING. If not, write to the Free Software
17Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
18
19#include "make.h"
20#include "hash.h"
21#ifdef CONFIG_WITH_STRCACHE2
22# include <assert.h>
23#endif
24
25
26#define CALLOC(t, n) ((t *) calloc (sizeof (t), (n)))
27#define MALLOC(t, n) ((t *) xmalloc (sizeof (t) * (n)))
28#define REALLOC(o, t, n) ((t *) xrealloc ((o), sizeof (t) * (n)))
29#define CLONE(o, t, n) ((t *) memcpy (MALLOC (t, (n)), (o), sizeof (t) * (n)))
30
31static void hash_rehash __P((struct hash_table* ht));
32static unsigned long round_up_2 __P((unsigned long rough));
33
34/* Implement double hashing with open addressing. The table size is
35 always a power of two. The secondary (`increment') hash function
36 is forced to return an odd-value, in order to be relatively prime
37 to the table size. This guarantees that the increment can
38 potentially hit every slot in the table during collision
39 resolution. */
40
41void *hash_deleted_item = &hash_deleted_item;
42
43/* Force the table size to be a power of two, possibly rounding up the
44 given size. */
45
46void
47hash_init (struct hash_table *ht, unsigned long size,
48 hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp)
49{
50 ht->ht_size = round_up_2 (size);
51 ht->ht_empty_slots = ht->ht_size;
52 ht->ht_vec = (void**) CALLOC (struct token *, ht->ht_size);
53 if (ht->ht_vec == 0)
54 {
55 fprintf (stderr, _("can't allocate %ld bytes for hash table: memory exhausted"),
56 ht->ht_size * sizeof(struct token *));
57 exit (1);
58 }
59
60 ht->ht_capacity = ht->ht_size - (ht->ht_size / 16); /* 93.75% loading factor */
61 ht->ht_fill = 0;
62 ht->ht_collisions = 0;
63 ht->ht_lookups = 0;
64 ht->ht_rehashes = 0;
65 ht->ht_hash_1 = hash_1;
66 ht->ht_hash_2 = hash_2;
67 ht->ht_compare = hash_cmp;
68#ifdef CONFIG_WITH_STRCACHE2
69 ht->ht_strcache = 0;
70 ht->ht_off_string = 0;
71#endif
72}
73
74#ifdef CONFIG_WITH_STRCACHE2
75/* Same as hash_init, except that no callbacks are needed since all
76 keys - including the ones being searched for - are from a string
77 cache. This means that any give string will only have one pointer
78 value and that the hash and length can be retrived very cheaply,
79 thus permitting some nice optimizations.
80
81 STRCACHE points to the string cache, while OFF_STRING gives the
82 offset of the string pointer in the item structures the hash table
83 entries points to. */
84void hash_init_strcached (struct hash_table *ht, unsigned long size,
85 struct strcache2 *strcache, unsigned int off_string)
86{
87 hash_init (ht, size, 0, 0, 0);
88 ht->ht_strcache = strcache;
89 ht->ht_off_string = off_string;
90}
91#endif /* CONFIG_WITH_STRCACHE2 */
92
93/* Load an array of items into `ht'. */
94
95void
96hash_load (struct hash_table *ht, void *item_table,
97 unsigned long cardinality, unsigned long size)
98{
99 char *items = (char *) item_table;
100#ifndef CONFIG_WITH_STRCACHE2
101 while (cardinality--)
102 {
103 hash_insert (ht, items);
104 items += size;
105 }
106#else /* CONFIG_WITH_STRCACHE2 */
107 if (ht->ht_strcache)
108 while (cardinality--)
109 {
110 hash_insert_strcached (ht, items);
111 items += size;
112 }
113 else
114 while (cardinality--)
115 {
116 hash_insert (ht, items);
117 items += size;
118 }
119#endif /* CONFIG_WITH_STRCACHE2 */
120}
121
122/* Returns the address of the table slot matching `key'. If `key' is
123 not found, return the address of an empty slot suitable for
124 inserting `key'. The caller is responsible for incrementing
125 ht_fill on insertion. */
126
127void **
128hash_find_slot (struct hash_table *ht, const void *key)
129{
130 void **slot;
131 void **deleted_slot = 0;
132 unsigned int hash_2 = 0;
133 unsigned int hash_1 = (*ht->ht_hash_1) (key);
134
135#ifdef CONFIG_WITH_STRCACHE2
136 assert (ht->ht_strcache == 0);
137#endif
138
139 MAKE_STATS (ht->ht_lookups++);
140 MAKE_STATS_3 (make_stats_ht_lookups++);
141 for (;;)
142 {
143 hash_1 &= (ht->ht_size - 1);
144 slot = &ht->ht_vec[hash_1];
145
146 if (*slot == 0)
147 return (deleted_slot ? deleted_slot : slot);
148 if (*slot == hash_deleted_item)
149 {
150 if (deleted_slot == 0)
151 deleted_slot = slot;
152 }
153 else
154 {
155 if (key == *slot)
156 return slot;
157 if ((*ht->ht_compare) (key, *slot) == 0)
158 return slot;
159 MAKE_STATS (ht->ht_collisions++);
160 MAKE_STATS_3 (make_stats_ht_collisions++);
161 }
162 if (!hash_2)
163 hash_2 = (*ht->ht_hash_2) (key) | 1;
164 hash_1 += hash_2;
165 }
166}
167
168#ifdef CONFIG_WITH_STRCACHE2
169/* hash_find_slot version for tables created with hash_init_strcached. */
170void **
171hash_find_slot_strcached (struct hash_table *ht, const void *key)
172{
173 void **slot;
174 void **deleted_slot = 0;
175 const char *str1 = *(const char **)((const char *)key + ht->ht_off_string);
176 const char *str2;
177 unsigned int hash_1 = strcache2_calc_ptr_hash (ht->ht_strcache, str1);
178 unsigned int hash_2;
179
180#ifdef CONFIG_WITH_STRCACHE2
181 assert (ht->ht_strcache != 0);
182#endif
183
184 MAKE_STATS (ht->ht_lookups++);
185 MAKE_STATS_3 (make_stats_ht_lookups++);
186
187 /* first iteration unrolled. */
188
189 hash_1 &= (ht->ht_size - 1);
190 slot = &ht->ht_vec[hash_1];
191 if (*slot == 0)
192 return slot;
193 if (*slot != hash_deleted_item)
194 {
195 str2 = *(const char **)((const char *)(*slot) + ht->ht_off_string);
196 if (str1 == str2)
197 return slot;
198
199 MAKE_STATS (ht->ht_collisions++);
200 MAKE_STATS_3 (make_stats_ht_collisions++);
201 }
202 else
203 deleted_slot = slot;
204
205 /* the rest of the loop. */
206
207 hash_2 = strcache2_get_hash (ht->ht_strcache, str1) | 1;
208 hash_1 += hash_2;
209 for (;;)
210 {
211 hash_1 &= (ht->ht_size - 1);
212 slot = &ht->ht_vec[hash_1];
213
214 if (*slot == 0)
215 return (deleted_slot ? deleted_slot : slot);
216 if (*slot == hash_deleted_item)
217 {
218 if (deleted_slot == 0)
219 deleted_slot = slot;
220 }
221 else
222 {
223 str2 = *(const char **)((const char *)(*slot) + ht->ht_off_string);
224 if (str1 == str2)
225 return slot;
226
227 MAKE_STATS (ht->ht_collisions++);
228 MAKE_STATS_3 (make_stats_ht_collisions++);
229 }
230
231 hash_1 += hash_2;
232 }
233}
234#endif /* CONFIG_WITH_STRCACHE2 */
235
236void *
237hash_find_item (struct hash_table *ht, const void *key)
238{
239 void **slot = hash_find_slot (ht, key);
240 return ((HASH_VACANT (*slot)) ? 0 : *slot);
241}
242
243#ifdef CONFIG_WITH_STRCACHE2
244void *
245hash_find_item_strcached (struct hash_table *ht, const void *key)
246{
247 void **slot = hash_find_slot_strcached (ht, key);
248 return ((HASH_VACANT (*slot)) ? 0 : *slot);
249}
250#endif /* CONFIG_WITH_STRCACHE2 */
251
252void *
253hash_insert (struct hash_table *ht, const void *item)
254{
255 void **slot = hash_find_slot (ht, item);
256 const void *old_item = slot ? *slot : 0;
257 hash_insert_at (ht, item, slot);
258 return (void *)((HASH_VACANT (old_item)) ? 0 : old_item);
259}
260
261#ifdef CONFIG_WITH_STRCACHE2
262void *
263hash_insert_strcached (struct hash_table *ht, const void *item)
264{
265 void **slot = hash_find_slot_strcached (ht, item);
266 const void *old_item = slot ? *slot : 0;
267 hash_insert_at (ht, item, slot);
268 return (void *)((HASH_VACANT (old_item)) ? 0 : old_item);
269}
270#endif /* CONFIG_WITH_STRCACHE2 */
271
272void *
273hash_insert_at (struct hash_table *ht, const void *item, const void *slot)
274{
275 const void *old_item = *(void **) slot;
276 if (HASH_VACANT (old_item))
277 {
278 ht->ht_fill++;
279 if (old_item == 0)
280 ht->ht_empty_slots--;
281 old_item = item;
282 }
283 *(void const **) slot = item;
284 if (ht->ht_empty_slots < ht->ht_size - ht->ht_capacity)
285 {
286 hash_rehash (ht);
287#ifdef CONFIG_WITH_STRCACHE2
288 if (ht->ht_strcache)
289 return (void *)hash_find_slot_strcached (ht, item);
290#endif /* CONFIG_WITH_STRCACHE2 */
291 return (void *) hash_find_slot (ht, item);
292 }
293 else
294 return (void *) slot;
295}
296
297void *
298hash_delete (struct hash_table *ht, const void *item)
299{
300 void **slot = hash_find_slot (ht, item);
301 return hash_delete_at (ht, slot);
302}
303
304#ifdef CONFIG_WITH_STRCACHE2
305void *
306hash_delete_strcached (struct hash_table *ht, const void *item)
307{
308 void **slot = hash_find_slot_strcached (ht, item);
309 return hash_delete_at (ht, slot);
310}
311#endif /* CONFIG_WITH_STRCACHE2 */
312
313void *
314hash_delete_at (struct hash_table *ht, const void *slot)
315{
316 void *item = *(void **) slot;
317 if (!HASH_VACANT (item))
318 {
319 *(void const **) slot = hash_deleted_item;
320 ht->ht_fill--;
321 return item;
322 }
323 else
324 return 0;
325}
326
327void
328hash_free_items (struct hash_table *ht)
329{
330 void **vec = ht->ht_vec;
331 void **end = &vec[ht->ht_size];
332 for (; vec < end; vec++)
333 {
334 void *item = *vec;
335 if (!HASH_VACANT (item))
336 free (item);
337 *vec = 0;
338 }
339 ht->ht_fill = 0;
340 ht->ht_empty_slots = ht->ht_size;
341}
342
343#ifdef CONFIG_WITH_ALLOC_CACHES
344void
345hash_free_items_cached (struct hash_table *ht, struct alloccache *cache)
346{
347 void **vec = ht->ht_vec;
348 void **end = &vec[ht->ht_size];
349 for (; vec < end; vec++)
350 {
351 void *item = *vec;
352 if (!HASH_VACANT (item))
353 alloccache_free (cache, item);
354 *vec = 0;
355 }
356 ht->ht_fill = 0;
357 ht->ht_empty_slots = ht->ht_size;
358}
359#endif /* CONFIG_WITH_ALLOC_CACHES */
360
361void
362hash_delete_items (struct hash_table *ht)
363{
364 void **vec = ht->ht_vec;
365 void **end = &vec[ht->ht_size];
366 for (; vec < end; vec++)
367 *vec = 0;
368 ht->ht_fill = 0;
369 ht->ht_collisions = 0;
370 ht->ht_lookups = 0;
371 ht->ht_rehashes = 0;
372 ht->ht_empty_slots = ht->ht_size;
373}
374
375void
376hash_free (struct hash_table *ht, int free_items)
377{
378 if (free_items)
379 hash_free_items (ht);
380 else
381 {
382 ht->ht_fill = 0;
383 ht->ht_empty_slots = ht->ht_size;
384 }
385 free (ht->ht_vec);
386 ht->ht_vec = 0;
387 ht->ht_capacity = 0;
388}
389
390#ifdef CONFIG_WITH_ALLOC_CACHES
391void
392hash_free_cached (struct hash_table *ht, int free_items, struct alloccache *cache)
393{
394 if (free_items)
395 hash_free_items_cached (ht, cache);
396 else
397 {
398 ht->ht_fill = 0;
399 ht->ht_empty_slots = ht->ht_size;
400 }
401 free (ht->ht_vec);
402 ht->ht_vec = 0;
403 ht->ht_capacity = 0;
404}
405#endif /* CONFIG_WITH_ALLOC_CACHES */
406
407void
408hash_map (struct hash_table *ht, hash_map_func_t map)
409{
410 void **slot;
411 void **end = &ht->ht_vec[ht->ht_size];
412
413 for (slot = ht->ht_vec; slot < end; slot++)
414 {
415 if (!HASH_VACANT (*slot))
416 (*map) (*slot);
417 }
418}
419
420void
421hash_map_arg (struct hash_table *ht, hash_map_arg_func_t map, void *arg)
422{
423 void **slot;
424 void **end = &ht->ht_vec[ht->ht_size];
425
426 for (slot = ht->ht_vec; slot < end; slot++)
427 {
428 if (!HASH_VACANT (*slot))
429 (*map) (*slot, arg);
430 }
431}
432
433/* Double the size of the hash table in the event of overflow... */
434
435static void
436hash_rehash (struct hash_table *ht)
437{
438 unsigned long old_ht_size = ht->ht_size;
439 void **old_vec = ht->ht_vec;
440 void **ovp;
441
442 if (ht->ht_fill >= ht->ht_capacity)
443 {
444 ht->ht_size *= 2;
445 ht->ht_capacity = ht->ht_size - (ht->ht_size >> 4);
446 }
447 ht->ht_rehashes++;
448 ht->ht_vec = (void **) CALLOC (struct token *, ht->ht_size);
449
450#ifndef CONFIG_WITH_STRCACHE2
451 for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
452 {
453 if (! HASH_VACANT (*ovp))
454 {
455 void **slot = hash_find_slot (ht, *ovp);
456 *slot = *ovp;
457 }
458 }
459#else /* CONFIG_WITH_STRCACHE2 */
460 if (ht->ht_strcache)
461 for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
462 {
463 if (! HASH_VACANT (*ovp))
464 {
465 void **slot = hash_find_slot_strcached (ht, *ovp);
466 *slot = *ovp;
467 }
468 }
469 else
470 for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
471 {
472 if (! HASH_VACANT (*ovp))
473 {
474 void **slot = hash_find_slot (ht, *ovp);
475 *slot = *ovp;
476 }
477 }
478#endif /* CONFIG_WITH_STRCACHE2 */
479 ht->ht_empty_slots = ht->ht_size - ht->ht_fill;
480 free (old_vec);
481}
482
483void
484hash_print_stats (struct hash_table *ht, FILE *out_FILE)
485{
486 /* GKM FIXME: honor NO_FLOAT */
487 fprintf (out_FILE, _("Load=%ld/%ld=%.0f%%, "), ht->ht_fill, ht->ht_size,
488 100.0 * (double) ht->ht_fill / (double) ht->ht_size);
489 fprintf (out_FILE, _("Rehash=%d, "), ht->ht_rehashes);
490 MAKE_STATS(
491 fprintf (out_FILE, _("Collisions=%ld/%ld=%.0f%%"), ht->ht_collisions, ht->ht_lookups,
492 (ht->ht_lookups
493 ? (100.0 * (double) ht->ht_collisions / (double) ht->ht_lookups)
494 : 0));
495 );
496}
497
498/* Dump all items into a NULL-terminated vector. Use the
499 user-supplied vector, or malloc one. */
500
501void **
502hash_dump (struct hash_table *ht, void **vector_0, qsort_cmp_t compare)
503{
504 void **vector;
505 void **slot;
506 void **end = &ht->ht_vec[ht->ht_size];
507
508 if (vector_0 == 0)
509 vector_0 = MALLOC (void *, ht->ht_fill + 1);
510 vector = vector_0;
511
512 for (slot = ht->ht_vec; slot < end; slot++)
513 if (!HASH_VACANT (*slot))
514 *vector++ = *slot;
515 *vector = 0;
516
517 if (compare)
518 qsort (vector_0, ht->ht_fill, sizeof (void *), compare);
519 return vector_0;
520}
521
522/* Round a given number up to the nearest power of 2. */
523
524static unsigned long
525round_up_2 (unsigned long n)
526{
527 n |= (n >> 1);
528 n |= (n >> 2);
529 n |= (n >> 4);
530 n |= (n >> 8);
531 n |= (n >> 16);
532
533#if !defined(HAVE_LIMITS_H) || ULONG_MAX > 4294967295
534 /* We only need this on systems where unsigned long is >32 bits. */
535 n |= (n >> 32);
536#endif
537
538 return n + 1;
539}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette