xref: /netbsd-src/common/lib/libprop/prop_array.c (revision b7ae68fde0d8ef1c03714e8bbb1ee7c6118ea93b)
1 /*	$NetBSD: prop_array.c,v 1.6 2006/08/22 21:21:23 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006 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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the NetBSD
21  *      Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <prop/prop_array.h>
40 #include "prop_object_impl.h"
41 
42 #if !defined(_KERNEL) && !defined(_STANDALONE)
43 #include <errno.h>
44 #endif
45 
46 struct _prop_array {
47 	struct _prop_object	pa_obj;
48 	prop_object_t *		pa_array;
49 	unsigned int		pa_capacity;
50 	unsigned int		pa_count;
51 	int			pa_flags;
52 
53 	uint32_t		pa_version;
54 };
55 
56 #define	PA_F_IMMUTABLE		0x01	/* array is immutable */
57 
58 _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay")
59 _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array",
60 		    "property array container object")
61 
62 static void		_prop_array_free(void *);
63 static boolean_t	_prop_array_externalize(
64 				struct _prop_object_externalize_context *,
65 				void *);
66 static boolean_t	_prop_array_equals(void *, void *);
67 
68 static const struct _prop_object_type _prop_object_type_array = {
69 	.pot_type	=	PROP_TYPE_ARRAY,
70 	.pot_free	=	_prop_array_free,
71 	.pot_extern	=	_prop_array_externalize,
72 	.pot_equals	=	_prop_array_equals,
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 void
88 _prop_array_free(void *v)
89 {
90 	prop_array_t pa = v;
91 	prop_object_t po;
92 	unsigned int idx;
93 
94 	_PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
95 	_PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) ||
96 		     (pa->pa_capacity != 0 && pa->pa_array != NULL));
97 
98 	for (idx = 0; idx < pa->pa_count; idx++) {
99 		po = pa->pa_array[idx];
100 		_PROP_ASSERT(po != NULL);
101 		prop_object_release(po);
102 	}
103 
104 	if (pa->pa_array != NULL)
105 		_PROP_FREE(pa->pa_array, M_PROP_ARRAY);
106 
107 	_PROP_POOL_PUT(_prop_array_pool, pa);
108 }
109 
110 static boolean_t
111 _prop_array_externalize(struct _prop_object_externalize_context *ctx,
112 			void *v)
113 {
114 	prop_array_t pa = v;
115 	struct _prop_object *po;
116 	prop_object_iterator_t pi;
117 	unsigned int i;
118 
119 	if (pa->pa_count == 0)
120 		return (_prop_object_externalize_empty_tag(ctx, "array"));
121 
122 	/* XXXJRT Hint "count" for the internalize step? */
123 	if (_prop_object_externalize_start_tag(ctx, "array") == FALSE ||
124 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
125 		return (FALSE);
126 
127 	pi = prop_array_iterator(pa);
128 	if (pi == NULL)
129 		return (FALSE);
130 
131 	ctx->poec_depth++;
132 	_PROP_ASSERT(ctx->poec_depth != 0);
133 
134 	while ((po = prop_object_iterator_next(pi)) != NULL) {
135 		if ((*po->po_type->pot_extern)(ctx, po) == FALSE) {
136 			prop_object_iterator_release(pi);
137 			return (FALSE);
138 		}
139 	}
140 
141 	prop_object_iterator_release(pi);
142 
143 	ctx->poec_depth--;
144 	for (i = 0; i < ctx->poec_depth; i++) {
145 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
146 			return (FALSE);
147 	}
148 	if (_prop_object_externalize_end_tag(ctx, "array") == FALSE)
149 		return (FALSE);
150 
151 	return (TRUE);
152 }
153 
154 static boolean_t
155 _prop_array_equals(void *v1, void *v2)
156 {
157 	prop_array_t array1 = v1;
158 	prop_array_t array2 = v2;
159 	unsigned int idx;
160 
161 	if (! (prop_object_is_array(array1) &&
162 	       prop_object_is_array(array2)))
163 		return (FALSE);
164 
165 	if (array1 == array2)
166 		return (TRUE);
167 	if (array1->pa_count != array2->pa_count)
168 		return (FALSE);
169 
170 	for (idx = 0; idx < array1->pa_count; idx++) {
171 		if (prop_object_equals(array1->pa_array[idx],
172 				       array2->pa_array[idx]) == FALSE)
173 			return (FALSE);
174 	}
175 
176 	return (TRUE);
177 }
178 
179 static prop_array_t
180 _prop_array_alloc(unsigned int capacity)
181 {
182 	prop_array_t pa;
183 	prop_object_t *array;
184 
185 	if (capacity != 0) {
186 		array = _PROP_CALLOC(capacity * sizeof(prop_object_t),
187 				     M_PROP_ARRAY);
188 		if (array == NULL)
189 			return (NULL);
190 	} else
191 		array = NULL;
192 
193 
194 	pa = _PROP_POOL_GET(_prop_array_pool);
195 	if (pa != NULL) {
196 		_prop_object_init(&pa->pa_obj, &_prop_object_type_array);
197 		pa->pa_obj.po_type = &_prop_object_type_array;
198 
199 		pa->pa_array = array;
200 		pa->pa_capacity = capacity;
201 		pa->pa_count = 0;
202 		pa->pa_flags = 0;
203 
204 		pa->pa_version = 0;
205 	} else if (array != NULL)
206 		_PROP_FREE(array, M_PROP_ARRAY);
207 
208 	return (pa);
209 }
210 
211 static boolean_t
212 _prop_array_expand(prop_array_t pa, unsigned int capacity)
213 {
214 	prop_object_t *array, *oarray;
215 
216 	oarray = pa->pa_array;
217 
218 	array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY);
219 	if (array == NULL)
220 		return (FALSE);
221 	if (oarray != NULL)
222 		memcpy(array, oarray, pa->pa_capacity * sizeof(*array));
223 	pa->pa_array = array;
224 	pa->pa_capacity = capacity;
225 
226 	if (oarray != NULL)
227 		_PROP_FREE(oarray, M_PROP_ARRAY);
228 
229 	return (TRUE);
230 }
231 
232 static prop_object_t
233 _prop_array_iterator_next_object(void *v)
234 {
235 	struct _prop_array_iterator *pai = v;
236 	prop_array_t pa = pai->pai_base.pi_obj;
237 	prop_object_t po;
238 
239 	_PROP_ASSERT(prop_object_is_array(pa));
240 
241 	if (pa->pa_version != pai->pai_base.pi_version)
242 		return (NULL);	/* array changed during iteration */
243 
244 	_PROP_ASSERT(pai->pai_index <= pa->pa_count);
245 
246 	if (pai->pai_index == pa->pa_count)
247 		return (NULL);	/* we've iterated all objects */
248 
249 	po = pa->pa_array[pai->pai_index];
250 	pai->pai_index++;
251 
252 	return (po);
253 }
254 
255 static void
256 _prop_array_iterator_reset(void *v)
257 {
258 	struct _prop_array_iterator *pai = v;
259 	prop_array_t pa = pai->pai_base.pi_obj;
260 
261 	_PROP_ASSERT(prop_object_is_array(pa));
262 
263 	pai->pai_index = 0;
264 	pai->pai_base.pi_version = pa->pa_version;
265 }
266 
267 /*
268  * prop_array_create --
269  *	Create an empty array.
270  */
271 prop_array_t
272 prop_array_create(void)
273 {
274 
275 	return (_prop_array_alloc(0));
276 }
277 
278 /*
279  * prop_array_create_with_capacity --
280  *	Create an array with the capacity to store N objects.
281  */
282 prop_array_t
283 prop_array_create_with_capacity(unsigned int capacity)
284 {
285 
286 	return (_prop_array_alloc(capacity));
287 }
288 
289 /*
290  * prop_array_copy --
291  *	Copy an array.  The new array has an initial capacity equal to
292  *	the number of objects stored in the original array.  The new
293  *	array contains references to the original array's objects, not
294  *	copies of those objects (i.e. a shallow copy).
295  */
296 prop_array_t
297 prop_array_copy(prop_array_t opa)
298 {
299 	prop_array_t pa;
300 	prop_object_t po;
301 	unsigned int idx;
302 
303 	if (! prop_object_is_array(opa))
304 		return (NULL);
305 
306 	pa = _prop_array_alloc(opa->pa_count);
307 	if (pa != NULL) {
308 		for (idx = 0; idx < opa->pa_count; idx++) {
309 			po = opa->pa_array[idx];
310 			prop_object_retain(po);
311 			pa->pa_array[idx] = po;
312 		}
313 		pa->pa_count = opa->pa_count;
314 		pa->pa_flags = opa->pa_flags;
315 	}
316 	return (pa);
317 }
318 
319 /*
320  * prop_array_copy_mutable --
321  *	Like prop_array_copy(), but the resulting array is mutable.
322  */
323 prop_array_t
324 prop_array_copy_mutable(prop_array_t opa)
325 {
326 	prop_array_t pa;
327 
328 	pa = prop_array_copy(opa);
329 	if (pa != NULL)
330 		pa->pa_flags &= ~PA_F_IMMUTABLE;
331 
332 	return (pa);
333 }
334 
335 /*
336  * prop_array_capacity --
337  *	Return the capacity of the array.
338  */
339 unsigned int
340 prop_array_capacity(prop_array_t pa)
341 {
342 
343 	if (! prop_object_is_array(pa))
344 		return (0);
345 
346 	return (pa->pa_capacity);
347 }
348 
349 /*
350  * prop_array_count --
351  *	Return the number of objects stored in the array.
352  */
353 unsigned int
354 prop_array_count(prop_array_t pa)
355 {
356 
357 	if (! prop_object_is_array(pa))
358 		return (0);
359 
360 	return (pa->pa_count);
361 }
362 
363 /*
364  * prop_array_ensure_capacity --
365  *	Ensure that the array has the capacity to store the specified
366  *	total number of objects (inluding the objects already stored
367  *	in the array).
368  */
369 boolean_t
370 prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity)
371 {
372 
373 	if (! prop_object_is_array(pa))
374 		return (FALSE);
375 
376 	if (capacity > pa->pa_capacity)
377 		return (_prop_array_expand(pa, capacity));
378 	return (TRUE);
379 }
380 
381 /*
382  * prop_array_iterator --
383  *	Return an iterator for the array.  The array is retained by
384  *	the iterator.
385  */
386 prop_object_iterator_t
387 prop_array_iterator(prop_array_t pa)
388 {
389 	struct _prop_array_iterator *pai;
390 
391 	if (! prop_object_is_array(pa))
392 		return (NULL);
393 
394 	pai = _PROP_CALLOC(sizeof(*pai), M_TEMP);
395 	if (pai == NULL)
396 		return (NULL);
397 	pai->pai_base.pi_next_object = _prop_array_iterator_next_object;
398 	pai->pai_base.pi_reset = _prop_array_iterator_reset;
399 	prop_object_retain(pa);
400 	pai->pai_base.pi_obj = pa;
401 	pai->pai_base.pi_version = pa->pa_version;
402 
403 	_prop_array_iterator_reset(pai);
404 
405 	return (&pai->pai_base);
406 }
407 
408 /*
409  * prop_array_make_immutable --
410  *	Make the array immutable.
411  */
412 void
413 prop_array_make_immutable(prop_array_t pa)
414 {
415 
416 	if (prop_array_is_immutable(pa) == FALSE)
417 		pa->pa_flags |= PA_F_IMMUTABLE;
418 }
419 
420 /*
421  * prop_array_mutable --
422  *	Returns TRUE if the array is mutable.
423  */
424 boolean_t
425 prop_array_mutable(prop_array_t pa)
426 {
427 
428 	return (prop_array_is_immutable(pa) == FALSE);
429 }
430 
431 /*
432  * prop_array_get --
433  *	Return the object stored at the specified array index.
434  */
435 prop_object_t
436 prop_array_get(prop_array_t pa, unsigned int idx)
437 {
438 	prop_object_t po;
439 
440 	if (! prop_object_is_array(pa))
441 		return (NULL);
442 
443 	if (idx >= pa->pa_count)
444 		return (NULL);
445 	po = pa->pa_array[idx];
446 	_PROP_ASSERT(po != NULL);
447 	return (po);
448 }
449 
450 /*
451  * prop_array_set --
452  *	Store a reference to an object at the specified array index.
453  *	This method is not allowed to create holes in the array; the
454  *	caller must either be setting the object just beyond the existing
455  *	count or replacing an already existing object reference.
456  */
457 boolean_t
458 prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po)
459 {
460 	prop_object_t opo;
461 
462 	if (! prop_object_is_array(pa))
463 		return (FALSE);
464 
465 	if (prop_array_is_immutable(pa))
466 		return (FALSE);
467 
468 	if (idx == pa->pa_count)
469 		return (prop_array_add(pa, po));
470 
471 	_PROP_ASSERT(idx < pa->pa_count);
472 
473 	opo = pa->pa_array[idx];
474 	_PROP_ASSERT(opo != NULL);
475 
476 	prop_object_retain(po);
477 	pa->pa_array[idx] = po;
478 	pa->pa_version++;
479 
480 	prop_object_release(opo);
481 
482 	return (TRUE);
483 }
484 
485 /*
486  * prop_array_add --
487  *	Add a refrerence to an object to the specified array, appending
488  *	to the end and growing the array's capacity, if necessary.
489  */
490 boolean_t
491 prop_array_add(prop_array_t pa, prop_object_t po)
492 {
493 
494 	if (! prop_object_is_array(pa))
495 		return (FALSE);
496 
497 	_PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
498 
499 	if (prop_array_is_immutable(pa) ||
500 	    (pa->pa_count == pa->pa_capacity &&
501 	    _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == FALSE))
502 		return (FALSE);
503 
504 	prop_object_retain(po);
505 	pa->pa_array[pa->pa_count++] = po;
506 	pa->pa_version++;
507 
508 	return (TRUE);
509 }
510 
511 /*
512  * prop_array_remove --
513  *	Remove the reference to an object from an array at the specified
514  *	index.  The array will be compacted following the removal.
515  */
516 void
517 prop_array_remove(prop_array_t pa, unsigned int idx)
518 {
519 	prop_object_t po;
520 
521 	if (! prop_object_is_array(pa))
522 		return;
523 
524 	_PROP_ASSERT(idx < pa->pa_count);
525 
526 	/* XXX Should this be a _PROP_ASSERT()? */
527 	if (prop_array_is_immutable(pa))
528 		return;
529 
530 	po = pa->pa_array[idx];
531 	_PROP_ASSERT(po != NULL);
532 
533 	for (++idx; idx < pa->pa_count; idx++)
534 		pa->pa_array[idx - 1] = pa->pa_array[idx];
535 	pa->pa_count--;
536 	pa->pa_version++;
537 
538 	prop_object_release(po);
539 }
540 
541 /*
542  * prop_array_equals --
543  *	Return TRUE if the two arrays are equivalent.  Note we do a
544  *	by-value comparison of the objects in the array.
545  */
546 boolean_t
547 prop_array_equals(prop_array_t array1, prop_array_t array2)
548 {
549 
550 	return (_prop_array_equals(array1, array2));
551 }
552 
553 /*
554  * prop_array_externalize --
555  *	Externalize an array, return a NUL-terminated buffer
556  *	containing the XML-style representation.  The buffer is allocated
557  * 	with the M_TEMP memory type.
558  */
559 char *
560 prop_array_externalize(prop_array_t pa)
561 {
562 	struct _prop_object_externalize_context *ctx;
563 	char *cp;
564 
565 	ctx = _prop_object_externalize_context_alloc();
566 	if (ctx == NULL)
567 		return (NULL);
568 
569 	if (_prop_object_externalize_header(ctx) == FALSE ||
570 	    (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == FALSE ||
571 	    _prop_object_externalize_footer(ctx) == FALSE) {
572 		/* We are responsible for releasing the buffer. */
573 		_PROP_FREE(ctx->poec_buf, M_TEMP);
574 		_prop_object_externalize_context_free(ctx);
575 		return (NULL);
576 	}
577 
578 	cp = ctx->poec_buf;
579 	_prop_object_externalize_context_free(ctx);
580 
581 	return (cp);
582 }
583 
584 /*
585  * _prop_array_internalize --
586  *	Parse an <array>...</array> and return the object created from the
587  *	external representation.
588  */
589 prop_object_t
590 _prop_array_internalize(struct _prop_object_internalize_context *ctx)
591 {
592 	prop_array_t array;
593 	prop_object_t obj;
594 
595 	/* We don't currently understand any attributes. */
596 	if (ctx->poic_tagattr != NULL)
597 		return (NULL);
598 
599 	array = prop_array_create();
600 	if (array == NULL)
601 		return (NULL);
602 
603 	if (ctx->poic_is_empty_element)
604 		return (array);
605 
606 	for (;;) {
607 		/* Fetch the next tag. */
608 		if (_prop_object_internalize_find_tag(ctx, NULL,
609 					_PROP_TAG_TYPE_EITHER) == FALSE)
610 			goto bad;
611 
612 		/* Check to see if this is the end of the array. */
613 		if (_PROP_TAG_MATCH(ctx, "array") &&
614 		    ctx->poic_tag_type == _PROP_TAG_TYPE_END)
615 		    	break;
616 
617 		/* Fetch the object. */
618 		obj = _prop_object_internalize_by_tag(ctx);
619 		if (obj == NULL)
620 			goto bad;
621 
622 		if (prop_array_add(array, obj) == FALSE) {
623 			prop_object_release(obj);
624 			goto bad;
625 		}
626 		prop_object_release(obj);
627 	}
628 
629 	return (array);
630 
631  bad:
632 	prop_object_release(array);
633 	return (NULL);
634 }
635 
636 /*
637  * prop_array_internalize --
638  *	Create an array by parsing the XML-style representation.
639  */
640 prop_array_t
641 prop_array_internalize(const char *xml)
642 {
643 	prop_array_t array = NULL;
644 	struct _prop_object_internalize_context *ctx;
645 
646 	ctx = _prop_object_internalize_context_alloc(xml);
647 	if (ctx == NULL)
648 		return (NULL);
649 
650 	/* We start with a <plist> tag. */
651 	if (_prop_object_internalize_find_tag(ctx, "plist",
652 					      _PROP_TAG_TYPE_START) == FALSE)
653 		goto out;
654 
655 	/* Plist elements cannot be empty. */
656 	if (ctx->poic_is_empty_element)
657 		goto out;
658 
659 	/*
660 	 * We don't understand any plist attributes, but Apple XML
661 	 * property lists often have a "version" attribute.  If we
662 	 * see that one, we simply ignore it.
663 	 */
664 	if (ctx->poic_tagattr != NULL &&
665 	    !_PROP_TAGATTR_MATCH(ctx, "version"))
666 	    	goto out;
667 
668 	/* Next we expect to see <array>. */
669 	if (_prop_object_internalize_find_tag(ctx, "array",
670 					      _PROP_TAG_TYPE_START) == FALSE)
671 		goto out;
672 
673 	array = _prop_array_internalize(ctx);
674 	if (array == NULL)
675 		goto out;
676 
677 	/* We've advanced past </array>.  Now we want </plist>. */
678 	if (_prop_object_internalize_find_tag(ctx, "plist",
679 					      _PROP_TAG_TYPE_END) == FALSE) {
680 		prop_object_release(array);
681 		array = NULL;
682 	}
683 
684  out:
685 	_prop_object_internalize_context_free(ctx);
686 	return (array);
687 }
688 
689 #if !defined(_KERNEL) && !defined(_STANDALONE)
690 /*
691  * prop_array_externalize_to_file --
692  *	Externalize an array to the specified file.
693  */
694 boolean_t
695 prop_array_externalize_to_file(prop_array_t array, const char *fname)
696 {
697 	char *xml;
698 	boolean_t rv;
699 	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
700 
701 	xml = prop_array_externalize(array);
702 	if (xml == NULL)
703 		return (FALSE);
704 	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
705 	if (rv == FALSE)
706 		save_errno = errno;
707 	_PROP_FREE(xml, M_TEMP);
708 	if (rv == FALSE)
709 		errno = save_errno;
710 
711 	return (rv);
712 }
713 
714 /*
715  * prop_array_internalize_from_file --
716  *	Internalize an array from a file.
717  */
718 prop_array_t
719 prop_array_internalize_from_file(const char *fname)
720 {
721 	struct _prop_object_internalize_mapped_file *mf;
722 	prop_array_t array;
723 
724 	mf = _prop_object_internalize_map_file(fname);
725 	if (mf == NULL)
726 		return (NULL);
727 	array = prop_array_internalize(mf->poimf_xml);
728 	_prop_object_internalize_unmap_file(mf);
729 
730 	return (array);
731 }
732 #endif /* _KERNEL && !_STANDALONE */
733