/*- * 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. */ #include #include struct bsdiff_stream { void* opaque; void* (*malloc)(size_t size); void (*free)(void* ptr); int (*write)(struct bsdiff_stream* stream, const void* buffer, int size); }; int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream); #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 writedata(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; 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 lastscan,lastpos,lastoffset; int64_t oldscore,scsc; int64_t s,Sf,lenf,Sb,lenb; int64_t overlap,Ss,lens; int64_t i; 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; /* 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;imalloc((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; result = bsdiff_internal(req); stream->free(req.eb); stream->free(req.db); stream->free(req.I); return result; } #if defined(BSDIFF_EXECUTABLE) #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; 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; 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 size_t written = sizeof(compress_buffer) - bz2->avail_out; if (written != fwrite(compress_buffer, 1, written, pf)) return -1; } if (bz2->avail_in == 0) return 0; } } static int bz2_finish(struct bsdiff_stream* stream) { int err; bz_stream* bz2; FILE* pf; char compress_buffer[4096]; bz2 = (bz_stream*)stream->opaque; pf = (FILE*)bz2->opaque; 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 size_t written = sizeof(compress_buffer) - bz2->avail_out; if (written != fwrite(compress_buffer, 1, written, pf)) return -1; } if (BZ_STREAM_END == err) break; } BZ2_bzCompressEnd(bz2); return 0; } int main(int argc,char *argv[]) { int fd; uint8_t *old,*new; off_t oldsize,newsize; uint8_t buf[8]; FILE * pf; struct bsdiff_stream stream; bz_stream bz2; memset(&bz2, 0, sizeof(bz2)); stream.malloc = malloc; stream.free = free; stream.opaque = &bz2; stream.write = bz2_write; 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]); /* Write header (signature+newsize)*/ offtout(newsize, buf); if (fwrite("BSDIFF40", 8, 1, pf) != 1 || fwrite(buf, sizeof(buf), 1, pf) != 1) err(1, "Failed to write header"); bz2.opaque = pf; if (bsdiff(old, oldsize, new, newsize, &stream)) err(1, "bsdiff"); if (bz2_finish(&stream)) err(1, "stream.finish"); if (fclose(pf)) err(1, "fclose"); /* Free the memory we used */ free(old); free(new); return 0; } #endif #endif