rb-bsdiff 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ bsdiff: Copyright 2003-2005 Colin Percival
2
+ cloudability/rb-bsdiff: Copyright (c) 2012, Cloudability Inc.
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+
25
+ ---
26
+
27
+ Note: This covers only Cloudability's contributions to the software, and the
28
+ original bsdiff software. Contact Todd Fisher for more information about his
29
+ modifications to bsdiff, and original Ruby wrapper code.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Ruby bindings to bsdiff and bspatch
2
+
3
+
4
+ ## Installation
5
+
6
+ ```bash
7
+ gem install 'rb-bsdiff'
8
+ ```
9
+
10
+ Or via Gemfile:
11
+
12
+ ```ruby
13
+ gem 'rb-bsdiff'
14
+ ```
15
+
16
+ As this uses native-code extensions you will of course require a working build
17
+ toolchain. Additionally, you will need the BZip2 libraries to be present on
18
+ your system. On OSX they should already be present. On Linux, you may need to
19
+ install relevant packages, for example:
20
+
21
+ ```bash
22
+ sudo apt-get install libbz2-1.0 libbz2-dev
23
+ ```
24
+
25
+ See http://bzip.org/ for more information.
26
+
27
+
28
+ ## Usage
29
+
30
+ ```ruby
31
+ # Generate a patch from ext/b0 to ext/b1 as p0.
32
+ BSDiff.diff('ext/b0', 'ext/b1', 'p0')
33
+
34
+ # apply patch p0 to ext/b0 and produce output as b3. b3 should be
35
+ # identical to ext/b1.
36
+ BSDiff.patch('ext/b0', 'b3', 'p0')
37
+ ```
38
+
39
+
40
+ ## Caveats
41
+
42
+ As the underlying code only works with files, so does this API. Pass in only
43
+ filenames for parameters.
44
+
45
+
46
+ ## License
47
+
48
+ * Cloudability's contributions to this project are made available under the [Simplified BSD License](http://en.wikipedia.org/wiki/BSD_licenses#2-clause_license_.28.22Simplified_BSD_License.22_or_.22FreeBSD_License.22.29).
49
+ * BSDiff is produced under the [Simplified BSD License](http://en.wikipedia.org/wiki/BSD_licenses#2-clause_license_.28.22Simplified_BSD_License.22_or_.22FreeBSD_License.22.29)
50
+ * The original version of this gem, by Todd Fisher does not specify a license for his contributions, however:
51
+ * His direct modifications to the BSDiff source retain the original copyright notice without modification or annotation.
52
+ * His new contributions do not specify a license.
data/ext/bsdiff.c ADDED
@@ -0,0 +1,417 @@
1
+ /*-
2
+ * Copyright 2003-2005 Colin Percival
3
+ * All rights reserved
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted providing that the following conditions
7
+ * are met:
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ *
14
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ * POSSIBILITY OF SUCH DAMAGE.
25
+ */
26
+
27
+ #if 0
28
+ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05 cperciva Exp $");
29
+ #endif
30
+
31
+ #include <sys/types.h>
32
+
33
+ #include <bzlib.h>
34
+ #include <fcntl.h>
35
+ #include <stdio.h>
36
+ #include <stdlib.h>
37
+ #include <string.h>
38
+ #include <unistd.h>
39
+
40
+ #include <ruby.h>
41
+
42
+ #define MIN(x,y) (((x)<(y)) ? (x) : (y))
43
+
44
+ static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
45
+ {
46
+ off_t i,j,k,x,tmp,jj,kk;
47
+
48
+ if(len<16) {
49
+ for(k=start;k<start+len;k+=j) {
50
+ j=1;x=V[I[k]+h];
51
+ for(i=1;k+i<start+len;i++) {
52
+ if(V[I[k+i]+h]<x) {
53
+ x=V[I[k+i]+h];
54
+ j=0;
55
+ };
56
+ if(V[I[k+i]+h]==x) {
57
+ tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
58
+ j++;
59
+ };
60
+ };
61
+ for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
62
+ if(j==1) I[k]=-1;
63
+ };
64
+ return;
65
+ };
66
+
67
+ x=V[I[start+len/2]+h];
68
+ jj=0;kk=0;
69
+ for(i=start;i<start+len;i++) {
70
+ if(V[I[i]+h]<x) jj++;
71
+ if(V[I[i]+h]==x) kk++;
72
+ };
73
+ jj+=start;kk+=jj;
74
+
75
+ i=start;j=0;k=0;
76
+ while(i<jj) {
77
+ if(V[I[i]+h]<x) {
78
+ i++;
79
+ } else if(V[I[i]+h]==x) {
80
+ tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
81
+ j++;
82
+ } else {
83
+ tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
84
+ k++;
85
+ };
86
+ };
87
+
88
+ while(jj+j<kk) {
89
+ if(V[I[jj+j]+h]==x) {
90
+ j++;
91
+ } else {
92
+ tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
93
+ k++;
94
+ };
95
+ };
96
+
97
+ if(jj>start) split(I,V,start,jj-start,h);
98
+
99
+ for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
100
+ if(jj==kk-1) I[jj]=-1;
101
+
102
+ if(start+len>kk) split(I,V,kk,start+len-kk,h);
103
+ }
104
+
105
+ static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
106
+ {
107
+ off_t buckets[256];
108
+ off_t i,h,len;
109
+
110
+ for(i=0;i<256;i++) buckets[i]=0;
111
+ for(i=0;i<oldsize;i++) buckets[old[i]]++;
112
+ for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
113
+ for(i=255;i>0;i--) buckets[i]=buckets[i-1];
114
+ buckets[0]=0;
115
+
116
+ for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
117
+ I[0]=oldsize;
118
+ for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
119
+ V[oldsize]=0;
120
+ for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
121
+ I[0]=-1;
122
+
123
+ for(h=1;I[0]!=-(oldsize+1);h+=h) {
124
+ len=0;
125
+ for(i=0;i<oldsize+1;) {
126
+ if(I[i]<0) {
127
+ len-=I[i];
128
+ i-=I[i];
129
+ } else {
130
+ if(len) I[i-len]=-len;
131
+ len=V[I[i]]+1-i;
132
+ split(I,V,i,len,h);
133
+ i+=len;
134
+ len=0;
135
+ };
136
+ };
137
+ if(len) I[i-len]=-len;
138
+ };
139
+
140
+ for(i=0;i<oldsize+1;i++) I[V[i]]=i;
141
+ }
142
+
143
+ static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize)
144
+ {
145
+ off_t i;
146
+
147
+ for(i=0;(i<oldsize)&&(i<newsize);i++)
148
+ if(old[i]!=new[i]) break;
149
+
150
+ return i;
151
+ }
152
+
153
+ static off_t search(off_t *I,u_char *old,off_t oldsize,
154
+ u_char *new,off_t newsize,off_t st,off_t en,off_t *pos)
155
+ {
156
+ off_t x,y;
157
+
158
+ if(en-st<2) {
159
+ x=matchlen(old+I[st],oldsize-I[st],new,newsize);
160
+ y=matchlen(old+I[en],oldsize-I[en],new,newsize);
161
+
162
+ if(x>y) {
163
+ *pos=I[st];
164
+ return x;
165
+ } else {
166
+ *pos=I[en];
167
+ return y;
168
+ }
169
+ };
170
+
171
+ x=st+(en-st)/2;
172
+ if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) {
173
+ return search(I,old,oldsize,new,newsize,x,en,pos);
174
+ } else {
175
+ return search(I,old,oldsize,new,newsize,st,x,pos);
176
+ };
177
+ }
178
+
179
+ static void offtout(off_t x,u_char *buf)
180
+ {
181
+ off_t y;
182
+
183
+ if(x<0) y=-x; else y=x;
184
+
185
+ buf[0]=y%256;y-=buf[0];
186
+ y=y/256;buf[1]=y%256;y-=buf[1];
187
+ y=y/256;buf[2]=y%256;y-=buf[2];
188
+ y=y/256;buf[3]=y%256;y-=buf[3];
189
+ y=y/256;buf[4]=y%256;y-=buf[4];
190
+ y=y/256;buf[5]=y%256;y-=buf[5];
191
+ y=y/256;buf[6]=y%256;y-=buf[6];
192
+ y=y/256;buf[7]=y%256;
193
+
194
+ if(x<0) buf[7]|=0x80;
195
+ }
196
+
197
+ /* create a patch file from oldfile diff'ed by newfile */
198
+ int bsdiff_files(const char *oldfile, const char *newfile, const char *patchfile)
199
+ {
200
+ int fd;
201
+ u_char *old,*new;
202
+ off_t oldsize,newsize;
203
+ off_t *I,*V;
204
+ off_t scan,pos=0,len;
205
+ off_t lastscan,lastpos,lastoffset;
206
+ off_t oldscore,scsc;
207
+ off_t s,Sf,lenf,Sb,lenb;
208
+ off_t overlap,Ss,lens;
209
+ off_t i;
210
+ off_t dblen,eblen;
211
+ u_char *db,*eb;
212
+ u_char buf[8];
213
+ u_char header[32];
214
+ FILE * pf;
215
+ BZFILE * pfbz2;
216
+ int bz2err;
217
+
218
+ /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
219
+ that we never try to malloc(0) and get a NULL pointer */
220
+ if(((fd=open(oldfile,O_RDONLY,0))<0) ||
221
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
222
+ ((old=malloc(oldsize+1))==NULL) ||
223
+ (lseek(fd,0,SEEK_SET)!=0) ||
224
+ (read(fd,old,oldsize)!=oldsize) ||
225
+ (close(fd)==-1)) {
226
+ rb_raise(rb_eRuntimeError, "%s",oldfile);
227
+ }
228
+
229
+ if(((I=malloc((oldsize+1)*sizeof(off_t)))==NULL) ||
230
+ ((V=malloc((oldsize+1)*sizeof(off_t)))==NULL)) rb_raise(rb_eRuntimeError, "malloc error");
231
+
232
+ qsufsort(I,V,old,oldsize);
233
+
234
+ free(V);
235
+
236
+ /* Allocate newsize+1 bytes instead of newsize bytes to ensure
237
+ that we never try to malloc(0) and get a NULL pointer */
238
+ if(((fd=open(newfile,O_RDONLY,0))<0) ||
239
+ ((newsize=lseek(fd,0,SEEK_END))==-1) ||
240
+ ((new=malloc(newsize+1))==NULL) ||
241
+ (lseek(fd,0,SEEK_SET)!=0) ||
242
+ (read(fd,new,newsize)!=newsize) ||
243
+ (close(fd)==-1)) rb_raise(rb_eRuntimeError, "%s",newfile);
244
+
245
+ if(((db=malloc(newsize+1))==NULL) ||
246
+ ((eb=malloc(newsize+1))==NULL)) rb_raise(rb_eRuntimeError,"malloc error");
247
+ dblen=0;
248
+ eblen=0;
249
+
250
+ /* Create the patch file */
251
+ if ((pf = fopen(patchfile, "w")) == NULL)
252
+ rb_raise(rb_eRuntimeError, "%s", patchfile);
253
+
254
+ /* Header is
255
+ 0 8 "BSDIFF40"
256
+ 8 8 length of bzip2ed ctrl block
257
+ 16 8 length of bzip2ed diff block
258
+ 24 8 length of new file */
259
+ /* File is
260
+ 0 32 Header
261
+ 32 ?? Bzip2ed ctrl block
262
+ ?? ?? Bzip2ed diff block
263
+ ?? ?? Bzip2ed extra block */
264
+ memcpy(header,"BSDIFF40",8);
265
+ offtout(0, header + 8);
266
+ offtout(0, header + 16);
267
+ offtout(newsize, header + 24);
268
+ if (fwrite(header, 32, 1, pf) != 1)
269
+ rb_raise(rb_eRuntimeError, "fwrite(%s)", patchfile);
270
+
271
+ /* Compute the differences, writing ctrl as we go */
272
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
273
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
274
+ scan=0;len=0;
275
+ lastscan=0;lastpos=0;lastoffset=0;
276
+ while(scan<newsize) {
277
+ oldscore=0;
278
+
279
+ for(scsc=scan+=len;scan<newsize;scan++) {
280
+ len=search(I,old,oldsize,new+scan,newsize-scan,
281
+ 0,oldsize,&pos);
282
+
283
+ for(;scsc<scan+len;scsc++)
284
+ if((scsc+lastoffset<oldsize) &&
285
+ (old[scsc+lastoffset] == new[scsc]))
286
+ oldscore++;
287
+
288
+ if(((len==oldscore) && (len!=0)) ||
289
+ (len>oldscore+8)) break;
290
+
291
+ if((scan+lastoffset<oldsize) &&
292
+ (old[scan+lastoffset] == new[scan]))
293
+ oldscore--;
294
+ };
295
+
296
+ if((len!=oldscore) || (scan==newsize)) {
297
+ s=0;Sf=0;lenf=0;
298
+ for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
299
+ if(old[lastpos+i]==new[lastscan+i]) s++;
300
+ i++;
301
+ if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
302
+ };
303
+
304
+ lenb=0;
305
+ if(scan<newsize) {
306
+ s=0;Sb=0;
307
+ for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
308
+ if(old[pos-i]==new[scan-i]) s++;
309
+ if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
310
+ };
311
+ };
312
+
313
+ if(lastscan+lenf>scan-lenb) {
314
+ overlap=(lastscan+lenf)-(scan-lenb);
315
+ s=0;Ss=0;lens=0;
316
+ for(i=0;i<overlap;i++) {
317
+ if(new[lastscan+lenf-overlap+i]==
318
+ old[lastpos+lenf-overlap+i]) s++;
319
+ if(new[scan-lenb+i]==
320
+ old[pos-lenb+i]) s--;
321
+ if(s>Ss) { Ss=s; lens=i+1; };
322
+ };
323
+
324
+ lenf+=lens-overlap;
325
+ lenb-=lens;
326
+ };
327
+
328
+ for(i=0;i<lenf;i++)
329
+ db[dblen+i]=new[lastscan+i]-old[lastpos+i];
330
+ for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
331
+ eb[eblen+i]=new[lastscan+lenf+i];
332
+
333
+ dblen+=lenf;
334
+ eblen+=(scan-lenb)-(lastscan+lenf);
335
+
336
+ offtout(lenf,buf);
337
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
338
+ if (bz2err != BZ_OK)
339
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
340
+
341
+ offtout((scan-lenb)-(lastscan+lenf),buf);
342
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
343
+ if (bz2err != BZ_OK)
344
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
345
+
346
+ offtout((pos-lenb)-(lastpos+lenf),buf);
347
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
348
+ if (bz2err != BZ_OK)
349
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
350
+
351
+ lastscan=scan-lenb;
352
+ lastpos=pos-lenb;
353
+ lastoffset=pos-scan;
354
+ };
355
+ };
356
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
357
+ if (bz2err != BZ_OK)
358
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
359
+
360
+ /* Compute size of compressed ctrl data */
361
+ if ((len = ftello(pf)) == -1)
362
+ rb_raise(rb_eRuntimeError, "ftello");
363
+ offtout(len-32, header + 8);
364
+
365
+ /* Write compressed diff data */
366
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
367
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
368
+ BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
369
+ if (bz2err != BZ_OK)
370
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
371
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
372
+ if (bz2err != BZ_OK)
373
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
374
+
375
+ /* Compute size of compressed diff data */
376
+ if ((newsize = ftello(pf)) == -1)
377
+ rb_raise(rb_eRuntimeError, "ftello");
378
+ offtout(newsize - len, header + 16);
379
+
380
+ /* Write compressed extra data */
381
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
382
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
383
+ BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
384
+ if (bz2err != BZ_OK)
385
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
386
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
387
+ if (bz2err != BZ_OK)
388
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
389
+
390
+ /* Seek to the beginning, write the header, and close the file */
391
+ if (fseeko(pf, 0, SEEK_SET))
392
+ rb_raise(rb_eRuntimeError, "fseeko");
393
+ if (fwrite(header, 32, 1, pf) != 1)
394
+ rb_raise(rb_eRuntimeError, "fwrite(%s)", patchfile);
395
+ if (fclose(pf))
396
+ rb_raise(rb_eRuntimeError, "fclose");
397
+
398
+ /* Free the memory we used */
399
+ free(db);
400
+ free(eb);
401
+ free(I);
402
+ free(old);
403
+ free(new);
404
+
405
+ return 0;
406
+ }
407
+
408
+ /*
409
+ * create a patch buffer from oldbfufer diff'ed by newbfufer
410
+ *
411
+ * same as bsdiff_files, except works in memory
412
+ *
413
+ * TODO
414
+ */
415
+ //char* bsdiff_buffer(const char *oldbuffer, const char *newbuffer)
416
+ //{
417
+ //}
data/ext/bsdiff.h ADDED
@@ -0,0 +1,6 @@
1
+ #ifndef RB_BSDIFF_H
2
+ #define RB_BSDIFF_H
3
+
4
+ int bsdiff_files(const char *oldfile, const char *newfile, const char *patchfile);
5
+
6
+ #endif
data/ext/bspatch.c ADDED
@@ -0,0 +1,203 @@
1
+ /*-
2
+ * Copyright 2003-2005 Colin Percival
3
+ * All rights reserved
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted providing that the following conditions
7
+ * are met:
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ *
14
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ * POSSIBILITY OF SUCH DAMAGE.
25
+ */
26
+
27
+ #if 0
28
+ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
29
+ #endif
30
+
31
+ #include <bzlib.h>
32
+ #include <stdlib.h>
33
+ #include <stdio.h>
34
+ #include <string.h>
35
+ #include <unistd.h>
36
+ #include <fcntl.h>
37
+
38
+ #include <ruby.h>
39
+
40
+ static off_t offtin(u_char *buf)
41
+ {
42
+ off_t y;
43
+
44
+ y=buf[7]&0x7F;
45
+ y=y*256;y+=buf[6];
46
+ y=y*256;y+=buf[5];
47
+ y=y*256;y+=buf[4];
48
+ y=y*256;y+=buf[3];
49
+ y=y*256;y+=buf[2];
50
+ y=y*256;y+=buf[1];
51
+ y=y*256;y+=buf[0];
52
+
53
+ if(buf[7]&0x80) y=-y;
54
+
55
+ return y;
56
+ }
57
+
58
+ int bspatch_files(const char *oldfile, const char *newfile, const char *patchfile)
59
+ {
60
+ FILE * f, * cpf, * dpf, * epf;
61
+ BZFILE * cpfbz2, * dpfbz2, * epfbz2;
62
+ int cbz2err, dbz2err, ebz2err;
63
+ int fd;
64
+ ssize_t oldsize,newsize;
65
+ ssize_t bzctrllen,bzdatalen;
66
+ u_char header[32],buf[8];
67
+ u_char *old, *new;
68
+ off_t oldpos,newpos;
69
+ off_t ctrl[3];
70
+ off_t lenread;
71
+ off_t i;
72
+
73
+ /* Open patch file */
74
+ if ((f = fopen(patchfile, "r")) == NULL)
75
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
76
+
77
+ /*
78
+ File format:
79
+ 0 8 "BSDIFF40"
80
+ 8 8 X
81
+ 16 8 Y
82
+ 24 8 sizeof(newfile)
83
+ 32 X bzip2(control block)
84
+ 32+X Y bzip2(diff block)
85
+ 32+X+Y ??? bzip2(extra block)
86
+ with control block a set of triples (x,y,z) meaning "add x bytes
87
+ from oldfile to x bytes from the diff block; copy y bytes from the
88
+ extra block; seek forwards in oldfile by z bytes".
89
+ */
90
+
91
+ /* Read header */
92
+ if (fread(header, 1, 32, f) < 32) {
93
+ if (feof(f))
94
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
95
+ rb_raise(rb_eRuntimeError, "fread(%s)", patchfile);
96
+ }
97
+
98
+ /* Check for appropriate magic */
99
+ if (memcmp(header, "BSDIFF40", 8) != 0)
100
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
101
+
102
+ /* Read lengths from header */
103
+ bzctrllen=offtin(header+8);
104
+ bzdatalen=offtin(header+16);
105
+ newsize=offtin(header+24);
106
+ if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
107
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
108
+
109
+ /* Close patch file and re-open it via libbzip2 at the right places */
110
+ if (fclose(f))
111
+ rb_raise(rb_eRuntimeError, "fclose(%s)", patchfile);
112
+ if ((cpf = fopen(patchfile, "r")) == NULL)
113
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
114
+ if (fseeko(cpf, 32, SEEK_SET))
115
+ rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
116
+ (long long)32);
117
+ if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
118
+ rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
119
+ if ((dpf = fopen(patchfile, "r")) == NULL)
120
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
121
+ if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
122
+ rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
123
+ (long long)(32 + bzctrllen));
124
+ if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
125
+ rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
126
+ if ((epf = fopen(patchfile, "r")) == NULL)
127
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
128
+ if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
129
+ rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
130
+ (long long)(32 + bzctrllen + bzdatalen));
131
+ if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
132
+ rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
133
+
134
+ if(((fd=open(oldfile,O_RDONLY,0))<0) ||
135
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
136
+ ((old=malloc(oldsize+1))==NULL) ||
137
+ (lseek(fd,0,SEEK_SET)!=0) ||
138
+ (read(fd,old,oldsize)!=oldsize) ||
139
+ (close(fd)==-1)) rb_raise(rb_eRuntimeError,"%s",oldfile);
140
+ if((new=malloc(newsize+1))==NULL) rb_raise(rb_eRuntimeError,"malloc");
141
+
142
+ oldpos=0;newpos=0;
143
+ while(newpos<newsize) {
144
+ /* Read control data */
145
+ for(i=0;i<=2;i++) {
146
+ lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
147
+ if ((lenread < 8) || ((cbz2err != BZ_OK) &&
148
+ (cbz2err != BZ_STREAM_END)))
149
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
150
+ ctrl[i]=offtin(buf);
151
+ };
152
+
153
+ /* Sanity-check */
154
+ if(newpos+ctrl[0]>newsize)
155
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
156
+
157
+ /* Read diff string */
158
+ lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
159
+ if ((lenread < ctrl[0]) ||
160
+ ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
161
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
162
+
163
+ /* Add old data to diff string */
164
+ for(i=0;i<ctrl[0];i++)
165
+ if((oldpos+i>=0) && (oldpos+i<oldsize))
166
+ new[newpos+i]+=old[oldpos+i];
167
+
168
+ /* Adjust pointers */
169
+ newpos+=ctrl[0];
170
+ oldpos+=ctrl[0];
171
+
172
+ /* Sanity-check */
173
+ if(newpos+ctrl[1]>newsize)
174
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
175
+
176
+ /* Read extra string */
177
+ lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
178
+ if ((lenread < ctrl[1]) ||
179
+ ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
180
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
181
+
182
+ /* Adjust pointers */
183
+ newpos+=ctrl[1];
184
+ oldpos+=ctrl[2];
185
+ };
186
+
187
+ /* Clean up the bzip2 reads */
188
+ BZ2_bzReadClose(&cbz2err, cpfbz2);
189
+ BZ2_bzReadClose(&dbz2err, dpfbz2);
190
+ BZ2_bzReadClose(&ebz2err, epfbz2);
191
+ if (fclose(cpf) || fclose(dpf) || fclose(epf))
192
+ rb_raise(rb_eRuntimeError, "fclose(%s)", patchfile);
193
+
194
+ /* Write the new file */
195
+ if(((fd=open(newfile,O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
196
+ (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
197
+ rb_raise(rb_eRuntimeError,"%s",newfile);
198
+
199
+ free(new);
200
+ free(old);
201
+
202
+ return 0;
203
+ }
data/ext/bspatch.h ADDED
@@ -0,0 +1,6 @@
1
+ #ifndef BS_PATCH_H
2
+ #define BS_PATCH_H
3
+
4
+ int bspatch_files(const char *oldfile, const char *newfile, const char *patchfile);
5
+
6
+ #endif
data/ext/extconf.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('bsdiff')
4
+ fail unless have_header('unistd.h')
5
+ fail unless have_header('bzlib.h')
6
+ fail unless have_library('bz2')
7
+ fail unless have_func('BZ2_bzWrite','bzlib.h')
8
+ fail unless have_macro('BZ_OK','bzlib.h')
9
+ create_header('bsdiff_config.h')
10
+ create_makefile('bsdiff')
data/ext/rb_bsdiff.c ADDED
@@ -0,0 +1,30 @@
1
+ #include <ruby.h>
2
+ #include "bsdiff.h"
3
+ #include "bspatch.h"
4
+
5
+ VALUE BSDiff;
6
+
7
+ static VALUE bsdiff_diff(VALUE mod, VALUE oldfile, VALUE newfile, VALUE patchfile)
8
+ {
9
+ if( bsdiff_files(StringValuePtr(oldfile), StringValuePtr(newfile), StringValuePtr(patchfile)) ) {
10
+ return Qfalse;
11
+ }
12
+ return Qtrue;
13
+ }
14
+
15
+ static VALUE bsdiff_patch(VALUE mod, VALUE oldfile, VALUE newfile, VALUE patchfile)
16
+ {
17
+ if( bspatch_files(StringValuePtr(oldfile), StringValuePtr(newfile), StringValuePtr(patchfile)) ) {
18
+ return Qfalse;
19
+ }
20
+ return Qtrue;
21
+ }
22
+
23
+ /* main entry point */
24
+ void Init_bsdiff()
25
+ {
26
+ BSDiff = rb_define_module("BSDiff");
27
+
28
+ rb_define_singleton_method(BSDiff, "diff", bsdiff_diff, 3);
29
+ rb_define_singleton_method(BSDiff, "patch", bsdiff_patch, 3);
30
+ }
data/lib/rb-bsdiff.rb ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require File.join(File.dirname(__FILE__), '..', 'ext','bsdiff')
3
+ rescue Exception => e
4
+ puts "Got exception trying to load native extension. Did everything compile properly?"
5
+ puts e.message
6
+ puts "\t#{e.backtrace.join("\t\n")}\n"
7
+ end
8
+
9
+ module BSDiff
10
+ def self.version
11
+ return '0.1.1'
12
+ end
13
+ end
data/rb-bsdiff.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # WARNING: This file is auto-generated via 'rake gemspec'! DO NOT EDIT BY HAND!
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rb-bsdiff"
6
+ s.version = "0.1.1"
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["Todd Fisher", "Jon Frisby"]
10
+ s.date = "2012-12-17"
11
+ s.description = "Ruby bindings to bindary diff tools bsdiff and bspatch"
12
+ s.email = ["todd.fisher@gmail.com", "jon@cloudability.com"]
13
+ s.extensions = ["ext/extconf.rb"]
14
+ s.files = ["LICENSE", "README.md", "ext/bsdiff.c", "ext/bsdiff.h", "ext/bspatch.c", "ext/bspatch.h", "ext/extconf.rb", "ext/rb_bsdiff.c", "lib/rb-bsdiff.rb", "rb-bsdiff.gemspec"]
15
+ s.homepage = "http://github.com/cloudability/rb-bsdiff"
16
+ s.licenses = ["Simplified BSD"]
17
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = "rb-bsdiff"
20
+ s.rubygems_version = "1.8.24"
21
+ s.summary = "Ruby bindings to bindary diff tools bsdiff and bspatch"
22
+
23
+ if s.respond_to? :specification_version then
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb-bsdiff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Todd Fisher
9
+ - Jon Frisby
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-12-17 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Ruby bindings to bindary diff tools bsdiff and bspatch
16
+ email:
17
+ - todd.fisher@gmail.com
18
+ - jon@cloudability.com
19
+ executables: []
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files: []
23
+ files:
24
+ - LICENSE
25
+ - README.md
26
+ - ext/bsdiff.c
27
+ - ext/bsdiff.h
28
+ - ext/bspatch.c
29
+ - ext/bspatch.h
30
+ - ext/extconf.rb
31
+ - ext/rb_bsdiff.c
32
+ - lib/rb-bsdiff.rb
33
+ - rb-bsdiff.gemspec
34
+ homepage: http://github.com/cloudability/rb-bsdiff
35
+ licenses:
36
+ - Simplified BSD
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --inline-source
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ segments:
50
+ - 0
51
+ hash: 791425113861643640
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project: rb-bsdiff
60
+ rubygems_version: 1.8.24
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Ruby bindings to bindary diff tools bsdiff and bspatch
64
+ test_files: []