]>
git.saurik.com Git - apple/xnu.git/blob - libkern/kxld/kxld_dict.c
2 * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/types.h>
31 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
32 #include <AssertMacros.h>
34 #include "kxld_dict.h"
35 #include "kxld_util.h"
37 /*******************************************************************************
39 *******************************************************************************/
41 /* Ratio of num_entries:num_buckets that will cause a resize */
42 #define RESIZE_NUMER 7
43 #define RESIZE_DENOM 10
44 #define RESIZE_THRESHOLD(x) (((x)*RESIZE_NUMER) / RESIZE_DENOM)
45 #define MIN_BUCKETS(x) (((x)*RESIZE_DENOM) / RESIZE_NUMER)
47 /* Selected for good scaling qualities when resizing dictionary
48 * ... see: http://www.concentric.net/~ttwang/tech/hashsize.htm
50 #define DEFAULT_DICT_SIZE 89
52 typedef struct dict_entry DictEntry
;
66 /*******************************************************************************
68 *******************************************************************************/
70 static kern_return_t
get_locate_index(const KXLDDict
*dict
, const void *key
,
72 static kern_return_t
get_insert_index(const KXLDDict
*dict
, const void *key
,
74 static kern_return_t
resize_dict(KXLDDict
*dict
);
76 /*******************************************************************************
77 *******************************************************************************/
79 kxld_dict_init(KXLDDict
* dict
, kxld_dict_hash hash
, kxld_dict_cmp cmp
,
82 kern_return_t rval
= KERN_FAILURE
;
83 u_int min_buckets
= MIN_BUCKETS(num_entries
);
84 u_int num_buckets
= DEFAULT_DICT_SIZE
;
90 /* We want the number of allocated buckets to be at least twice that of the
91 * number to be inserted.
93 while (min_buckets
> num_buckets
) {
98 /* Allocate enough buckets for the anticipated number of entries */
99 rval
= kxld_array_init(&dict
->buckets
, sizeof(DictEntry
), num_buckets
);
100 require_noerr(rval
, finish
);
105 dict
->num_entries
= 0;
106 dict
->resize_threshold
= RESIZE_THRESHOLD(num_buckets
);
114 /*******************************************************************************
115 *******************************************************************************/
117 kxld_dict_clear(KXLDDict
*dict
)
123 dict
->num_entries
= 0;
124 dict
->resize_threshold
= 0;
125 kxld_array_clear(&dict
->buckets
);
126 kxld_array_clear(&dict
->resize_buckets
);
129 /*******************************************************************************
130 *******************************************************************************/
132 kxld_dict_iterator_init(KXLDDictIterator
*iter
, const KXLDDict
*dict
)
141 /*******************************************************************************
142 *******************************************************************************/
144 kxld_dict_deinit(KXLDDict
*dict
)
148 kxld_array_deinit(&dict
->buckets
);
149 kxld_array_deinit(&dict
->resize_buckets
);
152 /*******************************************************************************
153 *******************************************************************************/
155 kxld_dict_get_num_entries(const KXLDDict
*dict
)
159 return dict
->num_entries
;
162 /*******************************************************************************
163 *******************************************************************************/
165 kxld_dict_find(const KXLDDict
*dict
, const void *key
)
167 kern_return_t rval
= KERN_FAILURE
;
168 DictEntry
*entry
= NULL
;
174 rval
= get_locate_index(dict
, key
, &idx
);
175 if (rval
) return NULL
;
177 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
182 /*******************************************************************************
183 * This dictionary uses linear probing, which means that when there is a
184 * collision, we just walk along the buckets until a free bucket shows up.
185 * A consequence of this is that when looking up an item, items that lie between
186 * its hash value and its actual bucket may have been deleted since it was
187 * inserted. Thus, we should only stop a lookup when we've wrapped around the
188 * dictionary or encountered an EMPTY bucket.
189 ********************************************************************************/
191 get_locate_index(const KXLDDict
*dict
, const void *key
, u_int
*_idx
)
193 kern_return_t rval
= KERN_FAILURE
;
194 DictEntry
*entry
= NULL
;
197 base
= idx
= dict
->hash(dict
, key
);
199 /* Iterate until we match the key, wrap, or hit an empty bucket */
200 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
201 while (!dict
->cmp(entry
->key
, key
)) {
202 if (entry
->state
== EMPTY
) goto finish
;
204 idx
= (idx
+ 1) % dict
->buckets
.nitems
;
205 if (idx
== base
) goto finish
;
207 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
210 check(idx
< dict
->buckets
.nitems
);
219 /*******************************************************************************
220 *******************************************************************************/
222 kxld_dict_insert(KXLDDict
*dict
, const void *key
, void *value
)
224 kern_return_t rval
= KERN_FAILURE
;
225 DictEntry
*entry
= NULL
;
232 /* Resize if we are greater than the capacity threshold.
233 * Note: this is expensive, but the dictionary can be sized correctly at
234 * construction to avoid ever having to do this.
236 while (dict
->num_entries
> dict
->resize_threshold
) {
237 rval
= resize_dict(dict
);
238 require_noerr(rval
, finish
);
241 /* If this function returns FULL after we've already resized appropriately
242 * something is very wrong and we should return an error.
244 rval
= get_insert_index(dict
, key
, &idx
);
245 require_noerr(rval
, finish
);
247 /* Insert the new key-value pair into the bucket, but only count it as a
248 * new entry if we are not overwriting an existing entry.
250 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
251 if (entry
->state
!= USED
) {
256 entry
->value
= value
;
264 /*******************************************************************************
265 * Increases the hash table's capacity by 2N+1. Uses dictionary API. Not
266 * fast; just correct.
267 *******************************************************************************/
269 resize_dict(KXLDDict
*dict
)
271 kern_return_t rval
= KERN_FAILURE
;
273 DictEntry
*entry
= NULL
;
274 u_int nbuckets
= (dict
->buckets
.nitems
* 2 + 1);
279 /* Initialize a new set of buckets to hold more entries */
280 rval
= kxld_array_init(&dict
->resize_buckets
, sizeof(DictEntry
), nbuckets
);
281 require_noerr(rval
, finish
);
283 /* Swap the new buckets with the old buckets */
284 tmparray
= dict
->buckets
;
285 dict
->buckets
= dict
->resize_buckets
;
286 dict
->resize_buckets
= tmparray
;
288 /* Reset dictionary parameters */
289 dict
->num_entries
= 0;
290 dict
->resize_threshold
= RESIZE_THRESHOLD(dict
->buckets
.nitems
);
292 /* Rehash all of the entries */
293 for (i
= 0; i
< dict
->resize_buckets
.nitems
; ++i
) {
294 entry
= kxld_array_get_item(&dict
->resize_buckets
, i
);
295 if (entry
->state
== USED
) {
296 rval
= kxld_dict_insert(dict
, entry
->key
, entry
->value
);
297 require_noerr(rval
, finish
);
301 /* Clear the old buckets */
302 kxld_array_clear(&dict
->resize_buckets
);
310 /*******************************************************************************
311 * Simple function to find the first empty cell
312 *******************************************************************************/
314 get_insert_index(const KXLDDict
*dict
, const void *key
, u_int
*r_index
)
316 kern_return_t rval
= KERN_FAILURE
;
317 DictEntry
*entry
= NULL
;
320 base
= idx
= dict
->hash(dict
, key
);
322 /* Iterate through the buckets until we find an EMPTY bucket, a DELETED
323 * bucket, or a key match.
325 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
326 while (entry
->state
== USED
&& !dict
->cmp(entry
->key
, key
)) {
327 idx
= (idx
+ 1) % dict
->buckets
.nitems
;
328 require_action(base
!= idx
, finish
, rval
=KERN_FAILURE
);
329 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
339 /*******************************************************************************
340 *******************************************************************************/
342 kxld_dict_remove(KXLDDict
*dict
, const void *key
, void **value
)
344 kern_return_t rval
= KERN_FAILURE
;
345 DictEntry
*entry
= NULL
;
352 rval
= get_locate_index(dict
, key
, &idx
);
354 if (value
) *value
= NULL
;
358 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
360 /* Save the value if requested */
361 if (value
) *value
= entry
->value
;
363 /* Delete the item from the dictionary */
366 entry
->state
= DELETED
;
370 /*******************************************************************************
371 *******************************************************************************/
373 kxld_dict_iterator_get_next(KXLDDictIterator
*iter
, const void **key
,
376 DictEntry
*entry
= NULL
;
385 /* Walk over the dictionary looking for USED buckets */
386 for (; iter
->idx
< iter
->dict
->buckets
.nitems
; ++(iter
->idx
)) {
387 entry
= kxld_array_get_item(&iter
->dict
->buckets
, iter
->idx
);
388 if (entry
->state
== USED
) {
390 *value
= entry
->value
;
397 /*******************************************************************************
398 *******************************************************************************/
400 kxld_dict_iterator_reset(KXLDDictIterator
*iter
)
405 /*******************************************************************************
406 * This is Daniel Bernstein's hash algorithm from comp.lang.c
407 * It's fast and distributes well. Returns an idx into the symbol hash table.
408 * NOTE: Will not check for a valid pointer - performance
409 *******************************************************************************/
411 kxld_dict_string_hash(const KXLDDict
*dict
, const void *_key
)
413 const char *key
= _key
;
415 u_int hash_val
= 5381;
420 while ((c
= *key
++)) {
421 /* hash(i) = hash(i-1) *33 ^ name[i] */
422 hash_val
= ((hash_val
<< 5) + hash_val
) ^ c
;
425 return (hash_val
% dict
->buckets
.nitems
);
429 kxld_dict_uint32_hash(const KXLDDict
*dict
, const void *_key
)
431 uint32_t key
= *(const uint32_t *) _key
;
435 return (u_int
) (key
% dict
->buckets
.nitems
);
439 kxld_dict_kxldaddr_hash(const KXLDDict
*dict
, const void *_key
)
441 kxld_addr_t key
= *(const kxld_addr_t
*) _key
;
445 return (u_int
) (key
% dict
->buckets
.nitems
);
449 kxld_dict_string_cmp(const void *key1
, const void *key2
)
451 return streq(key1
, key2
);
455 kxld_dict_uint32_cmp(const void *key1
, const void *key2
)
457 const uint32_t *a
= key1
;
458 const uint32_t *b
= key2
;
460 return (a
&& b
&& (*a
== *b
));
464 kxld_dict_kxldaddr_cmp(const void *key1
, const void *key2
)
466 const kxld_addr_t
*a
= key1
;
467 const kxld_addr_t
*b
= key2
;
469 return (a
&& b
&& (*a
== *b
));