xref: /dragonfly/sys/libprop/prop_array.c (revision f3f3eadbf9de7a55ef1ff8cb23a68641403906ea)
1 /*        $NetBSD: prop_array.c,v 1.20 2008/08/11 05:54:21 christos 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 <libprop/prop_array.h>
33 #include "prop_object_impl.h"
34 
35 #if !defined(_KERNEL) && !defined(_STANDALONE)
36 #include <errno.h>
37 #endif
38 
39 struct _prop_array {
40           struct _prop_object pa_obj;
41           _PROP_RWLOCK_DECL(pa_rwlock)
42           prop_object_t *               pa_array;
43           unsigned int                  pa_capacity;
44           unsigned int                  pa_count;
45           int                           pa_flags;
46 
47           uint32_t            pa_version;
48 };
49 
50 #define PA_F_IMMUTABLE                  0x01      /* array is immutable */
51 
52 _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay");
53 _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array",
54                         "property array container object")
55 
56 static _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 = 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 = 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