diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/plist.c | 253 | ||||
| -rw-r--r-- | src/plist.h | 6 |
2 files changed, 211 insertions, 48 deletions
diff --git a/src/plist.c b/src/plist.c index 31f754d..ea285e0 100644 --- a/src/plist.c +++ b/src/plist.c | |||
| @@ -413,30 +413,59 @@ void plist_free_data(plist_data_t data) | |||
| 413 | } | 413 | } |
| 414 | } | 414 | } |
| 415 | 415 | ||
| 416 | static int plist_free_node(node_t node) | 416 | static int plist_free_node(node_t root) |
| 417 | { | 417 | { |
| 418 | if (!node) return NODE_ERR_INVALID_ARG; | 418 | if (!root) return NODE_ERR_INVALID_ARG; |
| 419 | plist_data_t data = NULL; | 419 | |
| 420 | int node_index = -1; | 420 | int root_index = -1; |
| 421 | 421 | ||
| 422 | if (node->parent) { | 422 | if (root->parent) { |
| 423 | node_index = node_detach(node->parent, node); | 423 | root_index = node_detach(root->parent, root); |
| 424 | if (root_index < 0) { | ||
| 425 | return root_index; | ||
| 426 | } | ||
| 424 | } | 427 | } |
| 425 | 428 | ||
| 426 | data = plist_get_data(node); | 429 | size_t cap = 64, sp = 0; |
| 427 | plist_free_data(data); | 430 | node_t *stack = (node_t*)malloc(cap * sizeof(*stack)); |
| 428 | node->data = NULL; | 431 | if (!stack) return NODE_ERR_NO_MEM; |
| 429 | 432 | ||
| 430 | node_t ch; | 433 | stack[sp++] = root; |
| 431 | for (ch = node_first_child(node); ch; ) { | 434 | |
| 432 | node_t next = node_next_sibling(ch); | 435 | while (sp) { |
| 433 | plist_free_node(ch); | 436 | node_t node = stack[sp - 1]; |
| 434 | ch = next; | 437 | node_t ch = node_first_child(node); |
| 435 | } | 438 | if (ch) { |
| 439 | int di = node_detach(node, ch); | ||
| 440 | if (di < 0) { | ||
| 441 | free(stack); | ||
| 442 | return di; | ||
| 443 | } | ||
| 444 | |||
| 445 | if (sp == cap) { | ||
| 446 | cap += 64; | ||
| 447 | node_t *tmp = (node_t*)realloc(stack, cap * sizeof(*stack)); | ||
| 448 | if (!tmp) { | ||
| 449 | free(stack); | ||
| 450 | return NODE_ERR_NO_MEM; | ||
| 451 | } | ||
| 452 | stack = tmp; | ||
| 453 | } | ||
| 454 | stack[sp++] = ch; | ||
| 455 | continue; | ||
| 456 | } | ||
| 457 | |||
| 458 | plist_data_t data = plist_get_data(node); | ||
| 459 | plist_free_data(data); | ||
| 460 | node->data = NULL; | ||
| 461 | |||
| 462 | node_destroy(node); | ||
| 436 | 463 | ||
| 437 | node_destroy(node); | 464 | sp--; |
| 465 | } | ||
| 438 | 466 | ||
| 439 | return node_index; | 467 | free(stack); |
| 468 | return root_index; | ||
| 440 | } | 469 | } |
| 441 | 470 | ||
| 442 | plist_t plist_new_dict(void) | 471 | plist_t plist_new_dict(void) |
| @@ -572,36 +601,43 @@ void plist_mem_free(void* ptr) | |||
| 572 | } | 601 | } |
| 573 | } | 602 | } |
| 574 | 603 | ||
| 575 | static plist_t plist_copy_node(node_t node) | 604 | static int plist_copy_node_shallow(node_t node, plist_t *out_newnode, plist_data_t *out_newdata, plist_type *out_type) |
| 576 | { | 605 | { |
| 577 | plist_type node_type = PLIST_NONE; | 606 | if (!node || !out_newnode || !out_newdata || !out_type) return NODE_ERR_INVALID_ARG; |
| 578 | plist_t newnode = NULL; | 607 | |
| 579 | plist_data_t data = plist_get_data(node); | 608 | plist_data_t data = plist_get_data(node); |
| 580 | plist_data_t newdata = plist_new_plist_data(); | 609 | if (!data) return NODE_ERR_INVALID_ARG; |
| 581 | 610 | ||
| 582 | assert(data); // plist should always have data | 611 | plist_data_t newdata = plist_new_plist_data(); |
| 583 | assert(newdata); | 612 | if (!newdata) return NODE_ERR_NO_MEM; |
| 584 | 613 | ||
| 585 | memcpy(newdata, data, sizeof(struct plist_data_s)); | 614 | memcpy(newdata, data, sizeof(struct plist_data_s)); |
| 586 | 615 | ||
| 587 | node_type = plist_get_node_type(node); | 616 | plist_type node_type = plist_get_node_type(node); |
| 588 | switch (node_type) { | 617 | switch (node_type) { |
| 589 | case PLIST_DATA: | 618 | case PLIST_DATA: |
| 590 | if (data->buff) { | 619 | if (data->buff) { |
| 591 | newdata->buff = (uint8_t *) malloc(data->length); | 620 | newdata->buff = (uint8_t*)malloc(data->length); |
| 592 | assert(newdata->buff); | 621 | if (!newdata->buff) { |
| 622 | plist_free_data(newdata); | ||
| 623 | return NODE_ERR_NO_MEM; | ||
| 624 | } | ||
| 593 | memcpy(newdata->buff, data->buff, data->length); | 625 | memcpy(newdata->buff, data->buff, data->length); |
| 594 | } else { | 626 | } else { |
| 595 | newdata->buff = NULL; | 627 | newdata->buff = NULL; |
| 596 | newdata->length = 0; | 628 | newdata->length = 0; |
| 597 | } | 629 | } |
| 598 | break; | 630 | break; |
| 631 | |||
| 599 | case PLIST_KEY: | 632 | case PLIST_KEY: |
| 600 | case PLIST_STRING: | 633 | case PLIST_STRING: |
| 601 | if (data->strval) { | 634 | if (data->strval) { |
| 602 | size_t n = strlen(data->strval); | 635 | size_t n = strlen(data->strval); |
| 603 | newdata->strval = (char*)malloc(n+1); | 636 | newdata->strval = (char*)malloc(n+1); |
| 604 | assert(newdata->strval); | 637 | if (!newdata->strval) { |
| 638 | plist_free_data(newdata); | ||
| 639 | return NODE_ERR_NO_MEM; | ||
| 640 | } | ||
| 605 | memcpy(newdata->strval, data->strval, n+1); | 641 | memcpy(newdata->strval, data->strval, n+1); |
| 606 | newdata->length = (uint64_t)n; | 642 | newdata->length = (uint64_t)n; |
| 607 | } else { | 643 | } else { |
| @@ -609,59 +645,180 @@ static plist_t plist_copy_node(node_t node) | |||
| 609 | newdata->length = 0; | 645 | newdata->length = 0; |
| 610 | } | 646 | } |
| 611 | break; | 647 | break; |
| 648 | |||
| 612 | case PLIST_ARRAY: | 649 | case PLIST_ARRAY: |
| 613 | if (data->hashtable) { | 650 | if (data->hashtable) { |
| 614 | ptrarray_t* pa = ptr_array_new(((ptrarray_t*)data->hashtable)->capacity); | 651 | ptrarray_t* pa = ptr_array_new(((ptrarray_t*)data->hashtable)->capacity); |
| 615 | assert(pa); | 652 | if (!pa) { |
| 653 | plist_free_data(newdata); | ||
| 654 | return NODE_ERR_NO_MEM; | ||
| 655 | } | ||
| 616 | newdata->hashtable = pa; | 656 | newdata->hashtable = pa; |
| 617 | } | 657 | } |
| 618 | break; | 658 | break; |
| 659 | |||
| 619 | case PLIST_DICT: | 660 | case PLIST_DICT: |
| 620 | if (data->hashtable) { | 661 | if (data->hashtable) { |
| 621 | hashtable_t* ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); | 662 | hashtable_t* ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); |
| 622 | assert(ht); | 663 | if (!ht) { |
| 664 | plist_free_data(newdata); | ||
| 665 | return NODE_ERR_NO_MEM; | ||
| 666 | } | ||
| 623 | newdata->hashtable = ht; | 667 | newdata->hashtable = ht; |
| 624 | } | 668 | } |
| 625 | break; | 669 | break; |
| 670 | |||
| 626 | default: | 671 | default: |
| 627 | break; | 672 | break; |
| 628 | } | 673 | } |
| 629 | newnode = plist_new_node(newdata); | ||
| 630 | 674 | ||
| 631 | node_t ch; | 675 | plist_t newnode = plist_new_node(newdata); |
| 632 | unsigned int node_index = 0; | 676 | if (!newnode) { |
| 633 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { | 677 | plist_free_data(newdata); |
| 634 | /* copy child node */ | 678 | return NODE_ERR_NO_MEM; |
| 635 | plist_t newch = plist_copy_node(ch); | 679 | } |
| 636 | if (!newch) { | 680 | |
| 637 | plist_free_node((node_t)newnode); | 681 | *out_newnode = newnode; |
| 682 | *out_newdata = newdata; | ||
| 683 | *out_type = node_type; | ||
| 684 | return NODE_ERR_SUCCESS; | ||
| 685 | } | ||
| 686 | |||
| 687 | static plist_t plist_copy_node(node_t root) | ||
| 688 | { | ||
| 689 | typedef struct copy_frame { | ||
| 690 | node_t orig; // original node | ||
| 691 | plist_t copy; // copied node | ||
| 692 | plist_data_t copydata; // copied node's data (for cache updates) | ||
| 693 | plist_type type; // copied node type | ||
| 694 | node_t next_child; // next child of orig to process | ||
| 695 | unsigned int node_index; // child index (for dict key/value odd/even) | ||
| 696 | int depth; // optional depth tracking | ||
| 697 | } copy_frame_t; | ||
| 698 | |||
| 699 | if (!root) return NULL; | ||
| 700 | |||
| 701 | // shallow-copy root first | ||
| 702 | plist_t newroot = NULL; | ||
| 703 | plist_data_t newroot_data = NULL; | ||
| 704 | plist_type newroot_type = PLIST_NONE; | ||
| 705 | |||
| 706 | int r = plist_copy_node_shallow(root, &newroot, &newroot_data, &newroot_type); | ||
| 707 | if (r != NODE_ERR_SUCCESS) { | ||
| 708 | PLIST_ERR("%s: shallow node copy failed (%d)\n", __func__, r); | ||
| 709 | return NULL; | ||
| 710 | } | ||
| 711 | |||
| 712 | // stack of frames | ||
| 713 | size_t cap = 64, sp = 0; | ||
| 714 | copy_frame_t *st = (copy_frame_t*)malloc(cap * sizeof(*st)); | ||
| 715 | if (!st) { | ||
| 716 | plist_free_node((node_t)newroot); | ||
| 717 | return NULL; | ||
| 718 | } | ||
| 719 | |||
| 720 | copy_frame_t cf; | ||
| 721 | cf.orig = root; | ||
| 722 | cf.copy = newroot; | ||
| 723 | cf.copydata = newroot_data; | ||
| 724 | cf.type = newroot_type; | ||
| 725 | cf.next_child = node_first_child(root); | ||
| 726 | cf.node_index = 0; | ||
| 727 | cf.depth = 0; | ||
| 728 | st[sp++] = cf; | ||
| 729 | |||
| 730 | while (sp) { | ||
| 731 | copy_frame_t *f = &st[sp - 1]; | ||
| 732 | |||
| 733 | if (f->depth > NODE_MAX_DEPTH) { | ||
| 734 | plist_free_node((node_t)newroot); | ||
| 735 | free(st); | ||
| 736 | PLIST_ERR("%s: maximum nesting depth exceeded\n", __func__); | ||
| 737 | return NULL; | ||
| 738 | } | ||
| 739 | |||
| 740 | // done with this node? | ||
| 741 | if (!f->next_child) { | ||
| 742 | sp--; | ||
| 743 | continue; | ||
| 744 | } | ||
| 745 | |||
| 746 | // take next child and advance iterator | ||
| 747 | node_t ch = f->next_child; | ||
| 748 | f->next_child = node_next_sibling(ch); | ||
| 749 | |||
| 750 | // shallow copy child | ||
| 751 | plist_t newch = NULL; | ||
| 752 | plist_data_t newch_data = NULL; | ||
| 753 | plist_type newch_type = PLIST_NONE; | ||
| 754 | |||
| 755 | r = plist_copy_node_shallow(ch, &newch, &newch_data, &newch_type); | ||
| 756 | if (r != NODE_ERR_SUCCESS) { | ||
| 757 | plist_free_node((node_t)newroot); | ||
| 758 | free(st); | ||
| 759 | PLIST_ERR("%s: shallow node copy failed (%d)\n", __func__, r); | ||
| 638 | return NULL; | 760 | return NULL; |
| 639 | } | 761 | } |
| 640 | /* attach to new parent node */ | 762 | |
| 641 | int r = node_attach((node_t)newnode, (node_t)newch); | 763 | // attach child to copied parent |
| 764 | r = node_attach((node_t)f->copy, (node_t)newch); | ||
| 642 | if (r != NODE_ERR_SUCCESS) { | 765 | if (r != NODE_ERR_SUCCESS) { |
| 643 | plist_free_node((node_t)newch); | 766 | plist_free_node((node_t)newch); |
| 644 | plist_free_node((node_t)newnode); | 767 | plist_free_node((node_t)newroot); |
| 768 | free(st); | ||
| 769 | PLIST_ERR("%s: failed to attach child to copied parent (%d)\n", __func__, r); | ||
| 645 | return NULL; | 770 | return NULL; |
| 646 | } | 771 | } |
| 647 | /* if needed, add child node to lookup table of parent node */ | 772 | |
| 648 | switch (node_type) { | 773 | // update lookup cache on the *parent* copy |
| 774 | switch (f->type) { | ||
| 649 | case PLIST_ARRAY: | 775 | case PLIST_ARRAY: |
| 650 | if (newdata->hashtable) { | 776 | if (f->copydata->hashtable) { |
| 651 | ptr_array_add((ptrarray_t*)newdata->hashtable, newch); | 777 | ptr_array_add((ptrarray_t*)f->copydata->hashtable, newch); |
| 652 | } | 778 | } |
| 653 | break; | 779 | break; |
| 780 | |||
| 654 | case PLIST_DICT: | 781 | case PLIST_DICT: |
| 655 | if (newdata->hashtable && (node_index % 2 != 0)) { | 782 | if (f->copydata->hashtable && (f->node_index % 2 != 0)) { |
| 656 | hash_table_insert((hashtable_t*)newdata->hashtable, (node_prev_sibling((node_t)newch))->data, newch); | 783 | node_t new_key = node_prev_sibling((node_t)newch); |
| 784 | if (new_key) { | ||
| 785 | hash_table_insert((hashtable_t*)f->copydata->hashtable, new_key->data, newch); | ||
| 786 | } | ||
| 657 | } | 787 | } |
| 658 | break; | 788 | break; |
| 789 | |||
| 659 | default: | 790 | default: |
| 660 | break; | 791 | break; |
| 661 | } | 792 | } |
| 662 | node_index++; | 793 | |
| 794 | f->node_index++; | ||
| 795 | |||
| 796 | // push child frame to process its children | ||
| 797 | if (sp == cap) { | ||
| 798 | cap += 64; | ||
| 799 | copy_frame_t *tmp = (copy_frame_t*)realloc(st, cap * sizeof(*st)); | ||
| 800 | if (!tmp) { | ||
| 801 | plist_free_node((node_t)newroot); | ||
| 802 | free(st); | ||
| 803 | PLIST_ERR("%s: out of memory when reallocating\n", __func__); | ||
| 804 | return NULL; | ||
| 805 | } | ||
| 806 | st = tmp; | ||
| 807 | } | ||
| 808 | |||
| 809 | copy_frame_t nf; | ||
| 810 | nf.orig = ch; | ||
| 811 | nf.copy = newch; | ||
| 812 | nf.copydata = newch_data; | ||
| 813 | nf.type = newch_type; | ||
| 814 | nf.next_child = node_first_child(ch); | ||
| 815 | nf.node_index = 0; | ||
| 816 | nf.depth = f->depth + 1; | ||
| 817 | st[sp++] = nf; | ||
| 663 | } | 818 | } |
| 664 | return newnode; | 819 | |
| 820 | free(st); | ||
| 821 | return newroot; | ||
| 665 | } | 822 | } |
| 666 | 823 | ||
| 667 | plist_t plist_copy(plist_t node) | 824 | plist_t plist_copy(plist_t node) |
diff --git a/src/plist.h b/src/plist.h index e310bcc..7228696 100644 --- a/src/plist.h +++ b/src/plist.h | |||
| @@ -49,9 +49,15 @@ | |||
| 49 | #endif | 49 | #endif |
| 50 | #endif | 50 | #endif |
| 51 | 51 | ||
| 52 | #include "node.h" | ||
| 53 | |||
| 52 | #ifndef PLIST_MAX_NESTING_DEPTH | 54 | #ifndef PLIST_MAX_NESTING_DEPTH |
| 55 | #ifdef NODE_MAX_DEPTH | ||
| 56 | #define PLIST_MAX_NESTING_DEPTH NODE_MAX_DEPTH | ||
| 57 | #else | ||
| 53 | #define PLIST_MAX_NESTING_DEPTH 512 | 58 | #define PLIST_MAX_NESTING_DEPTH 512 |
| 54 | #endif | 59 | #endif |
| 60 | #endif | ||
| 55 | 61 | ||
| 56 | #include "plist/plist.h" | 62 | #include "plist/plist.h" |
| 57 | 63 | ||
