Changeset 116
- Timestamp:
- 01/11/05 19:33:39 (4 years ago)
- Location:
- trunk
- Files:
-
- 3 modified
-
plugins/evolution2_sync/src (modified) (1 prop)
-
src/opensync_convert.c (modified) (8 diffs)
-
tests/check_conv.c (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/plugins/evolution2_sync/src
- Property svn:ignore
-
old new 5 5 .libs 6 6 .deps 7 evolution_sync.loT
-
- Property svn:ignore
-
trunk/src/opensync_convert.c
r93 r116 567 567 char *origdata; 568 568 size_t origsize; 569 osync_debug("OSFRM", 4, "Invoking converter to cheap conversion: %s -> %s", 570 converter->source_format->name, converter->target_format->name); 571 569 572 /* Duplicate the data, if the converter will take it to himself */ 570 573 if (converter->flags & CONV_TAKEOVER) { 571 574 if (!converter->source_format->copy_func) { 572 osync_debug("OSFRM", 0, "Converter %s->%s is CONV_TAKEOVER, but no copy_func", converter->source_format , converter->target_format);575 osync_debug("OSFRM", 0, "Converter %s->%s is CONV_TAKEOVER, but no copy_func", converter->source_format->name, converter->target_format->name); 573 576 return FALSE; 574 577 } … … 661 664 } 662 665 663 typedef struct edge {666 typedef struct vertice { 664 667 OSyncObjFormat *format; 665 668 … … 675 678 /** @} */ 676 679 677 /* Keep reference counts because 680 /** The converter that needs to be run 681 * on previous->data to get the 682 * vertice data 683 */ 684 OSyncFormatConverter *converter; 685 686 /** Keep reference counts because 678 687 * the data returned by converters 679 688 * can be references to other data, 680 689 * and we can't free them too early 690 */ 691 size_t references; 692 693 /** A reference to the previous vertice 694 * on the path 681 695 * 696 * Used when a conversion is necessary 697 */ 698 struct vertice *previous; 699 700 /** The path of converters */ 701 GList *path; 702 703 /** Distance data 682 704 * @{ 683 705 */ 684 struct edge *referenced_data; 685 size_t references; 706 unsigned losses; 707 unsigned objtype_changes; 708 unsigned conversions; 686 709 /** @} */ 687 710 688 /** The path of converters */ 689 GList *path; 690 691 } edge; 711 } vertice; 712 713 /** Compare the distance of two vertices 714 * 715 * First, try to minimize the losses. Then, 716 * try to minimize the conversions between 717 * different objtypes. Then, try to minimize 718 * the total number of conversions. 719 */ 720 int compare_vertice_distance(const void *a, const void *b) 721 { 722 const vertice *va = a; 723 const vertice *vb = b; 724 if (va->losses < vb->losses) 725 return -1; 726 else if (va->losses > vb->losses) 727 return 1; 728 else if (va->objtype_changes < vb->objtype_changes) 729 return -1; 730 else if (va->objtype_changes > vb->objtype_changes) 731 return 1; 732 else if (va->conversions < vb->conversions) 733 return -1; 734 else if (va->conversions > vb->conversions) 735 return 1; 736 else 737 return 0; 738 } 692 739 693 740 typedef struct conv_tree { … … 701 748 } conv_tree; 702 749 703 edge *get_next_edge_neighbour(OSyncFormatEnv *env, conv_tree *tree, edge *me) 750 751 /** Increment a vertice reference count */ 752 static void ref_vertice(vertice *v) 753 { 754 v->references++; 755 } 756 757 /** Dereference an vertice 758 */ 759 static void deref_vertice(vertice *vertice) 760 { 761 /* Decrement the reference count, 762 * and just return if we still 763 * have a reference 764 */ 765 if (--vertice->references > 0) 766 return; 767 768 g_list_free(vertice->path); 769 if (vertice->free_data) { 770 if (!vertice->format->destroy_func) 771 osync_debug("OSFRM", 1, "Memory leak: can't free data of type %s", vertice->format->name); 772 else { 773 osync_debug("OSFRM", 4, "Freeing data of type %s", vertice->format->name); 774 vertice->format->destroy_func(vertice->data, vertice->datasize); 775 } 776 } 777 /* Drop the v->previous reference */ 778 if (vertice->previous) 779 deref_vertice(vertice->previous); 780 g_free(vertice); 781 } 782 783 osync_bool calc_vertice_data(vertice *v) 784 { 785 g_assert(v); 786 787 /* The data was already calculated */ 788 if (v->data) return TRUE; 789 790 /* Ask the previous vertice to calculate its data, too. 791 * 792 * FIXME: Remove recursive call. It doesn't do too much harm, 793 * but it is better to avoid it 794 */ 795 calc_vertice_data(v->previous); 796 797 /* Convert the data, trying to keep references, not copies */ 798 osync_cheap_convert(v->converter, v->previous->data, v->previous->datasize, &v->data, &v->datasize, &v->free_data); 799 800 /* Keep references to old data, if we have done a 'nocopy' 801 * conversion. 802 * 803 * free_data == 0 means that the returned data is a reference 804 * to the original data. 805 * 806 * So, the reference on v->previous will be kept, if 807 * free_data == 0. Otherwise, drop the reference. 808 */ 809 if (v->free_data) { 810 deref_vertice(v->previous); 811 v->previous = NULL; 812 } 813 814 return TRUE; 815 } 816 817 /** Returns a neighbour of the vertice ve 818 * 819 * Returns a new reference to te vertice. The reference 820 * should be dropped using deref_vertice(), later. 821 */ 822 vertice *get_next_vertice_neighbour(OSyncFormatEnv *env, conv_tree *tree, vertice *ve) 704 823 { 705 824 GList *c = NULL; 706 825 OSyncObjFormat *detected_fmt = NULL; 707 if (me->format->detect_func) 708 me->format->detect_func(env, me->data, me->datasize, &detected_fmt); 826 827 if (ve->format->detect_func) { 828 calc_vertice_data(ve); 829 ve->format->detect_func(env, ve->data, ve->datasize, &detected_fmt); 830 } 709 831 710 832 for (c = tree->unused; c; c = c->next) { … … 713 835 714 836 /* Check only valid converters, from the right format */ 715 if (strcmp(converter->source_format->name, me->format->name))837 if (strcmp(converter->source_format->name, ve->format->name)) 716 838 continue; 717 839 … … 734 856 } 735 857 /* Call the detector, and don't use the converter, if it returns FALSE */ 736 if (!det->detect_func(env, me->data, me->datasize)) 858 calc_vertice_data(ve); 859 if (!det->detect_func(env, ve->data, ve->datasize)) 737 860 continue; 738 861 } else { … … 744 867 } 745 868 } 869 870 871 /* From this point, we already found an edge (i.e. a converter) that may 872 * be used 873 */ 874 875 /* Remove the converter from the unused list */ 746 876 tree->unused = g_list_remove(tree->unused, converter); 747 edge *neighbour = g_malloc0(sizeof(edge)); 877 878 /* Allocate the new neighbour */ 879 vertice *neigh = g_malloc0(sizeof(vertice)); 748 880 /* Start with a reference count = 1 */ 749 neighbour->references = 1; 750 neighbour->format = fmt_target; 751 neighbour->path = g_list_copy(me->path); 752 neighbour->path = g_list_append(neighbour->path, converter); 753 754 /* Convert the data, trying to keep references, not copies */ 755 /*FIXME: we may convert only if we need to run a detector */ 756 osync_cheap_convert(converter, me->data, me->datasize, &neighbour->data, &neighbour->datasize, &neighbour->free_data); 757 /* Keep references to old data, to avoid 758 * destroying the edges that have 759 * references to their data 881 neigh->references = 1; 882 neigh->converter = converter; 883 neigh->format = fmt_target; 884 neigh->path = g_list_copy(ve->path); 885 neigh->path = g_list_append(neigh->path, converter); 886 887 /* Distance calculation */ 888 neigh->conversions = ve->conversions + 1; 889 neigh->losses = ve->losses; 890 if (!(converter->flags & CONV_NOTLOSSY)) 891 neigh->losses++; 892 neigh->objtype_changes = ve->objtype_changes; 893 if (converter->source_format->objtype != converter->target_format->objtype) 894 neigh->objtype_changes++; 895 896 /* Keep the reference to the previous vertice, as we may need 897 * its data later 760 898 */ 761 if (!neighbour->free_data) { 762 /* !free_data means that the returned data is a reference */ 763 me->references++; 764 neighbour->referenced_data = me; 765 } 766 return neighbour; 899 ref_vertice(ve); 900 neigh->previous = ve; 901 902 return neigh; 767 903 } 768 904 return NULL; 769 905 } 770 906 771 /** Dereference an edge 772 */ 773 void put_edge(edge *edge) 774 { 775 /* Decrement the reference count, 776 * and just return if we still 777 * have a reference 778 */ 779 if (--edge->references > 0) 780 return; 781 782 g_list_free(edge->path); 783 if (edge->free_data) { 784 if (!edge->format->destroy_func) 785 osync_debug("OSFRM", 1, "Memory leak: can't free data of type %s", edge->format->name); 786 else { 787 osync_debug("OSFRM", 4, "Freeing data of type %s", edge->format->name); 788 edge->format->destroy_func(edge->data, edge->datasize); 789 } 790 } 791 if (edge->referenced_data) 792 /* We are not referencing the data of another edge, anymore */ 793 put_edge(edge->referenced_data); 794 g_free(edge); 795 } 796 907 /*TODO: make the path search function work in a 908 * find-path-to-objtype mode, that will be used for detection: 909 * The only change is: instead of using osync_conv_fmt_in_list(current->format, targets), 910 * stop when the objtype of the vertice is different than the original 911 * objtype. 912 * 913 * Maybe we can have a validate_vertice_fn parameter, a function that would 914 * be called to check if a vertice is 'final' or not. 915 */ 797 916 osync_bool osync_conv_find_shortest_path(OSyncFormatEnv *env, GList *vertices, OSyncChange *start, GList/*OSyncObjFormat * */ *targets, GList **retlist) 798 917 { … … 800 919 osync_bool ret = FALSE; 801 920 conv_tree *tree = g_malloc0(sizeof(conv_tree)); 921 802 922 tree->unused = g_list_copy(vertices); 803 edge *current = g_malloc0(sizeof(edge)); 804 805 edge *neighbour = NULL; 806 current->format = osync_change_get_objformat(start); 807 current->path = NULL; 808 current->data = start->data; 809 current->datasize = start->size; 810 current->free_data = FALSE; 811 current->references = 1; 812 813 while (g_list_length(tree->unused) || g_list_length(tree->search)) { 814 osync_debug("OSFRM", 4, "Searching %s neighbours", current->format->name); 815 while ((neighbour = get_next_edge_neighbour(env, tree, current))) { 923 924 vertice *result = NULL; 925 vertice *begin = g_malloc0(sizeof(vertice)); 926 begin->format = osync_change_get_objformat(start); 927 begin->path = NULL; 928 begin->data = start->data; 929 begin->datasize = start->size; 930 begin->free_data = FALSE; 931 begin->references = 1; 932 933 tree->search = g_list_append(NULL, begin); 934 while (g_list_length(tree->search)) { 935 vertice *neighbour; 936 937 /* Get the first vertice, 938 * and remove it from the queue 939 */ 940 vertice *current = g_list_first(tree->search)->data; 941 tree->search = g_list_delete_link(tree->search, g_list_first(tree->search)); 942 943 osync_debug("OSFRM", 4, "Searching %s. distance: (l: %d, oc: %d, c: %d).", current->format->name, 944 current->losses, current->objtype_changes, current->conversions); 945 /* Check if we have reached a target format */ 946 if (osync_conv_fmt_in_list(current->format, targets)) { 947 /* Done. return the result */ 948 result = current; 949 /* Note: the reference to 'current' will be dropped 950 * after getting the path from 'result' at 951 * the end of the function. 952 */ 953 break; 954 } 955 osync_debug("OSFRM", 4, "Looking %s neighbours.", current->format->name); 956 while ((neighbour = get_next_vertice_neighbour(env, tree, current))) { 816 957 osync_debug("OSFRM", 4, "%s neighbour: %s", current->format->name, neighbour->format->name); 817 /* Check if we have reached a target format */818 if (osync_conv_fmt_in_list(neighbour->format, targets)) {819 //We are done!820 *retlist = neighbour->path;821 ret = TRUE;822 goto reply;823 }824 958 osync_debug("OSFRM", 4, "Adding %s to queue", neighbour->format->name); 825 tree->search = g_list_append(tree->search, neighbour); 826 } 827 if (!tree->search) 828 goto reply; 829 put_edge(current); 830 831 /* Get the next item on the queue */ 832 /*TODO: Use a priority queue to get the 833 * nearest vertices first 834 * (i.e. try to use less lossy converters) 835 */ 836 current = g_list_first(tree->search)->data; 837 tree->search = g_list_remove(tree->search, current); 838 839 } 840 841 reply: 842 g_list_foreach(tree->search, (GFunc)put_edge, NULL); 843 844 if (neighbour) 845 g_free(neighbour); 846 if (current) 847 put_edge(current); 959 tree->search = g_list_insert_sorted(tree->search, neighbour, compare_vertice_distance); 960 } 961 /* Done, drop the reference to the vertice */ 962 deref_vertice(current); 963 } 964 965 /* Remove the references on the search queue */ 966 g_list_foreach(tree->search, (GFunc)deref_vertice, NULL); 967 968 if (result) { 969 /* Found it. Copy the conversion path */ 970 *retlist = g_list_copy(result->path); 971 /* Drop the reference to the result vertice */ 972 deref_vertice(result); 973 ret = TRUE; 974 } 848 975 849 976 g_list_free(tree->unused); -
trunk/tests/check_conv.c
r93 r116 592 592 format = change->format; 593 593 fail_unless(format == format1, NULL); 594 } 595 END_TEST 596 597 START_TEST(conv_prefer_not_lossy) 598 { 599 /* Test if the converter is getting the path that have no 600 * lossy detectors 601 * 602 * F1 -- F2 -- F3 -- F5 603 * \ / 604 * --- F4 ----- 605 * 606 * All converters are not lossy, except F1->F4. 607 * The result path should be: F1 -> F2 -> F3 -> F5 608 */ 609 OSyncFormatEnv *env = osync_conv_env_new(); 610 OSyncObjType *type = osync_conv_register_objtype(env, "O1"); 611 612 OSyncObjFormat *format1 = osync_conv_register_objformat(env, "O1", "F1"); 613 osync_conv_register_objformat(env, "O1", "F2"); 614 osync_conv_register_objformat(env, "O1", "F3"); 615 osync_conv_register_objformat(env, "O1", "F4"); 616 OSyncObjFormat *format5 = osync_conv_register_objformat(env, "O1", "F5"); 617 618 osync_conv_register_converter(env, CONVERTER_DESENCAP, "F1", "F2", convert_addtest, CONV_NOTLOSSY); 619 osync_conv_register_converter(env, CONVERTER_ENCAP, "F2", "F3", convert_addtest, CONV_NOTLOSSY); 620 osync_conv_register_converter(env, CONVERTER_ENCAP, "F3", "F5", convert_addtest, CONV_NOTLOSSY); 621 osync_conv_register_converter(env, CONVERTER_ENCAP, "F1", "F4", convert_addtest2, 0); /* Lossy converter */ 622 osync_conv_register_converter(env, CONVERTER_ENCAP, "F4", "F5", convert_addtest2, CONV_NOTLOSSY); 623 mark_point(); 624 625 OSyncChange *change = osync_change_new(); 626 osync_change_set_objformat(change, format1); 627 osync_change_set_data(change, "data", 5, TRUE); 628 629 mark_point(); 630 631 fail_unless(osync_conv_detect_and_convert(env, change, g_list_append(NULL, format5)), NULL); 632 mark_point(); 633 char *data = osync_change_get_data(change); 634 fail_unless(!strcmp(data, "datatesttesttest"), NULL); 635 636 fail_unless(change->objtype == type, NULL); 637 fail_unless(change->format == format5, NULL); 638 } 639 END_TEST 640 641 START_TEST(conv_prefer_same_objtype) 642 { 643 /* Test if the converter is getting the path 644 * that doesn't change the objtype, even 645 * if it is longer. 646 * 647 * Objtypes: F and G 648 * 649 * F1 -- F2 -- F3 -- F5 -- F6 650 * \ 651 * --- G1 652 * 653 * The target list will be [ F6, G1 ]. 654 * 655 * The result should be F6. 656 */ 657 OSyncFormatEnv *env = osync_conv_env_new(); 658 OSyncObjType *typef = osync_conv_register_objtype(env, "F"); 659 osync_conv_register_objtype(env, "G"); 660 661 OSyncObjFormat *f1 = osync_conv_register_objformat(env, "F", "F1"); 662 osync_conv_register_objformat(env, "F", "F2"); 663 osync_conv_register_objformat(env, "F", "F3"); 664 osync_conv_register_objformat(env, "F", "F4"); 665 osync_conv_register_objformat(env, "F", "F5"); 666 OSyncObjFormat *f6 = osync_conv_register_objformat(env, "F", "F6"); 667 668 OSyncObjFormat *g1 = osync_conv_register_objformat(env, "G", "G1"); 669 670 osync_conv_register_converter(env, CONVERTER_DESENCAP, "F1", "F2", convert_addtest, CONV_NOTLOSSY); 671 osync_conv_register_converter(env, CONVERTER_ENCAP, "F2", "F3", convert_addtest, CONV_NOTLOSSY); 672 osync_conv_register_converter(env, CONVERTER_ENCAP, "F3", "F4", convert_addtest, CONV_NOTLOSSY); 673 osync_conv_register_converter(env, CONVERTER_ENCAP, "F4", "F5", convert_addtest, CONV_NOTLOSSY); 674 osync_conv_register_converter(env, CONVERTER_ENCAP, "F5", "F6", convert_addtest, CONV_NOTLOSSY); 675 osync_conv_register_converter(env, CONVERTER_ENCAP, "F1", "G1", convert_addtest2, CONV_NOTLOSSY); 676 mark_point(); 677 678 OSyncChange *change = osync_change_new(); 679 osync_change_set_objformat(change, f1); 680 osync_change_set_data(change, "data", 5, TRUE); 681 682 mark_point(); 683 684 GList *targets = g_list_append(NULL, g1); 685 targets = g_list_append(targets, f6); 686 fail_unless(osync_conv_detect_and_convert(env, change, targets), NULL); 687 mark_point(); 688 char *data = osync_change_get_data(change); 689 fail_unless(!strcmp(data, "datatesttesttesttesttest"), NULL); 690 691 fail_unless(change->objtype == typef, NULL); 692 fail_unless(change->format == f6, NULL); 693 } 694 END_TEST 695 696 START_TEST(conv_prefer_not_lossy_objtype_change) 697 { 698 /* Test if the converter is getting the path 699 * that have no lossy converters, even if 700 * the objtype is being changed. 701 * 702 * Objtypes: F and G 703 * 704 * F1 -- F2 -- F3 -- F5 -- F6 705 * \ 706 * --- G1 707 * 708 * The target list will be [ F6, G1 ]. 709 * 710 * The converter F2 -> F3 is lossy. 711 * 712 * The result should be G1. 713 */ 714 OSyncFormatEnv *env = osync_conv_env_new(); 715 osync_conv_register_objtype(env, "F"); 716 OSyncObjType *typeg = osync_conv_register_objtype(env, "G"); 717 718 OSyncObjFormat *f1 = osync_conv_register_objformat(env, "F", "F1"); 719 osync_conv_register_objformat(env, "F", "F2"); 720 osync_conv_register_objformat(env, "F", "F3"); 721 osync_conv_register_objformat(env, "F", "F4"); 722 osync_conv_register_objformat(env, "F", "F5"); 723 OSyncObjFormat *f6 = osync_conv_register_objformat(env, "F", "F6"); 724 725 OSyncObjFormat *g1 = osync_conv_register_objformat(env, "G", "G1"); 726 727 osync_conv_register_converter(env, CONVERTER_DESENCAP, "F1", "F2", convert_addtest, CONV_NOTLOSSY); 728 osync_conv_register_converter(env, CONVERTER_ENCAP, "F2", "F3", convert_addtest, 0); /* Lossy */ 729 osync_conv_register_converter(env, CONVERTER_ENCAP, "F3", "F4", convert_addtest, CONV_NOTLOSSY); 730 osync_conv_register_converter(env, CONVERTER_ENCAP, "F4", "F5", convert_addtest, CONV_NOTLOSSY); 731 osync_conv_register_converter(env, CONVERTER_ENCAP, "F5", "F6", convert_addtest, CONV_NOTLOSSY); 732 osync_conv_register_converter(env, CONVERTER_ENCAP, "F1", "G1", convert_addtest2, CONV_NOTLOSSY); 733 mark_point(); 734 735 OSyncChange *change = osync_change_new(); 736 osync_change_set_objformat(change, f1); 737 osync_change_set_data(change, "data", 5, TRUE); 738 739 mark_point(); 740 741 GList *targets = g_list_append(NULL, g1); 742 targets = g_list_append(targets, f6); 743 fail_unless(osync_conv_detect_and_convert(env, change, targets), NULL); 744 mark_point(); 745 char *data = osync_change_get_data(change); 746 fail_unless(!strcmp(data, "datatest2"), NULL); 747 748 fail_unless(change->objtype == typeg, NULL); 749 fail_unless(change->format == g1, NULL); 594 750 } 595 751 END_TEST … … 657 813 tcase_add_test(tc_convert, conv_env_convert_desenc); 658 814 tcase_add_test(tc_convert, conv_env_convert_desenc_complex); 815 tcase_add_test(tc_convert, conv_prefer_not_lossy); 816 tcase_add_test(tc_convert, conv_prefer_same_objtype); 817 tcase_add_test(tc_convert, conv_prefer_not_lossy_objtype_change); 659 818 tcase_add_test(tc_detect, conv_env_detect_and_convert); 660 819 tcase_add_test(tc_detect, conv_env_detect_false);
