]>
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 array
->itemsize
= itemsize
;
145 pool
= STAILQ_FIRST(&array
->pools
);
147 require_action(itemsize
* nitems
< array
->pool_capacity
,
148 finish
, rval
=KERN_FAILURE
);
149 require_action(array
->npools
== 1, finish
, rval
=KERN_FAILURE
);
150 bzero(pool
->buffer
, array
->pool_capacity
);
152 array
->pool_capacity
= round_page(array
->itemsize
* nitems
);
154 pool
= pool_create(array
->pool_capacity
);
155 require_action(pool
, finish
, rval
=KERN_RESOURCE_SHORTAGE
);
156 STAILQ_INSERT_HEAD(&array
->pools
, pool
, entries
);
158 pool
->nitems
= nitems
;
160 array
->pool_maxitems
= (u_int
) (array
->pool_capacity
/ array
->itemsize
);
161 array
->maxitems
= array
->pool_maxitems
;
162 array
->nitems
= nitems
;
170 /*******************************************************************************
171 *******************************************************************************/
172 static KXLDArrayPool
*
173 pool_create(size_t capacity
)
175 KXLDArrayPool
*pool
= NULL
, *rval
= NULL
;
177 pool
= kxld_alloc(sizeof(*pool
));
178 require(pool
, finish
);
180 pool
->buffer
= kxld_page_alloc(capacity
);
181 require(pool
->buffer
, finish
);
182 bzero(pool
->buffer
, capacity
);
188 if (pool
) pool_destroy(pool
, capacity
);
192 /*******************************************************************************
193 *******************************************************************************/
195 pool_destroy(KXLDArrayPool
*pool
, size_t capacity
)
198 if (pool
->buffer
) kxld_page_free(pool
->buffer
, capacity
);
199 kxld_free(pool
, sizeof(*pool
));
203 /*******************************************************************************
204 *******************************************************************************/
206 kxld_array_copy(KXLDArray
*dstarray
, const KXLDArray
*srcarray
)
208 kern_return_t rval
= KERN_FAILURE
;
209 KXLDArrayPool
*dstpool
= NULL
, *srcpool
= NULL
;
210 u_long needed_capacity
= 0;
211 u_long current_capacity
= 0;
218 /* When copying array, we only want to copy to an array with a single
219 * pool. If the array has more than one pool or the array is too small,
220 * we destroy the array and build it from scratch for the copy.
222 needed_capacity
= round_page(srcarray
->nitems
* srcarray
->itemsize
);
223 current_capacity
= dstarray
->npools
* dstarray
->pool_capacity
;
224 if (dstarray
->npools
> 1 || needed_capacity
> current_capacity
) {
225 kxld_array_deinit(dstarray
);
228 rval
= array_init(dstarray
, srcarray
->itemsize
, srcarray
->nitems
);
229 require_noerr(rval
, finish
);
231 dstpool
= STAILQ_FIRST(&dstarray
->pools
);
232 require_action(dstpool
, finish
, rval
=KERN_FAILURE
);
234 /* Copy the data from the source pools to the single destination pool. */
235 STAILQ_FOREACH(srcpool
, &srcarray
->pools
, entries
) {
236 copysize
= srcpool
->nitems
* srcarray
->itemsize
;
237 memcpy(dstpool
->buffer
+ offset
, srcpool
->buffer
, copysize
);
246 /*******************************************************************************
247 *******************************************************************************/
249 kxld_array_reset(KXLDArray
*array
)
251 KXLDArrayPool
*pool
= NULL
;
254 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
261 /*******************************************************************************
262 *******************************************************************************/
264 kxld_array_clear(KXLDArray
*array
)
266 KXLDArrayPool
*pool
= NULL
;
269 kxld_array_reset(array
);
270 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
271 bzero(pool
->buffer
, array
->pool_capacity
);
276 /*******************************************************************************
277 *******************************************************************************/
279 kxld_array_deinit(KXLDArray
*array
)
281 KXLDArrayPool
*pool
= NULL
, *tmp
= NULL
;
284 STAILQ_FOREACH_SAFE(pool
, &array
->pools
, entries
, tmp
) {
285 STAILQ_REMOVE(&array
->pools
, pool
, kxld_array_pool
, entries
);
286 pool_destroy(pool
, array
->pool_capacity
);
288 bzero(array
, sizeof(*array
));
292 /*******************************************************************************
293 *******************************************************************************/
295 kxld_array_get_item(const KXLDArray
*array
, u_int idx
)
297 KXLDArrayPool
*pool
= NULL
;
302 if (idx
>= array
->nitems
) goto finish
;
304 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
305 if (idx
< pool
->nitems
) {
306 item
= (void *) (pool
->buffer
+ (array
->itemsize
* idx
));
310 idx
-= array
->pool_maxitems
;
317 /*******************************************************************************
318 *******************************************************************************/
320 kxld_array_get_slot(const KXLDArray
*array
, u_int idx
)
322 KXLDArrayPool
*pool
= NULL
;
327 if (idx
>= array
->maxitems
) goto finish
;
329 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
330 if (idx
< array
->pool_maxitems
) {
331 item
= (void *) (pool
->buffer
+ (array
->itemsize
* idx
));
335 idx
-= array
->pool_maxitems
;
342 /*******************************************************************************
343 *******************************************************************************/
345 kxld_array_get_index(const KXLDArray
*array
, const void *item
, u_int
*_idx
)
347 kern_return_t rval
= KERN_FAILURE
;
348 KXLDArrayPool
*pool
= NULL
;
360 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
361 if (pool
->buffer
<= it
&& it
< pool
->buffer
+ array
->pool_capacity
) {
362 diff
= it
- pool
->buffer
;
363 idx
= (u_int
) (diff
/ array
->itemsize
);
372 base_idx
+= array
->pool_maxitems
;
380 /*******************************************************************************
381 *******************************************************************************/
383 kxld_array_resize(KXLDArray
*array
, u_int nitems
)
385 kern_return_t rval
= KERN_FAILURE
;
386 KXLDArrayPool
*pool
= NULL
;
388 /* Grow the list of pools until we have enough to fit all of the entries */
390 while (nitems
> array
->maxitems
) {
391 pool
= pool_create(array
->pool_capacity
);
392 require_action(pool
, finish
, rval
=KERN_FAILURE
);
394 STAILQ_INSERT_TAIL(&array
->pools
, pool
, entries
);
396 array
->maxitems
+= array
->pool_maxitems
;
400 nitems
= reinit_pools(array
, nitems
);
401 require_action(nitems
== 0, finish
, rval
=KERN_FAILURE
);
408 /*******************************************************************************
409 * Sets the number of items for the array and each pool. Returns zero if there
410 * is enough space for all items, and the number of additional items needed
411 * if there is not enough space.
412 *******************************************************************************/
414 reinit_pools(KXLDArray
*array
, u_int nitems
)
416 KXLDArrayPool
*pool
= NULL
;
417 u_int pool_nitems
= 0;
419 /* Set the number of items for each pool */
421 pool_nitems
= nitems
;
422 STAILQ_FOREACH(pool
, &array
->pools
, entries
) {
423 if (pool_nitems
> array
->pool_maxitems
) {
424 pool
->nitems
= array
->pool_maxitems
;
425 pool_nitems
-= array
->pool_maxitems
;
427 pool
->nitems
= pool_nitems
;
431 array
->nitems
= nitems
;
436 /*******************************************************************************
437 *******************************************************************************/
439 kxld_array_remove(KXLDArray
*array
, u_int idx
)
441 kern_return_t rval
= KERN_FAILURE
;
442 KXLDArrayPool
*pool
= NULL
;
449 if (idx
>= array
->nitems
) {
454 /* We only support removing an item if all the items are contained in a
455 * single pool (for now).
457 require_action(array
->npools
< 2 || array
->nitems
< array
->pool_maxitems
,
458 finish
, rval
=KERN_NOT_SUPPORTED
);
460 pool
= STAILQ_FIRST(&array
->pools
);
461 require_action(pool
, finish
, rval
=KERN_FAILURE
);
464 dst
+= idx
* array
->itemsize
;
467 src
+= ((idx
+ 1) * array
->itemsize
);
469 nitems
= pool
->nitems
- idx
- 1;
470 memmove(dst
, src
, array
->itemsize
* nitems
);
476 dst
+= pool
->nitems
* array
->itemsize
;
477 bzero(dst
, array
->itemsize
);