Changeset 116

Show
Ignore:
Timestamp:
01/11/05 19:33:39 (4 years ago)
Author:
ehabkost
Message:

- Refactored the path search function:

  • Made it try to use non-lossy converters and not change objtypes, added tests for these cases
  • Made it call the converters to get the converted data only if it is necessary to run a detector on the data
Location:
trunk
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • trunk/plugins/evolution2_sync/src

    • Property svn:ignore
      •  

        old new  
        55.libs 
        66.deps 
         7evolution_sync.loT 
  • trunk/src/opensync_convert.c

    r93 r116  
    567567        char *origdata; 
    568568        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 
    569572        /* Duplicate the data, if the converter will take it to himself */ 
    570573        if (converter->flags & CONV_TAKEOVER) { 
    571574                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); 
    573576                        return FALSE; 
    574577                } 
     
    661664} 
    662665 
    663 typedef struct edge { 
     666typedef struct vertice { 
    664667        OSyncObjFormat *format; 
    665668 
     
    675678        /** @} */ 
    676679 
    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 
    678687         * the data returned by converters 
    679688         * can be references to other data, 
    680689         * 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 
    681695         * 
     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 
    682704         * @{ 
    683705         */ 
    684         struct edge *referenced_data; 
    685         size_t references; 
     706        unsigned losses; 
     707        unsigned objtype_changes; 
     708        unsigned conversions; 
    686709        /** @} */ 
    687710 
    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 */ 
     720int 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} 
    692739 
    693740typedef struct conv_tree { 
     
    701748} conv_tree; 
    702749 
    703 edge *get_next_edge_neighbour(OSyncFormatEnv *env, conv_tree *tree, edge *me) 
     750 
     751/** Increment a vertice reference count */ 
     752static void ref_vertice(vertice *v) 
     753{ 
     754        v->references++; 
     755} 
     756 
     757/** Dereference an vertice 
     758 */ 
     759static 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 
     783osync_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 */ 
     822vertice *get_next_vertice_neighbour(OSyncFormatEnv *env, conv_tree *tree, vertice *ve) 
    704823{ 
    705824        GList *c = NULL; 
    706825        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        } 
    709831                 
    710832        for (c = tree->unused; c; c = c->next) { 
     
    713835                 
    714836                /* 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)) 
    716838                        continue; 
    717839 
     
    734856                                } 
    735857                                /* 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)) 
    737860                                        continue; 
    738861                        } else { 
     
    744867                        } 
    745868                } 
     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 */ 
    746876                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)); 
    748880                /* 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 
    760898                 */ 
    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; 
    767903        } 
    768904        return NULL; 
    769905} 
    770906 
    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 */ 
    797916osync_bool osync_conv_find_shortest_path(OSyncFormatEnv *env, GList *vertices, OSyncChange *start, GList/*OSyncObjFormat * */ *targets, GList **retlist) 
    798917{        
     
    800919        osync_bool ret = FALSE; 
    801920        conv_tree *tree = g_malloc0(sizeof(conv_tree)); 
     921 
    802922        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))) { 
    816957                        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                         } 
    824958                        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        } 
    848975         
    849976        g_list_free(tree->unused); 
  • trunk/tests/check_conv.c

    r93 r116  
    592592  format = change->format; 
    593593  fail_unless(format == format1, NULL); 
     594} 
     595END_TEST 
     596 
     597START_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} 
     639END_TEST 
     640 
     641START_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} 
     694END_TEST 
     695 
     696START_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); 
    594750} 
    595751END_TEST 
     
    657813        tcase_add_test(tc_convert, conv_env_convert_desenc); 
    658814        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); 
    659818        tcase_add_test(tc_detect, conv_env_detect_and_convert); 
    660819        tcase_add_test(tc_detect, conv_env_detect_false);