VirtualBox

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

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

kmk: Moved the strcache hash optimizations into the hash code.

  • 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 ht->ht_lookups++;
140#ifdef CONFIG_WITH_MAKE_STATS
141 make_stats_ht_lookups++;
142#endif
143 for (;;)
144 {
145 hash_1 &= (ht->ht_size - 1);
146 slot = &ht->ht_vec[hash_1];
147
148 if (*slot == 0)
149 return (deleted_slot ? deleted_slot : slot);
150 if (*slot == hash_deleted_item)
151 {
152 if (deleted_slot == 0)
153 deleted_slot = slot;
154 }
155 else
156 {
157 if (key == *slot)
158 return slot;
159 if ((*ht->ht_compare) (key, *slot) == 0)
160 return slot;
161 ht->ht_collisions++;
162#ifdef CONFIG_WITH_MAKE_STATS
163 make_stats_ht_collisions++;
164#endif
165 }
166 if (!hash_2)
167 hash_2 = (*ht->ht_hash_2) (key) | 1;
168 hash_1 += hash_2;
169 }
170}
171
172#ifdef CONFIG_WITH_STRCACHE2
173/* hash_find_slot version for tables created with hash_init_strcached. */
174void **
175hash_find_slot_strcached (struct hash_table *ht, const void *key)
176{
177 void **slot;
178 void **deleted_slot = 0;
179 const char *str1 = *(const char **)((const char *)key + ht->ht_off_string);
180 const char *str2;
181 unsigned int hash_1 = strcache2_get_ptr_hash (ht->ht_strcache, str1);
182 unsigned int hash_2;
183
184#ifdef CONFIG_WITH_STRCACHE2
185 assert (ht->ht_strcache != 0);
186#endif
187
188 ht->ht_lookups++;
189#ifdef CONFIG_WITH_MAKE_STATS
190 make_stats_ht_lookups++;
191#endif
192
193 /* first iteration unrolled. */
194
195 hash_1 &= (ht->ht_size - 1);
196 slot = &ht->ht_vec[hash_1];
197 if (*slot == 0)
198 return slot;
199 if (*slot != hash_deleted_item)
200 {
201 str2 = *(const char **)((const char *)(*slot) + ht->ht_off_string);
202 if (str1 == str2)
203 return slot;
204
205 ht->ht_collisions++;
206#ifdef CONFIG_WITH_MAKE_STATS
207 make_stats_ht_collisions++;
208#endif
209 }
210 else
211 deleted_slot = slot;
212
213 /* the rest of the loop. */
214
215 hash_2 = strcache2_get_hash1 (ht->ht_strcache, str1) | 1;
216 hash_1 += hash_2;
217 for (;;)
218 {
219 hash_1 &= (ht->ht_size - 1);
220 slot = &ht->ht_vec[hash_1];
221
222 if (*slot == 0)
223 return (deleted_slot ? deleted_slot : slot);
224 if (*slot == hash_deleted_item)
225 {
226 if (deleted_slot == 0)
227 deleted_slot = slot;
228 }
229 else
230 {
231 str2 = *(const char **)((const char *)(*slot) + ht->ht_off_string);
232 if (str1 == str2)
233 return slot;
234
235 ht->ht_collisions++;
236#ifdef CONFIG_WITH_MAKE_STATS
237 make_stats_ht_collisions++;
238#endif
239 }
240
241 hash_1 += hash_2;
242 }
243}
244#endif /* CONFIG_WITH_STRCACHE2 */
245
246void *
247hash_find_item (struct hash_table *ht, const void *key)
248{
249 void **slot = hash_find_slot (ht, key);
250 return ((HASH_VACANT (*slot)) ? 0 : *slot);
251}
252
253#ifdef CONFIG_WITH_STRCACHE2
254void *
255hash_find_item_strcached (struct hash_table *ht, const void *key)
256{
257 void **slot = hash_find_slot_strcached (ht, key);
258 return ((HASH_VACANT (*slot)) ? 0 : *slot);
259}
260#endif /* CONFIG_WITH_STRCACHE2 */
261
262void *
263hash_insert (struct hash_table *ht, const void *item)
264{
265 void **slot = hash_find_slot (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
271#ifdef CONFIG_WITH_STRCACHE2
272void *
273hash_insert_strcached (struct hash_table *ht, const void *item)
274{
275 void **slot = hash_find_slot_strcached (ht, item);
276 const void *old_item = slot ? *slot : 0;
277 hash_insert_at (ht, item, slot);
278 return (void *)((HASH_VACANT (old_item)) ? 0 : old_item);
279}
280#endif /* CONFIG_WITH_STRCACHE2 */
281
282void *
283hash_insert_at (struct hash_table *ht, const void *item, const void *slot)
284{
285 const void *old_item = *(void **) slot;
286 if (HASH_VACANT (old_item))
287 {
288 ht->ht_fill++;
289 if (old_item == 0)
290 ht->ht_empty_slots--;
291 old_item = item;
292 }
293 *(void const **) slot = item;
294 if (ht->ht_empty_slots < ht->ht_size - ht->ht_capacity)
295 {
296 hash_rehash (ht);
297#ifdef CONFIG_WITH_STRCACHE2
298 if (ht->ht_strcache)
299 return (void *)hash_find_slot_strcached (ht, item);
300#endif /* CONFIG_WITH_STRCACHE2 */
301 return (void *) hash_find_slot (ht, item);
302 }
303 else
304 return (void *) slot;
305}
306
307void *
308hash_delete (struct hash_table *ht, const void *item)
309{
310 void **slot = hash_find_slot (ht, item);
311 return hash_delete_at (ht, slot);
312}
313
314#ifdef CONFIG_WITH_STRCACHE2
315void *
316hash_delete_strcached (struct hash_table *ht, const void *item)
317{
318 void **slot = hash_find_slot_strcached (ht, item);
319 return hash_delete_at (ht, slot);
320}
321#endif /* CONFIG_WITH_STRCACHE2 */
322
323void *
324hash_delete_at (struct hash_table *ht, const void *slot)
325{
326 void *item = *(void **) slot;
327 if (!HASH_VACANT (item))
328 {
329 *(void const **) slot = hash_deleted_item;
330 ht->ht_fill--;
331 return item;
332 }
333 else
334 return 0;
335}
336
337void
338hash_free_items (struct hash_table *ht)
339{
340 void **vec = ht->ht_vec;
341 void **end = &vec[ht->ht_size];
342 for (; vec < end; vec++)
343 {
344 void *item = *vec;
345 if (!HASH_VACANT (item))
346 free (item);
347 *vec = 0;
348 }
349 ht->ht_fill = 0;
350 ht->ht_empty_slots = ht->ht_size;
351}
352
353#ifdef CONFIG_WITH_ALLOC_CACHES
354void
355hash_free_items_cached (struct hash_table *ht, struct alloccache *cache)
356{
357 void **vec = ht->ht_vec;
358 void **end = &vec[ht->ht_size];
359 for (; vec < end; vec++)
360 {
361 void *item = *vec;
362 if (!HASH_VACANT (item))
363 alloccache_free (cache, item);
364 *vec = 0;
365 }
366 ht->ht_fill = 0;
367 ht->ht_empty_slots = ht->ht_size;
368}
369#endif /* CONFIG_WITH_ALLOC_CACHES */
370
371void
372hash_delete_items (struct hash_table *ht)
373{
374 void **vec = ht->ht_vec;
375 void **end = &vec[ht->ht_size];
376 for (; vec < end; vec++)
377 *vec = 0;
378 ht->ht_fill = 0;
379 ht->ht_collisions = 0;
380 ht->ht_lookups = 0;
381 ht->ht_rehashes = 0;
382 ht->ht_empty_slots = ht->ht_size;
383}
384
385void
386hash_free (struct hash_table *ht, int free_items)
387{
388 if (free_items)
389 hash_free_items (ht);
390 else
391 {
392 ht->ht_fill = 0;
393 ht->ht_empty_slots = ht->ht_size;
394 }
395 free (ht->ht_vec);
396 ht->ht_vec = 0;
397 ht->ht_capacity = 0;
398}
399
400#ifdef CONFIG_WITH_ALLOC_CACHES
401void
402hash_free_cached (struct hash_table *ht, int free_items, struct alloccache *cache)
403{
404 if (free_items)
405 hash_free_items_cached (ht, cache);
406 else
407 {
408 ht->ht_fill = 0;
409 ht->ht_empty_slots = ht->ht_size;
410 }
411 free (ht->ht_vec);
412 ht->ht_vec = 0;
413 ht->ht_capacity = 0;
414}
415#endif /* CONFIG_WITH_ALLOC_CACHES */
416
417void
418hash_map (struct hash_table *ht, hash_map_func_t map)
419{
420 void **slot;
421 void **end = &ht->ht_vec[ht->ht_size];
422
423 for (slot = ht->ht_vec; slot < end; slot++)
424 {
425 if (!HASH_VACANT (*slot))
426 (*map) (*slot);
427 }
428}
429
430void
431hash_map_arg (struct hash_table *ht, hash_map_arg_func_t map, void *arg)
432{
433 void **slot;
434 void **end = &ht->ht_vec[ht->ht_size];
435
436 for (slot = ht->ht_vec; slot < end; slot++)
437 {
438 if (!HASH_VACANT (*slot))
439 (*map) (*slot, arg);
440 }
441}
442
443/* Double the size of the hash table in the event of overflow... */
444
445static void
446hash_rehash (struct hash_table *ht)
447{
448 unsigned long old_ht_size = ht->ht_size;
449 void **old_vec = ht->ht_vec;
450 void **ovp;
451
452 if (ht->ht_fill >= ht->ht_capacity)
453 {
454 ht->ht_size *= 2;
455 ht->ht_capacity = ht->ht_size - (ht->ht_size >> 4);
456 }
457 ht->ht_rehashes++;
458 ht->ht_vec = (void **) CALLOC (struct token *, ht->ht_size);
459
460#ifndef CONFIG_WITH_STRCACHE2
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 (ht, *ovp);
466 *slot = *ovp;
467 }
468 }
469#else /* CONFIG_WITH_STRCACHE2 */
470 if (ht->ht_strcache)
471 for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
472 {
473 if (! HASH_VACANT (*ovp))
474 {
475 void **slot = hash_find_slot_strcached (ht, *ovp);
476 *slot = *ovp;
477 }
478 }
479 else
480 for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
481 {
482 if (! HASH_VACANT (*ovp))
483 {
484 void **slot = hash_find_slot (ht, *ovp);
485 *slot = *ovp;
486 }
487 }
488#endif /* CONFIG_WITH_STRCACHE2 */
489 ht->ht_empty_slots = ht->ht_size - ht->ht_fill;
490 free (old_vec);
491}
492
493void
494hash_print_stats (struct hash_table *ht, FILE *out_FILE)
495{
496 /* GKM FIXME: honor NO_FLOAT */
497 fprintf (out_FILE, _("Load=%ld/%ld=%.0f%%, "), ht->ht_fill, ht->ht_size,
498 100.0 * (double) ht->ht_fill / (double) ht->ht_size);
499 fprintf (out_FILE, _("Rehash=%d, "), ht->ht_rehashes);
500 fprintf (out_FILE, _("Collisions=%ld/%ld=%.0f%%"), ht->ht_collisions, ht->ht_lookups,
501 (ht->ht_lookups
502 ? (100.0 * (double) ht->ht_collisions / (double) ht->ht_lookups)
503 : 0));
504}
505
506/* Dump all items into a NULL-terminated vector. Use the
507 user-supplied vector, or malloc one. */
508
509void **
510hash_dump (struct hash_table *ht, void **vector_0, qsort_cmp_t compare)
511{
512 void **vector;
513 void **slot;
514 void **end = &ht->ht_vec[ht->ht_size];
515
516 if (vector_0 == 0)
517 vector_0 = MALLOC (void *, ht->ht_fill + 1);
518 vector = vector_0;
519
520 for (slot = ht->ht_vec; slot < end; slot++)
521 if (!HASH_VACANT (*slot))
522 *vector++ = *slot;
523 *vector = 0;
524
525 if (compare)
526 qsort (vector_0, ht->ht_fill, sizeof (void *), compare);
527 return vector_0;
528}
529
530/* Round a given number up to the nearest power of 2. */
531
532static unsigned long
533round_up_2 (unsigned long n)
534{
535 n |= (n >> 1);
536 n |= (n >> 2);
537 n |= (n >> 4);
538 n |= (n >> 8);
539 n |= (n >> 16);
540
541#if !defined(HAVE_LIMITS_H) || ULONG_MAX > 4294967295
542 /* We only need this on systems where unsigned long is >32 bits. */
543 n |= (n >> 32);
544#endif
545
546 return n + 1;
547}
Note: See TracBrowser for help on using the repository browser.

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