1 /* $NetBSD: prop_array.c,v 1.19 2008/08/03 04:00:12 thorpej 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 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 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 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 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 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 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 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 273 pa = _PROP_POOL_GET(_prop_array_pool); 274 if (pa != NULL) { 275 _prop_object_init(&pa->pa_obj, &_prop_object_type_array); 276 pa->pa_obj.po_type = &_prop_object_type_array; 277 278 _PROP_RWLOCK_INIT(pa->pa_rwlock); 279 pa->pa_array = array; 280 pa->pa_capacity = capacity; 281 pa->pa_count = 0; 282 pa->pa_flags = 0; 283 284 pa->pa_version = 0; 285 } else if (array != NULL) 286 _PROP_FREE(array, M_PROP_ARRAY); 287 288 return (pa); 289 } 290 291 static bool 292 _prop_array_expand(prop_array_t pa, unsigned int capacity) 293 { 294 prop_object_t *array, *oarray; 295 296 /* 297 * Array must be WRITE-LOCKED. 298 */ 299 300 oarray = pa->pa_array; 301 302 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY); 303 if (array == NULL) 304 return (false); 305 if (oarray != NULL) 306 memcpy(array, oarray, pa->pa_capacity * sizeof(*array)); 307 pa->pa_array = array; 308 pa->pa_capacity = capacity; 309 310 if (oarray != NULL) 311 _PROP_FREE(oarray, M_PROP_ARRAY); 312 313 return (true); 314 } 315 316 static prop_object_t 317 _prop_array_iterator_next_object_locked(void *v) 318 { 319 struct _prop_array_iterator *pai = v; 320 prop_array_t pa = pai->pai_base.pi_obj; 321 prop_object_t po = NULL; 322 323 _PROP_ASSERT(prop_object_is_array(pa)); 324 325 if (pa->pa_version != pai->pai_base.pi_version) 326 goto out; /* array changed during iteration */ 327 328 _PROP_ASSERT(pai->pai_index <= pa->pa_count); 329 330 if (pai->pai_index == pa->pa_count) 331 goto out; /* we've iterated all objects */ 332 333 po = pa->pa_array[pai->pai_index]; 334 pai->pai_index++; 335 336 out: 337 return (po); 338 } 339 340 static prop_object_t 341 _prop_array_iterator_next_object(void *v) 342 { 343 struct _prop_array_iterator *pai = v; 344 prop_array_t pa __unused = pai->pai_base.pi_obj; 345 prop_object_t po; 346 347 _PROP_ASSERT(prop_object_is_array(pa)); 348 349 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 350 po = _prop_array_iterator_next_object_locked(pai); 351 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 352 return (po); 353 } 354 355 static void 356 _prop_array_iterator_reset_locked(void *v) 357 { 358 struct _prop_array_iterator *pai = v; 359 prop_array_t pa = pai->pai_base.pi_obj; 360 361 _PROP_ASSERT(prop_object_is_array(pa)); 362 363 pai->pai_index = 0; 364 pai->pai_base.pi_version = pa->pa_version; 365 } 366 367 static void 368 _prop_array_iterator_reset(void *v) 369 { 370 struct _prop_array_iterator *pai = v; 371 prop_array_t pa __unused = pai->pai_base.pi_obj; 372 373 _PROP_ASSERT(prop_object_is_array(pa)); 374 375 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 376 _prop_array_iterator_reset_locked(pai); 377 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 378 } 379 380 /* 381 * prop_array_create -- 382 * Create an empty array. 383 */ 384 prop_array_t 385 prop_array_create(void) 386 { 387 388 return (_prop_array_alloc(0)); 389 } 390 391 /* 392 * prop_array_create_with_capacity -- 393 * Create an array with the capacity to store N objects. 394 */ 395 prop_array_t 396 prop_array_create_with_capacity(unsigned int capacity) 397 { 398 399 return (_prop_array_alloc(capacity)); 400 } 401 402 /* 403 * prop_array_copy -- 404 * Copy an array. The new array has an initial capacity equal to 405 * the number of objects stored in the original array. The new 406 * array contains references to the original array's objects, not 407 * copies of those objects (i.e. a shallow copy). 408 */ 409 prop_array_t 410 prop_array_copy(prop_array_t opa) 411 { 412 prop_array_t pa; 413 prop_object_t po; 414 unsigned int idx; 415 416 if (! prop_object_is_array(opa)) 417 return (NULL); 418 419 _PROP_RWLOCK_RDLOCK(opa->pa_rwlock); 420 421 pa = _prop_array_alloc(opa->pa_count); 422 if (pa != NULL) { 423 for (idx = 0; idx < opa->pa_count; idx++) { 424 po = opa->pa_array[idx]; 425 prop_object_retain(po); 426 pa->pa_array[idx] = po; 427 } 428 pa->pa_count = opa->pa_count; 429 pa->pa_flags = opa->pa_flags; 430 } 431 _PROP_RWLOCK_UNLOCK(opa->pa_rwlock); 432 return (pa); 433 } 434 435 /* 436 * prop_array_copy_mutable -- 437 * Like prop_array_copy(), but the resulting array is mutable. 438 */ 439 prop_array_t 440 prop_array_copy_mutable(prop_array_t opa) 441 { 442 prop_array_t pa; 443 444 pa = prop_array_copy(opa); 445 if (pa != NULL) 446 pa->pa_flags &= ~PA_F_IMMUTABLE; 447 448 return (pa); 449 } 450 451 /* 452 * prop_array_capacity -- 453 * Return the capacity of the array. 454 */ 455 unsigned int 456 prop_array_capacity(prop_array_t pa) 457 { 458 unsigned int rv; 459 460 if (! prop_object_is_array(pa)) 461 return (0); 462 463 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 464 rv = pa->pa_capacity; 465 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 466 467 return (rv); 468 } 469 470 /* 471 * prop_array_count -- 472 * Return the number of objects stored in the array. 473 */ 474 unsigned int 475 prop_array_count(prop_array_t pa) 476 { 477 unsigned int rv; 478 479 if (! prop_object_is_array(pa)) 480 return (0); 481 482 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 483 rv = pa->pa_count; 484 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 485 486 return (rv); 487 } 488 489 /* 490 * prop_array_ensure_capacity -- 491 * Ensure that the array has the capacity to store the specified 492 * total number of objects (inluding the objects already stored 493 * in the array). 494 */ 495 bool 496 prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) 497 { 498 bool rv; 499 500 if (! prop_object_is_array(pa)) 501 return (false); 502 503 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 504 if (capacity > pa->pa_capacity) 505 rv = _prop_array_expand(pa, capacity); 506 else 507 rv = true; 508 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 509 510 return (rv); 511 } 512 513 static prop_object_iterator_t 514 _prop_array_iterator_locked(prop_array_t pa) 515 { 516 struct _prop_array_iterator *pai; 517 518 if (! prop_object_is_array(pa)) 519 return (NULL); 520 521 pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); 522 if (pai == NULL) 523 return (NULL); 524 pai->pai_base.pi_next_object = _prop_array_iterator_next_object; 525 pai->pai_base.pi_reset = _prop_array_iterator_reset; 526 prop_object_retain(pa); 527 pai->pai_base.pi_obj = pa; 528 _prop_array_iterator_reset_locked(pai); 529 530 return (&pai->pai_base); 531 } 532 533 /* 534 * prop_array_iterator -- 535 * Return an iterator for the array. The array is retained by 536 * the iterator. 537 */ 538 prop_object_iterator_t 539 prop_array_iterator(prop_array_t pa) 540 { 541 prop_object_iterator_t pi; 542 543 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 544 pi = _prop_array_iterator_locked(pa); 545 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 546 return (pi); 547 } 548 549 /* 550 * prop_array_make_immutable -- 551 * Make the array immutable. 552 */ 553 void 554 prop_array_make_immutable(prop_array_t pa) 555 { 556 557 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 558 if (prop_array_is_immutable(pa) == false) 559 pa->pa_flags |= PA_F_IMMUTABLE; 560 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 561 } 562 563 /* 564 * prop_array_mutable -- 565 * Returns true if the array is mutable. 566 */ 567 bool 568 prop_array_mutable(prop_array_t pa) 569 { 570 bool rv; 571 572 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 573 rv = prop_array_is_immutable(pa) == false; 574 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 575 576 return (rv); 577 } 578 579 /* 580 * prop_array_get -- 581 * Return the object stored at the specified array index. 582 */ 583 prop_object_t 584 prop_array_get(prop_array_t pa, unsigned int idx) 585 { 586 prop_object_t po = NULL; 587 588 if (! prop_object_is_array(pa)) 589 return (NULL); 590 591 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 592 if (idx >= pa->pa_count) 593 goto out; 594 po = pa->pa_array[idx]; 595 _PROP_ASSERT(po != NULL); 596 out: 597 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 598 return (po); 599 } 600 601 static bool 602 _prop_array_add(prop_array_t pa, prop_object_t po) 603 { 604 605 /* 606 * Array must be WRITE-LOCKED. 607 */ 608 609 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 610 611 if (prop_array_is_immutable(pa) || 612 (pa->pa_count == pa->pa_capacity && 613 _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) 614 return (false); 615 616 prop_object_retain(po); 617 pa->pa_array[pa->pa_count++] = po; 618 pa->pa_version++; 619 620 return (true); 621 } 622 623 /* 624 * prop_array_set -- 625 * Store a reference to an object at the specified array index. 626 * This method is not allowed to create holes in the array; the 627 * caller must either be setting the object just beyond the existing 628 * count or replacing an already existing object reference. 629 */ 630 bool 631 prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) 632 { 633 prop_object_t opo; 634 bool rv = false; 635 636 if (! prop_object_is_array(pa)) 637 return (false); 638 639 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 640 641 if (prop_array_is_immutable(pa)) 642 goto out; 643 644 if (idx == pa->pa_count) { 645 rv = _prop_array_add(pa, po); 646 goto out; 647 } 648 649 _PROP_ASSERT(idx < pa->pa_count); 650 651 opo = pa->pa_array[idx]; 652 _PROP_ASSERT(opo != NULL); 653 654 prop_object_retain(po); 655 pa->pa_array[idx] = po; 656 pa->pa_version++; 657 658 prop_object_release(opo); 659 660 rv = true; 661 662 out: 663 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 664 return (rv); 665 } 666 667 /* 668 * prop_array_add -- 669 * Add a refrerence to an object to the specified array, appending 670 * to the end and growing the array's capacity, if necessary. 671 */ 672 bool 673 prop_array_add(prop_array_t pa, prop_object_t po) 674 { 675 bool rv; 676 677 if (! prop_object_is_array(pa)) 678 return (false); 679 680 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 681 rv = _prop_array_add(pa, po); 682 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 683 684 return (rv); 685 } 686 687 /* 688 * prop_array_remove -- 689 * Remove the reference to an object from an array at the specified 690 * index. The array will be compacted following the removal. 691 */ 692 void 693 prop_array_remove(prop_array_t pa, unsigned int idx) 694 { 695 prop_object_t po; 696 697 if (! prop_object_is_array(pa)) 698 return; 699 700 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 701 702 _PROP_ASSERT(idx < pa->pa_count); 703 704 /* XXX Should this be a _PROP_ASSERT()? */ 705 if (prop_array_is_immutable(pa)) { 706 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 707 return; 708 } 709 710 po = pa->pa_array[idx]; 711 _PROP_ASSERT(po != NULL); 712 713 for (++idx; idx < pa->pa_count; idx++) 714 pa->pa_array[idx - 1] = pa->pa_array[idx]; 715 pa->pa_count--; 716 pa->pa_version++; 717 718 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 719 720 prop_object_release(po); 721 } 722 723 /* 724 * prop_array_equals -- 725 * Return true if the two arrays are equivalent. Note we do a 726 * by-value comparison of the objects in the array. 727 */ 728 bool 729 prop_array_equals(prop_array_t array1, prop_array_t array2) 730 { 731 if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) 732 return (false); 733 734 return (prop_object_equals(array1, array2)); 735 } 736 737 /* 738 * prop_array_externalize -- 739 * Externalize an array, return a NUL-terminated buffer 740 * containing the XML-style representation. The buffer is allocated 741 * with the M_TEMP memory type. 742 */ 743 char * 744 prop_array_externalize(prop_array_t pa) 745 { 746 struct _prop_object_externalize_context *ctx; 747 char *cp; 748 749 ctx = _prop_object_externalize_context_alloc(); 750 if (ctx == NULL) 751 return (NULL); 752 753 if (_prop_object_externalize_header(ctx) == false || 754 (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || 755 _prop_object_externalize_footer(ctx) == false) { 756 /* We are responsible for releasing the buffer. */ 757 _PROP_FREE(ctx->poec_buf, M_TEMP); 758 _prop_object_externalize_context_free(ctx); 759 return (NULL); 760 } 761 762 cp = ctx->poec_buf; 763 _prop_object_externalize_context_free(ctx); 764 765 return (cp); 766 } 767 768 /* 769 * _prop_array_internalize -- 770 * Parse an <array>...</array> and return the object created from the 771 * external representation. 772 */ 773 static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *, 774 struct _prop_object_internalize_context *); 775 776 bool 777 _prop_array_internalize(prop_stack_t stack, prop_object_t *obj, 778 struct _prop_object_internalize_context *ctx) 779 { 780 /* We don't currently understand any attributes. */ 781 if (ctx->poic_tagattr != NULL) 782 return (true); 783 784 *obj = prop_array_create(); 785 /* 786 * We are done if the create failed or no child elements exist. 787 */ 788 if (*obj == NULL || ctx->poic_is_empty_element) 789 return (true); 790 791 /* 792 * Opening tag is found, now continue to the first element. 793 */ 794 return (_prop_array_internalize_body(stack, obj, ctx)); 795 } 796 797 static bool 798 _prop_array_internalize_continue(prop_stack_t stack, 799 prop_object_t *obj, 800 struct _prop_object_internalize_context *ctx, 801 void *data, prop_object_t child) 802 { 803 prop_array_t array; 804 805 _PROP_ASSERT(data == NULL); 806 807 if (child == NULL) 808 goto bad; /* Element could not be parsed. */ 809 810 array = *obj; 811 812 if (prop_array_add(array, child) == false) { 813 prop_object_release(child); 814 goto bad; 815 } 816 prop_object_release(child); 817 818 /* 819 * Current element is processed and added, look for next. 820 */ 821 return (_prop_array_internalize_body(stack, obj, ctx)); 822 823 bad: 824 prop_object_release(*obj); 825 *obj = NULL; 826 return (true); 827 } 828 829 static bool 830 _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, 831 struct _prop_object_internalize_context *ctx) 832 { 833 prop_array_t array = *obj; 834 835 _PROP_ASSERT(array != NULL); 836 837 /* Fetch the next tag. */ 838 if (_prop_object_internalize_find_tag(ctx, NULL, 839 _PROP_TAG_TYPE_EITHER) == false) 840 goto bad; 841 842 /* Check to see if this is the end of the array. */ 843 if (_PROP_TAG_MATCH(ctx, "array") && 844 ctx->poic_tag_type == _PROP_TAG_TYPE_END) { 845 /* It is, so don't iterate any further. */ 846 return (true); 847 } 848 849 if (_prop_stack_push(stack, array, 850 _prop_array_internalize_continue, NULL, NULL)) 851 return (false); 852 853 bad: 854 prop_object_release(array); 855 *obj = NULL; 856 return (true); 857 } 858 859 /* 860 * prop_array_internalize -- 861 * Create an array by parsing the XML-style representation. 862 */ 863 prop_array_t 864 prop_array_internalize(const char *xml) 865 { 866 return _prop_generic_internalize(xml, "array"); 867 } 868 869 #if !defined(_KERNEL) && !defined(_STANDALONE) 870 /* 871 * prop_array_externalize_to_file -- 872 * Externalize an array to the specified file. 873 */ 874 bool 875 prop_array_externalize_to_file(prop_array_t array, const char *fname) 876 { 877 char *xml; 878 bool rv; 879 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 880 881 xml = prop_array_externalize(array); 882 if (xml == NULL) 883 return (false); 884 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 885 if (rv == false) 886 save_errno = errno; 887 _PROP_FREE(xml, M_TEMP); 888 if (rv == false) 889 errno = save_errno; 890 891 return (rv); 892 } 893 894 /* 895 * prop_array_internalize_from_file -- 896 * Internalize an array from a file. 897 */ 898 prop_array_t 899 prop_array_internalize_from_file(const char *fname) 900 { 901 struct _prop_object_internalize_mapped_file *mf; 902 prop_array_t array; 903 904 mf = _prop_object_internalize_map_file(fname); 905 if (mf == NULL) 906 return (NULL); 907 array = prop_array_internalize(mf->poimf_xml); 908 _prop_object_internalize_unmap_file(mf); 909 910 return (array); 911 } 912 #endif /* _KERNEL && !_STANDALONE */ 913