]> git.saurik.com Git - apple/xnu.git/blame - libkern/kxld/kxld_dict.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / libkern / kxld / kxld_dict.c
CommitLineData
b0d623f7
A
1/*
2 * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
b0d623f7
A
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.
0a7de745 14 *
b0d623f7
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
b0d623f7
A
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.
0a7de745 25 *
b0d623f7
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <string.h>
29#include <sys/types.h>
30
31#define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
32#include <AssertMacros.h>
33
34#include "kxld_dict.h"
35#include "kxld_util.h"
36
37/*******************************************************************************
38* Types and macros
39*******************************************************************************/
40
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)
0a7de745 45#define MIN_BUCKETS(x) (((x)*RESIZE_DENOM) / RESIZE_NUMER)
b0d623f7
A
46
47/* Selected for good scaling qualities when resizing dictionary
48 * ... see: http://www.concentric.net/~ttwang/tech/hashsize.htm
49 */
50#define DEFAULT_DICT_SIZE 89
51
52typedef struct dict_entry DictEntry;
53
54typedef enum {
0a7de745
A
55 EMPTY = 0,
56 USED = 1,
57 DELETED = 2
b0d623f7
A
58} DictEntryState;
59
60struct dict_entry {
0a7de745
A
61 const void *key;
62 void *value;
63 DictEntryState state;
b0d623f7
A
64};
65
66/*******************************************************************************
67* Function prototypes
68*******************************************************************************/
69
0a7de745 70static kern_return_t get_locate_index(const KXLDDict *dict, const void *key,
b0d623f7 71 u_int *idx);
0a7de745 72static kern_return_t get_insert_index(const KXLDDict *dict, const void *key,
b0d623f7
A
73 u_int *idx);
74static kern_return_t resize_dict(KXLDDict *dict);
75
76/*******************************************************************************
77*******************************************************************************/
78kern_return_t
0a7de745
A
79kxld_dict_init(KXLDDict * dict, kxld_dict_hash hash, kxld_dict_cmp cmp,
80 u_int num_entries)
b0d623f7 81{
0a7de745
A
82 kern_return_t rval = KERN_FAILURE;
83 u_int min_buckets = MIN_BUCKETS(num_entries);
84 u_int num_buckets = DEFAULT_DICT_SIZE;
85
86 check(dict);
87 check(hash);
88 check(cmp);
89
90 /* We want the number of allocated buckets to be at least twice that of the
91 * number to be inserted.
92 */
93 while (min_buckets > num_buckets) {
94 num_buckets *= 2;
95 num_buckets++;
96 }
97
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);
101
102 /* Initialize */
103 dict->hash = hash;
104 dict->cmp = cmp;
105 dict->num_entries = 0;
106 dict->resize_threshold = RESIZE_THRESHOLD(num_buckets);
107
108 rval = KERN_SUCCESS;
109
b0d623f7 110finish:
0a7de745 111 return rval;
b0d623f7
A
112}
113
114/*******************************************************************************
115*******************************************************************************/
116void
117kxld_dict_clear(KXLDDict *dict)
118{
0a7de745
A
119 check(dict);
120
121 dict->hash = NULL;
122 dict->cmp = NULL;
123 dict->num_entries = 0;
124 dict->resize_threshold = 0;
125 kxld_array_clear(&dict->buckets);
126 kxld_array_clear(&dict->resize_buckets);
b0d623f7
A
127}
128
129/*******************************************************************************
130*******************************************************************************/
131void
132kxld_dict_iterator_init(KXLDDictIterator *iter, const KXLDDict *dict)
133{
0a7de745
A
134 check(iter);
135 check(dict);
b0d623f7 136
0a7de745
A
137 iter->idx = 0;
138 iter->dict = dict;
b0d623f7
A
139}
140
141/*******************************************************************************
142*******************************************************************************/
143void
144kxld_dict_deinit(KXLDDict *dict)
145{
0a7de745
A
146 check(dict);
147
148 kxld_array_deinit(&dict->buckets);
149 kxld_array_deinit(&dict->resize_buckets);
b0d623f7
A
150}
151
152/*******************************************************************************
153*******************************************************************************/
154u_int
155kxld_dict_get_num_entries(const KXLDDict *dict)
156{
0a7de745 157 check(dict);
b0d623f7 158
0a7de745 159 return dict->num_entries;
b0d623f7
A
160}
161
162/*******************************************************************************
163*******************************************************************************/
164void *
165kxld_dict_find(const KXLDDict *dict, const void *key)
166{
0a7de745
A
167 kern_return_t rval = KERN_FAILURE;
168 DictEntry *entry = NULL;
169 u_int idx = 0;
170
171 check(dict);
172 check(key);
173
174 rval = get_locate_index(dict, key, &idx);
175 if (rval) {
176 return NULL;
177 }
178
179 entry = kxld_array_get_item(&dict->buckets, idx);
180
181 return entry->value;
b0d623f7
A
182}
183
184/*******************************************************************************
0a7de745
A
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 ********************************************************************************/
b0d623f7
A
192static kern_return_t
193get_locate_index(const KXLDDict *dict, const void *key, u_int *_idx)
194{
0a7de745
A
195 kern_return_t rval = KERN_FAILURE;
196 DictEntry *entry = NULL;
197 u_int base, idx;
b0d623f7 198
0a7de745 199 base = idx = dict->hash(dict, key);
b0d623f7 200
0a7de745
A
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) {
205 goto finish;
206 }
b0d623f7 207
0a7de745
A
208 idx = (idx + 1) % dict->buckets.nitems;
209 if (idx == base) {
210 goto finish;
211 }
b0d623f7 212
0a7de745
A
213 entry = kxld_array_get_item(&dict->buckets, idx);
214 }
b0d623f7 215
0a7de745
A
216 check(idx < dict->buckets.nitems);
217
218 *_idx = idx;
219 rval = KERN_SUCCESS;
b0d623f7
A
220
221finish:
0a7de745 222 return rval;
b0d623f7
A
223}
224
225/*******************************************************************************
226*******************************************************************************/
227kern_return_t
228kxld_dict_insert(KXLDDict *dict, const void *key, void *value)
229{
0a7de745
A
230 kern_return_t rval = KERN_FAILURE;
231 DictEntry *entry = NULL;
232 u_int idx = 0;
233
234 check(dict);
235 check(key);
236 check(value);
237
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.
241 */
242 while (dict->num_entries > dict->resize_threshold) {
243 rval = resize_dict(dict);
244 require_noerr(rval, finish);
245 }
246
247 /* If this function returns FULL after we've already resized appropriately
248 * something is very wrong and we should return an error.
249 */
250 rval = get_insert_index(dict, key, &idx);
251 require_noerr(rval, finish);
252
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.
255 */
256 entry = kxld_array_get_item(&dict->buckets, idx);
257 if (entry->state != USED) {
258 dict->num_entries++;
259 entry->key = key;
260 entry->state = USED;
261 }
262 entry->value = value;
263
264 rval = KERN_SUCCESS;
265
b0d623f7 266finish:
0a7de745 267 return rval;
b0d623f7
A
268}
269
270/*******************************************************************************
271* Increases the hash table's capacity by 2N+1. Uses dictionary API. Not
272* fast; just correct.
273*******************************************************************************/
274static kern_return_t
275resize_dict(KXLDDict *dict)
276{
0a7de745
A
277 kern_return_t rval = KERN_FAILURE;
278 KXLDArray tmparray;
279 DictEntry *entry = NULL;
280 u_int nbuckets = (dict->buckets.nitems * 2 + 1);
281 u_int i = 0;
282
283 check(dict);
284
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);
288
289 /* Swap the new buckets with the old buckets */
290 tmparray = dict->buckets;
291 dict->buckets = dict->resize_buckets;
292 dict->resize_buckets = tmparray;
293
294 /* Reset dictionary parameters */
295 dict->num_entries = 0;
296 dict->resize_threshold = RESIZE_THRESHOLD(dict->buckets.nitems);
297
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);
304 }
305 }
306
307 /* Clear the old buckets */
308 kxld_array_clear(&dict->resize_buckets);
309
310 rval = KERN_SUCCESS;
311
b0d623f7 312finish:
0a7de745 313 return rval;
b0d623f7
A
314}
315
316/*******************************************************************************
317* Simple function to find the first empty cell
318*******************************************************************************/
319static kern_return_t
320get_insert_index(const KXLDDict *dict, const void *key, u_int *r_index)
321{
0a7de745
A
322 kern_return_t rval = KERN_FAILURE;
323 DictEntry *entry = NULL;
324 u_int base, idx;
325
326 base = idx = dict->hash(dict, key);
327
328 /* Iterate through the buckets until we find an EMPTY bucket, a DELETED
329 * bucket, or a key match.
330 */
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);
336 }
337
338 *r_index = idx;
339 rval = KERN_SUCCESS;
340
b0d623f7 341finish:
0a7de745 342 return rval;
b0d623f7
A
343}
344
345/*******************************************************************************
346*******************************************************************************/
347void
348kxld_dict_remove(KXLDDict *dict, const void *key, void **value)
349{
0a7de745
A
350 kern_return_t rval = KERN_FAILURE;
351 DictEntry *entry = NULL;
352 u_int idx = 0;
353
354 check(dict);
355 check(key);
356
357 /* Find the item */
358 rval = get_locate_index(dict, key, &idx);
359 if (rval) {
360 if (value) {
361 *value = NULL;
362 }
363 return;
364 }
365
366 entry = kxld_array_get_item(&dict->buckets, idx);
367
368 /* Save the value if requested */
369 if (value) {
370 *value = entry->value;
371 }
372
373 /* Delete the item from the dictionary */
374 entry->key = NULL;
375 entry->value = NULL;
376 entry->state = DELETED;
377 dict->num_entries--;
b0d623f7
A
378}
379
380/*******************************************************************************
381*******************************************************************************/
0a7de745
A
382void
383kxld_dict_iterator_get_next(KXLDDictIterator *iter, const void **key,
b0d623f7
A
384 void **value)
385{
0a7de745
A
386 DictEntry *entry = NULL;
387
388 check(iter);
389 check(key);
390 check(value);
391
392 *key = NULL;
393 *value = NULL;
394
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) {
399 *key = entry->key;
400 *value = entry->value;
401 ++(iter->idx);
402 break;
403 }
404 }
b0d623f7
A
405}
406
407/*******************************************************************************
408*******************************************************************************/
0a7de745 409void
b0d623f7
A
410kxld_dict_iterator_reset(KXLDDictIterator *iter)
411{
0a7de745 412 iter->idx = 0;
b0d623f7
A
413}
414
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*******************************************************************************/
420u_int
0a7de745 421kxld_dict_string_hash(const KXLDDict *dict, const void *_key)
b0d623f7 422{
0a7de745
A
423 const char *key = _key;
424 u_int c = 0;
425 u_int hash_val = 5381;
426
427 check(dict);
428 check(_key);
429
430 while ((c = *key++)) {
431 /* hash(i) = hash(i-1) *33 ^ name[i] */
432 hash_val = ((hash_val << 5) + hash_val) ^ c;
433 }
434
435 return hash_val % dict->buckets.nitems;
b0d623f7
A
436}
437
438u_int
439kxld_dict_uint32_hash(const KXLDDict *dict, const void *_key)
440{
0a7de745 441 uint32_t key = *(const uint32_t *) _key;
b0d623f7 442
0a7de745 443 check(_key);
b0d623f7 444
0a7de745 445 return (u_int) (key % dict->buckets.nitems);
b0d623f7
A
446}
447
448u_int
449kxld_dict_kxldaddr_hash(const KXLDDict *dict, const void *_key)
450{
0a7de745 451 kxld_addr_t key = *(const kxld_addr_t *) _key;
b0d623f7 452
0a7de745 453 check(_key);
b0d623f7 454
0a7de745 455 return (u_int) (key % dict->buckets.nitems);
b0d623f7
A
456}
457
458u_int
459kxld_dict_string_cmp(const void *key1, const void *key2)
460{
0a7de745 461 return streq(key1, key2);
b0d623f7
A
462}
463
464u_int
465kxld_dict_uint32_cmp(const void *key1, const void *key2)
466{
0a7de745
A
467 const uint32_t *a = key1;
468 const uint32_t *b = key2;
b0d623f7 469
0a7de745 470 return a && b && (*a == *b);
b0d623f7
A
471}
472
473u_int
474kxld_dict_kxldaddr_cmp(const void *key1, const void *key2)
475{
0a7de745
A
476 const kxld_addr_t *a = key1;
477 const kxld_addr_t *b = key2;
b0d623f7 478
0a7de745 479 return a && b && (*a == *b);
b0d623f7 480}