xref: /dragonfly/sys/libprop/prop_array.c (revision 1975d09e)
1 /*	$NetBSD: prop_array.c,v 1.20 2008/08/11 05:54:21 christos 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 <libprop/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 	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 = 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 = 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