xref: /minix3/common/lib/libprop/prop_array.c (revision f14fb602092e015ff630df58e17c2a9cd57d29b3)
1  /*	$NetBSD: prop_array.c,v 1.21 2012/07/27 09:10:59 pooka 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
_prop_array_free(prop_stack_t stack,prop_object_t * obj)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
_prop_array_emergency_free(prop_object_t obj)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
_prop_array_externalize(struct _prop_object_externalize_context * ctx,void * v)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
_prop_array_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)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
_prop_array_equals_finish(prop_object_t v1,prop_object_t v2)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
_prop_array_alloc(unsigned int capacity)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
_prop_array_expand(prop_array_t pa,unsigned int capacity)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
_prop_array_iterator_next_object_locked(void * v)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
_prop_array_iterator_next_object(void * v)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
_prop_array_iterator_reset_locked(void * v)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
_prop_array_iterator_reset(void * v)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
prop_array_create(void)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
prop_array_create_with_capacity(unsigned int capacity)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
prop_array_copy(prop_array_t opa)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
prop_array_copy_mutable(prop_array_t opa)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
prop_array_capacity(prop_array_t pa)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
prop_array_count(prop_array_t pa)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
prop_array_ensure_capacity(prop_array_t pa,unsigned int capacity)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
_prop_array_iterator_locked(prop_array_t pa)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
prop_array_iterator(prop_array_t pa)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
prop_array_make_immutable(prop_array_t pa)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
prop_array_mutable(prop_array_t pa)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
prop_array_get(prop_array_t pa,unsigned int idx)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
_prop_array_add(prop_array_t pa,prop_object_t po)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
prop_array_set(prop_array_t pa,unsigned int idx,prop_object_t po)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
prop_array_add(prop_array_t pa,prop_object_t po)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
prop_array_remove(prop_array_t pa,unsigned int idx)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
prop_array_equals(prop_array_t array1,prop_array_t array2)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 *
prop_array_externalize(prop_array_t pa)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
_prop_array_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)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
_prop_array_internalize_continue(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx,void * data,prop_object_t child)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
_prop_array_internalize_body(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)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
prop_array_internalize(const char * xml)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
prop_array_externalize_to_file(prop_array_t array,const char * fname)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
prop_array_internalize_from_file(const char * fname)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