xref: /netbsd-src/common/lib/libprop/prop_array.c (revision 04377267cc4233ba8f082feaf20125ed52dcd8c6)
1*04377267Sthorpej /*	$NetBSD: prop_array.c,v 1.9 2007/08/16 16:28:17 thorpej Exp $	*/
2774eb1a3Sthorpej 
3774eb1a3Sthorpej /*-
4774eb1a3Sthorpej  * Copyright (c) 2006 The NetBSD Foundation, Inc.
5774eb1a3Sthorpej  * All rights reserved.
6774eb1a3Sthorpej  *
7774eb1a3Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8774eb1a3Sthorpej  * by Jason R. Thorpe.
9774eb1a3Sthorpej  *
10774eb1a3Sthorpej  * Redistribution and use in source and binary forms, with or without
11774eb1a3Sthorpej  * modification, are permitted provided that the following conditions
12774eb1a3Sthorpej  * are met:
13774eb1a3Sthorpej  * 1. Redistributions of source code must retain the above copyright
14774eb1a3Sthorpej  *    notice, this list of conditions and the following disclaimer.
15774eb1a3Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
16774eb1a3Sthorpej  *    notice, this list of conditions and the following disclaimer in the
17774eb1a3Sthorpej  *    documentation and/or other materials provided with the distribution.
18774eb1a3Sthorpej  * 3. All advertising materials mentioning features or use of this software
19774eb1a3Sthorpej  *    must display the following acknowledgement:
20774eb1a3Sthorpej  *      This product includes software developed by the NetBSD
21774eb1a3Sthorpej  *      Foundation, Inc. and its contributors.
22774eb1a3Sthorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
23774eb1a3Sthorpej  *    contributors may be used to endorse or promote products derived
24774eb1a3Sthorpej  *    from this software without specific prior written permission.
25774eb1a3Sthorpej  *
26774eb1a3Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27774eb1a3Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28774eb1a3Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29774eb1a3Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30774eb1a3Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31774eb1a3Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32774eb1a3Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33774eb1a3Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34774eb1a3Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35774eb1a3Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36774eb1a3Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
37774eb1a3Sthorpej  */
38774eb1a3Sthorpej 
39774eb1a3Sthorpej #include <prop/prop_array.h>
40774eb1a3Sthorpej #include "prop_object_impl.h"
41774eb1a3Sthorpej 
42d21620b2Sthorpej #if !defined(_KERNEL) && !defined(_STANDALONE)
43d21620b2Sthorpej #include <errno.h>
44d21620b2Sthorpej #endif
45d21620b2Sthorpej 
46774eb1a3Sthorpej struct _prop_array {
47774eb1a3Sthorpej 	struct _prop_object	pa_obj;
48eb2acb85Sthorpej 	_PROP_RWLOCK_DECL(pa_rwlock)
49774eb1a3Sthorpej 	prop_object_t *		pa_array;
50774eb1a3Sthorpej 	unsigned int		pa_capacity;
51774eb1a3Sthorpej 	unsigned int		pa_count;
52774eb1a3Sthorpej 	int			pa_flags;
53774eb1a3Sthorpej 
54774eb1a3Sthorpej 	uint32_t		pa_version;
55774eb1a3Sthorpej };
56774eb1a3Sthorpej 
57774eb1a3Sthorpej #define	PA_F_IMMUTABLE		0x01	/* array is immutable */
58774eb1a3Sthorpej 
59774eb1a3Sthorpej _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay")
60774eb1a3Sthorpej _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array",
61774eb1a3Sthorpej 		    "property array container object")
62774eb1a3Sthorpej 
633e69f1b2Sthorpej static void		_prop_array_free(void *);
64*04377267Sthorpej static bool	_prop_array_externalize(
653e69f1b2Sthorpej 				struct _prop_object_externalize_context *,
663e69f1b2Sthorpej 				void *);
67*04377267Sthorpej static bool	_prop_array_equals(void *, void *);
683e69f1b2Sthorpej 
693e69f1b2Sthorpej static const struct _prop_object_type _prop_object_type_array = {
703e69f1b2Sthorpej 	.pot_type	=	PROP_TYPE_ARRAY,
713e69f1b2Sthorpej 	.pot_free	=	_prop_array_free,
723e69f1b2Sthorpej 	.pot_extern	=	_prop_array_externalize,
733e69f1b2Sthorpej 	.pot_equals	=	_prop_array_equals,
743e69f1b2Sthorpej };
753e69f1b2Sthorpej 
763e69f1b2Sthorpej #define	prop_object_is_array(x) 	\
77beabdd9bSthorpej 	((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array)
78774eb1a3Sthorpej 
79774eb1a3Sthorpej #define	prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0)
80774eb1a3Sthorpej 
81774eb1a3Sthorpej struct _prop_array_iterator {
82774eb1a3Sthorpej 	struct _prop_object_iterator pai_base;
83774eb1a3Sthorpej 	unsigned int		pai_index;
84774eb1a3Sthorpej };
85774eb1a3Sthorpej 
86774eb1a3Sthorpej #define	EXPAND_STEP		16
87774eb1a3Sthorpej 
88774eb1a3Sthorpej static void
89774eb1a3Sthorpej _prop_array_free(void *v)
90774eb1a3Sthorpej {
91774eb1a3Sthorpej 	prop_array_t pa = v;
92774eb1a3Sthorpej 	prop_object_t po;
93774eb1a3Sthorpej 	unsigned int idx;
94774eb1a3Sthorpej 
95774eb1a3Sthorpej 	_PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
96774eb1a3Sthorpej 	_PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) ||
97774eb1a3Sthorpej 		     (pa->pa_capacity != 0 && pa->pa_array != NULL));
98774eb1a3Sthorpej 
99774eb1a3Sthorpej 	for (idx = 0; idx < pa->pa_count; idx++) {
100774eb1a3Sthorpej 		po = pa->pa_array[idx];
101774eb1a3Sthorpej 		_PROP_ASSERT(po != NULL);
102774eb1a3Sthorpej 		prop_object_release(po);
103774eb1a3Sthorpej 	}
104774eb1a3Sthorpej 
105774eb1a3Sthorpej 	if (pa->pa_array != NULL)
106774eb1a3Sthorpej 		_PROP_FREE(pa->pa_array, M_PROP_ARRAY);
107774eb1a3Sthorpej 
108eb2acb85Sthorpej 	_PROP_RWLOCK_DESTROY(pa->pa_rwlock);
109eb2acb85Sthorpej 
110774eb1a3Sthorpej 	_PROP_POOL_PUT(_prop_array_pool, pa);
111774eb1a3Sthorpej }
112774eb1a3Sthorpej 
113*04377267Sthorpej static bool
114774eb1a3Sthorpej _prop_array_externalize(struct _prop_object_externalize_context *ctx,
115774eb1a3Sthorpej 			void *v)
116774eb1a3Sthorpej {
117774eb1a3Sthorpej 	prop_array_t pa = v;
118774eb1a3Sthorpej 	struct _prop_object *po;
119774eb1a3Sthorpej 	prop_object_iterator_t pi;
120774eb1a3Sthorpej 	unsigned int i;
121*04377267Sthorpej 	bool rv = false;
122774eb1a3Sthorpej 
123eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
124eb2acb85Sthorpej 
125eb2acb85Sthorpej 	if (pa->pa_count == 0) {
126eb2acb85Sthorpej 		_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
127774eb1a3Sthorpej 		return (_prop_object_externalize_empty_tag(ctx, "array"));
128eb2acb85Sthorpej 	}
129774eb1a3Sthorpej 
130774eb1a3Sthorpej 	/* XXXJRT Hint "count" for the internalize step? */
131*04377267Sthorpej 	if (_prop_object_externalize_start_tag(ctx, "array") == false ||
132*04377267Sthorpej 	    _prop_object_externalize_append_char(ctx, '\n') == false)
133eb2acb85Sthorpej 		goto out;
134774eb1a3Sthorpej 
135774eb1a3Sthorpej 	pi = prop_array_iterator(pa);
136774eb1a3Sthorpej 	if (pi == NULL)
137eb2acb85Sthorpej 		goto out;
138774eb1a3Sthorpej 
139774eb1a3Sthorpej 	ctx->poec_depth++;
140774eb1a3Sthorpej 	_PROP_ASSERT(ctx->poec_depth != 0);
141774eb1a3Sthorpej 
142774eb1a3Sthorpej 	while ((po = prop_object_iterator_next(pi)) != NULL) {
143*04377267Sthorpej 		if ((*po->po_type->pot_extern)(ctx, po) == false) {
144774eb1a3Sthorpej 			prop_object_iterator_release(pi);
145eb2acb85Sthorpej 			goto out;
146774eb1a3Sthorpej 		}
147774eb1a3Sthorpej 	}
148774eb1a3Sthorpej 
149774eb1a3Sthorpej 	prop_object_iterator_release(pi);
150774eb1a3Sthorpej 
151774eb1a3Sthorpej 	ctx->poec_depth--;
152774eb1a3Sthorpej 	for (i = 0; i < ctx->poec_depth; i++) {
153*04377267Sthorpej 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
154eb2acb85Sthorpej 			goto out;
155774eb1a3Sthorpej 	}
156*04377267Sthorpej 	if (_prop_object_externalize_end_tag(ctx, "array") == false)
157eb2acb85Sthorpej 		goto out;
158774eb1a3Sthorpej 
159*04377267Sthorpej 	rv = true;
160eb2acb85Sthorpej 
161eb2acb85Sthorpej  out:
162eb2acb85Sthorpej  	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
163eb2acb85Sthorpej 	return (rv);
164774eb1a3Sthorpej }
165774eb1a3Sthorpej 
166*04377267Sthorpej static bool
1673e69f1b2Sthorpej _prop_array_equals(void *v1, void *v2)
1683e69f1b2Sthorpej {
1693e69f1b2Sthorpej 	prop_array_t array1 = v1;
1703e69f1b2Sthorpej 	prop_array_t array2 = v2;
1713e69f1b2Sthorpej 	unsigned int idx;
172*04377267Sthorpej 	bool rv = false;
1733e69f1b2Sthorpej 
174d21620b2Sthorpej 	if (! (prop_object_is_array(array1) &&
175d21620b2Sthorpej 	       prop_object_is_array(array2)))
176*04377267Sthorpej 		return (false);
1773e69f1b2Sthorpej 
1783e69f1b2Sthorpej 	if (array1 == array2)
179*04377267Sthorpej 		return (true);
180eb2acb85Sthorpej 
181eb2acb85Sthorpej 	if ((uintptr_t)array1 < (uintptr_t)array2) {
182eb2acb85Sthorpej 		_PROP_RWLOCK_RDLOCK(array1->pa_rwlock);
183eb2acb85Sthorpej 		_PROP_RWLOCK_RDLOCK(array2->pa_rwlock);
184eb2acb85Sthorpej 	} else {
185eb2acb85Sthorpej 		_PROP_RWLOCK_RDLOCK(array2->pa_rwlock);
186eb2acb85Sthorpej 		_PROP_RWLOCK_RDLOCK(array1->pa_rwlock);
187eb2acb85Sthorpej 	}
188eb2acb85Sthorpej 
1893e69f1b2Sthorpej 	if (array1->pa_count != array2->pa_count)
190eb2acb85Sthorpej 		goto out;
1913e69f1b2Sthorpej 
1923e69f1b2Sthorpej 	for (idx = 0; idx < array1->pa_count; idx++) {
1933e69f1b2Sthorpej 		if (prop_object_equals(array1->pa_array[idx],
194*04377267Sthorpej 				       array2->pa_array[idx]) == false)
195eb2acb85Sthorpej 			goto out;
1963e69f1b2Sthorpej 	}
1973e69f1b2Sthorpej 
198*04377267Sthorpej 	rv = true;
199eb2acb85Sthorpej 
200eb2acb85Sthorpej  out:
201eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(array1->pa_rwlock);
202eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(array2->pa_rwlock);
203eb2acb85Sthorpej 	return (rv);
2043e69f1b2Sthorpej }
2053e69f1b2Sthorpej 
206774eb1a3Sthorpej static prop_array_t
207774eb1a3Sthorpej _prop_array_alloc(unsigned int capacity)
208774eb1a3Sthorpej {
209774eb1a3Sthorpej 	prop_array_t pa;
210774eb1a3Sthorpej 	prop_object_t *array;
211774eb1a3Sthorpej 
212774eb1a3Sthorpej 	if (capacity != 0) {
213774eb1a3Sthorpej 		array = _PROP_CALLOC(capacity * sizeof(prop_object_t),
214774eb1a3Sthorpej 				     M_PROP_ARRAY);
215774eb1a3Sthorpej 		if (array == NULL)
216774eb1a3Sthorpej 			return (NULL);
217774eb1a3Sthorpej 	} else
218774eb1a3Sthorpej 		array = NULL;
219774eb1a3Sthorpej 
220774eb1a3Sthorpej 
221774eb1a3Sthorpej 	pa = _PROP_POOL_GET(_prop_array_pool);
222774eb1a3Sthorpej 	if (pa != NULL) {
2233e69f1b2Sthorpej 		_prop_object_init(&pa->pa_obj, &_prop_object_type_array);
2243e69f1b2Sthorpej 		pa->pa_obj.po_type = &_prop_object_type_array;
225774eb1a3Sthorpej 
226eb2acb85Sthorpej 		_PROP_RWLOCK_INIT(pa->pa_rwlock);
227774eb1a3Sthorpej 		pa->pa_array = array;
228774eb1a3Sthorpej 		pa->pa_capacity = capacity;
229774eb1a3Sthorpej 		pa->pa_count = 0;
230774eb1a3Sthorpej 		pa->pa_flags = 0;
231774eb1a3Sthorpej 
232774eb1a3Sthorpej 		pa->pa_version = 0;
233774eb1a3Sthorpej 	} else if (array != NULL)
234774eb1a3Sthorpej 		_PROP_FREE(array, M_PROP_ARRAY);
235774eb1a3Sthorpej 
236774eb1a3Sthorpej 	return (pa);
237774eb1a3Sthorpej }
238774eb1a3Sthorpej 
239*04377267Sthorpej static bool
240774eb1a3Sthorpej _prop_array_expand(prop_array_t pa, unsigned int capacity)
241774eb1a3Sthorpej {
242774eb1a3Sthorpej 	prop_object_t *array, *oarray;
243774eb1a3Sthorpej 
244eb2acb85Sthorpej 	/*
245eb2acb85Sthorpej 	 * Array must be WRITE-LOCKED.
246eb2acb85Sthorpej 	 */
247eb2acb85Sthorpej 
248774eb1a3Sthorpej 	oarray = pa->pa_array;
249774eb1a3Sthorpej 
25042e8dee3Sthorpej 	array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY);
251774eb1a3Sthorpej 	if (array == NULL)
252*04377267Sthorpej 		return (false);
253774eb1a3Sthorpej 	if (oarray != NULL)
25442e8dee3Sthorpej 		memcpy(array, oarray, pa->pa_capacity * sizeof(*array));
255774eb1a3Sthorpej 	pa->pa_array = array;
256774eb1a3Sthorpej 	pa->pa_capacity = capacity;
257774eb1a3Sthorpej 
258774eb1a3Sthorpej 	if (oarray != NULL)
259774eb1a3Sthorpej 		_PROP_FREE(oarray, M_PROP_ARRAY);
260774eb1a3Sthorpej 
261*04377267Sthorpej 	return (true);
262774eb1a3Sthorpej }
263774eb1a3Sthorpej 
264774eb1a3Sthorpej static prop_object_t
265774eb1a3Sthorpej _prop_array_iterator_next_object(void *v)
266774eb1a3Sthorpej {
267774eb1a3Sthorpej 	struct _prop_array_iterator *pai = v;
268774eb1a3Sthorpej 	prop_array_t pa = pai->pai_base.pi_obj;
269eb2acb85Sthorpej 	prop_object_t po = NULL;
270774eb1a3Sthorpej 
271774eb1a3Sthorpej 	_PROP_ASSERT(prop_object_is_array(pa));
272774eb1a3Sthorpej 
273eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
274eb2acb85Sthorpej 
275774eb1a3Sthorpej 	if (pa->pa_version != pai->pai_base.pi_version)
276eb2acb85Sthorpej 		goto out;	/* array changed during iteration */
277774eb1a3Sthorpej 
278774eb1a3Sthorpej 	_PROP_ASSERT(pai->pai_index <= pa->pa_count);
279774eb1a3Sthorpej 
280774eb1a3Sthorpej 	if (pai->pai_index == pa->pa_count)
281eb2acb85Sthorpej 		goto out;	/* we've iterated all objects */
282774eb1a3Sthorpej 
283774eb1a3Sthorpej 	po = pa->pa_array[pai->pai_index];
284774eb1a3Sthorpej 	pai->pai_index++;
285774eb1a3Sthorpej 
286eb2acb85Sthorpej  out:
287eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
288774eb1a3Sthorpej 	return (po);
289774eb1a3Sthorpej }
290774eb1a3Sthorpej 
291774eb1a3Sthorpej static void
292774eb1a3Sthorpej _prop_array_iterator_reset(void *v)
293774eb1a3Sthorpej {
294774eb1a3Sthorpej 	struct _prop_array_iterator *pai = v;
295774eb1a3Sthorpej 	prop_array_t pa = pai->pai_base.pi_obj;
296774eb1a3Sthorpej 
297774eb1a3Sthorpej 	_PROP_ASSERT(prop_object_is_array(pa));
298774eb1a3Sthorpej 
299eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
300eb2acb85Sthorpej 
301774eb1a3Sthorpej 	pai->pai_index = 0;
302774eb1a3Sthorpej 	pai->pai_base.pi_version = pa->pa_version;
303eb2acb85Sthorpej 
304eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
305774eb1a3Sthorpej }
306774eb1a3Sthorpej 
307774eb1a3Sthorpej /*
308774eb1a3Sthorpej  * prop_array_create --
309774eb1a3Sthorpej  *	Create an empty array.
310774eb1a3Sthorpej  */
311774eb1a3Sthorpej prop_array_t
312774eb1a3Sthorpej prop_array_create(void)
313774eb1a3Sthorpej {
314774eb1a3Sthorpej 
315774eb1a3Sthorpej 	return (_prop_array_alloc(0));
316774eb1a3Sthorpej }
317774eb1a3Sthorpej 
318774eb1a3Sthorpej /*
319774eb1a3Sthorpej  * prop_array_create_with_capacity --
320774eb1a3Sthorpej  *	Create an array with the capacity to store N objects.
321774eb1a3Sthorpej  */
322774eb1a3Sthorpej prop_array_t
323774eb1a3Sthorpej prop_array_create_with_capacity(unsigned int capacity)
324774eb1a3Sthorpej {
325774eb1a3Sthorpej 
326774eb1a3Sthorpej 	return (_prop_array_alloc(capacity));
327774eb1a3Sthorpej }
328774eb1a3Sthorpej 
329774eb1a3Sthorpej /*
330774eb1a3Sthorpej  * prop_array_copy --
331774eb1a3Sthorpej  *	Copy an array.  The new array has an initial capacity equal to
332774eb1a3Sthorpej  *	the number of objects stored in the original array.  The new
333774eb1a3Sthorpej  *	array contains references to the original array's objects, not
334774eb1a3Sthorpej  *	copies of those objects (i.e. a shallow copy).
335774eb1a3Sthorpej  */
336774eb1a3Sthorpej prop_array_t
337774eb1a3Sthorpej prop_array_copy(prop_array_t opa)
338774eb1a3Sthorpej {
339774eb1a3Sthorpej 	prop_array_t pa;
340774eb1a3Sthorpej 	prop_object_t po;
341774eb1a3Sthorpej 	unsigned int idx;
342774eb1a3Sthorpej 
343d21620b2Sthorpej 	if (! prop_object_is_array(opa))
344d21620b2Sthorpej 		return (NULL);
345774eb1a3Sthorpej 
346eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(opa->pa_rwlock);
347eb2acb85Sthorpej 
348774eb1a3Sthorpej 	pa = _prop_array_alloc(opa->pa_count);
349774eb1a3Sthorpej 	if (pa != NULL) {
350774eb1a3Sthorpej 		for (idx = 0; idx < opa->pa_count; idx++) {
351774eb1a3Sthorpej 			po = opa->pa_array[idx];
352774eb1a3Sthorpej 			prop_object_retain(po);
353774eb1a3Sthorpej 			pa->pa_array[idx] = po;
354774eb1a3Sthorpej 		}
355774eb1a3Sthorpej 		pa->pa_count = opa->pa_count;
356774eb1a3Sthorpej 		pa->pa_flags = opa->pa_flags;
357774eb1a3Sthorpej 	}
358eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(opa->pa_rwlock);
359774eb1a3Sthorpej 	return (pa);
360774eb1a3Sthorpej }
361774eb1a3Sthorpej 
362774eb1a3Sthorpej /*
363774eb1a3Sthorpej  * prop_array_copy_mutable --
364774eb1a3Sthorpej  *	Like prop_array_copy(), but the resulting array is mutable.
365774eb1a3Sthorpej  */
366774eb1a3Sthorpej prop_array_t
367774eb1a3Sthorpej prop_array_copy_mutable(prop_array_t opa)
368774eb1a3Sthorpej {
369774eb1a3Sthorpej 	prop_array_t pa;
370774eb1a3Sthorpej 
371774eb1a3Sthorpej 	pa = prop_array_copy(opa);
372774eb1a3Sthorpej 	if (pa != NULL)
373774eb1a3Sthorpej 		pa->pa_flags &= ~PA_F_IMMUTABLE;
374774eb1a3Sthorpej 
375774eb1a3Sthorpej 	return (pa);
376774eb1a3Sthorpej }
377774eb1a3Sthorpej 
378774eb1a3Sthorpej /*
379774eb1a3Sthorpej  * prop_array_capacity --
380774eb1a3Sthorpej  *	Return the capacity of the array.
381774eb1a3Sthorpej  */
382774eb1a3Sthorpej unsigned int
383774eb1a3Sthorpej prop_array_capacity(prop_array_t pa)
384774eb1a3Sthorpej {
385eb2acb85Sthorpej 	unsigned int rv;
386774eb1a3Sthorpej 
387d21620b2Sthorpej 	if (! prop_object_is_array(pa))
388d21620b2Sthorpej 		return (0);
389d21620b2Sthorpej 
390eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
391eb2acb85Sthorpej 	rv = pa->pa_capacity;
392eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
393eb2acb85Sthorpej 
394eb2acb85Sthorpej 	return (rv);
395774eb1a3Sthorpej }
396774eb1a3Sthorpej 
397774eb1a3Sthorpej /*
398774eb1a3Sthorpej  * prop_array_count --
399774eb1a3Sthorpej  *	Return the number of objects stored in the array.
400774eb1a3Sthorpej  */
401774eb1a3Sthorpej unsigned int
402774eb1a3Sthorpej prop_array_count(prop_array_t pa)
403774eb1a3Sthorpej {
404eb2acb85Sthorpej 	unsigned int rv;
405774eb1a3Sthorpej 
406d21620b2Sthorpej 	if (! prop_object_is_array(pa))
407d21620b2Sthorpej 		return (0);
408d21620b2Sthorpej 
409eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
410eb2acb85Sthorpej 	rv = pa->pa_count;
411eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
412eb2acb85Sthorpej 
413eb2acb85Sthorpej 	return (rv);
414774eb1a3Sthorpej }
415774eb1a3Sthorpej 
416774eb1a3Sthorpej /*
417774eb1a3Sthorpej  * prop_array_ensure_capacity --
418774eb1a3Sthorpej  *	Ensure that the array has the capacity to store the specified
419774eb1a3Sthorpej  *	total number of objects (inluding the objects already stored
420774eb1a3Sthorpej  *	in the array).
421774eb1a3Sthorpej  */
422*04377267Sthorpej bool
423774eb1a3Sthorpej prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity)
424774eb1a3Sthorpej {
425*04377267Sthorpej 	bool rv;
426774eb1a3Sthorpej 
427d21620b2Sthorpej 	if (! prop_object_is_array(pa))
428*04377267Sthorpej 		return (false);
429d21620b2Sthorpej 
430eb2acb85Sthorpej 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
431774eb1a3Sthorpej 	if (capacity > pa->pa_capacity)
432eb2acb85Sthorpej 		rv = _prop_array_expand(pa, capacity);
433eb2acb85Sthorpej 	else
434*04377267Sthorpej 		rv = true;
435eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
436eb2acb85Sthorpej 
437eb2acb85Sthorpej 	return (rv);
438774eb1a3Sthorpej }
439774eb1a3Sthorpej 
440774eb1a3Sthorpej /*
441774eb1a3Sthorpej  * prop_array_iterator --
442774eb1a3Sthorpej  *	Return an iterator for the array.  The array is retained by
443774eb1a3Sthorpej  *	the iterator.
444774eb1a3Sthorpej  */
445774eb1a3Sthorpej prop_object_iterator_t
446774eb1a3Sthorpej prop_array_iterator(prop_array_t pa)
447774eb1a3Sthorpej {
448774eb1a3Sthorpej 	struct _prop_array_iterator *pai;
449774eb1a3Sthorpej 
450d21620b2Sthorpej 	if (! prop_object_is_array(pa))
451d21620b2Sthorpej 		return (NULL);
452774eb1a3Sthorpej 
453774eb1a3Sthorpej 	pai = _PROP_CALLOC(sizeof(*pai), M_TEMP);
454774eb1a3Sthorpej 	if (pai == NULL)
455774eb1a3Sthorpej 		return (NULL);
456774eb1a3Sthorpej 	pai->pai_base.pi_next_object = _prop_array_iterator_next_object;
457774eb1a3Sthorpej 	pai->pai_base.pi_reset = _prop_array_iterator_reset;
458774eb1a3Sthorpej 	prop_object_retain(pa);
459774eb1a3Sthorpej 	pai->pai_base.pi_obj = pa;
460774eb1a3Sthorpej 	_prop_array_iterator_reset(pai);
461774eb1a3Sthorpej 
462774eb1a3Sthorpej 	return (&pai->pai_base);
463774eb1a3Sthorpej }
464774eb1a3Sthorpej 
465774eb1a3Sthorpej /*
466774eb1a3Sthorpej  * prop_array_make_immutable --
467774eb1a3Sthorpej  *	Make the array immutable.
468774eb1a3Sthorpej  */
469774eb1a3Sthorpej void
470774eb1a3Sthorpej prop_array_make_immutable(prop_array_t pa)
471774eb1a3Sthorpej {
472774eb1a3Sthorpej 
473eb2acb85Sthorpej 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
474*04377267Sthorpej 	if (prop_array_is_immutable(pa) == false)
475774eb1a3Sthorpej 		pa->pa_flags |= PA_F_IMMUTABLE;
476eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
477774eb1a3Sthorpej }
478774eb1a3Sthorpej 
479774eb1a3Sthorpej /*
480774eb1a3Sthorpej  * prop_array_mutable --
481*04377267Sthorpej  *	Returns true if the array is mutable.
482774eb1a3Sthorpej  */
483*04377267Sthorpej bool
484774eb1a3Sthorpej prop_array_mutable(prop_array_t pa)
485774eb1a3Sthorpej {
486*04377267Sthorpej 	bool rv;
487774eb1a3Sthorpej 
488eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
489*04377267Sthorpej 	rv = prop_array_is_immutable(pa) == false;
490eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
491eb2acb85Sthorpej 
492eb2acb85Sthorpej 	return (rv);
493774eb1a3Sthorpej }
494774eb1a3Sthorpej 
495774eb1a3Sthorpej /*
496774eb1a3Sthorpej  * prop_array_get --
497774eb1a3Sthorpej  *	Return the object stored at the specified array index.
498774eb1a3Sthorpej  */
499774eb1a3Sthorpej prop_object_t
500774eb1a3Sthorpej prop_array_get(prop_array_t pa, unsigned int idx)
501774eb1a3Sthorpej {
502eb2acb85Sthorpej 	prop_object_t po = NULL;
503774eb1a3Sthorpej 
504d21620b2Sthorpej 	if (! prop_object_is_array(pa))
505d21620b2Sthorpej 		return (NULL);
506d21620b2Sthorpej 
507eb2acb85Sthorpej 	_PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
508774eb1a3Sthorpej 	if (idx >= pa->pa_count)
509eb2acb85Sthorpej 		goto out;
510774eb1a3Sthorpej 	po = pa->pa_array[idx];
511774eb1a3Sthorpej 	_PROP_ASSERT(po != NULL);
512eb2acb85Sthorpej  out:
513eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
514774eb1a3Sthorpej 	return (po);
515774eb1a3Sthorpej }
516774eb1a3Sthorpej 
517*04377267Sthorpej static bool
518eb2acb85Sthorpej _prop_array_add(prop_array_t pa, prop_object_t po)
519774eb1a3Sthorpej {
520774eb1a3Sthorpej 
521eb2acb85Sthorpej 	/*
522eb2acb85Sthorpej 	 * Array must be WRITE-LOCKED.
523eb2acb85Sthorpej 	 */
524d21620b2Sthorpej 
525774eb1a3Sthorpej 	_PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
526774eb1a3Sthorpej 
527774eb1a3Sthorpej 	if (prop_array_is_immutable(pa) ||
528774eb1a3Sthorpej 	    (pa->pa_count == pa->pa_capacity &&
529*04377267Sthorpej 	    _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false))
530*04377267Sthorpej 		return (false);
531774eb1a3Sthorpej 
532774eb1a3Sthorpej 	prop_object_retain(po);
533774eb1a3Sthorpej 	pa->pa_array[pa->pa_count++] = po;
534774eb1a3Sthorpej 	pa->pa_version++;
535774eb1a3Sthorpej 
536*04377267Sthorpej 	return (true);
537774eb1a3Sthorpej }
538774eb1a3Sthorpej 
539774eb1a3Sthorpej /*
540eb2acb85Sthorpej  * prop_array_set --
541eb2acb85Sthorpej  *	Store a reference to an object at the specified array index.
542eb2acb85Sthorpej  *	This method is not allowed to create holes in the array; the
543eb2acb85Sthorpej  *	caller must either be setting the object just beyond the existing
544eb2acb85Sthorpej  *	count or replacing an already existing object reference.
545eb2acb85Sthorpej  */
546*04377267Sthorpej bool
547eb2acb85Sthorpej prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po)
548eb2acb85Sthorpej {
549eb2acb85Sthorpej 	prop_object_t opo;
550*04377267Sthorpej 	bool rv = false;
551eb2acb85Sthorpej 
552eb2acb85Sthorpej 	if (! prop_object_is_array(pa))
553*04377267Sthorpej 		return (false);
554eb2acb85Sthorpej 
555eb2acb85Sthorpej 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
556eb2acb85Sthorpej 
557eb2acb85Sthorpej 	if (prop_array_is_immutable(pa))
558eb2acb85Sthorpej 		goto out;
559eb2acb85Sthorpej 
560eb2acb85Sthorpej 	if (idx == pa->pa_count) {
561eb2acb85Sthorpej 		rv = _prop_array_add(pa, po);
562eb2acb85Sthorpej 		goto out;
563eb2acb85Sthorpej 	}
564eb2acb85Sthorpej 
565eb2acb85Sthorpej 	_PROP_ASSERT(idx < pa->pa_count);
566eb2acb85Sthorpej 
567eb2acb85Sthorpej 	opo = pa->pa_array[idx];
568eb2acb85Sthorpej 	_PROP_ASSERT(opo != NULL);
569eb2acb85Sthorpej 
570eb2acb85Sthorpej 	prop_object_retain(po);
571eb2acb85Sthorpej 	pa->pa_array[idx] = po;
572eb2acb85Sthorpej 	pa->pa_version++;
573eb2acb85Sthorpej 
574eb2acb85Sthorpej 	prop_object_release(opo);
575eb2acb85Sthorpej 
576*04377267Sthorpej 	rv = true;
577eb2acb85Sthorpej 
578eb2acb85Sthorpej  out:
579eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
580eb2acb85Sthorpej 	return (rv);
581eb2acb85Sthorpej }
582eb2acb85Sthorpej 
583eb2acb85Sthorpej /*
584eb2acb85Sthorpej  * prop_array_add --
585eb2acb85Sthorpej  *	Add a refrerence to an object to the specified array, appending
586eb2acb85Sthorpej  *	to the end and growing the array's capacity, if necessary.
587eb2acb85Sthorpej  */
588*04377267Sthorpej bool
589eb2acb85Sthorpej prop_array_add(prop_array_t pa, prop_object_t po)
590eb2acb85Sthorpej {
591*04377267Sthorpej 	bool rv;
592eb2acb85Sthorpej 
593eb2acb85Sthorpej 	if (! prop_object_is_array(pa))
594*04377267Sthorpej 		return (false);
595eb2acb85Sthorpej 
596eb2acb85Sthorpej 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
597eb2acb85Sthorpej 	rv = _prop_array_add(pa, po);
598eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
599eb2acb85Sthorpej 
600eb2acb85Sthorpej 	return (rv);
601eb2acb85Sthorpej }
602eb2acb85Sthorpej 
603eb2acb85Sthorpej /*
604774eb1a3Sthorpej  * prop_array_remove --
605774eb1a3Sthorpej  *	Remove the reference to an object from an array at the specified
606774eb1a3Sthorpej  *	index.  The array will be compacted following the removal.
607774eb1a3Sthorpej  */
608774eb1a3Sthorpej void
609774eb1a3Sthorpej prop_array_remove(prop_array_t pa, unsigned int idx)
610774eb1a3Sthorpej {
611774eb1a3Sthorpej 	prop_object_t po;
612774eb1a3Sthorpej 
613d21620b2Sthorpej 	if (! prop_object_is_array(pa))
614d21620b2Sthorpej 		return;
615d21620b2Sthorpej 
616eb2acb85Sthorpej 	_PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
617eb2acb85Sthorpej 
618774eb1a3Sthorpej 	_PROP_ASSERT(idx < pa->pa_count);
619774eb1a3Sthorpej 
620774eb1a3Sthorpej 	/* XXX Should this be a _PROP_ASSERT()? */
621eb2acb85Sthorpej 	if (prop_array_is_immutable(pa)) {
622eb2acb85Sthorpej 		_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
623774eb1a3Sthorpej 		return;
624eb2acb85Sthorpej 	}
625774eb1a3Sthorpej 
626774eb1a3Sthorpej 	po = pa->pa_array[idx];
627774eb1a3Sthorpej 	_PROP_ASSERT(po != NULL);
628774eb1a3Sthorpej 
629774eb1a3Sthorpej 	for (++idx; idx < pa->pa_count; idx++)
630774eb1a3Sthorpej 		pa->pa_array[idx - 1] = pa->pa_array[idx];
631774eb1a3Sthorpej 	pa->pa_count--;
632774eb1a3Sthorpej 	pa->pa_version++;
633774eb1a3Sthorpej 
634eb2acb85Sthorpej 	_PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
635eb2acb85Sthorpej 
636774eb1a3Sthorpej 	prop_object_release(po);
637774eb1a3Sthorpej }
638774eb1a3Sthorpej 
639774eb1a3Sthorpej /*
6403e69f1b2Sthorpej  * prop_array_equals --
641*04377267Sthorpej  *	Return true if the two arrays are equivalent.  Note we do a
6423e69f1b2Sthorpej  *	by-value comparison of the objects in the array.
6433e69f1b2Sthorpej  */
644*04377267Sthorpej bool
6453e69f1b2Sthorpej prop_array_equals(prop_array_t array1, prop_array_t array2)
6463e69f1b2Sthorpej {
6473e69f1b2Sthorpej 
6483e69f1b2Sthorpej 	return (_prop_array_equals(array1, array2));
6493e69f1b2Sthorpej }
6503e69f1b2Sthorpej 
6513e69f1b2Sthorpej /*
652d21620b2Sthorpej  * prop_array_externalize --
653d21620b2Sthorpej  *	Externalize an array, return a NUL-terminated buffer
654d21620b2Sthorpej  *	containing the XML-style representation.  The buffer is allocated
655d21620b2Sthorpej  * 	with the M_TEMP memory type.
656d21620b2Sthorpej  */
657d21620b2Sthorpej char *
658d21620b2Sthorpej prop_array_externalize(prop_array_t pa)
659d21620b2Sthorpej {
660d21620b2Sthorpej 	struct _prop_object_externalize_context *ctx;
661d21620b2Sthorpej 	char *cp;
662d21620b2Sthorpej 
663d21620b2Sthorpej 	ctx = _prop_object_externalize_context_alloc();
664d21620b2Sthorpej 	if (ctx == NULL)
665d21620b2Sthorpej 		return (NULL);
666d21620b2Sthorpej 
667*04377267Sthorpej 	if (_prop_object_externalize_header(ctx) == false ||
668*04377267Sthorpej 	    (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false ||
669*04377267Sthorpej 	    _prop_object_externalize_footer(ctx) == false) {
670d21620b2Sthorpej 		/* We are responsible for releasing the buffer. */
671d21620b2Sthorpej 		_PROP_FREE(ctx->poec_buf, M_TEMP);
672d21620b2Sthorpej 		_prop_object_externalize_context_free(ctx);
673d21620b2Sthorpej 		return (NULL);
674d21620b2Sthorpej 	}
675d21620b2Sthorpej 
676d21620b2Sthorpej 	cp = ctx->poec_buf;
677d21620b2Sthorpej 	_prop_object_externalize_context_free(ctx);
678d21620b2Sthorpej 
679d21620b2Sthorpej 	return (cp);
680d21620b2Sthorpej }
681d21620b2Sthorpej 
682d21620b2Sthorpej /*
683774eb1a3Sthorpej  * _prop_array_internalize --
684774eb1a3Sthorpej  *	Parse an <array>...</array> and return the object created from the
685774eb1a3Sthorpej  *	external representation.
686774eb1a3Sthorpej  */
687774eb1a3Sthorpej prop_object_t
688774eb1a3Sthorpej _prop_array_internalize(struct _prop_object_internalize_context *ctx)
689774eb1a3Sthorpej {
690774eb1a3Sthorpej 	prop_array_t array;
691774eb1a3Sthorpej 	prop_object_t obj;
692774eb1a3Sthorpej 
693774eb1a3Sthorpej 	/* We don't currently understand any attributes. */
694774eb1a3Sthorpej 	if (ctx->poic_tagattr != NULL)
695774eb1a3Sthorpej 		return (NULL);
696774eb1a3Sthorpej 
697774eb1a3Sthorpej 	array = prop_array_create();
698774eb1a3Sthorpej 	if (array == NULL)
699774eb1a3Sthorpej 		return (NULL);
700774eb1a3Sthorpej 
701774eb1a3Sthorpej 	if (ctx->poic_is_empty_element)
702774eb1a3Sthorpej 		return (array);
703774eb1a3Sthorpej 
704774eb1a3Sthorpej 	for (;;) {
705774eb1a3Sthorpej 		/* Fetch the next tag. */
706774eb1a3Sthorpej 		if (_prop_object_internalize_find_tag(ctx, NULL,
707*04377267Sthorpej 					_PROP_TAG_TYPE_EITHER) == false)
708774eb1a3Sthorpej 			goto bad;
709774eb1a3Sthorpej 
710774eb1a3Sthorpej 		/* Check to see if this is the end of the array. */
711774eb1a3Sthorpej 		if (_PROP_TAG_MATCH(ctx, "array") &&
712774eb1a3Sthorpej 		    ctx->poic_tag_type == _PROP_TAG_TYPE_END)
713774eb1a3Sthorpej 		    	break;
714774eb1a3Sthorpej 
715774eb1a3Sthorpej 		/* Fetch the object. */
716774eb1a3Sthorpej 		obj = _prop_object_internalize_by_tag(ctx);
717774eb1a3Sthorpej 		if (obj == NULL)
718774eb1a3Sthorpej 			goto bad;
719774eb1a3Sthorpej 
720*04377267Sthorpej 		if (prop_array_add(array, obj) == false) {
721774eb1a3Sthorpej 			prop_object_release(obj);
722774eb1a3Sthorpej 			goto bad;
723774eb1a3Sthorpej 		}
724774eb1a3Sthorpej 		prop_object_release(obj);
725774eb1a3Sthorpej 	}
726774eb1a3Sthorpej 
727774eb1a3Sthorpej 	return (array);
728774eb1a3Sthorpej 
729774eb1a3Sthorpej  bad:
730774eb1a3Sthorpej 	prop_object_release(array);
731774eb1a3Sthorpej 	return (NULL);
732774eb1a3Sthorpej }
733d21620b2Sthorpej 
734d21620b2Sthorpej /*
735d21620b2Sthorpej  * prop_array_internalize --
736d21620b2Sthorpej  *	Create an array by parsing the XML-style representation.
737d21620b2Sthorpej  */
738d21620b2Sthorpej prop_array_t
739d21620b2Sthorpej prop_array_internalize(const char *xml)
740d21620b2Sthorpej {
74139dccbf2Sjoerg 	return _prop_generic_internalize(xml, "array");
742d21620b2Sthorpej }
743d21620b2Sthorpej 
744d21620b2Sthorpej #if !defined(_KERNEL) && !defined(_STANDALONE)
745d21620b2Sthorpej /*
746d21620b2Sthorpej  * prop_array_externalize_to_file --
747d21620b2Sthorpej  *	Externalize an array to the specified file.
748d21620b2Sthorpej  */
749*04377267Sthorpej bool
750d21620b2Sthorpej prop_array_externalize_to_file(prop_array_t array, const char *fname)
751d21620b2Sthorpej {
752d21620b2Sthorpej 	char *xml;
753*04377267Sthorpej 	bool rv;
7541a119b51She 	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
755d21620b2Sthorpej 
756d21620b2Sthorpej 	xml = prop_array_externalize(array);
757d21620b2Sthorpej 	if (xml == NULL)
758*04377267Sthorpej 		return (false);
759d21620b2Sthorpej 	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
760*04377267Sthorpej 	if (rv == false)
761d21620b2Sthorpej 		save_errno = errno;
762d21620b2Sthorpej 	_PROP_FREE(xml, M_TEMP);
763*04377267Sthorpej 	if (rv == false)
764d21620b2Sthorpej 		errno = save_errno;
765d21620b2Sthorpej 
766d21620b2Sthorpej 	return (rv);
767d21620b2Sthorpej }
768d21620b2Sthorpej 
769d21620b2Sthorpej /*
770d21620b2Sthorpej  * prop_array_internalize_from_file --
771d21620b2Sthorpej  *	Internalize an array from a file.
772d21620b2Sthorpej  */
773d21620b2Sthorpej prop_array_t
774d21620b2Sthorpej prop_array_internalize_from_file(const char *fname)
775d21620b2Sthorpej {
776d21620b2Sthorpej 	struct _prop_object_internalize_mapped_file *mf;
777d21620b2Sthorpej 	prop_array_t array;
778d21620b2Sthorpej 
779d21620b2Sthorpej 	mf = _prop_object_internalize_map_file(fname);
780d21620b2Sthorpej 	if (mf == NULL)
781d21620b2Sthorpej 		return (NULL);
782d21620b2Sthorpej 	array = prop_array_internalize(mf->poimf_xml);
783d21620b2Sthorpej 	_prop_object_internalize_unmap_file(mf);
784d21620b2Sthorpej 
785d21620b2Sthorpej 	return (array);
786d21620b2Sthorpej }
787d21620b2Sthorpej #endif /* _KERNEL && !_STANDALONE */
788