1 /*
2 ** Copyright (c) 2006 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
13 **   drh@hwaci.com
14 **   http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This module implements the interface to the delta generator.
19 */
20 #include "config.h"
21 #include "deltacmd.h"
22 
23 /*
24 ** Create a delta that describes the change from pOriginal to pTarget
25 ** and put that delta in pDelta.  The pDelta blob is assumed to be
26 ** uninitialized.
27 */
blob_delta_create(Blob * pOriginal,Blob * pTarget,Blob * pDelta)28 int blob_delta_create(Blob *pOriginal, Blob *pTarget, Blob *pDelta){
29   const char *zOrig, *zTarg;
30   int lenOrig, lenTarg;
31   int len;
32   char *zRes;
33   blob_zero(pDelta);
34   zOrig = blob_materialize(pOriginal);
35   lenOrig = blob_size(pOriginal);
36   zTarg = blob_materialize(pTarget);
37   lenTarg = blob_size(pTarget);
38   blob_resize(pDelta, lenTarg+16);
39   zRes = blob_materialize(pDelta);
40   len = delta_create(zOrig, lenOrig, zTarg, lenTarg, zRes);
41   blob_resize(pDelta, len);
42   return 0;
43 }
44 
45 /*
46 ** COMMAND: test-delta-create
47 **
48 ** Usage: %fossil test-delta-create FILE1 FILE2 DELTA
49 **
50 ** Create and output a delta that carries FILE1 into FILE2.
51 ** Store the result in DELTA.
52 */
delta_create_cmd(void)53 void delta_create_cmd(void){
54   Blob orig, target, delta;
55   if( g.argc!=5 ){
56     usage("ORIGIN TARGET DELTA");
57   }
58   if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
59     fossil_fatal("cannot read %s", g.argv[2]);
60   }
61   if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
62     fossil_fatal("cannot read %s", g.argv[3]);
63   }
64   blob_delta_create(&orig, &target, &delta);
65   if( blob_write_to_file(&delta, g.argv[4])<blob_size(&delta) ){
66     fossil_fatal("cannot write %s", g.argv[4]);
67   }
68   blob_reset(&orig);
69   blob_reset(&target);
70   blob_reset(&delta);
71 }
72 
73 /*
74 ** COMMAND: test-delta-analyze
75 **
76 ** Usage: %fossil test-delta-analyze FILE1 FILE2
77 **
78 ** Create and a delta that carries FILE1 into FILE2.  Print the
79 ** number bytes copied and the number of bytes inserted.
80 */
delta_analyze_cmd(void)81 void delta_analyze_cmd(void){
82   Blob orig, target, delta;
83   int nCopy = 0;
84   int nInsert = 0;
85   int sz1, sz2, sz3;
86   if( g.argc!=4 ){
87     usage("ORIGIN TARGET");
88   }
89   if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
90     fossil_fatal("cannot read %s", g.argv[2]);
91   }
92   if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
93     fossil_fatal("cannot read %s", g.argv[3]);
94   }
95   blob_delta_create(&orig, &target, &delta);
96   delta_analyze(blob_buffer(&delta), blob_size(&delta), &nCopy, &nInsert);
97   sz1 = blob_size(&orig);
98   sz2 = blob_size(&target);
99   sz3 = blob_size(&delta);
100   blob_reset(&orig);
101   blob_reset(&target);
102   blob_reset(&delta);
103   fossil_print("original size:  %8d\n", sz1);
104   fossil_print("bytes copied:   %8d (%.2f%% of target)\n",
105                nCopy, (100.0*nCopy)/sz2);
106   fossil_print("bytes inserted: %8d (%.2f%% of target)\n",
107                nInsert, (100.0*nInsert)/sz2);
108   fossil_print("final size:     %8d\n", sz2);
109   fossil_print("delta size:     %8d\n", sz3);
110 }
111 
112 /*
113 ** Apply the delta in pDelta to the original file pOriginal to generate
114 ** the target file pTarget.  The pTarget blob is initialized by this
115 ** routine.
116 **
117 ** It works ok for pTarget and pOriginal to be the same blob.
118 **
119 ** Return the length of the target.  Return -1 if there is an error.
120 */
blob_delta_apply(Blob * pOriginal,Blob * pDelta,Blob * pTarget)121 int blob_delta_apply(Blob *pOriginal, Blob *pDelta, Blob *pTarget){
122   int len, n;
123   Blob out;
124 
125   n = delta_output_size(blob_buffer(pDelta), blob_size(pDelta));
126   blob_zero(&out);
127   if( n<0 ) return -1;
128   blob_resize(&out, n);
129   len = delta_apply(
130      blob_buffer(pOriginal), blob_size(pOriginal),
131      blob_buffer(pDelta), blob_size(pDelta),
132      blob_buffer(&out));
133   if( len<0 ){
134     blob_reset(&out);
135   }else if( len!=n ){
136     blob_resize(&out, len);
137   }
138   if( pTarget==pOriginal ){
139     blob_reset(pOriginal);
140   }
141   *pTarget = out;
142   return len;
143 }
144 
145 /*
146 ** COMMAND: test-delta-apply
147 **
148 ** Usage: %fossil test-delta-apply FILE1 DELTA
149 **
150 ** Apply DELTA to FILE1 and output the result.
151 */
delta_apply_cmd(void)152 void delta_apply_cmd(void){
153   Blob orig, target, delta;
154   if( g.argc!=5 ){
155     usage("ORIGIN DELTA TARGET");
156   }
157   if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
158     fossil_fatal("cannot read %s", g.argv[2]);
159   }
160   if( blob_read_from_file(&delta, g.argv[3], ExtFILE)<0 ){
161     fossil_fatal("cannot read %s", g.argv[3]);
162   }
163   blob_delta_apply(&orig, &delta, &target);
164   if( blob_write_to_file(&target, g.argv[4])<blob_size(&target) ){
165     fossil_fatal("cannot write %s", g.argv[4]);
166   }
167   blob_reset(&orig);
168   blob_reset(&target);
169   blob_reset(&delta);
170 }
171 
172 
173 /*
174 ** COMMAND: test-delta
175 **
176 ** Usage: %fossil test-delta FILE1 FILE2
177 **
178 ** Read two files named on the command-line.  Create and apply deltas
179 ** going in both directions.  Verify that the original files are
180 ** correctly recovered.
181 */
cmd_test_delta(void)182 void cmd_test_delta(void){
183   Blob f1, f2;     /* Original file content */
184   Blob d12, d21;   /* Deltas from f1->f2 and f2->f1 */
185   Blob a1, a2;     /* Recovered file content */
186   if( g.argc!=4 ) usage("FILE1 FILE2");
187   blob_read_from_file(&f1, g.argv[2], ExtFILE);
188   blob_read_from_file(&f2, g.argv[3], ExtFILE);
189   blob_delta_create(&f1, &f2, &d12);
190   blob_delta_create(&f2, &f1, &d21);
191   blob_delta_apply(&f1, &d12, &a2);
192   blob_delta_apply(&f2, &d21, &a1);
193   if( blob_compare(&f1,&a1) || blob_compare(&f2, &a2) ){
194     fossil_fatal("delta test failed");
195   }
196   fossil_print("ok\n");
197 }
198