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