/*- * Copyright 2003-2005 Colin Percival * Copyright 2012 Matthew Endsley * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if 0 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05 cperciva Exp $"); #endif #include #include struct bsdiff_header { uint8_t signature[8]; uint8_t ctrl_block_length[8]; uint8_t diff_block_length[8]; uint8_t new_file_length[8]; }; struct bsdiff_stream { void* opaque; void* (*malloc)(size_t size); void (*free)(void* ptr); int (*write)(struct bsdiff_stream* compresor, const void* buffer, int size); int (*finish)(struct bsdiff_stream* stream); }; int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream, struct bsdiff_header* header); #if !defined(BSDIFF_HEADER_ONLY) #include #include #define MIN(x,y) (((x)<(y)) ? (x) : (y)) static void split(int64_t *I,int64_t *V,int64_t start,int64_t len,int64_t h) { int64_t i,j,k,x,tmp,jj,kk; if(len<16) { for(k=start;kstart) split(I,V,start,jj-start,h); for(i=0;ikk) split(I,V,kk,start+len-kk,h); } static void qsufsort(int64_t *I,int64_t *V,const uint8_t *old,int64_t oldsize) { int64_t buckets[256]; int64_t i,h,len; for(i=0;i<256;i++) buckets[i]=0; for(i=0;i0;i--) buckets[i]=buckets[i-1]; buckets[0]=0; for(i=0;iy) { *pos=I[st]; return x; } else { *pos=I[en]; return y; } }; x=st+(en-st)/2; if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) { return search(I,old,oldsize,new,newsize,x,en,pos); } else { return search(I,old,oldsize,new,newsize,st,x,pos); }; } static void offtout(int64_t x,uint8_t *buf) { int64_t y; if(x<0) y=-x; else y=x; buf[0]=y%256;y-=buf[0]; y=y/256;buf[1]=y%256;y-=buf[1]; y=y/256;buf[2]=y%256;y-=buf[2]; y=y/256;buf[3]=y%256;y-=buf[3]; y=y/256;buf[4]=y%256;y-=buf[4]; y=y/256;buf[5]=y%256;y-=buf[5]; y=y/256;buf[6]=y%256;y-=buf[6]; y=y/256;buf[7]=y%256; if(x<0) buf[7]|=0x80; } static int64_t writecompressed(struct bsdiff_stream* stream, const void* buffer, int64_t length) { int64_t result = 0; while (length > 0) { const int smallsize = (int)MIN(length, INT_MAX); const int writeresult = stream->write(stream, buffer, smallsize); if (writeresult == -1) { return -1; } result += writeresult; length -= smallsize; buffer = (uint8_t*)buffer + smallsize; } return result; } struct bsdiff_request { const uint8_t* old; int64_t oldsize; const uint8_t* new; int64_t newsize; struct bsdiff_stream* stream; struct bsdiff_header* header; int64_t *I; uint8_t *db, *eb; }; static int bsdiff_internal(const struct bsdiff_request req) { int64_t *I,*V; int64_t scan,pos,len; int64_t compresslen, filelen; int64_t lastscan,lastpos,lastoffset; int64_t oldscore,scsc; int64_t s,Sf,lenf,Sb,lenb; int64_t overlap,Ss,lens; int64_t i; int64_t dblen,eblen; uint8_t *db,*eb; uint8_t buf[8 * 3]; if((V=req.stream->malloc((req.oldsize+1)*sizeof(int64_t)))==NULL) return -1; I = req.I; qsufsort(I,V,req.old,req.oldsize); req.stream->free(V); db = req.db; eb = req.eb; dblen=0; eblen=0; filelen=0; /* Header is 0 8 "BSDIFF40" 8 8 length of bzip2ed ctrl block 16 8 length of bzip2ed diff block 24 8 length of new file */ /* File is 0 32 Header 32 ?? Bzip2ed ctrl block ?? ?? Bzip2ed diff block ?? ?? Bzip2ed extra block */ memcpy(req.header->signature,"BSDIFF40",sizeof(req.header->signature)); offtout(req.newsize, req.header->new_file_length); /* Compute the differences, writing ctrl as we go */ scan=0;len=0; lastscan=0;lastpos=0;lastoffset=0; while(scanoldscore+8)) break; if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; }; }; lenb=0; if(scan=lastscan+i)&&(pos>=i);i++) { if(req.old[pos-i]==req.new[scan-i]) s++; if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; }; }; if(lastscan+lenf>scan-lenb) { overlap=(lastscan+lenf)-(scan-lenb); s=0;Ss=0;lens=0; for(i=0;iSs) { Ss=s; lens=i+1; }; }; lenf+=lens-overlap; lenb-=lens; }; for(i=0;ifinish(req.stream); if (compresslen == -1) return -1; filelen += compresslen; /* Compute size of compressed ctrl data */ offtout(filelen, req.header->ctrl_block_length); /* Write compressed diff data */ filelen = 0; compresslen = writecompressed(req.stream, db, dblen); if (compresslen == -1) return -1; filelen += compresslen; compresslen = req.stream->finish(req.stream); if (compresslen == -1) return -1; filelen += compresslen; /* Compute size of compressed diff data */ offtout(filelen, req.header->diff_block_length); /* Write compressed extra data */ compresslen = writecompressed(req.stream, eb, eblen); if (compresslen == -1) return -1; compresslen = req.stream->finish(req.stream); if (compresslen == -1) return -1; return 0; } int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream, struct bsdiff_header* header) { int result; struct bsdiff_request req; if((req.I=stream->malloc((oldsize+1)*sizeof(int64_t)))==NULL) return -1; if((req.db=stream->malloc(newsize+1))==NULL) { stream->free(req.I); return -1; } if((req.eb=stream->malloc(newsize+1))==NULL) { stream->free(req.db); stream->free(req.I); return -1; } req.old = old; req.oldsize = oldsize; req.new = new; req.newsize = newsize; req.stream = stream; req.header = header; result = bsdiff_internal(req); stream->free(req.eb); stream->free(req.db); stream->free(req.I); return result; } #if !defined(BSDIFF_LIBRARY) #include #include #include #include #include #include #include static int bz2_write(struct bsdiff_stream* stream, const void* buffer, int size) { bz_stream* bz2; FILE* pf; int err; int totalwritten; char compress_buffer[4096]; bz2 = (bz_stream*)stream->opaque; pf = (FILE*)bz2->opaque; if (!bz2->state) { if (BZ_OK != BZ2_bzCompressInit(bz2, 9, 0, 0)) return -1; } bz2->next_in = (char*)buffer; bz2->avail_in = size; totalwritten = 0; for (;;) { bz2->next_out = compress_buffer; bz2->avail_out = sizeof(compress_buffer); if (BZ_RUN_OK != (err = BZ2_bzCompress(bz2, BZ_RUN))) return -1; if (bz2->avail_out < sizeof(compress_buffer)) { const int written = sizeof(compress_buffer) - bz2->avail_out; if (written != fwrite(compress_buffer, 1, written, pf)) return -1; totalwritten += written; } if (bz2->avail_in == 0) return totalwritten; } } static int bz2_finish(struct bsdiff_stream* stream) { int err; int totalwritten; bz_stream* bz2; FILE* pf; char compress_buffer[4096]; bz2 = (bz_stream*)stream->opaque; pf = (FILE*)bz2->opaque; totalwritten = 0; for (;;) { bz2->next_out = compress_buffer; bz2->avail_out = sizeof(compress_buffer); err = BZ2_bzCompress(bz2, BZ_FINISH); if (BZ_FINISH_OK != err && BZ_STREAM_END != err) return -1; if (bz2->avail_out < sizeof(compress_buffer)) { const int written = sizeof(compress_buffer) - bz2->avail_out; if (written != fwrite(compress_buffer, 1, written, pf)) return -1; totalwritten += written; } if (BZ_STREAM_END == err) break; } BZ2_bzCompressEnd(bz2); return totalwritten; } int main(int argc,char *argv[]) { int fd; uint8_t *old,*new; off_t oldsize,newsize; struct bsdiff_header header; FILE * pf; struct bsdiff_stream stream; bz_stream bz2 = {0}; stream.malloc = malloc; stream.free = free; stream.opaque = &bz2; stream.write = bz2_write; stream.finish = bz2_finish; if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure that we never try to malloc(0) and get a NULL pointer */ if(((fd=open(argv[1],O_RDONLY,0))<0) || ((oldsize=lseek(fd,0,SEEK_END))==-1) || ((old=malloc(oldsize+1))==NULL) || (lseek(fd,0,SEEK_SET)!=0) || (read(fd,old,oldsize)!=oldsize) || (close(fd)==-1)) err(1,"%s",argv[1]); /* Allocate newsize+1 bytes instead of newsize bytes to ensure that we never try to malloc(0) and get a NULL pointer */ if(((fd=open(argv[2],O_RDONLY,0))<0) || ((newsize=lseek(fd,0,SEEK_END))==-1) || ((new=malloc(newsize+1))==NULL) || (lseek(fd,0,SEEK_SET)!=0) || (read(fd,new,newsize)!=newsize) || (close(fd)==-1)) err(1,"%s",argv[2]); /* Create the patch file */ if ((pf = fopen(argv[3], "w")) == NULL) err(1, "%s", argv[3]); /* Save space for header */ if (fwrite(&header, sizeof(header), 1, pf) != 1) err(1, "Failed to write header"); bz2.opaque = pf; if (bsdiff(old, oldsize, new, newsize, &stream, &header)) err(1, "bsdiff"); if (fseek(pf, 0, SEEK_SET) || fwrite(&header, sizeof(header), 1, pf) != 1) err(1, "Failed to write header"); if (fclose(pf)) err(1, "fclose"); /* Free the memory we used */ free(old); free(new); return 0; } #endif //BSDIFF_LIBRARY #endif // BSDIFF_HEADER_ONLY