Home | History | Annotate | Line # | Download | only in libprop
prop_array.c revision 1.21.34.2
      1 /*	$NetBSD: prop_array.c,v 1.21.34.2 2020/04/21 19:37:51 martin 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