Try normal method (uncompressed) if Z_DATA_ERROR is returned.
[portableproplib.git] / src / prop_array.c
blob15437fbd618d3635d3a1ab3a6fbdbf9eca0e6f00
1 /* $NetBSD: prop_array.c,v 1.20 2008/08/11 05:54:21 christos Exp $ */
3 /*-
4 * Copyright (c) 2010 Juan Romero Pardines (zlib/gzip support).
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /*-
29 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
30 * All rights reserved.
32 * This code is derived from software contributed to The NetBSD Foundation
33 * by Jason R. Thorpe.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
44 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
45 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
46 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
47 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
48 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
49 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
50 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
51 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
52 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
53 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
54 * POSSIBILITY OF SUCH DAMAGE.
57 #include <prop/prop_array.h>
58 #include "prop_object_impl.h"
59 #include <errno.h>
61 #include <zlib.h>
63 struct _prop_array {
64 struct _prop_object pa_obj;
65 _PROP_RWLOCK_DECL(pa_rwlock)
66 prop_object_t * pa_array;
67 unsigned int pa_capacity;
68 unsigned int pa_count;
69 int pa_flags;
71 uint32_t pa_version;
74 #define PA_F_IMMUTABLE 0x01 /* array is immutable */
76 _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay")
77 _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array",
78 "property array container object")
80 static _prop_object_free_rv_t
81 _prop_array_free(prop_stack_t, prop_object_t *);
82 static void _prop_array_emergency_free(prop_object_t);
83 static bool _prop_array_externalize(
84 struct _prop_object_externalize_context *,
85 void *);
86 static _prop_object_equals_rv_t
87 _prop_array_equals(prop_object_t, prop_object_t,
88 void **, void **,
89 prop_object_t *, prop_object_t *);
90 static void _prop_array_equals_finish(prop_object_t, prop_object_t);
91 static prop_object_iterator_t
92 _prop_array_iterator_locked(prop_array_t);
93 static prop_object_t
94 _prop_array_iterator_next_object_locked(void *);
95 static void _prop_array_iterator_reset_locked(void *);
97 static const struct _prop_object_type _prop_object_type_array = {
98 .pot_type = PROP_TYPE_ARRAY,
99 .pot_free = _prop_array_free,
100 .pot_emergency_free = _prop_array_emergency_free,
101 .pot_extern = _prop_array_externalize,
102 .pot_equals = _prop_array_equals,
103 .pot_equals_finish = _prop_array_equals_finish,
106 #define prop_object_is_array(x) \
107 ((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array)
109 #define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0)
111 struct _prop_array_iterator {
112 struct _prop_object_iterator pai_base;
113 unsigned int pai_index;
116 #define EXPAND_STEP 16
118 static _prop_object_free_rv_t
119 _prop_array_free(prop_stack_t stack, prop_object_t *obj)
121 prop_array_t pa = *obj;
122 prop_object_t po;
124 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
125 _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) ||
126 (pa->pa_capacity != 0 && pa->pa_array != NULL));
128 /* The easy case is an empty array, just free and return. */
129 if (pa->pa_count == 0) {
130 if (pa->pa_array != NULL)
131 _PROP_FREE(pa->pa_array, M_PROP_ARRAY);
133 _PROP_RWLOCK_DESTROY(pa->pa_rwlock);
135 _PROP_POOL_PUT(_prop_array_pool, pa);
137 return (_PROP_OBJECT_FREE_DONE);
140 po = pa->pa_array[pa->pa_count - 1];
141 _PROP_ASSERT(po != NULL);
143 if (stack == NULL) {
145 * If we are in emergency release mode,
146 * just let caller recurse down.
148 *obj = po;
149 return (_PROP_OBJECT_FREE_FAILED);
152 /* Otherwise, try to push the current object on the stack. */
153 if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) {
154 /* Push failed, entering emergency release mode. */
155 return (_PROP_OBJECT_FREE_FAILED);
157 /* Object pushed on stack, caller will release it. */
158 --pa->pa_count;
159 *obj = po;
160 return (_PROP_OBJECT_FREE_RECURSE);
163 static void
164 _prop_array_emergency_free(prop_object_t obj)
166 prop_array_t pa = obj;
168 _PROP_ASSERT(pa->pa_count != 0);
169 --pa->pa_count;
172 static bool
173 _prop_array_externalize(struct _prop_object_externalize_context *ctx,
174 void *v)
176 prop_array_t pa = v;
177 struct _prop_object *po;
178 prop_object_iterator_t pi;
179 unsigned int i;
180 bool rv = false;
182 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
184 if (pa->pa_count == 0) {
185 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
186 return (_prop_object_externalize_empty_tag(ctx, "array"));
189 /* XXXJRT Hint "count" for the internalize step? */
190 if (_prop_object_externalize_start_tag(ctx, "array") == false ||
191 _prop_object_externalize_append_char(ctx, '\n') == false)
192 goto out;
194 pi = _prop_array_iterator_locked(pa);
195 if (pi == NULL)
196 goto out;
198 ctx->poec_depth++;
199 _PROP_ASSERT(ctx->poec_depth != 0);
201 while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) {
202 if ((*po->po_type->pot_extern)(ctx, po) == false) {
203 prop_object_iterator_release(pi);
204 goto out;
208 prop_object_iterator_release(pi);
210 ctx->poec_depth--;
211 for (i = 0; i < ctx->poec_depth; i++) {
212 if (_prop_object_externalize_append_char(ctx, '\t') == false)
213 goto out;
215 if (_prop_object_externalize_end_tag(ctx, "array") == false)
216 goto out;
218 rv = true;
220 out:
221 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
222 return (rv);
225 /* ARGSUSED */
226 static _prop_object_equals_rv_t
227 _prop_array_equals(prop_object_t v1, prop_object_t v2,
228 void **stored_pointer1, void **stored_pointer2,
229 prop_object_t *next_obj1, prop_object_t *next_obj2)
231 prop_array_t array1 = v1;
232 prop_array_t array2 = v2;
233 uintptr_t idx;
234 _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE;
236 if (array1 == array2)
237 return (_PROP_OBJECT_EQUALS_TRUE);
239 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
240 idx = (uintptr_t)*stored_pointer1;
242 /* For the first iteration, lock the objects. */
243 if (idx == 0) {
244 if ((uintptr_t)array1 < (uintptr_t)array2) {
245 _PROP_RWLOCK_RDLOCK(array1->pa_rwlock);
246 _PROP_RWLOCK_RDLOCK(array2->pa_rwlock);
247 } else {
248 _PROP_RWLOCK_RDLOCK(array2->pa_rwlock);
249 _PROP_RWLOCK_RDLOCK(array1->pa_rwlock);
253 if (array1->pa_count != array2->pa_count)
254 goto out;
255 if (idx == array1->pa_count) {
256 rv = _PROP_OBJECT_EQUALS_TRUE;
257 goto out;
259 _PROP_ASSERT(idx < array1->pa_count);
261 *stored_pointer1 = (void *)(idx + 1);
262 *stored_pointer2 = (void *)(idx + 1);
264 *next_obj1 = array1->pa_array[idx];
265 *next_obj2 = array2->pa_array[idx];
267 return (_PROP_OBJECT_EQUALS_RECURSE);
269 out:
270 _PROP_RWLOCK_UNLOCK(array1->pa_rwlock);
271 _PROP_RWLOCK_UNLOCK(array2->pa_rwlock);
272 return (rv);
275 static void
276 _prop_array_equals_finish(prop_object_t v1, prop_object_t v2)
278 _PROP_RWLOCK_UNLOCK(((prop_array_t)v1)->pa_rwlock);
279 _PROP_RWLOCK_UNLOCK(((prop_array_t)v2)->pa_rwlock);
282 static prop_array_t
283 _prop_array_alloc(unsigned int capacity)
285 prop_array_t pa;
286 prop_object_t *array;
288 if (capacity != 0) {
289 array = _PROP_CALLOC(capacity * sizeof(prop_object_t),
290 M_PROP_ARRAY);
291 if (array == NULL)
292 return (NULL);
293 } else
294 array = NULL;
296 pa = _PROP_POOL_GET(_prop_array_pool);
297 if (pa != NULL) {
298 _prop_object_init(&pa->pa_obj, &_prop_object_type_array);
299 pa->pa_obj.po_type = &_prop_object_type_array;
301 _PROP_RWLOCK_INIT(pa->pa_rwlock);
302 pa->pa_array = array;
303 pa->pa_capacity = capacity;
304 pa->pa_count = 0;
305 pa->pa_flags = 0;
307 pa->pa_version = 0;
308 } else if (array != NULL)
309 _PROP_FREE(array, M_PROP_ARRAY);
311 return (pa);
314 static bool
315 _prop_array_expand(prop_array_t pa, unsigned int capacity)
317 prop_object_t *array, *oarray;
320 * Array must be WRITE-LOCKED.
323 oarray = pa->pa_array;
325 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY);
326 if (array == NULL)
327 return (false);
328 if (oarray != NULL)
329 memcpy(array, oarray, pa->pa_capacity * sizeof(*array));
330 pa->pa_array = array;
331 pa->pa_capacity = capacity;
333 if (oarray != NULL)
334 _PROP_FREE(oarray, M_PROP_ARRAY);
336 return (true);
339 static prop_object_t
340 _prop_array_iterator_next_object_locked(void *v)
342 struct _prop_array_iterator *pai = v;
343 prop_array_t pa = pai->pai_base.pi_obj;
344 prop_object_t po = NULL;
346 _PROP_ASSERT(prop_object_is_array(pa));
348 if (pa->pa_version != pai->pai_base.pi_version)
349 goto out; /* array changed during iteration */
351 _PROP_ASSERT(pai->pai_index <= pa->pa_count);
353 if (pai->pai_index == pa->pa_count)
354 goto out; /* we've iterated all objects */
356 po = pa->pa_array[pai->pai_index];
357 pai->pai_index++;
359 out:
360 return (po);
363 static prop_object_t
364 _prop_array_iterator_next_object(void *v)
366 struct _prop_array_iterator *pai = v;
367 prop_array_t pa = pai->pai_base.pi_obj;
368 prop_object_t po;
370 _PROP_ASSERT(prop_object_is_array(pa));
372 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
373 po = _prop_array_iterator_next_object_locked(pai);
374 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
375 return (po);
378 static void
379 _prop_array_iterator_reset_locked(void *v)
381 struct _prop_array_iterator *pai = v;
382 prop_array_t pa = pai->pai_base.pi_obj;
384 _PROP_ASSERT(prop_object_is_array(pa));
386 pai->pai_index = 0;
387 pai->pai_base.pi_version = pa->pa_version;
390 static void
391 _prop_array_iterator_reset(void *v)
393 struct _prop_array_iterator *pai = v;
394 prop_array_t pa = pai->pai_base.pi_obj;
396 _PROP_ASSERT(prop_object_is_array(pa));
398 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
399 _prop_array_iterator_reset_locked(pai);
400 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
404 * prop_array_create --
405 * Create an empty array.
407 prop_array_t
408 prop_array_create(void)
411 return (_prop_array_alloc(0));
415 * prop_array_create_with_capacity --
416 * Create an array with the capacity to store N objects.
418 prop_array_t
419 prop_array_create_with_capacity(unsigned int capacity)
422 return (_prop_array_alloc(capacity));
426 * prop_array_copy --
427 * Copy an array. The new array has an initial capacity equal to
428 * the number of objects stored in the original array. The new
429 * array contains references to the original array's objects, not
430 * copies of those objects (i.e. a shallow copy).
432 prop_array_t
433 prop_array_copy(prop_array_t opa)
435 prop_array_t pa;
436 prop_object_t po;
437 unsigned int idx;
439 if (! prop_object_is_array(opa))
440 return (NULL);
442 _PROP_RWLOCK_RDLOCK(opa->pa_rwlock);
444 pa = _prop_array_alloc(opa->pa_count);
445 if (pa != NULL) {
446 for (idx = 0; idx < opa->pa_count; idx++) {
447 po = opa->pa_array[idx];
448 prop_object_retain(po);
449 pa->pa_array[idx] = po;
451 pa->pa_count = opa->pa_count;
452 pa->pa_flags = opa->pa_flags;
454 _PROP_RWLOCK_UNLOCK(opa->pa_rwlock);
455 return (pa);
459 * prop_array_copy_mutable --
460 * Like prop_array_copy(), but the resulting array is mutable.
462 prop_array_t
463 prop_array_copy_mutable(prop_array_t opa)
465 prop_array_t pa;
467 pa = prop_array_copy(opa);
468 if (pa != NULL)
469 pa->pa_flags &= ~PA_F_IMMUTABLE;
471 return (pa);
475 * prop_array_capacity --
476 * Return the capacity of the array.
478 unsigned int
479 prop_array_capacity(prop_array_t pa)
481 unsigned int rv;
483 if (! prop_object_is_array(pa))
484 return (0);
486 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
487 rv = pa->pa_capacity;
488 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
490 return (rv);
494 * prop_array_count --
495 * Return the number of objects stored in the array.
497 unsigned int
498 prop_array_count(prop_array_t pa)
500 unsigned int rv;
502 if (! prop_object_is_array(pa))
503 return (0);
505 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
506 rv = pa->pa_count;
507 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
509 return (rv);
513 * prop_array_ensure_capacity --
514 * Ensure that the array has the capacity to store the specified
515 * total number of objects (inluding the objects already stored
516 * in the array).
518 bool
519 prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity)
521 bool rv;
523 if (! prop_object_is_array(pa))
524 return (false);
526 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
527 if (capacity > pa->pa_capacity)
528 rv = _prop_array_expand(pa, capacity);
529 else
530 rv = true;
531 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
533 return (rv);
536 static prop_object_iterator_t
537 _prop_array_iterator_locked(prop_array_t pa)
539 struct _prop_array_iterator *pai;
541 if (! prop_object_is_array(pa))
542 return (NULL);
544 pai = _PROP_CALLOC(sizeof(*pai), M_TEMP);
545 if (pai == NULL)
546 return (NULL);
547 pai->pai_base.pi_next_object = _prop_array_iterator_next_object;
548 pai->pai_base.pi_reset = _prop_array_iterator_reset;
549 prop_object_retain(pa);
550 pai->pai_base.pi_obj = pa;
551 _prop_array_iterator_reset_locked(pai);
553 return (&pai->pai_base);
557 * prop_array_iterator --
558 * Return an iterator for the array. The array is retained by
559 * the iterator.
561 prop_object_iterator_t
562 prop_array_iterator(prop_array_t pa)
564 prop_object_iterator_t pi;
566 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
567 pi = _prop_array_iterator_locked(pa);
568 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
569 return (pi);
573 * prop_array_make_immutable --
574 * Make the array immutable.
576 void
577 prop_array_make_immutable(prop_array_t pa)
580 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
581 if (prop_array_is_immutable(pa) == false)
582 pa->pa_flags |= PA_F_IMMUTABLE;
583 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
587 * prop_array_mutable --
588 * Returns true if the array is mutable.
590 bool
591 prop_array_mutable(prop_array_t pa)
593 bool rv;
595 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
596 rv = prop_array_is_immutable(pa) == false;
597 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
599 return (rv);
603 * prop_array_get --
604 * Return the object stored at the specified array index.
606 prop_object_t
607 prop_array_get(prop_array_t pa, unsigned int idx)
609 prop_object_t po = NULL;
611 if (! prop_object_is_array(pa))
612 return (NULL);
614 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
615 if (idx >= pa->pa_count)
616 goto out;
617 po = pa->pa_array[idx];
618 _PROP_ASSERT(po != NULL);
619 out:
620 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
621 return (po);
624 static bool
625 _prop_array_add(prop_array_t pa, prop_object_t po)
629 * Array must be WRITE-LOCKED.
632 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
634 if (prop_array_is_immutable(pa) ||
635 (pa->pa_count == pa->pa_capacity &&
636 _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false))
637 return (false);
639 prop_object_retain(po);
640 pa->pa_array[pa->pa_count++] = po;
641 pa->pa_version++;
643 return (true);
647 * prop_array_set --
648 * Store a reference to an object at the specified array index.
649 * This method is not allowed to create holes in the array; the
650 * caller must either be setting the object just beyond the existing
651 * count or replacing an already existing object reference.
653 bool
654 prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po)
656 prop_object_t opo;
657 bool rv = false;
659 if (! prop_object_is_array(pa))
660 return (false);
662 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
664 if (prop_array_is_immutable(pa))
665 goto out;
667 if (idx == pa->pa_count) {
668 rv = _prop_array_add(pa, po);
669 goto out;
672 _PROP_ASSERT(idx < pa->pa_count);
674 opo = pa->pa_array[idx];
675 _PROP_ASSERT(opo != NULL);
677 prop_object_retain(po);
678 pa->pa_array[idx] = po;
679 pa->pa_version++;
681 prop_object_release(opo);
683 rv = true;
685 out:
686 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
687 return (rv);
691 * prop_array_add --
692 * Add a reference to an object to the specified array, appending
693 * to the end and growing the array's capacity, if necessary.
695 bool
696 prop_array_add(prop_array_t pa, prop_object_t po)
698 bool rv;
700 if (! prop_object_is_array(pa))
701 return (false);
703 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
704 rv = _prop_array_add(pa, po);
705 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
707 return (rv);
711 * prop_array_remove --
712 * Remove the reference to an object from an array at the specified
713 * index. The array will be compacted following the removal.
715 void
716 prop_array_remove(prop_array_t pa, unsigned int idx)
718 prop_object_t po;
720 if (! prop_object_is_array(pa))
721 return;
723 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
725 _PROP_ASSERT(idx < pa->pa_count);
727 /* XXX Should this be a _PROP_ASSERT()? */
728 if (prop_array_is_immutable(pa)) {
729 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
730 return;
733 po = pa->pa_array[idx];
734 _PROP_ASSERT(po != NULL);
736 for (++idx; idx < pa->pa_count; idx++)
737 pa->pa_array[idx - 1] = pa->pa_array[idx];
738 pa->pa_count--;
739 pa->pa_version++;
741 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
743 prop_object_release(po);
747 * prop_array_equals --
748 * Return true if the two arrays are equivalent. Note we do a
749 * by-value comparison of the objects in the array.
751 bool
752 prop_array_equals(prop_array_t array1, prop_array_t array2)
754 if (!prop_object_is_array(array1) || !prop_object_is_array(array2))
755 return (false);
757 return (prop_object_equals(array1, array2));
761 * prop_array_externalize --
762 * Externalize an array, return a NUL-terminated buffer
763 * containing the XML-style representation. The buffer is allocated
764 * with the M_TEMP memory type.
766 char *
767 prop_array_externalize(prop_array_t pa)
769 struct _prop_object_externalize_context *ctx;
770 char *cp;
772 ctx = _prop_object_externalize_context_alloc();
773 if (ctx == NULL)
774 return (NULL);
776 if (_prop_object_externalize_header(ctx) == false ||
777 (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false ||
778 _prop_object_externalize_footer(ctx) == false) {
779 /* We are responsible for releasing the buffer. */
780 _PROP_FREE(ctx->poec_buf, M_TEMP);
781 _prop_object_externalize_context_free(ctx);
782 return (NULL);
785 cp = ctx->poec_buf;
786 _prop_object_externalize_context_free(ctx);
788 return (cp);
792 * _prop_array_internalize --
793 * Parse an <array>...</array> and return the object created from the
794 * external representation.
796 static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *,
797 struct _prop_object_internalize_context *);
799 bool
800 _prop_array_internalize(prop_stack_t stack, prop_object_t *obj,
801 struct _prop_object_internalize_context *ctx)
803 /* We don't currently understand any attributes. */
804 if (ctx->poic_tagattr != NULL)
805 return (true);
807 *obj = prop_array_create();
809 * We are done if the create failed or no child elements exist.
811 if (*obj == NULL || ctx->poic_is_empty_element)
812 return (true);
815 * Opening tag is found, now continue to the first element.
817 return (_prop_array_internalize_body(stack, obj, ctx));
820 static bool
821 _prop_array_internalize_continue(prop_stack_t stack,
822 prop_object_t *obj,
823 struct _prop_object_internalize_context *ctx,
824 void *data, prop_object_t child)
826 prop_array_t array;
828 _PROP_ASSERT(data == NULL);
830 if (child == NULL)
831 goto bad; /* Element could not be parsed. */
833 array = *obj;
835 if (prop_array_add(array, child) == false) {
836 prop_object_release(child);
837 goto bad;
839 prop_object_release(child);
842 * Current element is processed and added, look for next.
844 return (_prop_array_internalize_body(stack, obj, ctx));
846 bad:
847 prop_object_release(*obj);
848 *obj = NULL;
849 return (true);
852 static bool
853 _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj,
854 struct _prop_object_internalize_context *ctx)
856 prop_array_t array = *obj;
858 _PROP_ASSERT(array != NULL);
860 /* Fetch the next tag. */
861 if (_prop_object_internalize_find_tag(ctx, NULL,
862 _PROP_TAG_TYPE_EITHER) == false)
863 goto bad;
865 /* Check to see if this is the end of the array. */
866 if (_PROP_TAG_MATCH(ctx, "array") &&
867 ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
868 /* It is, so don't iterate any further. */
869 return (true);
872 if (_prop_stack_push(stack, array,
873 _prop_array_internalize_continue, NULL, NULL))
874 return (false);
876 bad:
877 prop_object_release(array);
878 *obj = NULL;
879 return (true);
883 * prop_array_internalize --
884 * Create an array by parsing the XML-style representation.
886 prop_array_t
887 prop_array_internalize(const char *xml)
889 return _prop_generic_internalize(xml, "array");
893 * prop_array_externalize_to_file --
894 * Externalize an array to the specified file.
896 bool
897 prop_array_externalize_to_file(prop_array_t array, const char *fname)
899 char *xml;
900 bool rv;
901 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */
903 xml = prop_array_externalize(array);
904 if (xml == NULL)
905 return (false);
906 rv = _prop_object_externalize_write_file(fname, xml,
907 strlen(xml), false);
908 if (rv == false)
909 save_errno = errno;
910 _PROP_FREE(xml, M_TEMP);
911 if (rv == false)
912 errno = save_errno;
914 return (rv);
918 * prop_array_externalize_to_zfile ---
919 * Externalize an array to the specified file, and on the fly
920 * compressing the result with gzip (via zlib).
922 bool
923 prop_array_externalize_to_zfile(prop_array_t array, const char *fname)
925 char *xml;
926 bool rv;
927 int save_errno = 0;
929 xml = prop_array_externalize(array);
930 if (xml == NULL)
931 return false;
932 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml), true);
933 if (rv == false)
934 save_errno = errno;
935 _PROP_FREE(xml, M_TEMP);
936 if (rv == false)
937 errno = save_errno;
939 return rv;
943 * prop_array_internalize_from_file --
944 * Internalize an array from a file.
946 prop_array_t
947 prop_array_internalize_from_file(const char *fname)
949 struct _prop_object_internalize_mapped_file *mf;
950 prop_array_t array;
952 mf = _prop_object_internalize_map_file(fname);
953 if (mf == NULL)
954 return (NULL);
955 array = prop_array_internalize(mf->poimf_xml);
956 _prop_object_internalize_unmap_file(mf);
958 return (array);
961 #define _READ_CHUNK 512
963 * prop_array_internalize_from_zfile ---
964 * Internalize an array from a compressed gzip file.
966 prop_array_t
967 prop_array_internalize_from_zfile(const char *fname)
969 struct _prop_object_internalize_mapped_file *mf;
970 prop_array_t array;
971 z_stream strm;
972 unsigned char out[_READ_CHUNK];
973 char *uncomp_xml = NULL;
974 size_t have;
975 ssize_t totalsize = 0;
976 int rv = 0;
978 mf = _prop_object_internalize_map_file(fname);
979 if (mf == NULL)
980 return NULL;
982 /* Decompress the mmap'ed buffer with zlib */
983 strm.zalloc = Z_NULL;
984 strm.zfree = Z_NULL;
985 strm.opaque = Z_NULL;
986 strm.avail_in = 0;
987 strm.next_in = Z_NULL;
989 /* 15+16 to use gzip method */
990 if (inflateInit2(&strm, 15+16) != Z_OK) {
991 _prop_object_internalize_unmap_file(mf);
992 return NULL;
995 strm.avail_in = mf->poimf_mapsize;
996 strm.next_in = mf->poimf_xml;
998 /* Output buffer (decompressed) */
999 uncomp_xml = _PROP_MALLOC(_READ_CHUNK, M_TEMP);
1000 if (uncomp_xml == NULL) {
1001 _prop_object_internalize_unmap_file(mf);
1002 (void)inflateEnd(&strm);
1003 return NULL;
1006 /* Inflate the input buffer and copy into 'dest' */
1007 do {
1008 strm.avail_out = _READ_CHUNK;
1009 strm.next_out = out;
1010 rv = inflate(&strm, Z_NO_FLUSH);
1011 switch (rv) {
1012 case Z_DATA_ERROR:
1014 * Wrong compressed data or uncompressed, try
1015 * normal method as last resort.
1017 (void)inflateEnd(&strm);
1018 _PROP_FREE(uncomp_xml, M_TEMP);
1019 array = prop_array_internalize(mf->poimf_xml);
1020 _prop_object_internalize_unmap_file(mf);
1021 return array;
1022 case Z_STREAM_ERROR:
1023 case Z_NEED_DICT:
1024 case Z_MEM_ERROR:
1025 (void)inflateEnd(&strm);
1026 _PROP_FREE(uncomp_xml, M_TEMP);
1027 _prop_object_internalize_unmap_file(mf);
1028 errno = rv;
1029 return NULL;
1031 have = _READ_CHUNK - strm.avail_out;
1032 totalsize += have;
1033 uncomp_xml = _PROP_REALLOC(uncomp_xml, totalsize, M_TEMP);
1034 memcpy(uncomp_xml + totalsize - have, out, have);
1035 } while (strm.avail_out == 0);
1037 /* we are done */
1038 (void)inflateEnd(&strm);
1039 array = prop_array_internalize(uncomp_xml);
1040 _PROP_FREE(uncomp_xml, M_TEMP);
1041 _prop_object_internalize_unmap_file(mf);
1043 return array;