xref: /netbsd-src/common/lib/libprop/prop_array.c (revision cac8e449158efc7261bebc8657cbb0125a2cfdde)
1 /*	$NetBSD: prop_array.c,v 1.19 2008/08/03 04:00:12 thorpej 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/prop_array.h>
33 #include "prop_object_impl.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 
273 	pa = _PROP_POOL_GET(_prop_array_pool);
274 	if (pa != NULL) {
275 		_prop_object_init(&pa->pa_obj, &_prop_object_type_array);
276 		pa->pa_obj.po_type = &_prop_object_type_array;
277 
278 		_PROP_RWLOCK_INIT(pa->pa_rwlock);
279 		pa->pa_array = array;
280 		pa->pa_capacity = capacity;
281 		pa->pa_count = 0;
282 		pa->pa_flags = 0;
283 
284 		pa->pa_version = 0;
285 	} else if (array != NULL)
286 		_PROP_FREE(array, M_PROP_ARRAY);
287 
288 	return (pa);
289 }
290 
291 static bool
292 _prop_array_expand(prop_array_t pa, unsigned int capacity)
293 {
294 	prop_object_t *array, *oarray;
295 
296 	/*
297 	 * Array must be WRITE-LOCKED.
298 	 */
299 
300 	oarray = pa->pa_array;
301 
302 	array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY);
303 	if (array == NULL)
304 		return (false);
305 	if (oarray != NULL)
306 		memcpy(array, oarray, pa->pa_capacity * sizeof(*array));
307 	pa->pa_array = array;
308 	pa->pa_capacity = capacity;
309 
310 	if (oarray != NULL)
311 		_PROP_FREE(oarray, M_PROP_ARRAY);
312 
313 	return (true);
314 }
315 
316 static prop_object_t
317 _prop_array_iterator_next_object_locked(void *v)
318 {
319 	struct _prop_array_iterator *pai = v;
320 	prop_array_t pa = pai->pai_base.pi_obj;
321 	prop_object_t po = NULL;
322 
323 	_PROP_ASSERT(prop_object_is_array(pa));
324 
325 	if (pa->pa_version != pai->pai_base.pi_version)
326 		goto out;	/* array changed during iteration */
327 
328 	_PROP_ASSERT(pai->pai_index <= pa->pa_count);
329 
330 	if (pai->pai_index == pa->pa_count)
331 		goto out;	/* we've iterated all objects */
332 
333 	po = pa->pa_array[pai->pai_index];
334 	pai->pai_index++;
335 
336  out:
337 	return (po);
338 }
339 
340 static prop_object_t
341 _prop_array_iterator_next_object(void *v)
342 {
343 	struct _prop_array_iterator *pai = v;
344 	prop_array_t pa __unused = pai->pai_base.pi_obj;
345 	prop_object_t po;
346 
347 	_PROP_ASSERT(prop_object_is_array(pa));
348 
349 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
350 	po = _prop_array_iterator_next_object_locked(pai);
351 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
352 	return (po);
353 }
354 
355 static void
356 _prop_array_iterator_reset_locked(void *v)
357 {
358 	struct _prop_array_iterator *pai = v;
359 	prop_array_t pa = pai->pai_base.pi_obj;
360 
361 	_PROP_ASSERT(prop_object_is_array(pa));
362 
363 	pai->pai_index = 0;
364 	pai->pai_base.pi_version = pa->pa_version;
365 }
366 
367 static void
368 _prop_array_iterator_reset(void *v)
369 {
370 	struct _prop_array_iterator *pai = v;
371 	prop_array_t pa __unused = pai->pai_base.pi_obj;
372 
373 	_PROP_ASSERT(prop_object_is_array(pa));
374 
375 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
376 	_prop_array_iterator_reset_locked(pai);
377 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
378 }
379 
380 /*
381  * prop_array_create --
382  *	Create an empty array.
383  */
384 prop_array_t
385 prop_array_create(void)
386 {
387 
388 	return (_prop_array_alloc(0));
389 }
390 
391 /*
392  * prop_array_create_with_capacity --
393  *	Create an array with the capacity to store N objects.
394  */
395 prop_array_t
396 prop_array_create_with_capacity(unsigned int capacity)
397 {
398 
399 	return (_prop_array_alloc(capacity));
400 }
401 
402 /*
403  * prop_array_copy --
404  *	Copy an array.  The new array has an initial capacity equal to
405  *	the number of objects stored in the original array.  The new
406  *	array contains references to the original array's objects, not
407  *	copies of those objects (i.e. a shallow copy).
408  */
409 prop_array_t
410 prop_array_copy(prop_array_t opa)
411 {
412 	prop_array_t pa;
413 	prop_object_t po;
414 	unsigned int idx;
415 
416 	if (! prop_object_is_array(opa))
417 		return (NULL);
418 
419 	_PROP_RWLOCK_RDLOCK(opa->pa_rwlock);
420 
421 	pa = _prop_array_alloc(opa->pa_count);
422 	if (pa != NULL) {
423 		for (idx = 0; idx < opa->pa_count; idx++) {
424 			po = opa->pa_array[idx];
425 			prop_object_retain(po);
426 			pa->pa_array[idx] = po;
427 		}
428 		pa->pa_count = opa->pa_count;
429 		pa->pa_flags = opa->pa_flags;
430 	}
431 	_PROP_RWLOCK_UNLOCK(opa->pa_rwlock);
432 	return (pa);
433 }
434 
435 /*
436  * prop_array_copy_mutable --
437  *	Like prop_array_copy(), but the resulting array is mutable.
438  */
439 prop_array_t
440 prop_array_copy_mutable(prop_array_t opa)
441 {
442 	prop_array_t pa;
443 
444 	pa = prop_array_copy(opa);
445 	if (pa != NULL)
446 		pa->pa_flags &= ~PA_F_IMMUTABLE;
447 
448 	return (pa);
449 }
450 
451 /*
452  * prop_array_capacity --
453  *	Return the capacity of the array.
454  */
455 unsigned int
456 prop_array_capacity(prop_array_t pa)
457 {
458 	unsigned int rv;
459 
460 	if (! prop_object_is_array(pa))
461 		return (0);
462 
463 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
464 	rv = pa->pa_capacity;
465 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
466 
467 	return (rv);
468 }
469 
470 /*
471  * prop_array_count --
472  *	Return the number of objects stored in the array.
473  */
474 unsigned int
475 prop_array_count(prop_array_t pa)
476 {
477 	unsigned int rv;
478 
479 	if (! prop_object_is_array(pa))
480 		return (0);
481 
482 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
483 	rv = pa->pa_count;
484 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
485 
486 	return (rv);
487 }
488 
489 /*
490  * prop_array_ensure_capacity --
491  *	Ensure that the array has the capacity to store the specified
492  *	total number of objects (inluding the objects already stored
493  *	in the array).
494  */
495 bool
496 prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity)
497 {
498 	bool rv;
499 
500 	if (! prop_object_is_array(pa))
501 		return (false);
502 
503 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
504 	if (capacity > pa->pa_capacity)
505 		rv = _prop_array_expand(pa, capacity);
506 	else
507 		rv = true;
508 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
509 
510 	return (rv);
511 }
512 
513 static prop_object_iterator_t
514 _prop_array_iterator_locked(prop_array_t pa)
515 {
516 	struct _prop_array_iterator *pai;
517 
518 	if (! prop_object_is_array(pa))
519 		return (NULL);
520 
521 	pai = _PROP_CALLOC(sizeof(*pai), M_TEMP);
522 	if (pai == NULL)
523 		return (NULL);
524 	pai->pai_base.pi_next_object = _prop_array_iterator_next_object;
525 	pai->pai_base.pi_reset = _prop_array_iterator_reset;
526 	prop_object_retain(pa);
527 	pai->pai_base.pi_obj = pa;
528 	_prop_array_iterator_reset_locked(pai);
529 
530 	return (&pai->pai_base);
531 }
532 
533 /*
534  * prop_array_iterator --
535  *	Return an iterator for the array.  The array is retained by
536  *	the iterator.
537  */
538 prop_object_iterator_t
539 prop_array_iterator(prop_array_t pa)
540 {
541 	prop_object_iterator_t pi;
542 
543 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
544 	pi = _prop_array_iterator_locked(pa);
545 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
546 	return (pi);
547 }
548 
549 /*
550  * prop_array_make_immutable --
551  *	Make the array immutable.
552  */
553 void
554 prop_array_make_immutable(prop_array_t pa)
555 {
556 
557 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
558 	if (prop_array_is_immutable(pa) == false)
559 		pa->pa_flags |= PA_F_IMMUTABLE;
560 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
561 }
562 
563 /*
564  * prop_array_mutable --
565  *	Returns true if the array is mutable.
566  */
567 bool
568 prop_array_mutable(prop_array_t pa)
569 {
570 	bool rv;
571 
572 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
573 	rv = prop_array_is_immutable(pa) == false;
574 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
575 
576 	return (rv);
577 }
578 
579 /*
580  * prop_array_get --
581  *	Return the object stored at the specified array index.
582  */
583 prop_object_t
584 prop_array_get(prop_array_t pa, unsigned int idx)
585 {
586 	prop_object_t po = NULL;
587 
588 	if (! prop_object_is_array(pa))
589 		return (NULL);
590 
591 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
592 	if (idx >= pa->pa_count)
593 		goto out;
594 	po = pa->pa_array[idx];
595 	_PROP_ASSERT(po != NULL);
596  out:
597 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
598 	return (po);
599 }
600 
601 static bool
602 _prop_array_add(prop_array_t pa, prop_object_t po)
603 {
604 
605 	/*
606 	 * Array must be WRITE-LOCKED.
607 	 */
608 
609 	_PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
610 
611 	if (prop_array_is_immutable(pa) ||
612 	    (pa->pa_count == pa->pa_capacity &&
613 	    _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false))
614 		return (false);
615 
616 	prop_object_retain(po);
617 	pa->pa_array[pa->pa_count++] = po;
618 	pa->pa_version++;
619 
620 	return (true);
621 }
622 
623 /*
624  * prop_array_set --
625  *	Store a reference to an object at the specified array index.
626  *	This method is not allowed to create holes in the array; the
627  *	caller must either be setting the object just beyond the existing
628  *	count or replacing an already existing object reference.
629  */
630 bool
631 prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po)
632 {
633 	prop_object_t opo;
634 	bool rv = false;
635 
636 	if (! prop_object_is_array(pa))
637 		return (false);
638 
639 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
640 
641 	if (prop_array_is_immutable(pa))
642 		goto out;
643 
644 	if (idx == pa->pa_count) {
645 		rv = _prop_array_add(pa, po);
646 		goto out;
647 	}
648 
649 	_PROP_ASSERT(idx < pa->pa_count);
650 
651 	opo = pa->pa_array[idx];
652 	_PROP_ASSERT(opo != NULL);
653 
654 	prop_object_retain(po);
655 	pa->pa_array[idx] = po;
656 	pa->pa_version++;
657 
658 	prop_object_release(opo);
659 
660 	rv = true;
661 
662  out:
663 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
664 	return (rv);
665 }
666 
667 /*
668  * prop_array_add --
669  *	Add a refrerence to an object to the specified array, appending
670  *	to the end and growing the array's capacity, if necessary.
671  */
672 bool
673 prop_array_add(prop_array_t pa, prop_object_t po)
674 {
675 	bool rv;
676 
677 	if (! prop_object_is_array(pa))
678 		return (false);
679 
680 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
681 	rv = _prop_array_add(pa, po);
682 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
683 
684 	return (rv);
685 }
686 
687 /*
688  * prop_array_remove --
689  *	Remove the reference to an object from an array at the specified
690  *	index.  The array will be compacted following the removal.
691  */
692 void
693 prop_array_remove(prop_array_t pa, unsigned int idx)
694 {
695 	prop_object_t po;
696 
697 	if (! prop_object_is_array(pa))
698 		return;
699 
700 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
701 
702 	_PROP_ASSERT(idx < pa->pa_count);
703 
704 	/* XXX Should this be a _PROP_ASSERT()? */
705 	if (prop_array_is_immutable(pa)) {
706 		_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
707 		return;
708 	}
709 
710 	po = pa->pa_array[idx];
711 	_PROP_ASSERT(po != NULL);
712 
713 	for (++idx; idx < pa->pa_count; idx++)
714 		pa->pa_array[idx - 1] = pa->pa_array[idx];
715 	pa->pa_count--;
716 	pa->pa_version++;
717 
718 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
719 
720 	prop_object_release(po);
721 }
722 
723 /*
724  * prop_array_equals --
725  *	Return true if the two arrays are equivalent.  Note we do a
726  *	by-value comparison of the objects in the array.
727  */
728 bool
729 prop_array_equals(prop_array_t array1, prop_array_t array2)
730 {
731 	if (!prop_object_is_array(array1) || !prop_object_is_array(array2))
732 		return (false);
733 
734 	return (prop_object_equals(array1, array2));
735 }
736 
737 /*
738  * prop_array_externalize --
739  *	Externalize an array, return a NUL-terminated buffer
740  *	containing the XML-style representation.  The buffer is allocated
741  * 	with the M_TEMP memory type.
742  */
743 char *
744 prop_array_externalize(prop_array_t pa)
745 {
746 	struct _prop_object_externalize_context *ctx;
747 	char *cp;
748 
749 	ctx = _prop_object_externalize_context_alloc();
750 	if (ctx == NULL)
751 		return (NULL);
752 
753 	if (_prop_object_externalize_header(ctx) == false ||
754 	    (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false ||
755 	    _prop_object_externalize_footer(ctx) == false) {
756 		/* We are responsible for releasing the buffer. */
757 		_PROP_FREE(ctx->poec_buf, M_TEMP);
758 		_prop_object_externalize_context_free(ctx);
759 		return (NULL);
760 	}
761 
762 	cp = ctx->poec_buf;
763 	_prop_object_externalize_context_free(ctx);
764 
765 	return (cp);
766 }
767 
768 /*
769  * _prop_array_internalize --
770  *	Parse an <array>...</array> and return the object created from the
771  *	external representation.
772  */
773 static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *,
774     struct _prop_object_internalize_context *);
775 
776 bool
777 _prop_array_internalize(prop_stack_t stack, prop_object_t *obj,
778     struct _prop_object_internalize_context *ctx)
779 {
780 	/* We don't currently understand any attributes. */
781 	if (ctx->poic_tagattr != NULL)
782 		return (true);
783 
784 	*obj = prop_array_create();
785 	/*
786 	 * We are done if the create failed or no child elements exist.
787 	 */
788 	if (*obj == NULL || ctx->poic_is_empty_element)
789 		return (true);
790 
791 	/*
792 	 * Opening tag is found, now continue to the first element.
793 	 */
794 	return (_prop_array_internalize_body(stack, obj, ctx));
795 }
796 
797 static bool
798 _prop_array_internalize_continue(prop_stack_t stack,
799     prop_object_t *obj,
800     struct _prop_object_internalize_context *ctx,
801     void *data, prop_object_t child)
802 {
803 	prop_array_t array;
804 
805 	_PROP_ASSERT(data == NULL);
806 
807 	if (child == NULL)
808 		goto bad; /* Element could not be parsed. */
809 
810 	array = *obj;
811 
812 	if (prop_array_add(array, child) == false) {
813 		prop_object_release(child);
814 		goto bad;
815 	}
816 	prop_object_release(child);
817 
818 	/*
819 	 * Current element is processed and added, look for next.
820 	 */
821 	return (_prop_array_internalize_body(stack, obj, ctx));
822 
823  bad:
824 	prop_object_release(*obj);
825 	*obj = NULL;
826 	return (true);
827 }
828 
829 static bool
830 _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj,
831     struct _prop_object_internalize_context *ctx)
832 {
833 	prop_array_t array = *obj;
834 
835 	_PROP_ASSERT(array != NULL);
836 
837 	/* Fetch the next tag. */
838 	if (_prop_object_internalize_find_tag(ctx, NULL,
839 				_PROP_TAG_TYPE_EITHER) == false)
840 		goto bad;
841 
842 	/* Check to see if this is the end of the array. */
843 	if (_PROP_TAG_MATCH(ctx, "array") &&
844 	    ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
845 		/* It is, so don't iterate any further. */
846 		return (true);
847 	}
848 
849 	if (_prop_stack_push(stack, array,
850 			     _prop_array_internalize_continue, NULL, NULL))
851 		return (false);
852 
853  bad:
854 	prop_object_release(array);
855 	*obj = NULL;
856 	return (true);
857 }
858 
859 /*
860  * prop_array_internalize --
861  *	Create an array by parsing the XML-style representation.
862  */
863 prop_array_t
864 prop_array_internalize(const char *xml)
865 {
866 	return _prop_generic_internalize(xml, "array");
867 }
868 
869 #if !defined(_KERNEL) && !defined(_STANDALONE)
870 /*
871  * prop_array_externalize_to_file --
872  *	Externalize an array to the specified file.
873  */
874 bool
875 prop_array_externalize_to_file(prop_array_t array, const char *fname)
876 {
877 	char *xml;
878 	bool rv;
879 	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
880 
881 	xml = prop_array_externalize(array);
882 	if (xml == NULL)
883 		return (false);
884 	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
885 	if (rv == false)
886 		save_errno = errno;
887 	_PROP_FREE(xml, M_TEMP);
888 	if (rv == false)
889 		errno = save_errno;
890 
891 	return (rv);
892 }
893 
894 /*
895  * prop_array_internalize_from_file --
896  *	Internalize an array from a file.
897  */
898 prop_array_t
899 prop_array_internalize_from_file(const char *fname)
900 {
901 	struct _prop_object_internalize_mapped_file *mf;
902 	prop_array_t array;
903 
904 	mf = _prop_object_internalize_map_file(fname);
905 	if (mf == NULL)
906 		return (NULL);
907 	array = prop_array_internalize(mf->poimf_xml);
908 	_prop_object_internalize_unmap_file(mf);
909 
910 	return (array);
911 }
912 #endif /* _KERNEL && !_STANDALONE */
913