]>
git.saurik.com Git - apple/xnu.git/blob - libkern/kxld/kxld_array.c
2 * Copyright (c) 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@
31 #include <mach/vm_param.h>
33 #include <mach/mach_init.h>
36 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
37 #include <AssertMacros.h>
39 #include "kxld_array.h"
40 #include "kxld_util.h"
42 static kern_return_t
array_init(KXLDArray
*array
, size_t itemsize
, u_int nitems
);
43 static KXLDArrayPool
* pool_create(size_t capacity
);
44 static void pool_destroy(KXLDArrayPool
*pool
, size_t capacity
);
45 static u_int
reinit_pools(KXLDArray
*array
, u_int nitems
);
47 /*******************************************************************************
48 *******************************************************************************/
50 kxld_array_init(KXLDArray
*array
, size_t itemsize
, u_int nitems
)
52 kern_return_t rval
= KERN_FAILURE
;
53 KXLDArrayPool
*dstpool
= NULL
, *srcpool
= NULL
, *tmp
= NULL
;
54 KXLDArrayHead srcpools
= STAILQ_HEAD_INITIALIZER(srcpools
);
55 size_t srcpool_capacity
= 0;
61 kxld_array_reset(array
);
66 require_action(itemsize
, finish
, rval
=KERN_INVALID_ARGUMENT
);
68 /* If the array has some pools, we need to see if there is enough space in
69 * those pools to accomodate the requested size array. If there isn't
70 * enough space, we save the existing pools to a temporary STAILQ and zero
71 * out the array structure. This will cause a new pool of sufficient size
72 * to be created, and we then copy the data from the old pools into the new
76 /* Update the array's maxitems based on the new itemsize */
77 array
->pool_maxitems
= (u_int
) (array
->pool_capacity
/ itemsize
);
79 STAILQ_FOREACH(srcpool
, &array
->pools
, entries
) {
80 array
->maxitems
+= array
->pool_maxitems
;
83 /* If there's not enough space, save the pools to a temporary STAILQ
84 * and zero out the array structure. Otherwise, rescan the pools to
85 * update their internal nitems counts.
87 if (array
->maxitems
< nitems
) {
88 STAILQ_FOREACH_SAFE(srcpool
, &array
->pools
, entries
, tmp
) {
89 STAILQ_REMOVE(&array
->pools
, srcpool
, kxld_array_pool
, entries
);
90 STAILQ_INSERT_TAIL(&srcpools
, srcpool
, entries
);
92 srcpool_capacity
= array
->pool_capacity
;
93 bzero(array
, sizeof(*array
));
95 nitems
= reinit_pools(array
, nitems
);
96 require_action(nitems
== 0, finish
, rval
=KERN_FAILURE
);
100 array
->itemsize
= itemsize
;
102 /* If array->maxitems is zero, it means we are either rebuilding an array
103 * that was too small, or we're initializing an array for the first time.
104 * In either case, we need to set up a pool of the requested size, and
105 * if we're rebuilding an old array, we'll also copy the data from the old
106 * pools into the new pool.
108 if (array
->maxitems
== 0) {
110 rval
= array_init(array
, itemsize
, nitems
);
111 require_noerr(rval
, finish
);
113 dstpool
= STAILQ_FIRST(&array
->pools
);
114 require_action(dstpool
, finish
, rval
=KERN_FAILURE
);
116 STAILQ_FOREACH_SAFE(srcpool
, &srcpools
, entries
, tmp
) {
117 memcpy(dstpool
->buffer
+ offset
, srcpool
->buffer
, srcpool_capacity
);
118 offset
+= srcpool_capacity
;
120 STAILQ_REMOVE(&srcpools
, srcpool
, kxld_array_pool
, entries
);
121 pool_destroy(srcpool
, srcpool_capacity
);
128 if (rval
) kxld_array_deinit(array
);
132 /*******************************************************************************
133 * This may only be called to initialize (or reinitialize) an array with exactly
134 * zero or one pool. Calling this on an array with more than one pool is an
136 *******************************************************************************/
138 array_init(KXLDArray
*array
, size_t itemsize
, u_int nitems
)
140 kern_return_t rval
= KERN_FAILURE
;
141 KXLDArrayPool
*pool
= NULL
;
143 require_action(itemsize
, finish
, rval
=KERN_INVALID_ARGUMENT
);
144 require_action(array
->npools
< 2, finish
, rval
=KERN_INVALID_ARGUMENT
);
146 array
->itemsize
= itemsize
;
148 pool
= STAILQ_FIRST(&array
->pools
);
150 require_action(itemsize
* nitems
< array
->pool_capacity
,
151 finish
, rval
=KERN_FAILURE
);
152 require_action(array
->npools
== 1, finish
, rval
=KERN_FAILURE
);
153 bzero(pool
->buffer
, array
->pool_capacity
);
155 array
->pool_capacity
= round_page(array
->itemsize
* nitems
);
157 pool
= pool_create(array
->pool_capacity
);
158 require_action(pool
, finish
, rval
=KERN_RESOURCE_SHORTAGE
);
159 STAILQ_INSERT_HEAD(&array
->pools
, pool
, entries
);
161 pool
->nitems
= nitems
;
163 array
->pool_maxitems
= (u_int
) (array
->pool_capacity
/ array
->itemsize
);
164 array
->maxitems
= array
->pool_maxitems
;
165 array
->nitems
= nitems
;
173 /*******************************************************************************
174 *******************************************************************************/
175 static KXLDArrayPool
*
176 pool_create(size_t capacity
)
178 KXLDArrayPool
*pool
= NULL
, *rval
= NULL
;
180 pool
= kxld_alloc(sizeof(*pool
));
181 require(pool
, finish
);
183 pool
->buffer
= kxld_page_alloc(capacity
);
184 require(pool
->buffer
, finish
);
185 bzero(pool
->buffer
, capacity
);
191 if (pool
) pool_destroy(pool
, capacity
);
195 /*******************************************************************************
196 *******************************************************************************/
198 pool_destroy(KXLDArrayPool
*pool
, size_t capacity
)
201 if (pool
->buffer
) kxld_page_free(pool
->buffer
, capacity
);
202 kxld_free(pool
, sizeof(*pool
));
206 /*******************************************************************************
207 *******************************************************************************/
209 kxld_array_copy(KXLDArray
*dstarray
, const KXLDArray
*srcarray
)
211 kern_return_t rval
= KERN_FAILURE
;
212 KXLDArrayPool
*dstpool
= NULL
, *srcpool
= NULL
;
213 u_long needed_capacity
= 0;
214 u_long current_capacity
= 0;
221 /* When copying array, we only want to copy to an array with a single
222 * pool. If the array has more than one pool or the array is too small,
223 * we destroy the array and build it from scratch for the copy.
225 needed_capacity
= round_page(srcarray
->nitems
* srcarray
->itemsize
);
226 current_capacity
= dstarray
->npools
* dstarray
->pool_capacity
;
227 if (dstarray
->npools
> 1 || needed_capacity
> current_capacity
) {
228 kxld_array_deinit(dstarray
);
231 rval
= array_init(dstarray
, srcarray
->itemsize
, srcarray
->nitems
);
232 require_noerr(rval
, finish
);
234 dstpool
= STAILQ_FIRST(&dstarray
->pools
);
235 require_action(dstpool
, finish
, rval
=KERN_FAILURE
);
237 /* Copy the data from the source pools to the single destination pool. */
238 STAILQ_FOREACH(srcpool
, &srcarray
->pools
, entries
) {
239 copysize
= srcpool
->nitems
* srcarray
->itemsize
;
240 memcpy(dstpool
->buffer
+ offset
, srcpool
->buffer
, copysize
);
249 /*******************************************************************************
250 *******************************************************************************/
252 kxld_array_reset(KXLDArray
*array
)
254 KXLDArrayPool
*pool
= NULL
;
257 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
264 /*******************************************************************************
265 *******************************************************************************/
267 kxld_array_clear(KXLDArray
*array
)
269 KXLDArrayPool
*pool
= NULL
;
272 kxld_array_reset(array
);
273 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
274 bzero(pool
->buffer
, array
->pool_capacity
);
279 /*******************************************************************************
280 *******************************************************************************/
282 kxld_array_deinit(KXLDArray
*array
)
284 KXLDArrayPool
*pool
= NULL
, *tmp
= NULL
;
287 STAILQ_FOREACH_SAFE(pool
, &array
->pools
, entries
, tmp
) {
288 STAILQ_REMOVE(&array
->pools
, pool
, kxld_array_pool
, entries
);
289 pool_destroy(pool
, array
->pool_capacity
);
291 bzero(array
, sizeof(*array
));
295 /*******************************************************************************
296 *******************************************************************************/
298 kxld_array_get_item(const KXLDArray
*array
, u_int idx
)
300 KXLDArrayPool
*pool
= NULL
;
305 if (idx
>= array
->nitems
) goto finish
;
307 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
308 if (idx
< pool
->nitems
) {
309 item
= (void *) (pool
->buffer
+ (array
->itemsize
* idx
));
313 idx
-= array
->pool_maxitems
;
320 /*******************************************************************************
321 *******************************************************************************/
323 kxld_array_get_slot(const KXLDArray
*array
, u_int idx
)
325 KXLDArrayPool
*pool
= NULL
;
330 if (idx
>= array
->maxitems
) goto finish
;
332 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
333 if (idx
< array
->pool_maxitems
) {
334 item
= (void *) (pool
->buffer
+ (array
->itemsize
* idx
));
338 idx
-= array
->pool_maxitems
;
345 /*******************************************************************************
346 *******************************************************************************/
348 kxld_array_get_index(const KXLDArray
*array
, const void *item
, u_int
*_idx
)
350 kern_return_t rval
= KERN_FAILURE
;
351 KXLDArrayPool
*pool
= NULL
;
363 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
364 if (pool
->buffer
<= it
&& it
< pool
->buffer
+ array
->pool_capacity
) {
365 diff
= it
- pool
->buffer
;
366 idx
= (u_int
) (diff
/ array
->itemsize
);
375 base_idx
+= array
->pool_maxitems
;
383 /*******************************************************************************
384 *******************************************************************************/
386 kxld_array_resize(KXLDArray
*array
, u_int nitems
)
388 kern_return_t rval
= KERN_FAILURE
;
389 KXLDArrayPool
*pool
= NULL
;
391 /* Grow the list of pools until we have enough to fit all of the entries */
393 while (nitems
> array
->maxitems
) {
394 pool
= pool_create(array
->pool_capacity
);
395 require_action(pool
, finish
, rval
=KERN_FAILURE
);
397 STAILQ_INSERT_TAIL(&array
->pools
, pool
, entries
);
399 array
->maxitems
+= array
->pool_maxitems
;
403 nitems
= reinit_pools(array
, nitems
);
404 require_action(nitems
== 0, finish
, rval
=KERN_FAILURE
);
411 /*******************************************************************************
412 * Sets the number of items for the array and each pool. Returns zero if there
413 * is enough space for all items, and the number of additional items needed
414 * if there is not enough space.
415 *******************************************************************************/
417 reinit_pools(KXLDArray
*array
, u_int nitems
)
419 KXLDArrayPool
*pool
= NULL
;
420 u_int pool_nitems
= 0;
422 /* Set the number of items for each pool */
424 pool_nitems
= nitems
;
425 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
426 if (pool_nitems
> array
->pool_maxitems
) {
427 pool
->nitems
= array
->pool_maxitems
;
428 pool_nitems
-= array
->pool_maxitems
;
430 pool
->nitems
= pool_nitems
;
434 array
->nitems
= nitems
;
439 /*******************************************************************************
440 *******************************************************************************/
442 kxld_array_remove(KXLDArray
*array
, u_int idx
)
444 kern_return_t rval
= KERN_FAILURE
;
445 KXLDArrayPool
*pool
= NULL
;
452 if (idx
>= array
->nitems
) {
457 /* We only support removing an item if all the items are contained in a
458 * single pool (for now).
460 require_action(array
->npools
< 2 || array
->nitems
< array
->pool_maxitems
,
461 finish
, rval
=KERN_NOT_SUPPORTED
);
463 pool
= STAILQ_FIRST(&array
->pools
);
464 require_action(pool
, finish
, rval
=KERN_FAILURE
);
467 dst
+= idx
* array
->itemsize
;
470 src
+= ((idx
+ 1) * array
->itemsize
);
472 nitems
= pool
->nitems
- idx
- 1;
473 memmove(dst
, src
, array
->itemsize
* nitems
);
479 dst
+= pool
->nitems
* array
->itemsize
;
480 bzero(dst
, array
->itemsize
);