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