]>
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
);
179 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
184 /*******************************************************************************
185 * This dictionary uses linear probing, which means that when there is a
186 * collision, we just walk along the buckets until a free bucket shows up.
187 * A consequence of this is that when looking up an item, items that lie between
188 * its hash value and its actual bucket may have been deleted since it was
189 * inserted. Thus, we should only stop a lookup when we've wrapped around the
190 * dictionary or encountered an EMPTY bucket.
191 ********************************************************************************/
193 get_locate_index(const KXLDDict
*dict
, const void *key
, u_int
*_idx
)
195 kern_return_t rval
= KERN_FAILURE
;
196 DictEntry
*entry
= NULL
;
199 base
= idx
= dict
->hash(dict
, key
);
201 /* Iterate until we match the key, wrap, or hit an empty bucket */
202 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
203 while (!dict
->cmp(entry
->key
, key
)) {
204 if (entry
->state
== EMPTY
) {
208 idx
= (idx
+ 1) % dict
->buckets
.nitems
;
213 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
216 check(idx
< dict
->buckets
.nitems
);
225 /*******************************************************************************
226 *******************************************************************************/
228 kxld_dict_insert(KXLDDict
*dict
, const void *key
, void *value
)
230 kern_return_t rval
= KERN_FAILURE
;
231 DictEntry
*entry
= NULL
;
238 /* Resize if we are greater than the capacity threshold.
239 * Note: this is expensive, but the dictionary can be sized correctly at
240 * construction to avoid ever having to do this.
242 while (dict
->num_entries
> dict
->resize_threshold
) {
243 rval
= resize_dict(dict
);
244 require_noerr(rval
, finish
);
247 /* If this function returns FULL after we've already resized appropriately
248 * something is very wrong and we should return an error.
250 rval
= get_insert_index(dict
, key
, &idx
);
251 require_noerr(rval
, finish
);
253 /* Insert the new key-value pair into the bucket, but only count it as a
254 * new entry if we are not overwriting an existing entry.
256 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
257 if (entry
->state
!= USED
) {
262 entry
->value
= value
;
270 /*******************************************************************************
271 * Increases the hash table's capacity by 2N+1. Uses dictionary API. Not
272 * fast; just correct.
273 *******************************************************************************/
275 resize_dict(KXLDDict
*dict
)
277 kern_return_t rval
= KERN_FAILURE
;
279 DictEntry
*entry
= NULL
;
280 u_int nbuckets
= (dict
->buckets
.nitems
* 2 + 1);
285 /* Initialize a new set of buckets to hold more entries */
286 rval
= kxld_array_init(&dict
->resize_buckets
, sizeof(DictEntry
), nbuckets
);
287 require_noerr(rval
, finish
);
289 /* Swap the new buckets with the old buckets */
290 tmparray
= dict
->buckets
;
291 dict
->buckets
= dict
->resize_buckets
;
292 dict
->resize_buckets
= tmparray
;
294 /* Reset dictionary parameters */
295 dict
->num_entries
= 0;
296 dict
->resize_threshold
= RESIZE_THRESHOLD(dict
->buckets
.nitems
);
298 /* Rehash all of the entries */
299 for (i
= 0; i
< dict
->resize_buckets
.nitems
; ++i
) {
300 entry
= kxld_array_get_item(&dict
->resize_buckets
, i
);
301 if (entry
->state
== USED
) {
302 rval
= kxld_dict_insert(dict
, entry
->key
, entry
->value
);
303 require_noerr(rval
, finish
);
307 /* Clear the old buckets */
308 kxld_array_clear(&dict
->resize_buckets
);
316 /*******************************************************************************
317 * Simple function to find the first empty cell
318 *******************************************************************************/
320 get_insert_index(const KXLDDict
*dict
, const void *key
, u_int
*r_index
)
322 kern_return_t rval
= KERN_FAILURE
;
323 DictEntry
*entry
= NULL
;
326 base
= idx
= dict
->hash(dict
, key
);
328 /* Iterate through the buckets until we find an EMPTY bucket, a DELETED
329 * bucket, or a key match.
331 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
332 while (entry
->state
== USED
&& !dict
->cmp(entry
->key
, key
)) {
333 idx
= (idx
+ 1) % dict
->buckets
.nitems
;
334 require_action(base
!= idx
, finish
, rval
= KERN_FAILURE
);
335 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
345 /*******************************************************************************
346 *******************************************************************************/
348 kxld_dict_remove(KXLDDict
*dict
, const void *key
, void **value
)
350 kern_return_t rval
= KERN_FAILURE
;
351 DictEntry
*entry
= NULL
;
358 rval
= get_locate_index(dict
, key
, &idx
);
366 entry
= kxld_array_get_item(&dict
->buckets
, idx
);
368 /* Save the value if requested */
370 *value
= entry
->value
;
373 /* Delete the item from the dictionary */
376 entry
->state
= DELETED
;
380 /*******************************************************************************
381 *******************************************************************************/
383 kxld_dict_iterator_get_next(KXLDDictIterator
*iter
, const void **key
,
386 DictEntry
*entry
= NULL
;
395 /* Walk over the dictionary looking for USED buckets */
396 for (; iter
->idx
< iter
->dict
->buckets
.nitems
; ++(iter
->idx
)) {
397 entry
= kxld_array_get_item(&iter
->dict
->buckets
, iter
->idx
);
398 if (entry
->state
== USED
) {
400 *value
= entry
->value
;
407 /*******************************************************************************
408 *******************************************************************************/
410 kxld_dict_iterator_reset(KXLDDictIterator
*iter
)
415 /*******************************************************************************
416 * This is Daniel Bernstein's hash algorithm from comp.lang.c
417 * It's fast and distributes well. Returns an idx into the symbol hash table.
418 * NOTE: Will not check for a valid pointer - performance
419 *******************************************************************************/
421 kxld_dict_string_hash(const KXLDDict
*dict
, const void *_key
)
423 const char *key
= _key
;
425 u_int hash_val
= 5381;
430 while ((c
= *key
++)) {
431 /* hash(i) = hash(i-1) *33 ^ name[i] */
432 hash_val
= ((hash_val
<< 5) + hash_val
) ^ c
;
435 return hash_val
% dict
->buckets
.nitems
;
439 kxld_dict_uint32_hash(const KXLDDict
*dict
, const void *_key
)
441 uint32_t key
= *(const uint32_t *) _key
;
445 return (u_int
) (key
% dict
->buckets
.nitems
);
449 kxld_dict_kxldaddr_hash(const KXLDDict
*dict
, const void *_key
)
451 kxld_addr_t key
= *(const kxld_addr_t
*) _key
;
455 return (u_int
) (key
% dict
->buckets
.nitems
);
459 kxld_dict_string_cmp(const void *key1
, const void *key2
)
461 return streq(key1
, key2
);
465 kxld_dict_uint32_cmp(const void *key1
, const void *key2
)
467 const uint32_t *a
= key1
;
468 const uint32_t *b
= key2
;
470 return a
&& b
&& (*a
== *b
);
474 kxld_dict_kxldaddr_cmp(const void *key1
, const void *key2
)
476 const kxld_addr_t
*a
= key1
;
477 const kxld_addr_t
*b
= key2
;
479 return a
&& b
&& (*a
== *b
);