prop_array.c revision 1.18
1/* $NetBSD: prop_array.c,v 1.18 2008/05/24 14:32:48 yamt 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/prop_array.h> 33#include "prop_object_impl.h" 34 35#if !defined(_KERNEL) && !defined(_STANDALONE) 36#include <errno.h> 37#endif 38 39struct _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 56static int _prop_array_free(prop_stack_t, prop_object_t *); 57static void _prop_array_emergency_free(prop_object_t); 58static bool _prop_array_externalize( 59 struct _prop_object_externalize_context *, 60 void *); 61static bool _prop_array_equals(prop_object_t, prop_object_t, 62 void **, void **, 63 prop_object_t *, prop_object_t *); 64static void _prop_array_equals_finish(prop_object_t, prop_object_t); 65static prop_object_iterator_t _prop_array_iterator_locked(prop_array_t); 66static prop_object_t _prop_array_iterator_next_object_locked(void *); 67static void _prop_array_iterator_reset_locked(void *); 68 69static const struct _prop_object_type _prop_object_type_array = { 70 .pot_type = PROP_TYPE_ARRAY, 71 .pot_free = _prop_array_free, 72 .pot_emergency_free = _prop_array_emergency_free, 73 .pot_extern = _prop_array_externalize, 74 .pot_equals = _prop_array_equals, 75 .pot_equals_finish = _prop_array_equals_finish, 76}; 77 78#define prop_object_is_array(x) \ 79 ((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array) 80 81#define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0) 82 83struct _prop_array_iterator { 84 struct _prop_object_iterator pai_base; 85 unsigned int pai_index; 86}; 87 88#define EXPAND_STEP 16 89 90static int 91_prop_array_free(prop_stack_t stack, prop_object_t *obj) 92{ 93 prop_array_t pa = *obj; 94 prop_object_t po; 95 96 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 97 _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) || 98 (pa->pa_capacity != 0 && pa->pa_array != NULL)); 99 100 /* The easy case is an empty array, just free and return. */ 101 if (pa->pa_count == 0) { 102 if (pa->pa_array != NULL) 103 _PROP_FREE(pa->pa_array, M_PROP_ARRAY); 104 105 _PROP_RWLOCK_DESTROY(pa->pa_rwlock); 106 107 _PROP_POOL_PUT(_prop_array_pool, pa); 108 109 return (_PROP_OBJECT_FREE_DONE); 110 } 111 112 po = pa->pa_array[pa->pa_count - 1]; 113 _PROP_ASSERT(po != NULL); 114 115 if (stack == NULL) { 116 /* 117 * If we are in emergency release mode, 118 * just let caller recurse down. 119 */ 120 *obj = po; 121 return (_PROP_OBJECT_FREE_FAILED); 122 } 123 124 /* Otherwise, try to push the current object on the stack. */ 125 if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) { 126 /* Push failed, entering emergency release mode. */ 127 return (_PROP_OBJECT_FREE_FAILED); 128 } 129 /* Object pushed on stack, caller will release it. */ 130 --pa->pa_count; 131 *obj = po; 132 return (_PROP_OBJECT_FREE_RECURSE); 133} 134 135static void 136_prop_array_emergency_free(prop_object_t obj) 137{ 138 prop_array_t pa = obj; 139 140 _PROP_ASSERT(pa->pa_count != 0); 141 --pa->pa_count; 142} 143 144static bool 145_prop_array_externalize(struct _prop_object_externalize_context *ctx, 146 void *v) 147{ 148 prop_array_t pa = v; 149 struct _prop_object *po; 150 prop_object_iterator_t pi; 151 unsigned int i; 152 bool rv = false; 153 154 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 155 156 if (pa->pa_count == 0) { 157 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 158 return (_prop_object_externalize_empty_tag(ctx, "array")); 159 } 160 161 /* XXXJRT Hint "count" for the internalize step? */ 162 if (_prop_object_externalize_start_tag(ctx, "array") == false || 163 _prop_object_externalize_append_char(ctx, '\n') == false) 164 goto out; 165 166 pi = _prop_array_iterator_locked(pa); 167 if (pi == NULL) 168 goto out; 169 170 ctx->poec_depth++; 171 _PROP_ASSERT(ctx->poec_depth != 0); 172 173 while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) { 174 if ((*po->po_type->pot_extern)(ctx, po) == false) { 175 prop_object_iterator_release(pi); 176 goto out; 177 } 178 } 179 180 prop_object_iterator_release(pi); 181 182 ctx->poec_depth--; 183 for (i = 0; i < ctx->poec_depth; i++) { 184 if (_prop_object_externalize_append_char(ctx, '\t') == false) 185 goto out; 186 } 187 if (_prop_object_externalize_end_tag(ctx, "array") == false) 188 goto out; 189 190 rv = true; 191 192 out: 193 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 194 return (rv); 195} 196 197/* ARGSUSED */ 198static bool 199_prop_array_equals(prop_object_t v1, prop_object_t v2, 200 void **stored_pointer1, void **stored_pointer2, 201 prop_object_t *next_obj1, prop_object_t *next_obj2) 202{ 203 prop_array_t array1 = v1; 204 prop_array_t array2 = v2; 205 uintptr_t idx; 206 bool rv = _PROP_OBJECT_EQUALS_FALSE; 207 208 if (array1 == array2) 209 return (_PROP_OBJECT_EQUALS_TRUE); 210 211 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2); 212 idx = (uintptr_t)*stored_pointer1; 213 214 /* For the first iteration, lock the objects. */ 215 if (idx == 0) { 216 if ((uintptr_t)array1 < (uintptr_t)array2) { 217 _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); 218 _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); 219 } else { 220 _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); 221 _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); 222 } 223 } 224 225 if (array1->pa_count != array2->pa_count) 226 goto out; 227 if (idx == array1->pa_count) { 228 rv = true; 229 goto out; 230 } 231 _PROP_ASSERT(idx < array1->pa_count); 232 233 *stored_pointer1 = (void *)(idx + 1); 234 *stored_pointer2 = (void *)(idx + 1); 235 236 *next_obj1 = array1->pa_array[idx]; 237 *next_obj2 = array2->pa_array[idx]; 238 239 return (_PROP_OBJECT_EQUALS_RECURSE); 240 241 out: 242 _PROP_RWLOCK_UNLOCK(array1->pa_rwlock); 243 _PROP_RWLOCK_UNLOCK(array2->pa_rwlock); 244 return (rv); 245} 246 247static void 248_prop_array_equals_finish(prop_object_t v1, prop_object_t v2) 249{ 250 _PROP_RWLOCK_UNLOCK(((prop_array_t)v1)->pa_rwlock); 251 _PROP_RWLOCK_UNLOCK(((prop_array_t)v2)->pa_rwlock); 252} 253 254static prop_array_t 255_prop_array_alloc(unsigned int capacity) 256{ 257 prop_array_t pa; 258 prop_object_t *array; 259 260 if (capacity != 0) { 261 array = _PROP_CALLOC(capacity * sizeof(prop_object_t), 262 M_PROP_ARRAY); 263 if (array == NULL) 264 return (NULL); 265 } else 266 array = NULL; 267 268 269 pa = _PROP_POOL_GET(_prop_array_pool); 270 if (pa != NULL) { 271 _prop_object_init(&pa->pa_obj, &_prop_object_type_array); 272 pa->pa_obj.po_type = &_prop_object_type_array; 273 274 _PROP_RWLOCK_INIT(pa->pa_rwlock); 275 pa->pa_array = array; 276 pa->pa_capacity = capacity; 277 pa->pa_count = 0; 278 pa->pa_flags = 0; 279 280 pa->pa_version = 0; 281 } else if (array != NULL) 282 _PROP_FREE(array, M_PROP_ARRAY); 283 284 return (pa); 285} 286 287static bool 288_prop_array_expand(prop_array_t pa, unsigned int capacity) 289{ 290 prop_object_t *array, *oarray; 291 292 /* 293 * Array must be WRITE-LOCKED. 294 */ 295 296 oarray = pa->pa_array; 297 298 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY); 299 if (array == NULL) 300 return (false); 301 if (oarray != NULL) 302 memcpy(array, oarray, pa->pa_capacity * sizeof(*array)); 303 pa->pa_array = array; 304 pa->pa_capacity = capacity; 305 306 if (oarray != NULL) 307 _PROP_FREE(oarray, M_PROP_ARRAY); 308 309 return (true); 310} 311 312static prop_object_t 313_prop_array_iterator_next_object_locked(void *v) 314{ 315 struct _prop_array_iterator *pai = v; 316 prop_array_t pa = pai->pai_base.pi_obj; 317 prop_object_t po = NULL; 318 319 _PROP_ASSERT(prop_object_is_array(pa)); 320 321 if (pa->pa_version != pai->pai_base.pi_version) 322 goto out; /* array changed during iteration */ 323 324 _PROP_ASSERT(pai->pai_index <= pa->pa_count); 325 326 if (pai->pai_index == pa->pa_count) 327 goto out; /* we've iterated all objects */ 328 329 po = pa->pa_array[pai->pai_index]; 330 pai->pai_index++; 331 332 out: 333 return (po); 334} 335 336static prop_object_t 337_prop_array_iterator_next_object(void *v) 338{ 339 struct _prop_array_iterator *pai = v; 340 prop_array_t pa __unused = pai->pai_base.pi_obj; 341 prop_object_t po; 342 343 _PROP_ASSERT(prop_object_is_array(pa)); 344 345 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 346 po = _prop_array_iterator_next_object_locked(pai); 347 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 348 return (po); 349} 350 351static void 352_prop_array_iterator_reset_locked(void *v) 353{ 354 struct _prop_array_iterator *pai = v; 355 prop_array_t pa = pai->pai_base.pi_obj; 356 357 _PROP_ASSERT(prop_object_is_array(pa)); 358 359 pai->pai_index = 0; 360 pai->pai_base.pi_version = pa->pa_version; 361} 362 363static void 364_prop_array_iterator_reset(void *v) 365{ 366 struct _prop_array_iterator *pai = v; 367 prop_array_t pa __unused = pai->pai_base.pi_obj; 368 369 _PROP_ASSERT(prop_object_is_array(pa)); 370 371 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 372 _prop_array_iterator_reset_locked(pai); 373 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 374} 375 376/* 377 * prop_array_create -- 378 * Create an empty array. 379 */ 380prop_array_t 381prop_array_create(void) 382{ 383 384 return (_prop_array_alloc(0)); 385} 386 387/* 388 * prop_array_create_with_capacity -- 389 * Create an array with the capacity to store N objects. 390 */ 391prop_array_t 392prop_array_create_with_capacity(unsigned int capacity) 393{ 394 395 return (_prop_array_alloc(capacity)); 396} 397 398/* 399 * prop_array_copy -- 400 * Copy an array. The new array has an initial capacity equal to 401 * the number of objects stored in the original array. The new 402 * array contains references to the original array's objects, not 403 * copies of those objects (i.e. a shallow copy). 404 */ 405prop_array_t 406prop_array_copy(prop_array_t opa) 407{ 408 prop_array_t pa; 409 prop_object_t po; 410 unsigned int idx; 411 412 if (! prop_object_is_array(opa)) 413 return (NULL); 414 415 _PROP_RWLOCK_RDLOCK(opa->pa_rwlock); 416 417 pa = _prop_array_alloc(opa->pa_count); 418 if (pa != NULL) { 419 for (idx = 0; idx < opa->pa_count; idx++) { 420 po = opa->pa_array[idx]; 421 prop_object_retain(po); 422 pa->pa_array[idx] = po; 423 } 424 pa->pa_count = opa->pa_count; 425 pa->pa_flags = opa->pa_flags; 426 } 427 _PROP_RWLOCK_UNLOCK(opa->pa_rwlock); 428 return (pa); 429} 430 431/* 432 * prop_array_copy_mutable -- 433 * Like prop_array_copy(), but the resulting array is mutable. 434 */ 435prop_array_t 436prop_array_copy_mutable(prop_array_t opa) 437{ 438 prop_array_t pa; 439 440 pa = prop_array_copy(opa); 441 if (pa != NULL) 442 pa->pa_flags &= ~PA_F_IMMUTABLE; 443 444 return (pa); 445} 446 447/* 448 * prop_array_capacity -- 449 * Return the capacity of the array. 450 */ 451unsigned int 452prop_array_capacity(prop_array_t pa) 453{ 454 unsigned int rv; 455 456 if (! prop_object_is_array(pa)) 457 return (0); 458 459 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 460 rv = pa->pa_capacity; 461 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 462 463 return (rv); 464} 465 466/* 467 * prop_array_count -- 468 * Return the number of objects stored in the array. 469 */ 470unsigned int 471prop_array_count(prop_array_t pa) 472{ 473 unsigned int rv; 474 475 if (! prop_object_is_array(pa)) 476 return (0); 477 478 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 479 rv = pa->pa_count; 480 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 481 482 return (rv); 483} 484 485/* 486 * prop_array_ensure_capacity -- 487 * Ensure that the array has the capacity to store the specified 488 * total number of objects (inluding the objects already stored 489 * in the array). 490 */ 491bool 492prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) 493{ 494 bool rv; 495 496 if (! prop_object_is_array(pa)) 497 return (false); 498 499 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 500 if (capacity > pa->pa_capacity) 501 rv = _prop_array_expand(pa, capacity); 502 else 503 rv = true; 504 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 505 506 return (rv); 507} 508 509static prop_object_iterator_t 510_prop_array_iterator_locked(prop_array_t pa) 511{ 512 struct _prop_array_iterator *pai; 513 514 if (! prop_object_is_array(pa)) 515 return (NULL); 516 517 pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); 518 if (pai == NULL) 519 return (NULL); 520 pai->pai_base.pi_next_object = _prop_array_iterator_next_object; 521 pai->pai_base.pi_reset = _prop_array_iterator_reset; 522 prop_object_retain(pa); 523 pai->pai_base.pi_obj = pa; 524 _prop_array_iterator_reset_locked(pai); 525 526 return (&pai->pai_base); 527} 528 529/* 530 * prop_array_iterator -- 531 * Return an iterator for the array. The array is retained by 532 * the iterator. 533 */ 534prop_object_iterator_t 535prop_array_iterator(prop_array_t pa) 536{ 537 prop_object_iterator_t pi; 538 539 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 540 pi = _prop_array_iterator_locked(pa); 541 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 542 return (pi); 543} 544 545/* 546 * prop_array_make_immutable -- 547 * Make the array immutable. 548 */ 549void 550prop_array_make_immutable(prop_array_t pa) 551{ 552 553 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 554 if (prop_array_is_immutable(pa) == false) 555 pa->pa_flags |= PA_F_IMMUTABLE; 556 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 557} 558 559/* 560 * prop_array_mutable -- 561 * Returns true if the array is mutable. 562 */ 563bool 564prop_array_mutable(prop_array_t pa) 565{ 566 bool rv; 567 568 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 569 rv = prop_array_is_immutable(pa) == false; 570 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 571 572 return (rv); 573} 574 575/* 576 * prop_array_get -- 577 * Return the object stored at the specified array index. 578 */ 579prop_object_t 580prop_array_get(prop_array_t pa, unsigned int idx) 581{ 582 prop_object_t po = NULL; 583 584 if (! prop_object_is_array(pa)) 585 return (NULL); 586 587 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 588 if (idx >= pa->pa_count) 589 goto out; 590 po = pa->pa_array[idx]; 591 _PROP_ASSERT(po != NULL); 592 out: 593 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 594 return (po); 595} 596 597static bool 598_prop_array_add(prop_array_t pa, prop_object_t po) 599{ 600 601 /* 602 * Array must be WRITE-LOCKED. 603 */ 604 605 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 606 607 if (prop_array_is_immutable(pa) || 608 (pa->pa_count == pa->pa_capacity && 609 _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) 610 return (false); 611 612 prop_object_retain(po); 613 pa->pa_array[pa->pa_count++] = po; 614 pa->pa_version++; 615 616 return (true); 617} 618 619/* 620 * prop_array_set -- 621 * Store a reference to an object at the specified array index. 622 * This method is not allowed to create holes in the array; the 623 * caller must either be setting the object just beyond the existing 624 * count or replacing an already existing object reference. 625 */ 626bool 627prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) 628{ 629 prop_object_t opo; 630 bool rv = false; 631 632 if (! prop_object_is_array(pa)) 633 return (false); 634 635 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 636 637 if (prop_array_is_immutable(pa)) 638 goto out; 639 640 if (idx == pa->pa_count) { 641 rv = _prop_array_add(pa, po); 642 goto out; 643 } 644 645 _PROP_ASSERT(idx < pa->pa_count); 646 647 opo = pa->pa_array[idx]; 648 _PROP_ASSERT(opo != NULL); 649 650 prop_object_retain(po); 651 pa->pa_array[idx] = po; 652 pa->pa_version++; 653 654 prop_object_release(opo); 655 656 rv = true; 657 658 out: 659 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 660 return (rv); 661} 662 663/* 664 * prop_array_add -- 665 * Add a refrerence to an object to the specified array, appending 666 * to the end and growing the array's capacity, if necessary. 667 */ 668bool 669prop_array_add(prop_array_t pa, prop_object_t po) 670{ 671 bool rv; 672 673 if (! prop_object_is_array(pa)) 674 return (false); 675 676 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 677 rv = _prop_array_add(pa, po); 678 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 679 680 return (rv); 681} 682 683/* 684 * prop_array_remove -- 685 * Remove the reference to an object from an array at the specified 686 * index. The array will be compacted following the removal. 687 */ 688void 689prop_array_remove(prop_array_t pa, unsigned int idx) 690{ 691 prop_object_t po; 692 693 if (! prop_object_is_array(pa)) 694 return; 695 696 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 697 698 _PROP_ASSERT(idx < pa->pa_count); 699 700 /* XXX Should this be a _PROP_ASSERT()? */ 701 if (prop_array_is_immutable(pa)) { 702 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 703 return; 704 } 705 706 po = pa->pa_array[idx]; 707 _PROP_ASSERT(po != NULL); 708 709 for (++idx; idx < pa->pa_count; idx++) 710 pa->pa_array[idx - 1] = pa->pa_array[idx]; 711 pa->pa_count--; 712 pa->pa_version++; 713 714 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 715 716 prop_object_release(po); 717} 718 719/* 720 * prop_array_equals -- 721 * Return true if the two arrays are equivalent. Note we do a 722 * by-value comparison of the objects in the array. 723 */ 724bool 725prop_array_equals(prop_array_t array1, prop_array_t array2) 726{ 727 if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) 728 return (false); 729 730 return (prop_object_equals(array1, array2)); 731} 732 733/* 734 * prop_array_externalize -- 735 * Externalize an array, return a NUL-terminated buffer 736 * containing the XML-style representation. The buffer is allocated 737 * with the M_TEMP memory type. 738 */ 739char * 740prop_array_externalize(prop_array_t pa) 741{ 742 struct _prop_object_externalize_context *ctx; 743 char *cp; 744 745 ctx = _prop_object_externalize_context_alloc(); 746 if (ctx == NULL) 747 return (NULL); 748 749 if (_prop_object_externalize_header(ctx) == false || 750 (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || 751 _prop_object_externalize_footer(ctx) == false) { 752 /* We are responsible for releasing the buffer. */ 753 _PROP_FREE(ctx->poec_buf, M_TEMP); 754 _prop_object_externalize_context_free(ctx); 755 return (NULL); 756 } 757 758 cp = ctx->poec_buf; 759 _prop_object_externalize_context_free(ctx); 760 761 return (cp); 762} 763 764/* 765 * _prop_array_internalize -- 766 * Parse an <array>...</array> and return the object created from the 767 * external representation. 768 */ 769static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *, 770 struct _prop_object_internalize_context *); 771 772bool 773_prop_array_internalize(prop_stack_t stack, prop_object_t *obj, 774 struct _prop_object_internalize_context *ctx) 775{ 776 /* We don't currently understand any attributes. */ 777 if (ctx->poic_tagattr != NULL) 778 return (true); 779 780 *obj = prop_array_create(); 781 /* 782 * We are done if the create failed or no child elements exist. 783 */ 784 if (*obj == NULL || ctx->poic_is_empty_element) 785 return (true); 786 787 /* 788 * Opening tag is found, now continue to the first element. 789 */ 790 return (_prop_array_internalize_body(stack, obj, ctx)); 791} 792 793static bool 794_prop_array_internalize_continue(prop_stack_t stack, 795 prop_object_t *obj, 796 struct _prop_object_internalize_context *ctx, 797 void *data, prop_object_t child) 798{ 799 prop_array_t array; 800 801 _PROP_ASSERT(data == NULL); 802 803 if (child == NULL) 804 goto bad; /* Element could not be parsed. */ 805 806 array = *obj; 807 808 if (prop_array_add(array, child) == false) { 809 prop_object_release(child); 810 goto bad; 811 } 812 prop_object_release(child); 813 814 /* 815 * Current element is processed and added, look for next. 816 */ 817 return (_prop_array_internalize_body(stack, obj, ctx)); 818 819 bad: 820 prop_object_release(*obj); 821 *obj = NULL; 822 return (true); 823} 824 825static bool 826_prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, 827 struct _prop_object_internalize_context *ctx) 828{ 829 prop_array_t array = *obj; 830 831 _PROP_ASSERT(array != NULL); 832 833 /* Fetch the next tag. */ 834 if (_prop_object_internalize_find_tag(ctx, NULL, 835 _PROP_TAG_TYPE_EITHER) == false) 836 goto bad; 837 838 /* Check to see if this is the end of the array. */ 839 if (_PROP_TAG_MATCH(ctx, "array") && 840 ctx->poic_tag_type == _PROP_TAG_TYPE_END) { 841 /* It is, so don't iterate any further. */ 842 return (true); 843 } 844 845 if (_prop_stack_push(stack, array, 846 _prop_array_internalize_continue, NULL, NULL)) 847 return (false); 848 849 bad: 850 prop_object_release(array); 851 *obj = NULL; 852 return (true); 853} 854 855/* 856 * prop_array_internalize -- 857 * Create an array by parsing the XML-style representation. 858 */ 859prop_array_t 860prop_array_internalize(const char *xml) 861{ 862 return _prop_generic_internalize(xml, "array"); 863} 864 865#if !defined(_KERNEL) && !defined(_STANDALONE) 866/* 867 * prop_array_externalize_to_file -- 868 * Externalize an array to the specified file. 869 */ 870bool 871prop_array_externalize_to_file(prop_array_t array, const char *fname) 872{ 873 char *xml; 874 bool rv; 875 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 876 877 xml = prop_array_externalize(array); 878 if (xml == NULL) 879 return (false); 880 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 881 if (rv == false) 882 save_errno = errno; 883 _PROP_FREE(xml, M_TEMP); 884 if (rv == false) 885 errno = save_errno; 886 887 return (rv); 888} 889 890/* 891 * prop_array_internalize_from_file -- 892 * Internalize an array from a file. 893 */ 894prop_array_t 895prop_array_internalize_from_file(const char *fname) 896{ 897 struct _prop_object_internalize_mapped_file *mf; 898 prop_array_t array; 899 900 mf = _prop_object_internalize_map_file(fname); 901 if (mf == NULL) 902 return (NULL); 903 array = prop_array_internalize(mf->poimf_xml); 904 _prop_object_internalize_unmap_file(mf); 905 906 return (array); 907} 908#endif /* _KERNEL && !_STANDALONE */ 909