1 | /* $NetBSD: prop_array.c,v 1.21 2012/07/27 09:10:59 pooka Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include "prop_object_impl.h" |
33 | #include <prop/prop_array.h> |
34 | |
35 | #if !defined(_KERNEL) && !defined(_STANDALONE) |
36 | #include <errno.h> |
37 | #endif |
38 | |
39 | struct _prop_array { |
40 | struct _prop_object pa_obj; |
41 | _PROP_RWLOCK_DECL(pa_rwlock) |
42 | prop_object_t * pa_array; |
43 | unsigned int pa_capacity; |
44 | unsigned int pa_count; |
45 | int pa_flags; |
46 | |
47 | uint32_t pa_version; |
48 | }; |
49 | |
50 | #define PA_F_IMMUTABLE 0x01 /* array is immutable */ |
51 | |
52 | _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay" ) |
53 | _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array" , |
54 | "property array container object" ) |
55 | |
56 | static _prop_object_free_rv_t |
57 | _prop_array_free(prop_stack_t, prop_object_t *); |
58 | static void _prop_array_emergency_free(prop_object_t); |
59 | static bool _prop_array_externalize( |
60 | struct _prop_object_externalize_context *, |
61 | void *); |
62 | static _prop_object_equals_rv_t |
63 | _prop_array_equals(prop_object_t, prop_object_t, |
64 | void **, void **, |
65 | prop_object_t *, prop_object_t *); |
66 | static void _prop_array_equals_finish(prop_object_t, prop_object_t); |
67 | static prop_object_iterator_t |
68 | _prop_array_iterator_locked(prop_array_t); |
69 | static prop_object_t |
70 | _prop_array_iterator_next_object_locked(void *); |
71 | static void _prop_array_iterator_reset_locked(void *); |
72 | |
73 | static const struct _prop_object_type _prop_object_type_array = { |
74 | .pot_type = PROP_TYPE_ARRAY, |
75 | .pot_free = _prop_array_free, |
76 | .pot_emergency_free = _prop_array_emergency_free, |
77 | .pot_extern = _prop_array_externalize, |
78 | .pot_equals = _prop_array_equals, |
79 | .pot_equals_finish = _prop_array_equals_finish, |
80 | }; |
81 | |
82 | #define prop_object_is_array(x) \ |
83 | ((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array) |
84 | |
85 | #define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0) |
86 | |
87 | struct _prop_array_iterator { |
88 | struct _prop_object_iterator pai_base; |
89 | unsigned int pai_index; |
90 | }; |
91 | |
92 | #define EXPAND_STEP 16 |
93 | |
94 | static _prop_object_free_rv_t |
95 | _prop_array_free(prop_stack_t stack, prop_object_t *obj) |
96 | { |
97 | prop_array_t pa = *obj; |
98 | prop_object_t po; |
99 | |
100 | _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); |
101 | _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) || |
102 | (pa->pa_capacity != 0 && pa->pa_array != NULL)); |
103 | |
104 | /* The easy case is an empty array, just free and return. */ |
105 | if (pa->pa_count == 0) { |
106 | if (pa->pa_array != NULL) |
107 | _PROP_FREE(pa->pa_array, M_PROP_ARRAY); |
108 | |
109 | _PROP_RWLOCK_DESTROY(pa->pa_rwlock); |
110 | |
111 | _PROP_POOL_PUT(_prop_array_pool, pa); |
112 | |
113 | return (_PROP_OBJECT_FREE_DONE); |
114 | } |
115 | |
116 | po = pa->pa_array[pa->pa_count - 1]; |
117 | _PROP_ASSERT(po != NULL); |
118 | |
119 | if (stack == NULL) { |
120 | /* |
121 | * If we are in emergency release mode, |
122 | * just let caller recurse down. |
123 | */ |
124 | *obj = po; |
125 | return (_PROP_OBJECT_FREE_FAILED); |
126 | } |
127 | |
128 | /* Otherwise, try to push the current object on the stack. */ |
129 | if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) { |
130 | /* Push failed, entering emergency release mode. */ |
131 | return (_PROP_OBJECT_FREE_FAILED); |
132 | } |
133 | /* Object pushed on stack, caller will release it. */ |
134 | --pa->pa_count; |
135 | *obj = po; |
136 | return (_PROP_OBJECT_FREE_RECURSE); |
137 | } |
138 | |
139 | static void |
140 | _prop_array_emergency_free(prop_object_t obj) |
141 | { |
142 | prop_array_t pa = obj; |
143 | |
144 | _PROP_ASSERT(pa->pa_count != 0); |
145 | --pa->pa_count; |
146 | } |
147 | |
148 | static bool |
149 | _prop_array_externalize(struct _prop_object_externalize_context *ctx, |
150 | void *v) |
151 | { |
152 | prop_array_t pa = v; |
153 | struct _prop_object *po; |
154 | prop_object_iterator_t pi; |
155 | unsigned int i; |
156 | bool rv = false; |
157 | |
158 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
159 | |
160 | if (pa->pa_count == 0) { |
161 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
162 | return (_prop_object_externalize_empty_tag(ctx, "array" )); |
163 | } |
164 | |
165 | /* XXXJRT Hint "count" for the internalize step? */ |
166 | if (_prop_object_externalize_start_tag(ctx, "array" ) == false || |
167 | _prop_object_externalize_append_char(ctx, '\n') == false) |
168 | goto out; |
169 | |
170 | pi = _prop_array_iterator_locked(pa); |
171 | if (pi == NULL) |
172 | goto out; |
173 | |
174 | ctx->poec_depth++; |
175 | _PROP_ASSERT(ctx->poec_depth != 0); |
176 | |
177 | while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) { |
178 | if ((*po->po_type->pot_extern)(ctx, po) == false) { |
179 | prop_object_iterator_release(pi); |
180 | goto out; |
181 | } |
182 | } |
183 | |
184 | prop_object_iterator_release(pi); |
185 | |
186 | ctx->poec_depth--; |
187 | for (i = 0; i < ctx->poec_depth; i++) { |
188 | if (_prop_object_externalize_append_char(ctx, '\t') == false) |
189 | goto out; |
190 | } |
191 | if (_prop_object_externalize_end_tag(ctx, "array" ) == false) |
192 | goto out; |
193 | |
194 | rv = true; |
195 | |
196 | out: |
197 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
198 | return (rv); |
199 | } |
200 | |
201 | /* ARGSUSED */ |
202 | static _prop_object_equals_rv_t |
203 | _prop_array_equals(prop_object_t v1, prop_object_t v2, |
204 | void **stored_pointer1, void **stored_pointer2, |
205 | prop_object_t *next_obj1, prop_object_t *next_obj2) |
206 | { |
207 | prop_array_t array1 = v1; |
208 | prop_array_t array2 = v2; |
209 | uintptr_t idx; |
210 | _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE; |
211 | |
212 | if (array1 == array2) |
213 | return (_PROP_OBJECT_EQUALS_TRUE); |
214 | |
215 | _PROP_ASSERT(*stored_pointer1 == *stored_pointer2); |
216 | idx = (uintptr_t)*stored_pointer1; |
217 | |
218 | /* For the first iteration, lock the objects. */ |
219 | if (idx == 0) { |
220 | if ((uintptr_t)array1 < (uintptr_t)array2) { |
221 | _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); |
222 | _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); |
223 | } else { |
224 | _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); |
225 | _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); |
226 | } |
227 | } |
228 | |
229 | if (array1->pa_count != array2->pa_count) |
230 | goto out; |
231 | if (idx == array1->pa_count) { |
232 | rv = _PROP_OBJECT_EQUALS_TRUE; |
233 | goto out; |
234 | } |
235 | _PROP_ASSERT(idx < array1->pa_count); |
236 | |
237 | *stored_pointer1 = (void *)(idx + 1); |
238 | *stored_pointer2 = (void *)(idx + 1); |
239 | |
240 | *next_obj1 = array1->pa_array[idx]; |
241 | *next_obj2 = array2->pa_array[idx]; |
242 | |
243 | return (_PROP_OBJECT_EQUALS_RECURSE); |
244 | |
245 | out: |
246 | _PROP_RWLOCK_UNLOCK(array1->pa_rwlock); |
247 | _PROP_RWLOCK_UNLOCK(array2->pa_rwlock); |
248 | return (rv); |
249 | } |
250 | |
251 | static void |
252 | _prop_array_equals_finish(prop_object_t v1, prop_object_t v2) |
253 | { |
254 | _PROP_RWLOCK_UNLOCK(((prop_array_t)v1)->pa_rwlock); |
255 | _PROP_RWLOCK_UNLOCK(((prop_array_t)v2)->pa_rwlock); |
256 | } |
257 | |
258 | static prop_array_t |
259 | _prop_array_alloc(unsigned int capacity) |
260 | { |
261 | prop_array_t pa; |
262 | prop_object_t *array; |
263 | |
264 | if (capacity != 0) { |
265 | array = _PROP_CALLOC(capacity * sizeof(prop_object_t), |
266 | M_PROP_ARRAY); |
267 | if (array == NULL) |
268 | return (NULL); |
269 | } else |
270 | array = NULL; |
271 | |
272 | pa = _PROP_POOL_GET(_prop_array_pool); |
273 | if (pa != NULL) { |
274 | _prop_object_init(&pa->pa_obj, &_prop_object_type_array); |
275 | pa->pa_obj.po_type = &_prop_object_type_array; |
276 | |
277 | _PROP_RWLOCK_INIT(pa->pa_rwlock); |
278 | pa->pa_array = array; |
279 | pa->pa_capacity = capacity; |
280 | pa->pa_count = 0; |
281 | pa->pa_flags = 0; |
282 | |
283 | pa->pa_version = 0; |
284 | } else if (array != NULL) |
285 | _PROP_FREE(array, M_PROP_ARRAY); |
286 | |
287 | return (pa); |
288 | } |
289 | |
290 | static bool |
291 | _prop_array_expand(prop_array_t pa, unsigned int capacity) |
292 | { |
293 | prop_object_t *array, *oarray; |
294 | |
295 | /* |
296 | * Array must be WRITE-LOCKED. |
297 | */ |
298 | |
299 | oarray = pa->pa_array; |
300 | |
301 | array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY); |
302 | if (array == NULL) |
303 | return (false); |
304 | if (oarray != NULL) |
305 | memcpy(array, oarray, pa->pa_capacity * sizeof(*array)); |
306 | pa->pa_array = array; |
307 | pa->pa_capacity = capacity; |
308 | |
309 | if (oarray != NULL) |
310 | _PROP_FREE(oarray, M_PROP_ARRAY); |
311 | |
312 | return (true); |
313 | } |
314 | |
315 | static prop_object_t |
316 | _prop_array_iterator_next_object_locked(void *v) |
317 | { |
318 | struct _prop_array_iterator *pai = v; |
319 | prop_array_t pa = pai->pai_base.pi_obj; |
320 | prop_object_t po = NULL; |
321 | |
322 | _PROP_ASSERT(prop_object_is_array(pa)); |
323 | |
324 | if (pa->pa_version != pai->pai_base.pi_version) |
325 | goto out; /* array changed during iteration */ |
326 | |
327 | _PROP_ASSERT(pai->pai_index <= pa->pa_count); |
328 | |
329 | if (pai->pai_index == pa->pa_count) |
330 | goto out; /* we've iterated all objects */ |
331 | |
332 | po = pa->pa_array[pai->pai_index]; |
333 | pai->pai_index++; |
334 | |
335 | out: |
336 | return (po); |
337 | } |
338 | |
339 | static prop_object_t |
340 | _prop_array_iterator_next_object(void *v) |
341 | { |
342 | struct _prop_array_iterator *pai = v; |
343 | prop_array_t pa _PROP_ARG_UNUSED = pai->pai_base.pi_obj; |
344 | prop_object_t po; |
345 | |
346 | _PROP_ASSERT(prop_object_is_array(pa)); |
347 | |
348 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
349 | po = _prop_array_iterator_next_object_locked(pai); |
350 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
351 | return (po); |
352 | } |
353 | |
354 | static void |
355 | _prop_array_iterator_reset_locked(void *v) |
356 | { |
357 | struct _prop_array_iterator *pai = v; |
358 | prop_array_t pa = pai->pai_base.pi_obj; |
359 | |
360 | _PROP_ASSERT(prop_object_is_array(pa)); |
361 | |
362 | pai->pai_index = 0; |
363 | pai->pai_base.pi_version = pa->pa_version; |
364 | } |
365 | |
366 | static void |
367 | _prop_array_iterator_reset(void *v) |
368 | { |
369 | struct _prop_array_iterator *pai = v; |
370 | prop_array_t pa _PROP_ARG_UNUSED = pai->pai_base.pi_obj; |
371 | |
372 | _PROP_ASSERT(prop_object_is_array(pa)); |
373 | |
374 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
375 | _prop_array_iterator_reset_locked(pai); |
376 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
377 | } |
378 | |
379 | /* |
380 | * prop_array_create -- |
381 | * Create an empty array. |
382 | */ |
383 | prop_array_t |
384 | prop_array_create(void) |
385 | { |
386 | |
387 | return (_prop_array_alloc(0)); |
388 | } |
389 | |
390 | /* |
391 | * prop_array_create_with_capacity -- |
392 | * Create an array with the capacity to store N objects. |
393 | */ |
394 | prop_array_t |
395 | prop_array_create_with_capacity(unsigned int capacity) |
396 | { |
397 | |
398 | return (_prop_array_alloc(capacity)); |
399 | } |
400 | |
401 | /* |
402 | * prop_array_copy -- |
403 | * Copy an array. The new array has an initial capacity equal to |
404 | * the number of objects stored in the original array. The new |
405 | * array contains references to the original array's objects, not |
406 | * copies of those objects (i.e. a shallow copy). |
407 | */ |
408 | prop_array_t |
409 | prop_array_copy(prop_array_t opa) |
410 | { |
411 | prop_array_t pa; |
412 | prop_object_t po; |
413 | unsigned int idx; |
414 | |
415 | if (! prop_object_is_array(opa)) |
416 | return (NULL); |
417 | |
418 | _PROP_RWLOCK_RDLOCK(opa->pa_rwlock); |
419 | |
420 | pa = _prop_array_alloc(opa->pa_count); |
421 | if (pa != NULL) { |
422 | for (idx = 0; idx < opa->pa_count; idx++) { |
423 | po = opa->pa_array[idx]; |
424 | prop_object_retain(po); |
425 | pa->pa_array[idx] = po; |
426 | } |
427 | pa->pa_count = opa->pa_count; |
428 | pa->pa_flags = opa->pa_flags; |
429 | } |
430 | _PROP_RWLOCK_UNLOCK(opa->pa_rwlock); |
431 | return (pa); |
432 | } |
433 | |
434 | /* |
435 | * prop_array_copy_mutable -- |
436 | * Like prop_array_copy(), but the resulting array is mutable. |
437 | */ |
438 | prop_array_t |
439 | prop_array_copy_mutable(prop_array_t opa) |
440 | { |
441 | prop_array_t pa; |
442 | |
443 | pa = prop_array_copy(opa); |
444 | if (pa != NULL) |
445 | pa->pa_flags &= ~PA_F_IMMUTABLE; |
446 | |
447 | return (pa); |
448 | } |
449 | |
450 | /* |
451 | * prop_array_capacity -- |
452 | * Return the capacity of the array. |
453 | */ |
454 | unsigned int |
455 | prop_array_capacity(prop_array_t pa) |
456 | { |
457 | unsigned int rv; |
458 | |
459 | if (! prop_object_is_array(pa)) |
460 | return (0); |
461 | |
462 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
463 | rv = pa->pa_capacity; |
464 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
465 | |
466 | return (rv); |
467 | } |
468 | |
469 | /* |
470 | * prop_array_count -- |
471 | * Return the number of objects stored in the array. |
472 | */ |
473 | unsigned int |
474 | prop_array_count(prop_array_t pa) |
475 | { |
476 | unsigned int rv; |
477 | |
478 | if (! prop_object_is_array(pa)) |
479 | return (0); |
480 | |
481 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
482 | rv = pa->pa_count; |
483 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
484 | |
485 | return (rv); |
486 | } |
487 | |
488 | /* |
489 | * prop_array_ensure_capacity -- |
490 | * Ensure that the array has the capacity to store the specified |
491 | * total number of objects (inluding the objects already stored |
492 | * in the array). |
493 | */ |
494 | bool |
495 | prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) |
496 | { |
497 | bool rv; |
498 | |
499 | if (! prop_object_is_array(pa)) |
500 | return (false); |
501 | |
502 | _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); |
503 | if (capacity > pa->pa_capacity) |
504 | rv = _prop_array_expand(pa, capacity); |
505 | else |
506 | rv = true; |
507 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
508 | |
509 | return (rv); |
510 | } |
511 | |
512 | static prop_object_iterator_t |
513 | _prop_array_iterator_locked(prop_array_t pa) |
514 | { |
515 | struct _prop_array_iterator *pai; |
516 | |
517 | if (! prop_object_is_array(pa)) |
518 | return (NULL); |
519 | |
520 | pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); |
521 | if (pai == NULL) |
522 | return (NULL); |
523 | pai->pai_base.pi_next_object = _prop_array_iterator_next_object; |
524 | pai->pai_base.pi_reset = _prop_array_iterator_reset; |
525 | prop_object_retain(pa); |
526 | pai->pai_base.pi_obj = pa; |
527 | _prop_array_iterator_reset_locked(pai); |
528 | |
529 | return (&pai->pai_base); |
530 | } |
531 | |
532 | /* |
533 | * prop_array_iterator -- |
534 | * Return an iterator for the array. The array is retained by |
535 | * the iterator. |
536 | */ |
537 | prop_object_iterator_t |
538 | prop_array_iterator(prop_array_t pa) |
539 | { |
540 | prop_object_iterator_t pi; |
541 | |
542 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
543 | pi = _prop_array_iterator_locked(pa); |
544 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
545 | return (pi); |
546 | } |
547 | |
548 | /* |
549 | * prop_array_make_immutable -- |
550 | * Make the array immutable. |
551 | */ |
552 | void |
553 | prop_array_make_immutable(prop_array_t pa) |
554 | { |
555 | |
556 | _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); |
557 | if (prop_array_is_immutable(pa) == false) |
558 | pa->pa_flags |= PA_F_IMMUTABLE; |
559 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
560 | } |
561 | |
562 | /* |
563 | * prop_array_mutable -- |
564 | * Returns true if the array is mutable. |
565 | */ |
566 | bool |
567 | prop_array_mutable(prop_array_t pa) |
568 | { |
569 | bool rv; |
570 | |
571 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
572 | rv = prop_array_is_immutable(pa) == false; |
573 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
574 | |
575 | return (rv); |
576 | } |
577 | |
578 | /* |
579 | * prop_array_get -- |
580 | * Return the object stored at the specified array index. |
581 | */ |
582 | prop_object_t |
583 | prop_array_get(prop_array_t pa, unsigned int idx) |
584 | { |
585 | prop_object_t po = NULL; |
586 | |
587 | if (! prop_object_is_array(pa)) |
588 | return (NULL); |
589 | |
590 | _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); |
591 | if (idx >= pa->pa_count) |
592 | goto out; |
593 | po = pa->pa_array[idx]; |
594 | _PROP_ASSERT(po != NULL); |
595 | out: |
596 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
597 | return (po); |
598 | } |
599 | |
600 | static bool |
601 | _prop_array_add(prop_array_t pa, prop_object_t po) |
602 | { |
603 | |
604 | /* |
605 | * Array must be WRITE-LOCKED. |
606 | */ |
607 | |
608 | _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); |
609 | |
610 | if (prop_array_is_immutable(pa) || |
611 | (pa->pa_count == pa->pa_capacity && |
612 | _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) |
613 | return (false); |
614 | |
615 | prop_object_retain(po); |
616 | pa->pa_array[pa->pa_count++] = po; |
617 | pa->pa_version++; |
618 | |
619 | return (true); |
620 | } |
621 | |
622 | /* |
623 | * prop_array_set -- |
624 | * Store a reference to an object at the specified array index. |
625 | * This method is not allowed to create holes in the array; the |
626 | * caller must either be setting the object just beyond the existing |
627 | * count or replacing an already existing object reference. |
628 | */ |
629 | bool |
630 | prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) |
631 | { |
632 | prop_object_t opo; |
633 | bool rv = false; |
634 | |
635 | if (! prop_object_is_array(pa)) |
636 | return (false); |
637 | |
638 | _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); |
639 | |
640 | if (prop_array_is_immutable(pa)) |
641 | goto out; |
642 | |
643 | if (idx == pa->pa_count) { |
644 | rv = _prop_array_add(pa, po); |
645 | goto out; |
646 | } |
647 | |
648 | _PROP_ASSERT(idx < pa->pa_count); |
649 | |
650 | opo = pa->pa_array[idx]; |
651 | _PROP_ASSERT(opo != NULL); |
652 | |
653 | prop_object_retain(po); |
654 | pa->pa_array[idx] = po; |
655 | pa->pa_version++; |
656 | |
657 | prop_object_release(opo); |
658 | |
659 | rv = true; |
660 | |
661 | out: |
662 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
663 | return (rv); |
664 | } |
665 | |
666 | /* |
667 | * prop_array_add -- |
668 | * Add a reference to an object to the specified array, appending |
669 | * to the end and growing the array's capacity, if necessary. |
670 | */ |
671 | bool |
672 | prop_array_add(prop_array_t pa, prop_object_t po) |
673 | { |
674 | bool rv; |
675 | |
676 | if (! prop_object_is_array(pa)) |
677 | return (false); |
678 | |
679 | _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); |
680 | rv = _prop_array_add(pa, po); |
681 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
682 | |
683 | return (rv); |
684 | } |
685 | |
686 | /* |
687 | * prop_array_remove -- |
688 | * Remove the reference to an object from an array at the specified |
689 | * index. The array will be compacted following the removal. |
690 | */ |
691 | void |
692 | prop_array_remove(prop_array_t pa, unsigned int idx) |
693 | { |
694 | prop_object_t po; |
695 | |
696 | if (! prop_object_is_array(pa)) |
697 | return; |
698 | |
699 | _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); |
700 | |
701 | _PROP_ASSERT(idx < pa->pa_count); |
702 | |
703 | /* XXX Should this be a _PROP_ASSERT()? */ |
704 | if (prop_array_is_immutable(pa)) { |
705 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
706 | return; |
707 | } |
708 | |
709 | po = pa->pa_array[idx]; |
710 | _PROP_ASSERT(po != NULL); |
711 | |
712 | for (++idx; idx < pa->pa_count; idx++) |
713 | pa->pa_array[idx - 1] = pa->pa_array[idx]; |
714 | pa->pa_count--; |
715 | pa->pa_version++; |
716 | |
717 | _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); |
718 | |
719 | prop_object_release(po); |
720 | } |
721 | |
722 | /* |
723 | * prop_array_equals -- |
724 | * Return true if the two arrays are equivalent. Note we do a |
725 | * by-value comparison of the objects in the array. |
726 | */ |
727 | bool |
728 | prop_array_equals(prop_array_t array1, prop_array_t array2) |
729 | { |
730 | if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) |
731 | return (false); |
732 | |
733 | return (prop_object_equals(array1, array2)); |
734 | } |
735 | |
736 | /* |
737 | * prop_array_externalize -- |
738 | * Externalize an array, return a NUL-terminated buffer |
739 | * containing the XML-style representation. The buffer is allocated |
740 | * with the M_TEMP memory type. |
741 | */ |
742 | char * |
743 | prop_array_externalize(prop_array_t pa) |
744 | { |
745 | struct _prop_object_externalize_context *ctx; |
746 | char *cp; |
747 | |
748 | ctx = _prop_object_externalize_context_alloc(); |
749 | if (ctx == NULL) |
750 | return (NULL); |
751 | |
752 | if (_prop_object_externalize_header(ctx) == false || |
753 | (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || |
754 | _prop_object_externalize_footer(ctx) == false) { |
755 | /* We are responsible for releasing the buffer. */ |
756 | _PROP_FREE(ctx->poec_buf, M_TEMP); |
757 | _prop_object_externalize_context_free(ctx); |
758 | return (NULL); |
759 | } |
760 | |
761 | cp = ctx->poec_buf; |
762 | _prop_object_externalize_context_free(ctx); |
763 | |
764 | return (cp); |
765 | } |
766 | |
767 | /* |
768 | * _prop_array_internalize -- |
769 | * Parse an <array>...</array> and return the object created from the |
770 | * external representation. |
771 | */ |
772 | static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *, |
773 | struct _prop_object_internalize_context *); |
774 | |
775 | bool |
776 | _prop_array_internalize(prop_stack_t stack, prop_object_t *obj, |
777 | struct _prop_object_internalize_context *ctx) |
778 | { |
779 | /* We don't currently understand any attributes. */ |
780 | if (ctx->poic_tagattr != NULL) |
781 | return (true); |
782 | |
783 | *obj = prop_array_create(); |
784 | /* |
785 | * We are done if the create failed or no child elements exist. |
786 | */ |
787 | if (*obj == NULL || ctx->poic_is_empty_element) |
788 | return (true); |
789 | |
790 | /* |
791 | * Opening tag is found, now continue to the first element. |
792 | */ |
793 | return (_prop_array_internalize_body(stack, obj, ctx)); |
794 | } |
795 | |
796 | static bool |
797 | _prop_array_internalize_continue(prop_stack_t stack, |
798 | prop_object_t *obj, |
799 | struct _prop_object_internalize_context *ctx, |
800 | void *data, prop_object_t child) |
801 | { |
802 | prop_array_t array; |
803 | |
804 | _PROP_ASSERT(data == NULL); |
805 | |
806 | if (child == NULL) |
807 | goto bad; /* Element could not be parsed. */ |
808 | |
809 | array = *obj; |
810 | |
811 | if (prop_array_add(array, child) == false) { |
812 | prop_object_release(child); |
813 | goto bad; |
814 | } |
815 | prop_object_release(child); |
816 | |
817 | /* |
818 | * Current element is processed and added, look for next. |
819 | */ |
820 | return (_prop_array_internalize_body(stack, obj, ctx)); |
821 | |
822 | bad: |
823 | prop_object_release(*obj); |
824 | *obj = NULL; |
825 | return (true); |
826 | } |
827 | |
828 | static bool |
829 | _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, |
830 | struct _prop_object_internalize_context *ctx) |
831 | { |
832 | prop_array_t array = *obj; |
833 | |
834 | _PROP_ASSERT(array != NULL); |
835 | |
836 | /* Fetch the next tag. */ |
837 | if (_prop_object_internalize_find_tag(ctx, NULL, |
838 | _PROP_TAG_TYPE_EITHER) == false) |
839 | goto bad; |
840 | |
841 | /* Check to see if this is the end of the array. */ |
842 | if (_PROP_TAG_MATCH(ctx, "array" ) && |
843 | ctx->poic_tag_type == _PROP_TAG_TYPE_END) { |
844 | /* It is, so don't iterate any further. */ |
845 | return (true); |
846 | } |
847 | |
848 | if (_prop_stack_push(stack, array, |
849 | _prop_array_internalize_continue, NULL, NULL)) |
850 | return (false); |
851 | |
852 | bad: |
853 | prop_object_release(array); |
854 | *obj = NULL; |
855 | return (true); |
856 | } |
857 | |
858 | /* |
859 | * prop_array_internalize -- |
860 | * Create an array by parsing the XML-style representation. |
861 | */ |
862 | prop_array_t |
863 | prop_array_internalize(const char *xml) |
864 | { |
865 | return _prop_generic_internalize(xml, "array" ); |
866 | } |
867 | |
868 | #if !defined(_KERNEL) && !defined(_STANDALONE) |
869 | /* |
870 | * prop_array_externalize_to_file -- |
871 | * Externalize an array to the specified file. |
872 | */ |
873 | bool |
874 | prop_array_externalize_to_file(prop_array_t array, const char *fname) |
875 | { |
876 | char *xml; |
877 | bool rv; |
878 | int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ |
879 | |
880 | xml = prop_array_externalize(array); |
881 | if (xml == NULL) |
882 | return (false); |
883 | rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); |
884 | if (rv == false) |
885 | save_errno = errno; |
886 | _PROP_FREE(xml, M_TEMP); |
887 | if (rv == false) |
888 | errno = save_errno; |
889 | |
890 | return (rv); |
891 | } |
892 | |
893 | /* |
894 | * prop_array_internalize_from_file -- |
895 | * Internalize an array from a file. |
896 | */ |
897 | prop_array_t |
898 | prop_array_internalize_from_file(const char *fname) |
899 | { |
900 | struct _prop_object_internalize_mapped_file *mf; |
901 | prop_array_t array; |
902 | |
903 | mf = _prop_object_internalize_map_file(fname); |
904 | if (mf == NULL) |
905 | return (NULL); |
906 | array = prop_array_internalize(mf->poimf_xml); |
907 | _prop_object_internalize_unmap_file(mf); |
908 | |
909 | return (array); |
910 | } |
911 | #endif /* _KERNEL && !_STANDALONE */ |
912 | |