| | @@ -772,89 +772,140 @@ |
| 772 | 772 | db_bind_int(&s1, ":rid", rid); |
| 773 | 773 | db_exec(&s1); |
| 774 | 774 | } |
| 775 | 775 | |
| 776 | 776 | /* |
| 777 | | -** Change the storage of rid so that it is a delta of srcid. |
| 777 | +** Try to change the storage of rid so that it is a delta from one |
| 778 | +** of the artifacts given in aSrc[0]..aSrc[nSrc-1]. The aSrc[*] that |
| 779 | +** gives the smallest delta is choosen. |
| 778 | 780 | ** |
| 779 | 781 | ** If rid is already a delta from some other place then no |
| 780 | | -** conversion occurs and this is a no-op unless force==1. |
| 782 | +** conversion occurs and this is a no-op unless force==1. If force==1, |
| 783 | +** then nSrc must also be 1. |
| 781 | 784 | ** |
| 782 | 785 | ** Never generate a delta that carries a private artifact into a public |
| 783 | 786 | ** artifact. Otherwise, when we go to send the public artifact on a |
| 784 | 787 | ** sync operation, the other end of the sync will never be able to receive |
| 785 | 788 | ** the source of the delta. It is OK to delta private->private and |
| 786 | 789 | ** public->private and public->public. Just no private->public delta. |
| 787 | 790 | ** |
| 788 | | -** If srcid is a delta that depends on rid, then srcid is |
| 789 | | -** converted to undeltaed text. |
| 791 | +** If aSrc[bestSrc] is already a dleta that depends on rid, then it is |
| 792 | +** converted to undeltaed text before the aSrc[bestSrc]->rid delta is |
| 793 | +** created, in order to prevent a delta loop. |
| 790 | 794 | ** |
| 791 | | -** If either rid or srcid contain less than 50 bytes, or if the |
| 795 | +** If either rid or aSrc[i] contain less than 50 bytes, or if the |
| 792 | 796 | ** resulting delta does not achieve a compression of at least 25% |
| 793 | 797 | ** the rid is left untouched. |
| 794 | 798 | ** |
| 795 | 799 | ** Return 1 if a delta is made and 0 if no delta occurs. |
| 796 | 800 | */ |
| 797 | | -int content_deltify(int rid, int srcid, int force){ |
| 801 | +int content_deltify(int rid, int *aSrc, int nSrc, int force){ |
| 798 | 802 | int s; |
| 799 | | - Blob data, src, delta; |
| 800 | | - Stmt s1, s2; |
| 801 | | - int rc = 0; |
| 803 | + Blob data; /* Content of rid */ |
| 804 | + Blob src; /* Content of aSrc[i] */ |
| 805 | + Blob delta; /* Delta from aSrc[i] to rid */ |
| 806 | + Blob bestDelta; /* Best delta seen so far */ |
| 807 | + int bestSrc = 0; /* Which aSrc is the source of the best delta */ |
| 808 | + int rc = 0; /* Value to return */ |
| 809 | + int i; /* Loop variable for aSrc[] */ |
| 802 | 810 | |
| 803 | | - if( srcid==rid ) return 0; |
| 811 | + /* If rid is already a child (a delta) of some other artifact, return |
| 812 | + ** immediately if the force flags is false |
| 813 | + */ |
| 804 | 814 | if( !force && findSrcid(rid)>0 ) return 0; |
| 805 | | - if( content_is_private(srcid) && !content_is_private(rid) ){ |
| 806 | | - return 0; |
| 807 | | - } |
| 808 | | - s = srcid; |
| 809 | | - while( (s = findSrcid(s))>0 ){ |
| 810 | | - if( s==rid ){ |
| 811 | | - content_undelta(srcid); |
| 812 | | - break; |
| 813 | | - } |
| 814 | | - } |
| 815 | | - content_get(srcid, &src); |
| 816 | | - if( blob_size(&src)<50 ){ |
| 817 | | - blob_reset(&src); |
| 818 | | - return 0; |
| 819 | | - } |
| 815 | + |
| 816 | + /* Get the complete content of the object to be delta-ed. If the size |
| 817 | + ** is less than 50 bytes, then there really is no point in trying to do |
| 818 | + ** a delta, so return immediately |
| 819 | + */ |
| 820 | 820 | content_get(rid, &data); |
| 821 | 821 | if( blob_size(&data)<50 ){ |
| 822 | | - blob_reset(&src); |
| 822 | + /* Do not try to create a delta for objects smaller than 50 bytes */ |
| 823 | 823 | blob_reset(&data); |
| 824 | 824 | return 0; |
| 825 | 825 | } |
| 826 | | - blob_delta_create(&src, &data, &delta); |
| 827 | | - if( blob_size(&delta) <= blob_size(&data)*0.75 ){ |
| 826 | + blob_init(&bestDelta, 0, 0); |
| 827 | + |
| 828 | + /* Loop over all candidate delta sources */ |
| 829 | + for(i=0; i<nSrc; i++){ |
| 830 | + int srcid = aSrc[i]; |
| 831 | + if( srcid==rid ) continue; |
| 832 | + if( content_is_private(srcid) && !content_is_private(rid) ) continue; |
| 833 | + |
| 834 | + /* Compute all ancestors of srcid and make sure rid is not one of them. |
| 835 | + ** If rid is an ancestor of srcid, then making rid a decendent of srcid |
| 836 | + ** would create a delta loop. */ |
| 837 | + s = srcid; |
| 838 | + while( (s = findSrcid(s))>0 ){ |
| 839 | + if( s==rid ){ |
| 840 | + content_undelta(srcid); |
| 841 | + break; |
| 842 | + } |
| 843 | + } |
| 844 | + if( s!=0 ) continue; |
| 845 | + |
| 846 | + content_get(srcid, &src); |
| 847 | + if( blob_size(&src)<50 ){ |
| 848 | + /* The source is smaller then 50 bytes, so don't bother trying to use it*/ |
| 849 | + blob_reset(&src); |
| 850 | + continue; |
| 851 | + } |
| 852 | + blob_delta_create(&src, &data, &delta); |
| 853 | + if( blob_size(&delta) < blob_size(&data)*0.75 |
| 854 | + && (bestSrc<0 || blob_size(&delta)<blob_size(&bestDelta)) |
| 855 | + ){ |
| 856 | + /* This is the best delta seen so far. Remember it */ |
| 857 | + blob_reset(&bestDelta); |
| 858 | + bestDelta = delta; |
| 859 | + bestSrc = srcid; |
| 860 | + }else{ |
| 861 | + /* This delta is not a candidate for becoming the new parent of rid */ |
| 862 | + blob_reset(&delta); |
| 863 | + } |
| 864 | + blob_reset(&src); |
| 865 | + } |
| 866 | + |
| 867 | + /* If there is a winning candidate for the new parent of rid, then |
| 868 | + ** make that candidate the new parent now */ |
| 869 | + if( bestSrc>0 ){ |
| 870 | + Stmt s1, s2; /* Statements used to create the delta */ |
| 828 | 871 | blob_compress(&delta, &delta); |
| 829 | 872 | db_prepare(&s1, "UPDATE blob SET content=:data WHERE rid=%d", rid); |
| 830 | | - db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, srcid); |
| 831 | | - db_bind_blob(&s1, ":data", &delta); |
| 873 | + db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, bestSrc); |
| 874 | + db_bind_blob(&s1, ":data", &bestDelta); |
| 832 | 875 | db_begin_transaction(); |
| 833 | 876 | db_exec(&s1); |
| 834 | 877 | db_exec(&s2); |
| 835 | 878 | db_end_transaction(0); |
| 836 | 879 | db_finalize(&s1); |
| 837 | 880 | db_finalize(&s2); |
| 838 | 881 | verify_before_commit(rid); |
| 839 | 882 | rc = 1; |
| 840 | 883 | } |
| 841 | | - blob_reset(&src); |
| 842 | 884 | blob_reset(&data); |
| 843 | | - blob_reset(&delta); |
| 885 | + blob_reset(&bestDelta); |
| 844 | 886 | return rc; |
| 845 | 887 | } |
| 846 | 888 | |
| 847 | 889 | /* |
| 848 | 890 | ** COMMAND: test-content-deltify |
| 849 | 891 | ** |
| 850 | | -** Convert the content at RID into a delta from SRCID. |
| 892 | +** Usage: %fossil RID SRCID SRCID ... [-force] |
| 893 | +** |
| 894 | +** Convert the content at RID into a delta one of the from SRCIDs. |
| 851 | 895 | */ |
| 852 | 896 | void test_content_deltify_cmd(void){ |
| 853 | | - if( g.argc!=5 ) usage("RID SRCID FORCE"); |
| 897 | + int nSrc; |
| 898 | + int *aSrc; |
| 899 | + int i; |
| 900 | + int bForce = find_option("force",0,0)!=0; |
| 901 | + if( g.argc<3 ) usage("[--force] RID SRCID SRCID..."); |
| 902 | + aSrc = fossil_malloc( (g.argc-2)*sizeof(aSrc[0]) ); |
| 903 | + nSrc = 0; |
| 904 | + for(i=2; i<g.argc; i++) aSrc[nSrc++] = atoi(g.argv[i]); |
| 854 | 905 | db_must_be_within_tree(); |
| 855 | | - content_deltify(atoi(g.argv[2]), atoi(g.argv[3]), atoi(g.argv[4])); |
| 906 | + content_deltify(atoi(g.argv[2]), aSrc, nSrc, bForce); |
| 856 | 907 | } |
| 857 | 908 | |
| 858 | 909 | /* |
| 859 | 910 | ** Return true if Blob p looks like it might be a parsable control artifact. |
| 860 | 911 | */ |
| 861 | 912 | |