dockerfile

This commit is contained in:
Dylan Knutson
2023-10-08 19:33:56 -07:00
parent b97b82b1d8
commit 955a3021ae
74 changed files with 10681 additions and 19 deletions

13
.dockerignore Normal file
View File

@@ -0,0 +1,13 @@
.git
.gitignore
log
tmp
ext
build
/bin
node_modules
profiler
.bundle
.vscode
launch.json
settings.json

7
.gitignore vendored
View File

@@ -4,6 +4,13 @@
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
build
tmp
*.bundle
lib/xdiff
ext/xdiff/Makefile
ext/xdiff/xdiff
# Ignore bundler config.
/.bundle

67
Dockerfile Normal file
View File

@@ -0,0 +1,67 @@
FROM ruby:3.2.0 AS native-gems
RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install --no-install-recommends --no-install-suggests -y \
cmake
WORKDIR /usr/src/app
RUN gem install bundler -v '2.4.5'
COPY gems gems
WORKDIR /usr/src/app/gems/xdiff-rb
RUN bundle install
RUN rake compile
WORKDIR /usr/src/app/gems/rb-bsdiff
RUN bundle install
RUN rake compile
# Primary image
FROM ruby:3.2.0
# set up nodejs 18.x deb repo
RUN mkdir -p /etc/apt/keyrings && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" \
| tee /etc/apt/sources.list.d/nodesource.list
# apt caching & install packages
RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install --no-install-recommends --no-install-suggests -y \
libblas-dev liblapack-dev libvips42 ca-certificates curl gnupg nodejs
WORKDIR /usr/src/app
RUN gem install bundler -v '2.4.6'
RUN bundle config --global frozen 1
WORKDIR /usr/src/app
# install gems
COPY Gemfile Gemfile.lock ./
COPY gems gems
RUN bundle install
# install js dependencies
COPY package.json yarn.lock ./
RUN npm install -g yarn && \
rails yarn:install
COPY . .
COPY --from=native-gems /usr/src/app/gems/xdiff-rb/lib/xdiff gems/xdiff-rb/lib/xdiff
COPY --from=native-gems /usr/src/app/gems/rb-bsdiff/lib gems/rb-bsdiff/lib
# precompile assets
ENV RAILS_ENV production
RUN rails assets:precompile
ENV RAILS_ENV worker
ENV proxy direct
# CMD ["bundle" "exec" "good_job" "--max-threads=2"]
CMD ["/bin/bash"]

View File

@@ -82,11 +82,11 @@ group :test do
gem "rspec-rails"
end
gem "xdiff", path: "../xdiff-rb"
gem "xdiff", path: "gems/xdiff-rb"
# for legacy import
gem "diffy"
gem "rb-bsdiff", path: "../rb-bsdiff"
gem "rb-bsdiff", path: "gems/rb-bsdiff"
gem "ruby-prof"
gem "ruby-prof-speedscope"

View File

@@ -7,12 +7,12 @@ GIT
activerecord (>= 6)
PATH
remote: ../rb-bsdiff
remote: gems/rb-bsdiff
specs:
rb-bsdiff (0.1.0)
PATH
remote: ../xdiff-rb
remote: gems/xdiff-rb
specs:
xdiff (0.0.1)

View File

@@ -11,7 +11,7 @@ default: &default
redux_prod: &redux_prod
adapter: postgresql
host: postgres.local
host: 10.166.33.171
port: 5432
database: redux_prod
username: scraper_redux
@@ -37,7 +37,7 @@ redux_dev: &redux_dev
legacy_prod: &legacy_prod
adapter: postgresql
host: postgres.local
host: 10.166.33.171
port: 5432
database: legacy_prod
username: scraper_redux

View File

@@ -7,13 +7,13 @@ staging:
bucket: "redux_scraper_dev"
production:
host: "http://grafana.local:8086"
host: "http://10.166.33.66:8086"
bucket: "redux_scraper_prod"
worker:
host: "http://grafana.local:8086"
host: "http://10.166.33.66:8086"
bucket: "redux_scraper_prod"
test:
host: "http://grafana.local:8086"
host: "http://10.166.33.66:8086"
bucket: "redux_scraper_test"

View File

@@ -4,8 +4,8 @@ default: &default
gallery_dl: http://localhost:5001
proxy-1:
http: http://proxy-1.local:9292
gallery_dl: http://proxy-1.local:5001
http: http://10.166.33.10:9292
gallery_dl: http://10.166.33.10:5001
dedipath-1:
http: http://10.200.0.6:9292

5
gems/rb-bsdiff/Gemfile Normal file
View File

@@ -0,0 +1,5 @@
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gemspec

View File

@@ -0,0 +1,24 @@
PATH
remote: .
specs:
rb-bsdiff (0.1.0)
GEM
remote: https://rubygems.org/
specs:
minitest (5.18.0)
rake (13.0.6)
rake-compiler (1.2.1)
rake
PLATFORMS
arm64-darwin-21
DEPENDENCIES
minitest
rake (>= 1.9.1)
rake-compiler (>= 0.8.3)
rb-bsdiff!
BUNDLED WITH
2.4.1

27
gems/rb-bsdiff/LICENSE Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2012, Todd Fisher
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided 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.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by the <organization>.
4. Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''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 <COPYRIGHT HOLDER> 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.

6
gems/rb-bsdiff/README Normal file
View File

@@ -0,0 +1,6 @@
Ruby bindings to bsdiff and bspatch
BSDiff.diff('ext/b0', 'ext/b1', 'p0')
# apply patch to bspatch.o as bspatch2.o
BSDiff.patch('ext/b0', 'b3', 'p0')

25
gems/rb-bsdiff/Rakefile Normal file
View File

@@ -0,0 +1,25 @@
require "rake/clean"
require "rake/testtask"
require "rake/extensiontask"
gemspec = Gem::Specification.load("rb-bsdiff.gemspec")
Rake::ExtensionTask.new do |ext|
ext.name = "bsdiff"
ext.source_pattern = "*.{c,cc,h}"
ext.ext_dir = "ext"
ext.lib_dir = "lib"
ext.gem_spec = gemspec
end
task :default => [:compile, :test]
CLEAN.include([
"pkg/",
"tmp/",
"ext/Makefile",
"lib/bsdiff.so",
])
Rake::TestTask.new(:test) do |t|
t.libs << "lib"
t.test_files = FileList["test.rb"]
end

BIN
gems/rb-bsdiff/ext/b0 Normal file

Binary file not shown.

BIN
gems/rb-bsdiff/ext/b1 Normal file

Binary file not shown.

417
gems/rb-bsdiff/ext/bsdiff.c Normal file
View File

@@ -0,0 +1,417 @@
/*-
* Copyright 2003-2005 Colin Percival
* 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 <sys/types.h>
#include <bzlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ruby.h>
#define MIN(x,y) (((x)<(y)) ? (x) : (y))
static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
{
off_t i,j,k,x,tmp,jj,kk;
if(len<16) {
for(k=start;k<start+len;k+=j) {
j=1;x=V[I[k]+h];
for(i=1;k+i<start+len;i++) {
if(V[I[k+i]+h]<x) {
x=V[I[k+i]+h];
j=0;
};
if(V[I[k+i]+h]==x) {
tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
j++;
};
};
for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
if(j==1) I[k]=-1;
};
return;
};
x=V[I[start+len/2]+h];
jj=0;kk=0;
for(i=start;i<start+len;i++) {
if(V[I[i]+h]<x) jj++;
if(V[I[i]+h]==x) kk++;
};
jj+=start;kk+=jj;
i=start;j=0;k=0;
while(i<jj) {
if(V[I[i]+h]<x) {
i++;
} else if(V[I[i]+h]==x) {
tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
j++;
} else {
tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
k++;
};
};
while(jj+j<kk) {
if(V[I[jj+j]+h]==x) {
j++;
} else {
tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
k++;
};
};
if(jj>start) split(I,V,start,jj-start,h);
for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
if(jj==kk-1) I[jj]=-1;
if(start+len>kk) split(I,V,kk,start+len-kk,h);
}
static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
{
off_t buckets[256];
off_t i,h,len;
for(i=0;i<256;i++) buckets[i]=0;
for(i=0;i<oldsize;i++) buckets[old[i]]++;
for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
for(i=255;i>0;i--) buckets[i]=buckets[i-1];
buckets[0]=0;
for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
I[0]=oldsize;
for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
V[oldsize]=0;
for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
I[0]=-1;
for(h=1;I[0]!=-(oldsize+1);h+=h) {
len=0;
for(i=0;i<oldsize+1;) {
if(I[i]<0) {
len-=I[i];
i-=I[i];
} else {
if(len) I[i-len]=-len;
len=V[I[i]]+1-i;
split(I,V,i,len,h);
i+=len;
len=0;
};
};
if(len) I[i-len]=-len;
};
for(i=0;i<oldsize+1;i++) I[V[i]]=i;
}
static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize)
{
off_t i;
for(i=0;(i<oldsize)&&(i<newsize);i++)
if(old[i]!=new[i]) break;
return i;
}
static off_t search(off_t *I,u_char *old,off_t oldsize,
u_char *new,off_t newsize,off_t st,off_t en,off_t *pos)
{
off_t x,y;
if(en-st<2) {
x=matchlen(old+I[st],oldsize-I[st],new,newsize);
y=matchlen(old+I[en],oldsize-I[en],new,newsize);
if(x>y) {
*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(off_t x,u_char *buf)
{
off_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;
}
/* create a patch file from oldfile diff'ed by newfile */
int bsdiff_files(const char *oldfile, const char *newfile, const char *patchfile)
{
int fd;
u_char *old,*new;
off_t oldsize,newsize;
off_t *I,*V;
off_t scan,pos=0,len;
off_t lastscan,lastpos,lastoffset;
off_t oldscore,scsc;
off_t s,Sf,lenf,Sb,lenb;
off_t overlap,Ss,lens;
off_t i;
off_t dblen,eblen;
u_char *db,*eb;
u_char buf[8];
u_char header[32];
FILE * pf;
BZFILE * pfbz2;
int bz2err;
/* 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(oldfile,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)) {
rb_raise(rb_eRuntimeError, "%s",oldfile);
}
if(((I=malloc((oldsize+1)*sizeof(off_t)))==NULL) ||
((V=malloc((oldsize+1)*sizeof(off_t)))==NULL)) rb_raise(rb_eRuntimeError, "malloc error");
qsufsort(I,V,old,oldsize);
free(V);
/* 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(newfile,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)) rb_raise(rb_eRuntimeError, "%s",newfile);
if(((db=malloc(newsize+1))==NULL) ||
((eb=malloc(newsize+1))==NULL)) rb_raise(rb_eRuntimeError,"malloc error");
dblen=0;
eblen=0;
/* Create the patch file */
if ((pf = fopen(patchfile, "w")) == NULL)
rb_raise(rb_eRuntimeError, "%s", patchfile);
/* 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(header,"BSDIFF40",8);
offtout(0, header + 8);
offtout(0, header + 16);
offtout(newsize, header + 24);
if (fwrite(header, 32, 1, pf) != 1)
rb_raise(rb_eRuntimeError, "fwrite(%s)", patchfile);
/* Compute the differences, writing ctrl as we go */
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
scan=0;len=0;
lastscan=0;lastpos=0;lastoffset=0;
while(scan<newsize) {
oldscore=0;
for(scsc=scan+=len;scan<newsize;scan++) {
len=search(I,old,oldsize,new+scan,newsize-scan,
0,oldsize,&pos);
for(;scsc<scan+len;scsc++)
if((scsc+lastoffset<oldsize) &&
(old[scsc+lastoffset] == new[scsc]))
oldscore++;
if(((len==oldscore) && (len!=0)) ||
(len>oldscore+8)) break;
if((scan+lastoffset<oldsize) &&
(old[scan+lastoffset] == new[scan]))
oldscore--;
};
if((len!=oldscore) || (scan==newsize)) {
s=0;Sf=0;lenf=0;
for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
if(old[lastpos+i]==new[lastscan+i]) s++;
i++;
if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
};
lenb=0;
if(scan<newsize) {
s=0;Sb=0;
for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
if(old[pos-i]==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;i<overlap;i++) {
if(new[lastscan+lenf-overlap+i]==
old[lastpos+lenf-overlap+i]) s++;
if(new[scan-lenb+i]==
old[pos-lenb+i]) s--;
if(s>Ss) { Ss=s; lens=i+1; };
};
lenf+=lens-overlap;
lenb-=lens;
};
for(i=0;i<lenf;i++)
db[dblen+i]=new[lastscan+i]-old[lastpos+i];
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
eb[eblen+i]=new[lastscan+lenf+i];
dblen+=lenf;
eblen+=(scan-lenb)-(lastscan+lenf);
offtout(lenf,buf);
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
offtout((scan-lenb)-(lastscan+lenf),buf);
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
offtout((pos-lenb)-(lastpos+lenf),buf);
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
lastscan=scan-lenb;
lastpos=pos-lenb;
lastoffset=pos-scan;
};
};
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
/* Compute size of compressed ctrl data */
if ((len = ftello(pf)) == -1)
rb_raise(rb_eRuntimeError, "ftello");
offtout(len-32, header + 8);
/* Write compressed diff data */
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
/* Compute size of compressed diff data */
if ((newsize = ftello(pf)) == -1)
rb_raise(rb_eRuntimeError, "ftello");
offtout(newsize - len, header + 16);
/* Write compressed extra data */
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
if (bz2err != BZ_OK)
rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
/* Seek to the beginning, write the header, and close the file */
if (fseeko(pf, 0, SEEK_SET))
rb_raise(rb_eRuntimeError, "fseeko");
if (fwrite(header, 32, 1, pf) != 1)
rb_raise(rb_eRuntimeError, "fwrite(%s)", patchfile);
if (fclose(pf))
rb_raise(rb_eRuntimeError, "fclose");
/* Free the memory we used */
free(db);
free(eb);
free(I);
free(old);
free(new);
return 0;
}
/*
* create a patch buffer from oldbfufer diff'ed by newbfufer
*
* same as bsdiff_files, except works in memory
*
* TODO
*/
//char* bsdiff_buffer(const char *oldbuffer, const char *newbuffer)
//{
//}

View File

@@ -0,0 +1,6 @@
#ifndef RB_BSDIFF_H
#define RB_BSDIFF_H
int bsdiff_files(const char *oldfile, const char *newfile, const char *patchfile);
#endif

View File

@@ -0,0 +1,203 @@
/*-
* Copyright 2003-2005 Colin Percival
* 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/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
#endif
#include <bzlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ruby.h>
static off_t offtin(u_char *buf)
{
off_t y;
y=buf[7]&0x7F;
y=y*256;y+=buf[6];
y=y*256;y+=buf[5];
y=y*256;y+=buf[4];
y=y*256;y+=buf[3];
y=y*256;y+=buf[2];
y=y*256;y+=buf[1];
y=y*256;y+=buf[0];
if(buf[7]&0x80) y=-y;
return y;
}
int bspatch_files(const char *oldfile, const char *newfile, const char *patchfile)
{
FILE * f, * cpf, * dpf, * epf;
BZFILE * cpfbz2, * dpfbz2, * epfbz2;
int cbz2err, dbz2err, ebz2err;
int fd;
ssize_t oldsize,newsize;
ssize_t bzctrllen,bzdatalen;
u_char header[32],buf[8];
u_char *old, *new;
off_t oldpos,newpos;
off_t ctrl[3];
off_t lenread;
off_t i;
/* Open patch file */
if ((f = fopen(patchfile, "r")) == NULL)
rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
/*
File format:
0 8 "BSDIFF40"
8 8 X
16 8 Y
24 8 sizeof(newfile)
32 X bzip2(control block)
32+X Y bzip2(diff block)
32+X+Y ??? bzip2(extra block)
with control block a set of triples (x,y,z) meaning "add x bytes
from oldfile to x bytes from the diff block; copy y bytes from the
extra block; seek forwards in oldfile by z bytes".
*/
/* Read header */
if (fread(header, 1, 32, f) < 32) {
if (feof(f))
rb_raise(rb_eRuntimeError,"Corrupt patch\n");
rb_raise(rb_eRuntimeError, "fread(%s)", patchfile);
}
/* Check for appropriate magic */
if (memcmp(header, "BSDIFF40", 8) != 0)
rb_raise(rb_eRuntimeError, "Corrupt patch\n");
/* Read lengths from header */
bzctrllen=offtin(header+8);
bzdatalen=offtin(header+16);
newsize=offtin(header+24);
if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
rb_raise(rb_eRuntimeError,"Corrupt patch\n");
/* Close patch file and re-open it via libbzip2 at the right places */
if (fclose(f))
rb_raise(rb_eRuntimeError, "fclose(%s)", patchfile);
if ((cpf = fopen(patchfile, "r")) == NULL)
rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
if (fseeko(cpf, 32, SEEK_SET))
rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
(long long)32);
if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
if ((dpf = fopen(patchfile, "r")) == NULL)
rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
(long long)(32 + bzctrllen));
if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
if ((epf = fopen(patchfile, "r")) == NULL)
rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
(long long)(32 + bzctrllen + bzdatalen));
if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
if(((fd=open(oldfile,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)) rb_raise(rb_eRuntimeError,"%s",oldfile);
if((new=malloc(newsize+1))==NULL) rb_raise(rb_eRuntimeError,"malloc");
oldpos=0;newpos=0;
while(newpos<newsize) {
/* Read control data */
for(i=0;i<=2;i++) {
lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
if ((lenread < 8) || ((cbz2err != BZ_OK) &&
(cbz2err != BZ_STREAM_END)))
rb_raise(rb_eRuntimeError, "Corrupt patch\n");
ctrl[i]=offtin(buf);
};
/* Sanity-check */
if(newpos+ctrl[0]>newsize)
rb_raise(rb_eRuntimeError,"Corrupt patch\n");
/* Read diff string */
lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
if ((lenread < ctrl[0]) ||
((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
rb_raise(rb_eRuntimeError, "Corrupt patch\n");
/* Add old data to diff string */
for(i=0;i<ctrl[0];i++)
if((oldpos+i>=0) && (oldpos+i<oldsize))
new[newpos+i]+=old[oldpos+i];
/* Adjust pointers */
newpos+=ctrl[0];
oldpos+=ctrl[0];
/* Sanity-check */
if(newpos+ctrl[1]>newsize)
rb_raise(rb_eRuntimeError,"Corrupt patch\n");
/* Read extra string */
lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
if ((lenread < ctrl[1]) ||
((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
rb_raise(rb_eRuntimeError, "Corrupt patch\n");
/* Adjust pointers */
newpos+=ctrl[1];
oldpos+=ctrl[2];
};
/* Clean up the bzip2 reads */
BZ2_bzReadClose(&cbz2err, cpfbz2);
BZ2_bzReadClose(&dbz2err, dpfbz2);
BZ2_bzReadClose(&ebz2err, epfbz2);
if (fclose(cpf) || fclose(dpf) || fclose(epf))
rb_raise(rb_eRuntimeError, "fclose(%s)", patchfile);
/* Write the new file */
if(((fd=open(newfile,O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
(write(fd,new,newsize)!=newsize) || (close(fd)==-1))
rb_raise(rb_eRuntimeError,"%s",newfile);
free(new);
free(old);
return 0;
}

View File

@@ -0,0 +1,6 @@
#ifndef BS_PATCH_H
#define BS_PATCH_H
int bspatch_files(const char *oldfile, const char *newfile, const char *patchfile);
#endif

View File

@@ -0,0 +1,10 @@
require 'mkmf'
dir_config('bsdiff')
fail unless have_header('unistd.h')
fail unless have_header('bzlib.h')
fail unless have_library('bz2')
fail unless have_func('BZ2_bzWrite','bzlib.h')
fail unless have_macro('BZ_OK','bzlib.h')
create_header('bsdiff_config.h')
create_makefile('bsdiff')

View File

@@ -0,0 +1,30 @@
#include <ruby.h>
#include "bsdiff.h"
#include "bspatch.h"
VALUE BSDiff;
static VALUE bsdiff_diff(VALUE mod, VALUE oldfile, VALUE newfile, VALUE patchfile)
{
if( bsdiff_files(StringValuePtr(oldfile), StringValuePtr(newfile), StringValuePtr(patchfile)) ) {
return Qfalse;
}
return Qtrue;
}
static VALUE bsdiff_patch(VALUE mod, VALUE oldfile, VALUE newfile, VALUE patchfile)
{
if( bspatch_files(StringValuePtr(oldfile), StringValuePtr(newfile), StringValuePtr(patchfile)) ) {
return Qfalse;
}
return Qtrue;
}
/* main entry point */
void Init_bsdiff()
{
BSDiff = rb_define_module("BSDiff");
rb_define_singleton_method(BSDiff, "diff", bsdiff_diff, 3);
rb_define_singleton_method(BSDiff, "patch", bsdiff_patch, 3);
}

View File

@@ -0,0 +1,33 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{rb-bsdiff}
s.version = "0.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Todd Fisher"]
s.date = %q{2009-07-19}
s.description = %q{Ruby bindings to bindary diff tools bsdiff and bspatch}
s.email = %q{todd.fisher@gmail.com}
s.extensions = ["ext/extconf.rb"]
s.files = [
"README", "Rakefile", "rb-bsdiff.gemspec", "test.rb", "ext/b0",
"ext/b1", "ext/bsdiff.c", "ext/bsdiff.h", "ext/bspatch.c",
"ext/bspatch.h", "ext/extconf.rb", "ext/rb_bsdiff.c",
]
s.homepage = %q{http://github.com/taf2/rb-bsdiff}
s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{rb-bsdiff}
s.rubygems_version = %q{1.3.0}
s.summary = %q{Ruby bindings to bindary diff tools bsdiff and bspatch}
if s.respond_to? :specification_version
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2
end
s.add_development_dependency "rake", ">= 1.9.1"
s.add_development_dependency "rake-compiler", ">= 0.8.3"
s.add_development_dependency "minitest"
end

35
gems/rb-bsdiff/test.rb Normal file
View File

@@ -0,0 +1,35 @@
require "openssl"
require "test/unit"
require "bsdiff"
class TestPatch < Test::Unit::TestCase
def setup
File.unlink("b3") if File.exist?("b3")
File.unlink("p0") if File.exist?("p0")
end
def teardown
File.unlink("b3") if File.exist?("b3")
File.unlink("p0") if File.exist?("p0")
end
def test_diff_and_patch
#../bsdiff b0 b1 p0
#../bspatch b0 b3 p0
b0_chk = OpenSSL::Digest::MD5.hexdigest(File.read("ext/b0"))
b1_chk = OpenSSL::Digest::MD5.hexdigest(File.read("ext/b1"))
# create patch file from bspatch.o to bsdiff.o
BSDiff.diff("ext/b0", "ext/b1", "p0")
# apply patch to bspatch.o as bspatch2.o
BSDiff.patch("ext/b0", "b3", "p0")
b3_chk = OpenSSL::Digest::MD5.hexdigest(File.read("b3"))
# bspatch2.o should equal bsdiff.o
assert_equal(b1_chk, b3_chk)
end
end

5
gems/xdiff-rb/Gemfile Normal file
View File

@@ -0,0 +1,5 @@
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gemspec

View File

@@ -0,0 +1,26 @@
PATH
remote: .
specs:
xdiff (0.0.1)
GEM
remote: https://rubygems.org/
specs:
minitest (5.17.0)
rake (13.0.6)
rake-compiler (1.2.1)
rake
PLATFORMS
aarch64-linux
arm64-darwin-22
x86_64-linux
DEPENDENCIES
minitest
rake (>= 1.9.1)
rake-compiler (>= 0.8.3)
xdiff!
BUNDLED WITH
2.4.5

34
gems/xdiff-rb/Rakefile Normal file
View File

@@ -0,0 +1,34 @@
require "bundler/gem_tasks"
require "rake/testtask"
require "rake/extensiontask"
require "rake/clean"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/*_test.rb"]
end
gemspec = Gem::Specification.load("xdiff.gemspec")
Rake::ExtensionTask.new do |ext|
ext.name = "extension"
ext.source_pattern = "*.{c,cc,h}"
ext.ext_dir = "ext/xdiff"
ext.lib_dir = "lib/xdiff"
ext.gem_spec = gemspec
end
task :benchmark_patch do
require "xdiff"
XDiff::Native.patch(nil, nil)
end
task :default => [:compile, :test]
CLEAN.include([
"pkg/",
"tmp/",
"ext/xdiff/xdiff",
"ext/xdiff/xdiff_build",
"ext/xdiff/Makefile",
"lib/xdiff/*.so",
])

View File

@@ -0,0 +1,57 @@
require "mkmf"
require "timeout"
BASE_DIR = File.absolute_path(File.dirname(__FILE__))
# append_cppflags "-std=c++17 -O3"
# append_cppflags "-I#{BASE_DIR}/xdiff/xdiff"
$CXXFLAGS += " -std=c++17 -O3 "
$CXXFLAGS += " -I#{BASE_DIR}/xdiff/xdiff "
if !find_executable("cmake")
abort "ERROR: cmake is required."
end
if !find_executable("make")
abort "ERROR: make is required."
end
def sys(cmd)
puts "(#{Dir.pwd}) -- #{cmd}"
unless ret = xsystem(cmd)
raise "ERROR: '#{cmd}' failed"
end
ret
end
# helper to invoke cmake
def run_cmake(timeout, args)
# Set to process group so we can kill it and its children
pgroup = Gem.win_platform? ? :new_pgroup : :pgroup
pid = Process.spawn("cmake #{args}", pgroup => true)
Timeout.timeout(timeout) do
Process.waitpid(pid)
end
rescue Timeout::Error
# Kill it, #detach is essentially a background wait, since we don't actually
# care about waiting for it now
Process.kill(-9, pid)
Process.detach(pid)
raise StandardError.new("cmake has exceeded its timeout of #{timeout}s")
end
Dir.chdir("#{BASE_DIR}") do
sys("tar -xzf xdiff.tar.gz") unless Dir.exist?("xdiff")
Dir.mkdir("xdiff_build") unless Dir.exist?("xdiff_build")
Dir.chdir("xdiff_build") do
run_cmake(30, "#{BASE_DIR}/xdiff -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=C++17")
sys("make")
end
end
$srcs = ["extension.cc"]
append_ldflags "-L#{BASE_DIR}/xdiff_build"
append_ldflags "-lxdiff"
create_makefile "xdiff/extension"

View File

@@ -0,0 +1,188 @@
#include "ruby/ruby.h"
#include "xdiff.h"
#include <string>
#include <functional>
namespace {
class scope_guard {
public:
template<class Callable>
scope_guard(Callable && undo_func) try : f(std::forward<Callable>(undo_func)) {
} catch(...) {
undo_func();
throw;
}
scope_guard(scope_guard && other) : f(std::move(other.f)) {
other.f = nullptr;
}
~scope_guard() {
if(f) f(); // must not throw
}
void dismiss() noexcept {
f = nullptr;
}
scope_guard(const scope_guard&) = delete;
void operator = (const scope_guard&) = delete;
private:
std::function<void()> f;
};
void* wrap_malloc(void* priv, unsigned int size) {
return malloc(size);
}
void wrap_free(void* priv, void* ptr) {
free(ptr);
}
void* wrap_realloc(void* priv, void* ptr, unsigned int size) {
return realloc(ptr, size);
}
bool initialized = false;
VALUE init(VALUE self) {
if (initialized) {
return Qnil;
}
initialized = true;
memallocator_t malt;
malt.priv = NULL;
malt.malloc = wrap_malloc;
malt.free = wrap_free;
malt.realloc = wrap_realloc;
xdl_set_allocator(&malt);
return Qnil;
}
struct DiffWriter {
std::string accum;
static int write(void* writer_, mmbuffer_t* bufs, int bufsize) {
assert(bufsize > 0);
DiffWriter* writer = reinterpret_cast<DiffWriter*>(writer_);
for (auto i = 0; i < bufsize; i++) {
writer->accumulate_buf(bufs[i].ptr, bufs[i].size);
}
return 0;
}
void accumulate_buf(const char* str, size_t len) {
accum.append(str, len);
}
};
std::string get_diff(
char* base,
size_t base_len,
char* against,
size_t against_len
) {
mmbuffer_t base_mm;
base_mm.ptr = base;
base_mm.size = base_len;
mmbuffer_t against_mm;
against_mm.ptr = against;
against_mm.size = against_len;
bdiffparam_t bdp;
bdp.bsize = 16;
DiffWriter writer;
xdemitcb_t ecb;
ecb.priv = &writer;
ecb.outf = DiffWriter::write;
int err = xdl_bdiff_mb(&base_mm, &against_mm, &bdp, &ecb);
if (err < 0) {
return std::string("(error)");
}
return std::move(writer.accum);
}
std::string get_patched(
char *base,
size_t base_len,
char *patch,
size_t patch_len
) {
mmfile_t base_mm;
mmfile_t patch_mm;
int ret;
if ((ret = xdl_init_mmfile(&base_mm, base_len, XDL_MMF_ATOMIC))) {
return "(xdl_init_mmfile error - base)";
}
auto free_base_guard = scope_guard([&] { xdl_free_mmfile(&base_mm); });
if ((ret = xdl_init_mmfile(&patch_mm, patch_len, XDL_MMF_ATOMIC))) {
return "(xdl_init_mmfile error - patch)";
}
auto free_patch_guard = scope_guard([&] { xdl_free_mmfile(&patch_mm); });
if (xdl_write_mmfile(&base_mm, base, base_len) != base_len) {
return "(xdl_write_mmfile error - base)";
}
if (xdl_write_mmfile(&patch_mm, patch, patch_len) != patch_len) {
return "(xdl_write_mmfile error - patch)";
}
DiffWriter writer;
xdemitcb_t ecb;
ecb.priv = &writer;
ecb.outf = DiffWriter::write;
auto err = xdl_bpatch(&base_mm, &patch_mm, &ecb);
if (err != 0) {
return "(xdl_bpatch error)";
}
return std::move(writer.accum);
}
VALUE diff(VALUE self, VALUE base, VALUE against) {
std::string patch = get_diff(
StringValuePtr(base),
RSTRING_LEN(base),
StringValuePtr(against),
RSTRING_LEN(against)
);
return rb_str_new(patch.c_str(), patch.size());
}
VALUE patch(VALUE self, VALUE base, VALUE patch) {
std::string patched = get_patched(
StringValuePtr(base),
RSTRING_LEN(base),
StringValuePtr(patch),
RSTRING_LEN(patch)
);
return rb_str_new(patched.c_str(), patched.size());
}
}
extern "C" void Init_extension(void) {
#ifdef RB_EXT_RACTOR_SAFE
rb_ext_ractor_safe(true);
#endif
VALUE XDiff = rb_define_module("XDiff");
VALUE Native = rb_define_class_under(XDiff, "Native", rb_cObject);
rb_define_singleton_method(Native, "patch", patch, 2);
rb_define_singleton_method(Native, "diff", diff, 2);
rb_define_singleton_method(Native, "init", init, 0);
}

Binary file not shown.

View File

@@ -0,0 +1,2 @@
Davide Libenzi <davidel@xmailserver.org>

View File

@@ -0,0 +1,122 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.2)
# Project definition
PROJECT(LibXDiff)
# Package name and version
SET(PACKAGE_NAME xdiff)
SET(PACKAGE_MAJOR_VERSION 0)
SET(PACKAGE_VERSION "${PACKAGE_MAJOR_VERSION}.23")
# Some options to customise the build
OPTION(ENABLE_TESTS "Enable the tests to be built" OFF)
OPTION(ENABLE_TOOLS "Enable the tools to be built" OFF)
# Check for some include files and functions
INCLUDE(CheckIncludeFiles)
INCLUDE(CheckFunctionExists)
CHECK_INCLUDE_FILES(limits.h HAVE_LIMITS_H)
CHECK_FUNCTION_EXISTS(memchr HAVE_MEMCHR)
CHECK_FUNCTION_EXISTS(memcmp HAVE_MEMCMP)
CHECK_FUNCTION_EXISTS(memcpy HAVE_MEMCPY)
CHECK_FUNCTION_EXISTS(memset HAVE_MEMSET)
CHECK_INCLUDE_FILES(stdio.h HAVE_STDIO_H)
CHECK_INCLUDE_FILES(stdlib.h HAVE_STDLIB_H)
CHECK_INCLUDE_FILES(string.h HAVE_STRING_H)
CHECK_FUNCTION_EXISTS(strlen HAVE_STRLEN)
CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H)
# Configure LibXDiff
CONFIGURE_FILE(config.h.in config.h)
ADD_DEFINITIONS(-DHAVE_CONFIG_H)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
# Build LibXDiff
IF(WIN32)
ADD_DEFINITIONS(-DLIBXDIFF_DLL_EXPORT)
ENDIF()
ADD_LIBRARY(${PACKAGE_NAME} STATIC
xdiff/xadler32.c
xdiff/xalloc.c
xdiff/xbdiff.c
xdiff/xbpatchi.c
xdiff/xdiffi.c
xdiff/xemit.c
xdiff/xmerge3.c
xdiff/xmissing.c
xdiff/xpatchi.c
xdiff/xprepare.c
xdiff/xrabdiff.c
xdiff/xrabply.c
xdiff/xutils.c
xdiff/xversion.c
)
SET_TARGET_PROPERTIES(${PACKAGE_NAME} PROPERTIES VERSION ${PACKAGE_VERSION})
SET_TARGET_PROPERTIES(${PACKAGE_NAME} PROPERTIES SOVERSION ${PACKAGE_MAJOR_VERSION})
# Install LibXDiff
INSTALL(TARGETS ${PACKAGE_NAME}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES xdiff/xdiff.h
DESTINATION include)
# Set the RPATH information on Linux and macOS
IF(ENABLE_TESTS OR ENABLE_TOOLS)
IF(APPLE)
SET(CMAKE_INSTALL_RPATH "@executable_path/../lib")
ELSEIF(NOT WIN32)
SET(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
ENDIF()
ENDIF()
# Build the tests, if requested
IF(ENABLE_TESTS)
INCLUDE_DIRECTORIES(xdiff)
ADD_EXECUTABLE(xdiff_test
test/xdiff_test.c
test/xtestutils.c
)
TARGET_LINK_LIBRARIES(xdiff_test ${PACKAGE_NAME})
ADD_EXECUTABLE(xregression
test/xregression.c
test/xtestutils.c
)
TARGET_LINK_LIBRARIES(xregression ${PACKAGE_NAME})
INSTALL(TARGETS xregression
RUNTIME DESTINATION bin)
ENDIF()
# Build the tools, if requested
IF(ENABLE_TOOLS)
ADD_EXECUTABLE(xrabin
tools/xrabin.c
)
INSTALL(TARGETS xrabin
RUNTIME DESTINATION bin)
ENDIF()

View File

@@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -0,0 +1,5 @@
* Sun, Mar 26 2006 - Davide Libenzi <davidel@xmailserver.org>
Fixed the missing end of line problem for text diff and patch.

View File

@@ -0,0 +1,34 @@
LibXDiff 0.23 (http://www.xmailserver.org/xdiff-lib.html)
The LibXDiff library implements basic and yet complete functionalities to create
file differences/patches to both binary and text files. The library uses memory
files as file abstraction to achieve both performance and portability. For
binary files, LibXDiff implements both (with some modification) the algorithm
described in File System Support for Delta Compression by Joshua P. MacDonald,
and the algorithm described in Fingerprinting By Random Polynomials by Michael
O. Rabin. While for text files it follows directives described in An O(ND)
Difference Algorithm and Its Variations by Eugene W. Myers. Memory files used by
the library are basically a collection of buffers that store the file content.
There are two different requirements for memory files when passed to diff/patch
functions. Text files for diff/patch functions require that a single line do not
have to spawn across two different memory file blocks. Binary diff/patch
functions require memory files to be compact. A compact memory files is a file
whose content is stored inside a single block. Functionalities inside the
library are available to satisfy these rules. Using the XDL_MMF_ATOMIC memory
file flag it is possible to make writes to not split the written record across
different blocks, while the functions xdl_mmfile_iscompact() ,
xdl_mmfile_compact() and xdl_mmfile_writeallocate() are usefull to test if the
file is compact and to create a compacted version of the file itself. The text
file differential output uses the raw unified output format, by omitting the
file header since the result is always relative to a single compare operation
(between two files). The output format of the binary patch file is proprietary
(and binary) and it is basically a collection of copy and insert commands, like
described inside the MacDonald paper. The library is compatible with almost
every Unix implementation (configure script) and it is also compatible with
Windows through custom (nmake) make files. Examples are available inside the
test subdirectory of the distribution tarball that show how to use the library.
Also, inside the same subdirectory, a regression test in available that tests
the library with random data by requiring a diff followed by a patch and
comparing results. Regression tests ran successfully for days on my Linux,
Solaris, FreeBSD and Windows boxes, and this makes me believe that the library
itself is completely ready for production (despite the version number).

View File

@@ -0,0 +1,34 @@
/* config.h.in. Generated from configure.in by autoheader. */
/* Define to 1 if you have the <limits.h> header file. */
#cmakedefine HAVE_LIMITS_H 1
/* Define to 1 if you have the `memchr' function. */
#cmakedefine HAVE_MEMCHR 1
/* Define to 1 if you have the `memcmp' function. */
#cmakedefine HAVE_MEMCMP 1
/* Define to 1 if you have the `memcpy' function. */
#cmakedefine HAVE_MEMCPY 1
/* Define to 1 if you have the `memset' function. */
#cmakedefine HAVE_MEMSET 1
/* Define to 1 if you have the <stdio.h> header file. */
#cmakedefine HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#cmakedefine HAVE_STDLIB_H 1
/* Define to 1 if you have the <string.h> header file. */
#cmakedefine HAVE_STRING_H 1
/* Define to 1 if you have the `strlen' function. */
#cmakedefine HAVE_STRLEN 1
/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H 1
/* Define to the version of this package. */
#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta name="Author" content="Davide Libenzi">
<meta name="GENERATOR" content="Mozilla 1.2">
<meta name="Keywords"
content="diff,patch,library,portable,file,difference,binary diff,binary patch,myers,macdonald">
<title>LibXDiff Home Page</title>
</head>
<body style="color: rgb(0, 0, 255);">
<p style="font-family: monospace;" align="center"><b>LibXDiff</b><br>
</p>
<p style="font-family: monospace;" align="left">&nbsp;<br>
The <b>LibXDiff</b> library implements basic and yet complete
functionalities to create file differences/patches to both binary and
text files. The library uses memory files as file abstraction to
achieve
both performance and portability. For binary files, <b>LibXDiff</b>
implements both (with some modification) the algorithm described in <i>File
System Support for Delta Compression</i> by <i>Joshua P. MacDonald</i>,
and the algorithm described in <span style="font-style: italic;">Fingerprinting
By Random Polynomials</span> by <span style="font-style: italic;">Michael
O. Rabin</span>. While for text files it follows directives described
in <i>An O(ND)
Difference Algorithm and Its Variations</i> by <i>Eugene W. Myers</i>.
Memory files used by the library are basically a collection of buffers
that store the file content. There are two different requirements for
memory files when passed to diff/patch functions. Text files for
diff/patch functions require that a single line do not have to spawn
across two different memory file blocks. Binary diff/patch functions
require memory files to be compact. A compact memory files is a file
whose content is stored inside a single block. Functionalities inside
the library are available to satisfy these rules. Using the <b>XDL_MMF_ATOMIC</b>
memory file flag it is possible to make writes to not split the written
record across different blocks, while the functions <b>xdl_mmfile_iscompact</b>()
, <b>xdl_mmfile_compact</b>() and <b>xdl_mmfile_writeallocate</b>()
are usefull to test if the file is compact and to create a compacted
version of the file itself. The text file differential output uses the
raw unified output format, by omitting the file header since the result
is always relative to a single compare operation (between two files).
The output format of the binary patch file is proprietary (and binary)
and it is basically a collection of copy and insert commands, like
described inside the MacDonald paper. The library is compatible with
almost every Unix implementation (configure script) and it is also
compatible with Windows through custom (nmake) make files. Examples are
available inside the <span style="font-style: italic;">test</span>
subdirectory of the distribution tarball that show how to use the
library. Also, inside the same subdirectory, a regression test in
available that tests the library with random data by requiring a diff
followed by a patch and comparing results. Regression tests ran
successfully for days on my Linux, Solaris, FreeBSD and Windows boxes,
and this makes me believe that the library itself is completely ready
for production (despite the version number).<br>
</p>
<p style="font-family: monospace;" align="left"><br>
</p>
<p style="text-align: center; font-family: monospace;"><b>Documentation<br>
</b></p>
<div style="text-align: left; font-family: monospace;"><br>
The <span style="font-weight: bold;">LibXDiff</span> library man page
is available : <a href="xdiff.html">HTML</a>&nbsp;&nbsp;&nbsp;<a
href="xdiff.txt">TXT</a>&nbsp;&nbsp;&nbsp; <a href="xdiff.pdf">PDF<br>
</a><br>
<br>
</div>
<p style="font-family: monospace;" align="center"><b>License
and Software</b></p>
<p style="font-family: monospace;" align="left"><span
style="font-weight: bold;">LibXDiff</span>&nbsp;
is made available through the <a
href="http://www.gnu.org/copyleft/lesser.html">GNU LGPL</a> license
together with the complete sources. Please read carefully the <a
href="http://www.gnu.org/copyleft/lesser.html">license</a> before
using
the software. The latest library package is available here :</p>
<p style="font-family: monospace;" align="left"><a
href="http://www.xmailserver.org/libxdiff-0.23.tar.gz">Version
0.23</a></p>
<p style="font-family: monospace;" align="left"><br>
</p>
<p style="text-align: center; font-family: monospace;"><b>Links And Docs<br>
</b></p>
<div style="text-align: left; font-family: monospace;"><a
href="http://freshmeat.net/projects/xdiff-lib">LibXDiff FreshMeat Home
Page</a><a href="http://www.gnu.org/software/diffutils/diffutils.html"><br>
GNU DiffUtil</a><br>
<a href="diff2.pdf"><span style="font-style: italic;">An O(ND)
Difference Algorithm and Its Variations</span> by <i>Eugene W. Myers</i></a><br>
<a href="xdfs.pdf"><i>File System Support for Delta Compression</i> by <i>Joshua
P. MacDonald</i></a><br>
<a href="rabin.pdf"><i>Fingerprinting By Random Polynomials</i> by <i>Michael
O. Rabin</i></a><br>
<a href="rabin_impl.pdf"><i>Fingerprinting Using Polynomial</i> by <i>Calvin
Chan</i> and <i>Hahua Lu</i></a><br>
<a href="rabin_apps.pdf"><i>Some Applications of Rabin's Fingerprinting
Method</i> by <i>Andrei Z. Broder</i></a><br>
<br>
<br>
<br>
<a href="davide.html">Back Home</a><br>
<br>
<br>
</div>
</body>
</html>

View File

@@ -0,0 +1,871 @@
.\"
.\" LibXDiff by Davide Libenzi ( File Differential Library )
.\" Copyright (C) 2003 Davide Libenzi
.\"
.\" This library is free software; you can redistribute it and/or
.\" modify it under the terms of the GNU Lesser General Public
.\" License as published by the Free Software Foundation; either
.\" version 2.1 of the License, or (at your option) any later version.
.\"
.\" This library is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program; if not, write to the Free Software
.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
.\"
.\" Davide Libenzi <davidel@xmailserver.org>
.\"
.na
.TH LibXDiff 3 "0.23" "GNU" "File Differential Library"
.SH NAME
xdl_set_allocator, xdl_malloc, xdl_free, xdl_realloc, xdl_init_mmfile, xdl_free_mmfile,
xdl_mmfile_iscompact, xdl_seek_mmfile, xdl_read_mmfile, xdl_write_mmfile, xdl_writem_mmfile,
xdl_mmfile_writeallocate, xdl_mmfile_ptradd, xdl_mmfile_first, xdl_mmfile_next, xdl_mmfile_size, xdl_mmfile_cmp,
xdl_mmfile_compact, xdl_diff, xdl_patch, xdl_merge3, xdl_bdiff_mb, xdl_bdiff, xdl_rabdiff_mb, xdl_rabdiff,
xdl_bdiff_tgsize, xdl_bpatch \- File Differential Library support functions
.SH SYNOPSIS
.nf
.B #include <xdiff.h>
.sp
.BI "int xdl_set_allocator(memallocator_t const *" malt ");"
.nl
.BI "void *xdl_malloc(unsigned int " size ");"
.nl
.BI "void xdl_free(void *" ptr ");"
.nl
.BI "void *xdl_realloc(void *" ptr ", unsigned int " nsize ");"
.nl
.BI "int xdl_init_mmfile(mmfile_t *" mmf ", long " bsize ", unsigned long " flags ");"
.nl
.BI "void xdl_free_mmfile(mmfile_t *" mmf ");"
.nl
.BI "int xdl_mmfile_iscompact(mmfile_t *" mmf ");"
.nl
.BI "int xdl_seek_mmfile(mmfile_t *" mmf ", long " off ");"
.nl
.BI "long xdl_read_mmfile(mmfile_t *" mmf ", void *" data ", long " size ");"
.nl
.BI "long xdl_write_mmfile(mmfile_t *" mmf ", void const *" data ", long " size ");"
.nl
.BI "long xdl_writem_mmfile(mmfile_t *" mmf ", mmbuffer_t *" mb ", int " nbuf ");"
.nl
.BI "void *xdl_mmfile_writeallocate(mmfile_t *" mmf ", long " size ");"
.nl
.BI "long xdl_mmfile_ptradd(mmfile_t *" mmf ", char *" ptr ", long " size ", unsigned long " flags ");"
.nl
.BI "void *xdl_mmfile_first(mmfile_t *" mmf ", long *" size ");"
.nl
.BI "void *xdl_mmfile_next(mmfile_t *" mmf ", long *" size ");"
.nl
.BI "long xdl_mmfile_size(mmfile_t *" mmf ");"
.nl
.BI "int xdl_mmfile_cmp(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ");"
.nl
.BI "int xdl_mmfile_compact(mmfile_t *" mmfo ", mmfile_t *" mmfc ", long " bsize ", unsigned long " flags ");"
.nl
.BI "int xdl_diff(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", xpparam_t const *" xpp ", xdemitconf_t const *" xecfg ", xdemitcb_t *" ecb ");"
.nl
.BI "int xdl_patch(mmfile_t *" mmf ", mmfile_t *" mmfp ", int " mode ", xdemitcb_t *" ecb ", xdemitcb_t *" rjecb ");"
.nl
.BI "int xdl_merge3(mmfile_t *" mmfo ", mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", xdemitcb_t *" ecb ", xdemitcb_t *" rjecb ");"
.nl
.BI "int xdl_bdiff_mb(mmbuffer_t *" mmb1 ", mmbuffer_t *" mmb2 ", bdiffparam_t const *" bdp ", xdemitcb_t *" ecb ");"
.nl
.BI "int xdl_bdiff(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", bdiffparam_t const *" bdp ", xdemitcb_t *" ecb ");"
.nl
.BI "int xdl_rabdiff_mb(mmbuffer_t *" mmb1 ", mmbuffer_t *" mmb2 ", xdemitcb_t *" ecb ");"
.nl
.BI "int xdl_rabdiff(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", xdemitcb_t *" ecb ");"
.nl
.BI "long xdl_bdiff_tgsize(mmfile_t *" mmfp ");"
.nl
.BI "int xdl_bpatch(mmfile_t *" mmf ", mmfile_t *" mmfp ", xdemitcb_t *" ecb ");"
.SH DESCRIPTION
The
.B LibXDiff
library implements basic and yet complete functionalities to create
file differences/patches to both binary and text files. The library
uses memory files as file abstraction to achieve both performance and
portability. For binary files,
.B LibXDiff
implements both (with some modification) the algorithm described in
.IR "File System Support for Delta Compression" " by " "Joshua P. MacDonald" ,
and the method described in
.IR "Fingerprinting by Random Polynomials" " by " "Michael O. Rabin" "."
While for text files it follows directives described in
.IR "An O(ND) Difference Algorithm and Its Variations" " by " "Eugene W. Myers" .
Memory files used by the library are basically a collection of buffers
that store the file content. There are two different requirements for memory
files when passed to diff/patch functions. Text files for diff/patch functions
require that a single line do not have to spawn across two different memory
file blocks. Binary diff/patch functions require memory files to be compact.
A compact memory files is a file whose content is stored inside a single block.
Functionalities inside the library are available to satisfy these rules.
Using the
.B XDL_MMF_ATOMIC
memory file flag it is possible to make writes to not split the written
record across different blocks, while the functions
.BR xdl_mmfile_iscompact ()
,
.BR xdl_mmfile_compact ()
and
.BR xdl_mmfile_writeallocate ()
are usefull to test if the file is compact and to create a compacted version
of the file itself. The text file differential output uses the raw unified output
format, by omitting the file header since the result is always relative to
a single compare operation (between two files). The output format of the binary
patch file is proprietary (and binary) and it is basically a collection of
copy and insert commands, like described inside the MacDonald paper.
.SS Functions
The following functions are defined:
.TP
.BI "int xdl_set_allocator(memallocator_t const *" malt ");"
The
.B LibXDiff
library enable the user to set its own memory allocator, that will be used
for all the following memory requests. The allocator must be set before
to start calling the
.B LibXDiff
library with a call to
.BR xdl_set_allocator ().
The memory allocator structure contains the following members:
.nf
typedef struct s_memallocator {
void *priv;
void *(*malloc)(void *priv, unsigned int size);
void (*free)(void *priv, void *ptr);
void *(*realloc)(void *priv, void *ptr, unsigned int nsize);
} memallocator_t;
.fi
The
.BR malloc ()
function pointer will be used by
.B LibXDiff
to request a memory block of
.I size
bytes. The
.BR free ()
function pointer will be called to free a previously allocated block
.I ptr
, while the
.BR realloc ()
will be used to resize the
.I ptr
to a new
.I nsize
size in bytes. The
.B priv
structure member will be passed to the
.BR malloc (), free (), realloc ()
functions as first parameter. The
.B LibXDiff
user must call
.BR xdl_set_allocator ()
before starting using the library, otherwise
.B LibXDiff
functions will fail due to the lack of memory allocation support.
A typical initialization sequence for
.B POSIX
systems will use the standard
.BR malloc (3),
.BR free (3),
.BR realloc (3)
and will look like:
.nf
void *wrap_malloc(void *priv, unsigned int size) {
return malloc(size);
}
void wrap_free(void *priv, void *ptr) {
free(ptr);
}
void *wrap_realloc(void *priv, void *ptr, unsigned int size) {
return realloc(ptr, size);
}
void my_init_xdiff(void) {
memallocator_t malt;
malt.priv = NULL;
malt.malloc = wrap_malloc;
malt.free = wrap_free;
malt.realloc = wrap_realloc;
xdl_set_allocator(&malt);
}
.fi
.TP
.BI "void *xdl_malloc(unsigned int " size ");"
Allocates a memory block of
.I size
bytes using the
.B LibXDiff
memory allocator. The user can specify its own allocator using the
.BR xdl_set_allocator ()
function. The
.BR xdl_malloc ()
return a pointer to the newly allocated block, or
.B NULL
in case of failure.
.TP
.BI "void xdl_free(void *" ptr ");"
Free a previously allocated memory block pointed by
.IR ptr .
The
.I ptr
block must has been allocated using either
.BR xdl_malloc ()
or
.BR xdl_realloc ().
.TP
.BI "void *xdl_realloc(void *" ptr ", unsigned int " nsize ");"
Resizes the memory block pointed by
.I ptr
to a new size
.IR nsize .
Return the resized block if successful, or
.B NULL
in case the reallocation fails. After a successful reallocation, the old
.I ptr
block is to be considered no more valid.
.TP
.BI "int xdl_init_mmfile(mmfile_t *" mmf ", long " bsize ", unsigned long " flags ");"
Initialize the memory file
.I mmf
by requiring an internal block size of
.IR bsize .
The
.I flags
parameter is a combination of the following flags :
.br
.IP
.B XDL_MMF_ATOMIC
Writes on the memory file will be atomic. That is, the data will not be split
on two or more different blocks.
Once an
.BR xdl_init_mmfile ()
succeeded, a matching
.BR xdl_free_mmfile ()
must be called when the user has done using the memory file, otherwise serious
memory leaks will happen.
The function return 0 if succeed or -1 if an error is encountered.
.TP
.BI "void xdl_free_mmfile(mmfile_t *" mmf ");"
Free all the data associated with the
.I mmf
memory file.
.TP
.BI "int xdl_mmfile_iscompact(mmfile_t *" mmf ");"
Returns an integer different from 0 if the
.I mmf
memory file is compact, 0 otherwise. A compact memory file is one that have
the whole content stored inside a single block.
.TP
.BI "int xdl_seek_mmfile(mmfile_t *" mmf ", long " off ");"
Set the current data pointer of the memory file
.I mmf
to the specified offset
.I off
from the beginning of the file itself. Returns 0 if successful or -1 if an error
happened.
.TP
.BI "long xdl_read_mmfile(mmfile_t *" mmf ", void *" data ", long " size ");"
Request to read
.I size
bytes from the memory file
.I mmf
by storing the data inside the
.I data
buffer. Returns the number of bytes read into the
.I data
buffer. The amount of data read can be lower than the specified
.IR size .
The function returns -1 if an error happened.
.TP
.BI "long xdl_write_mmfile(mmfile_t *" mmf ", void const *" data ", long " size ");"
Request to write
.I size
bytes from the specified buffer
.I data
into the memory file
.IR mmf .
If the memory file has been created using the
.B XDL_MMF_ATOMIC
flag, the write request will not be split across different blocks.
Note that all write operations done on memory files do append data at the end
the file, and writes in the middle of it are allowed. This is because the library
memory file abstraction does not need this functionality to be available.
The function returns the number of bytes written or a number lower than
.I size
if an error happened.
.TP
.BI "long xdl_writem_mmfile(mmfile_t *" mmf ", mmbuffer_t *" mb ", int " nbuf ");"
Request to sequentially write
.I nbuf
memory buffers passed inside the array
.I mb
into the memory file
.IR mmf .
The memory buffer structure is defined as :
.nf
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
.fi
The
.I ptr
field is a pointer to the user data, whose size is specified inside the
.I size
structure field. The function returns the total number of bytes written
or a lower number if an error happened.
.TP
.BI "void *xdl_mmfile_writeallocate(mmfile_t *" mmf ", long " size ");"
The function request to allocate a write buffer of
.I size
bytes in the
.I mmf
memory file and returns the pointer to the allocated buffer. The user will
have the responsibility to store
.I size
bytes (no more, no less) inside the memory region pointed to by the returned pointer.
The files size will grow of
.I size
bytes as a consequence of this operation. The function will return
.B NULL
if an error happened.
.TP
.BI "long xdl_mmfile_ptradd(mmfile_t *" mmf ", char *" ptr ", long " size ", unsigned long " flags ");"
The function adds a user specified block to the end of the memory file
.IR mmf .
The block first byte is pointed to by
.I ptr
and its length is
.I size
bytes. The
.I flags
parameter can be used to specify attributes of the user memory block. Currently
supported attributes are:
.br
.IP
.B XDL_MMB_READONLY
Specify that the added memory block must be treated as read-only, and
every attempt to write on it should result in a failure of the memory file
writing functions.
The purpose of this function is basically to avoid copying memory around,
by helping the library to not drain the CPU cache. The function returns
.I size
in case of success, or -1 in case of error.
.TP
.BI "void *xdl_mmfile_first(mmfile_t *" mmf ", long *" size ");"
The function is used to return the first block of the
.I mmf
memory file block chain. The
.I size
parameter will receive the size of the block, while the function will return
the pointer the the first byte of the block itself. The function returns
.B NULL
if the file is empty.
.TP
.BI "void *xdl_mmfile_next(mmfile_t *" mmf ", long *" size ");"
The function is used to return the next block of the
.I mmf
memory file block chain. The
.I size
parameter will receive the size of the block, while the function will return
the pointer the the first byte of the block itself. The function returns
.B NULL
if the current block is the last one of the chain.
.TP
.BI "long xdl_mmfile_size(mmfile_t *" mmf ");"
The function returns the size of the specified memory file
.IR mmf .
.TP
.BI "int xdl_mmfile_cmp(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ");"
Request to compare two memory files
.IR mmf1 " and " mmf2
and returns 0 if files are identical, or a value different from 0 if
files are different.
.TP
.BI "int xdl_mmfile_compact(mmfile_t *" mmfo ", mmfile_t *" mmfc ", long " bsize ", unsigned long " flags ");"
Request to create a compact version of the memory file
.I mmfo
into the (uninitialized) memory file
.IR mmfc .
The
.I bsize
parameter specify the requested block size and
.I flags
specify flags to be used to create the new
.I mmfc
memory file (see
.BR xdl_init_mmfile ()
). The function returns 0 if succedded or -1 if an error happened.
.TP
.BI "int xdl_diff(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", xpparam_t const *" xpp ", xdemitconf_t const *" xecfg ", xdemitcb_t *" ecb ");"
Request to create the difference between the two text memory files
.IR mmf1 " and " mmf2 .
The
.I mmf1
memory files is considered the "old" file while
.I mmf2
is considered the "new" file. So the function will create a patch file
that once applied to
.I mmf1
will give
.I mmf2
as result. Files
.IR mmf1 " and " mmf2
must be atomic from a line point of view (or, as an extreme, compact), that
means that a single test line cannot spread among different memory file blocks.
The
.I xpp
parameter is a pointer to a structure :
.nf
typedef struct s_xpparam {
unsigned long flags;
} xpparam_t;
.fi
that is used to specify parameters to be used by the file differential algorithm.
The
.I flags
field is a combination of the following flags :
.IP
.B XDF_NEED_MINIMAL
Requires the minimal edit script to be found by the algorithm (may be slow).
The
.I xecfg
parameter point to a structure :
.nf
typedef struct s_xdemitconf {
long ctxlen;
} xdemitconf_t;
.fi
that is used to configure the algorithm responsible of the creation the the
differential file from an edit script. The
.I ctxlen
field is used to specify the amount of context to be emitted inside the
differential file (the value 3 is suggested for normal operations).
The parameter
.I ecb
is a pointer to a structure :
.nf
typedef struct s_xdemitcb {
void *priv;
int (*outf)(void *, mmbuffer_t *, int);
} xdemitcb_t;
.fi
that is used by the differential file creation algorithm to emit the created
data. The
.I priv
field is an opaque pointer to a user specified data, while the
.I outf
field point to a callback function that is called internally to emit algorithm
generated data rappresenting the differential file. The first parameter of the
callback is the same
.I priv
field specified inside the
.B xdemitcb_t
structure. The second parameter point to an array of
.B mmbuffer_t
(see above for a definition of the structure) whose element count is specified
inside the last parameter of the callback itself. The callback will always be
called with entire records (lines) and never a record (line) will be emitted
using two different callback calls. This is important because if the called will
use another memory file to store the result, by creating the target memory file with
.B XDL_MMF_ATOMIC
will guarantee the "atomicity" of the memory file itself.
The function returns 0 if succeeded or -1 if an error occurred.
.TP
.BI "int xdl_patch(mmfile_t *" mmf ", mmfile_t *" mmfp ", int " mode ", xdemitcb_t *" ecb ", xdemitcb_t *" rjecb ");"
Request to patch the memory file
.I mmf
using the patch file stored in
.IR mmfp .
The
.I mmf
memory file
.B is not
changed during the operation and can be considered as read only.
The
.I mode
parameter can be one of the following values :
.IP
.B XDL_PATCH_NORMAL
Perform standard patching like if the patch memory file
.I mmfp
has been created using
.I mmf
as "old" file.
.IP
.B XDL_PATCH_REVERSE
Apply the reverse patch. That means that the
.I mmf
memory file has to be considered as if it was specified as "new" file during
the differential operation (
.BR xdl_diff ()
). The result of the operation will then be the file content that was used
as "old" file during the differential operation.
The following flags can be specified (by or-ing them) to one of the above:
.IP
.B XDL_PATCH_IGNOREBSPACE
Ignore the whitespace at the beginning and the end of the line.
The
.I ecb
will be used by the patch algorithm to create the result file while the
.I rjecb
will be used to emit all differential chunks that cannot be applied.
Like explained above, callbacks are always called with entire records to guarantee
atomicity of the resulting output.
The function returns 0 if succeeded without performing any fuzzy hunk detection,
a positive value if it secceeded with fuzzy hunk detection or -1 if an error occurred
during the patch operation.
.TP
.BI "int xdl_merge3(mmfile_t *" mmfo ", mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", xdemitcb_t *" ecb ", xdemitcb_t *" rjecb ");"
Merges three files together. The
.I mmfo
file is the original one, while
.IR mmf1 " and " mmf2
are two modified versions of
.IR mmfo .
The function works by creating a differential between
.IR mmfo " and " mmf2
and by applying the resulting patch to
.IR mmf1 .
Because of this sequence,
.I mmf1
changes will be privileged against the ones of
.IR mmf2 .
The
.I ecb
will be used by the patch algorithm to create the result file while the
.I rjecb
will be used to emit all differential chunks that cannot be applied.
Like explained above, callbacks are always called with entire records to guarantee
atomicity of the resulting output.
The function returns 0 if succeeded or -1 if an error occurred during the patch operation.
.TP
.BI "int xdl_bdiff(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", bdiffparam_t const *" bdp ", xdemitcb_t *" ecb ");"
Request to create the difference between the two text memory files
.IR mmf1 " and " mmf2 .
The
.I mmf1
memory files is considered the "old" file while
.I mmf2
is considered the "new" file. So the function will create a patch file
that once applied to
.I mmf1
will give
.I mmf2
as result. Files
.IR mmf1 " and " mmf2
must be compact to make it easy and faster to perform the difference operation.
Functions are available to check for compactness (
.BR xdl_mmfile_iscompact ()
) and to make compact a non-compact file (
.BR xdl_mmfile_compact ()
). An example of how to create a compact memory file (described inside the test
subdirectory) is :
.nf
int xdlt_load_mmfile(char const *fname, mmfile_t *mf, int binmode) {
char cc;
int fd;
long size, bsize;
char *blk;
if (xdl_init_mmfile(mf, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0)
return -1;
if ((fd = open(fname, O_RDONLY)) == -1) {
perror(fname);
xdl_free_mmfile(mf);
return -1;
}
if ((size = bsize = lseek(fd, 0, SEEK_END)) > 0 && !binmode) {
if (lseek(fd, -1, SEEK_END) != (off_t) -1 &&
read(fd, &cc, 1) && cc != '\\n')
bsize++;
}
lseek(fd, 0, SEEK_SET);
if (!(blk = (char *) xdl_mmfile_writeallocate(mf, bsize))) {
xdl_free_mmfile(mf);
close(fd);
return -1;
}
if (read(fd, blk, (size_t) size) != (size_t) size) {
perror(fname);
xdl_free_mmfile(mf);
close(fd);
return -1;
}
close(fd);
if (bsize > size)
blk[size] = '\\n';
return 0;
}
.fi
The
.I bdp
parameter points to a structure :
.nf
typedef struct s_bdiffparam {
long bsize;
} bdiffparam_t;
.fi
that is used to pass information to the binary file differential algorithm.
The
.I bsize
parameter specify the size of the block that will be used to decompose
.I mmf1
during the block classification phase of the algorithm (see MacDonald paper).
Suggested values go from 16 to 64, with a preferred power of two characteristic.
The
.I ecb
parameter is used to pass the emission callback to the algorithm responsible
of the output file creation.
The function returns 0 if succeede or -1 if an error is occurred.
.TP
.BI "int xdl_bdiff_mb(mmbuffer_t *" mmb1 ", mmbuffer_t *" mmb2 ", bdiffparam_t const *" bdp ", xdemitcb_t *" ecb ");"
Same as
.BR xdl_bdiff ()
but it works on memory buffer directly. The
.BR xdl_bdiff ()
is implemented internally with a
.BR xdl_bdiff_mb ()
after having setup the two memory buffers from the passed memory files (that must be compact,
as described above). The memory buffer structure is defined as :
.nf
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
.fi
An empty memory buffer is specified by setting the
.I ptr
member as
.B NULL
and the
.I size
member as zero. The reason of having this function is to avoid the memory file
preparation, that might involve copying memory from other sources. Using
the
.BR xdl_bdiff_mb (),
the caller can setup the two memory buffer by using, for example,
.BR mmap (2),
and hence avoiding unnecessary memory copies. The other parameters and the return value
of the function
.BR xdl_bdiff_mb ()
are the same as the ones already described in
.BR xdl_bdiff ().
.TP
.BI "int xdl_rabdiff(mmfile_t *" mmf1 ", mmfile_t *" mmf2 ", xdemitcb_t *" ecb ");"
Request to create the difference between the two text memory files
.IR mmf1 " and " mmf2
using the Rabin's polynomial fingerprinting algorithm. This algorithm typically
performs faster and produces smaller deltas, when compared to the XDelta-like one.
The
.I mmf1
memory files is considered the "old" file while
.I mmf2
is considered the "new" file. So the function will create a patch file
that once applied to
.I mmf1
will give
.I mmf2
as result. Files
.IR mmf1 " and " mmf2
must be compact to make it easy and faster to perform the difference operation.
Functions are available to check for compactness (
.BR xdl_mmfile_iscompact ()
) and to make compact a non-compact file (
.BR xdl_mmfile_compact ()
). The
.I ecb
parameter is used to pass the emission callback to the algorithm responsible
of the output file creation.
The function returns 0 if succeede or -1 if an error is occurred.
.TP
.BI "int xdl_rabdiff_mb(mmbuffer_t *" mmb1 ", mmbuffer_t *" mmb2 ", xdemitcb_t *" ecb ");"
Same as
.BR xdl_rabdiff ()
but it works on memory buffer directly. The memory buffer structure is defined as :
.nf
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
.fi
An empty memory buffer is specified by setting the
.I ptr
member as
.B NULL
and the
.I size
member as zero. The reason of having this function is to avoid the memory file
preparation, that might involve copying memory from other sources. Using
the
.BR xdl_rabdiff_mb (),
the caller can setup the two memory buffer by using, for example,
.BR mmap (2),
and hence avoiding unnecessary memory copies. The other parameters and the return value
of the function
.BR xdl_rabdiff_mb ()
are the same as the ones already described in
.BR xdl_rabdiff ().
.TP
.BI "long xdl_bdiff_tgsize(mmfile_t *" mmfp ");"
Given a binary memory file patch, it returns the size that the result file
will have once the patch is applied to the target file. It can be used to
pre-allocate (or write-allocate) a memory block to store the patch result
so that a compact file will be available at the end of the operation. The
function returns the requested size, or -1 if an error occurred during the operation.
.TP
.BI "int xdl_bpatch(mmfile_t *" mmf ", mmfile_t *" mmfp ", xdemitcb_t *" ecb ");"
Request to patch the binary memory file
.I mmf
using the binary patch file stored in
.IR mmfp .
The
.I mmf
memory file
.B is not
changed during the operation and can be considered as read only. The binary
patch algorithm has no notion of context, so the patch operation cannot be
partial (either success or failure). The
.I ecb
parameter contain the callabck (see above for description) used by the binary
patch algorithm to emit the result file. The function returns 0 if succeeded
or -1 if an error occurred during the patch operation.
.SH SEE ALSO
Two papers drove the content of this library and these are :
.br
.IP o
.IR "File System Support for Delta Compression" " by " "Joshua P. MacDonald"
.BR http://www.xmailserver.org/xdfs.pdf
.br
.IP o
.IR "Fingerprinting by Random Polynomials" " by " "Michael O. Rabin"
.BR http://www.xmailserver.org/rabin.pdf
.br
.IP o
.IR "An O(ND) Difference Algorithm and Its Variations" " by " "Eugene W. Myers"
.BR http://www.xmailserver.org/diff2.pdf
.PP
Also usefull information can be looked up inside the
.B diffutil
GNU package :
.BR http://www.gnu.org/software/diffutils/diffutils.html
.SH LICENSE
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
A copy of the license is available at :
.BR http://www.gnu.org/copyleft/lesser.html
.SH AUTHOR
Developed by Davide Libenzi
.RB < davidel@xmailserver.org >
.SH AVAILABILITY
The latest version of
.B LibXDiff
can be found at :
.BR http://www.xmailserver.org/xdiff-lib.html
.SH BUGS
There are no known bugs. Bug reports and comments to Davide Libenzi
.RB < davidel@xmailserver.org >

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,611 @@
LibXDiff(3) File Differential Library LibXDiff(3)
NAME
xdl_set_allocator, xdl_malloc, xdl_free, xdl_realloc, xdl_init_mmfile,
xdl_free_mmfile, xdl_mmfile_iscompact, xdl_seek_mmfile,
xdl_read_mmfile, xdl_write_mmfile, xdl_writem_mmfile,
xdl_mmfile_writeallocate, xdl_mmfile_ptradd, xdl_mmfile_first,
xdl_mmfile_next, xdl_mmfile_size, xdl_mmfile_cmp, xdl_mmfile_compact,
xdl_diff, xdl_patch, xdl_merge3, xdl_bdiff_mb, xdl_bdiff, xdl_rabd-
iff_mb, xdl_rabdiff, xdl_bdiff_tgsize, xdl_bpatch - File Differential
Library support functions
SYNOPSIS
#include <xdiff.h>
int xdl_set_allocator(memallocator_t const *malt);
void *xdl_malloc(unsigned int size);
void xdl_free(void *ptr);
void *xdl_realloc(void *ptr, unsigned int nsize);
int xdl_init_mmfile(mmfile_t *mmf, long bsize, unsigned long flags);
void xdl_free_mmfile(mmfile_t *mmf);
int xdl_mmfile_iscompact(mmfile_t *mmf);
int xdl_seek_mmfile(mmfile_t *mmf, long off);
long xdl_read_mmfile(mmfile_t *mmf, void *data, long size);
long xdl_write_mmfile(mmfile_t *mmf, void const *data, long size);
long xdl_writem_mmfile(mmfile_t *mmf, mmbuffer_t *mb, int nbuf);
void *xdl_mmfile_writeallocate(mmfile_t *mmf, long size);
long xdl_mmfile_ptradd(mmfile_t *mmf, char *ptr, long size, unsigned long flags);
void *xdl_mmfile_first(mmfile_t *mmf, long *size);
void *xdl_mmfile_next(mmfile_t *mmf, long *size);
long xdl_mmfile_size(mmfile_t *mmf);
int xdl_mmfile_cmp(mmfile_t *mmf1, mmfile_t *mmf2);
int xdl_mmfile_compact(mmfile_t *mmfo, mmfile_t *mmfc, long bsize, unsigned long flags);
int xdl_diff(mmfile_t *mmf1, mmfile_t *mmf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
int xdl_patch(mmfile_t *mmf, mmfile_t *mmfp, int mode, xdemitcb_t *ecb, xdemitcb_t *rjecb);
int xdl_merge3(mmfile_t *mmfo, mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb, xdemitcb_t *rjecb);
int xdl_bdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, bdiffparam_t const *bdp, xdemitcb_t *ecb);
int xdl_bdiff(mmfile_t *mmf1, mmfile_t *mmf2, bdiffparam_t const *bdp, xdemitcb_t *ecb);
int xdl_rabdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, xdemitcb_t *ecb);
int xdl_rabdiff(mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb);
long xdl_bdiff_tgsize(mmfile_t *mmfp);
int xdl_bpatch(mmfile_t *mmf, mmfile_t *mmfp, xdemitcb_t *ecb);
DESCRIPTION
The LibXDiff library implements basic and yet complete functionalities
to create file differences/patches to both binary and text files. The
library uses memory files as file abstraction to achieve both perfor-
mance and portability. For binary files, LibXDiff implements both (with
some modification) the algorithm described in File System Support for
Delta Compression by Joshua P. MacDonald, and the method described in
Fingerprinting by Random Polynomials by Michael O. Rabin. While for
text files it follows directives described in An O(ND) Difference Algo-
rithm and Its Variations by Eugene W. Myers. Memory files used by the
library are basically a collection of buffers that store the file con-
tent. There are two different requirements for memory files when passed
to diff/patch functions. Text files for diff/patch functions require
that a single line do not have to spawn across two different memory
file blocks. Binary diff/patch functions require memory files to be
compact. A compact memory files is a file whose content is stored
inside a single block. Functionalities inside the library are avail-
able to satisfy these rules. Using the XDL_MMF_ATOMIC memory file flag
it is possible to make writes to not split the written record across
different blocks, while the functions xdl_mmfile_iscompact() ,
xdl_mmfile_compact() and xdl_mmfile_writeallocate() are usefull to test
if the file is compact and to create a compacted version of the file
itself. The text file differential output uses the raw unified output
format, by omitting the file header since the result is always relative
to a single compare operation (between two files). The output format of
the binary patch file is proprietary (and binary) and it is basically a
collection of copy and insert commands, like described inside the Mac-
Donald paper.
Functions
The following functions are defined:
int xdl_set_allocator(memallocator_t const *malt);
The LibXDiff library enable the user to set its own memory allo-
cator, that will be used for all the following memory requests.
The allocator must be set before to start calling the LibXDiff
library with a call to xdl_set_allocator(). The memory alloca-
tor structure contains the following members:
typedef struct s_memallocator {
void *priv;
void *(*malloc)(void *priv, unsigned int size);
void (*free)(void *priv, void *ptr);
void *(*realloc)(void *priv, void *ptr, unsigned int nsize);
} memallocator_t;
The malloc() function pointer will be used by LibXDiff to
request a memory block of size bytes. The free() function
pointer will be called to free a previously allocated block ptr
, while the realloc() will be used to resize the ptr to a new
nsize size in bytes. The priv structure member will be passed to
the malloc(),free(),realloc() functions as first parameter. The
LibXDiff user must call xdl_set_allocator() before starting
using the library, otherwise LibXDiff functions will fail due to
the lack of memory allocation support. A typical initialization
sequence for POSIX systems will use the standard malloc(3),
free(3), realloc(3) and will look like:
void *wrap_malloc(void *priv, unsigned int size) {
return malloc(size);
}
void wrap_free(void *priv, void *ptr) {
free(ptr);
}
void *wrap_realloc(void *priv, void *ptr, unsigned int size) {
return realloc(ptr, size);
}
void my_init_xdiff(void) {
memallocator_t malt;
malt.priv = NULL;
malt.malloc = wrap_malloc;
malt.free = wrap_free;
malt.realloc = wrap_realloc;
xdl_set_allocator(&malt);
}
void *xdl_malloc(unsigned int size);
Allocates a memory block of size bytes using the LibXDiff memory
allocator. The user can specify its own allocator using the
xdl_set_allocator() function. The xdl_malloc() return a pointer
to the newly allocated block, or NULL in case of failure.
void xdl_free(void *ptr);
Free a previously allocated memory block pointed by ptr. The
ptr block must has been allocated using either xdl_malloc() or
xdl_realloc().
void *xdl_realloc(void *ptr, unsigned int nsize);
Resizes the memory block pointed by ptr to a new size nsize.
Return the resized block if successful, or NULL in case the
reallocation fails. After a successful reallocation, the old ptr
block is to be considered no more valid.
int xdl_init_mmfile(mmfile_t *mmf, long bsize, unsigned long flags);
Initialize the memory file mmf by requiring an internal block
size of bsize. The flags parameter is a combination of the fol-
lowing flags :
XDL_MMF_ATOMIC Writes on the memory file will be atomic. That
is, the data will not be split on two or more different blocks.
Once an xdl_init_mmfile() succeeded, a matching
xdl_free_mmfile() must be called when the user has done using
the memory file, otherwise serious memory leaks will happen.
The function return 0 if succeed or -1 if an error is encoun-
tered.
void xdl_free_mmfile(mmfile_t *mmf);
Free all the data associated with the mmf memory file.
int xdl_mmfile_iscompact(mmfile_t *mmf);
Returns an integer different from 0 if the mmf memory file is
compact, 0 otherwise. A compact memory file is one that have the
whole content stored inside a single block.
int xdl_seek_mmfile(mmfile_t *mmf, long off);
Set the current data pointer of the memory file mmf to the spec-
ified offset off from the beginning of the file itself. Returns
0 if successful or -1 if an error happened.
long xdl_read_mmfile(mmfile_t *mmf, void *data, long size);
Request to read size bytes from the memory file mmf by storing
the data inside the data buffer. Returns the number of bytes
read into the data buffer. The amount of data read can be lower
than the specified size. The function returns -1 if an error
happened.
long xdl_write_mmfile(mmfile_t *mmf, void const *data, long size);
Request to write size bytes from the specified buffer data into
the memory file mmf. If the memory file has been created using
the XDL_MMF_ATOMIC flag, the write request will not be split
across different blocks. Note that all write operations done on
memory files do append data at the end the file, and writes in
the middle of it are allowed. This is because the library memory
file abstraction does not need this functionality to be avail-
able. The function returns the number of bytes written or a
number lower than size if an error happened.
long xdl_writem_mmfile(mmfile_t *mmf, mmbuffer_t *mb, int nbuf);
Request to sequentially write nbuf memory buffers passed inside
the array mb into the memory file mmf. The memory buffer struc-
ture is defined as :
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
The ptr field is a pointer to the user data, whose size is spec-
ified inside the size structure field. The function returns the
total number of bytes written or a lower number if an error hap-
pened.
void *xdl_mmfile_writeallocate(mmfile_t *mmf, long size);
The function request to allocate a write buffer of size bytes in
the mmf memory file and returns the pointer to the allocated
buffer. The user will have the responsibility to store size
bytes (no more, no less) inside the memory region pointed to by
the returned pointer. The files size will grow of size bytes as
a consequence of this operation. The function will return NULL
if an error happened.
long xdl_mmfile_ptradd(mmfile_t *mmf, char *ptr, long size, unsigned
long flags);
The function adds a user specified block to the end of the mem-
ory file mmf. The block first byte is pointed to by ptr and its
length is size bytes. The flags parameter can be used to specify
attributes of the user memory block. Currently supported
attributes are:
XDL_MMB_READONLY Specify that the added memory block must be
treated as read-only, and every attempt to write on it should
result in a failure of the memory file writing functions.
The purpose of this function is basically to avoid copying mem-
ory around, by helping the library to not drain the CPU cache.
The function returns size in case of success, or -1 in case of
error.
void *xdl_mmfile_first(mmfile_t *mmf, long *size);
The function is used to return the first block of the mmf memory
file block chain. The size parameter will receive the size of
the block, while the function will return the pointer the the
first byte of the block itself. The function returns NULL if the
file is empty.
void *xdl_mmfile_next(mmfile_t *mmf, long *size);
The function is used to return the next block of the mmf memory
file block chain. The size parameter will receive the size of
the block, while the function will return the pointer the the
first byte of the block itself. The function returns NULL if the
current block is the last one of the chain.
long xdl_mmfile_size(mmfile_t *mmf);
The function returns the size of the specified memory file mmf.
int xdl_mmfile_cmp(mmfile_t *mmf1, mmfile_t *mmf2);
Request to compare two memory files mmf1 and mmf2 and returns 0
if files are identical, or a value different from 0 if files are
different.
int xdl_mmfile_compact(mmfile_t *mmfo, mmfile_t *mmfc, long bsize,
unsigned long flags);
Request to create a compact version of the memory file mmfo into
the (uninitialized) memory file mmfc. The bsize parameter spec-
ify the requested block size and flags specify flags to be used
to create the new mmfc memory file (see xdl_init_mmfile() ). The
function returns 0 if succedded or -1 if an error happened.
int xdl_diff(mmfile_t *mmf1, mmfile_t *mmf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, xdemitcb_t *ecb);
Request to create the difference between the two text memory
files mmf1 and mmf2. The mmf1 memory files is considered the
"old" file while mmf2 is considered the "new" file. So the func-
tion will create a patch file that once applied to mmf1 will
give mmf2 as result. Files mmf1 and mmf2 must be atomic from a
line point of view (or, as an extreme, compact), that means that
a single test line cannot spread among different memory file
blocks. The xpp parameter is a pointer to a structure :
typedef struct s_xpparam {
unsigned long flags;
} xpparam_t;
that is used to specify parameters to be used by the file dif-
ferential algorithm. The flags field is a combination of the
following flags :
XDF_NEED_MINIMAL Requires the minimal edit script to be found by
the algorithm (may be slow).
The xecfg parameter point to a structure :
typedef struct s_xdemitconf {
long ctxlen;
} xdemitconf_t;
that is used to configure the algorithm responsible of the
creation the the differential file from an edit script. The
ctxlen field is used to specify the amount of context to be
emitted inside the differential file (the value 3 is suggested
for normal operations). The parameter ecb is a pointer to a
structure :
typedef struct s_xdemitcb {
void *priv;
int (*outf)(void *, mmbuffer_t *, int);
} xdemitcb_t;
that is used by the differential file creation algorithm to emit
the created data. The priv field is an opaque pointer to a user
specified data, while the outf field point to a callback func-
tion that is called internally to emit algorithm generated data
rappresenting the differential file. The first parameter of the
callback is the same priv field specified inside the xdemitcb_t
structure. The second parameter point to an array of mmbuffer_t
(see above for a definition of the structure) whose element
count is specified inside the last parameter of the callback
itself. The callback will always be called with entire records
(lines) and never a record (line) will be emitted using two dif-
ferent callback calls. This is important because if the called
will use another memory file to store the result, by creating
the target memory file with XDL_MMF_ATOMIC will guarantee the
"atomicity" of the memory file itself. The function returns 0
if succeeded or -1 if an error occurred.
int xdl_patch(mmfile_t *mmf, mmfile_t *mmfp, int mode, xdemitcb_t *ecb,
xdemitcb_t *rjecb);
Request to patch the memory file mmf using the patch file stored
in mmfp. The mmf memory file is not changed during the opera-
tion and can be considered as read only. The mode parameter can
be one of the following values :
XDL_PATCH_NORMAL Perform standard patching like if the patch
memory file mmfp has been created using mmf as "old" file.
XDL_PATCH_REVERSE Apply the reverse patch. That means that the
mmf memory file has to be considered as if it was specified as
"new" file during the differential operation ( xdl_diff() ). The
result of the operation will then be the file content that was
used as "old" file during the differential operation.
The following flags can be specified (by or-ing them) to one of
the above:
XDL_PATCH_IGNOREBSPACE Ignore the whitespace at the beginning
and the end of the line.
The ecb will be used by the patch algorithm to create the result
file while the rjecb will be used to emit all differential
chunks that cannot be applied. Like explained above, callbacks
are always called with entire records to guarantee atomicity of
the resulting output. The function returns 0 if succeeded with-
out performing any fuzzy hunk detection, a positive value if it
secceeded with fuzzy hunk detection or -1 if an error occurred
during the patch operation.
int xdl_merge3(mmfile_t *mmfo, mmfile_t *mmf1, mmfile_t *mmf2,
xdemitcb_t *ecb, xdemitcb_t *rjecb);
Merges three files together. The mmfo file is the original one,
while mmf1 and mmf2 are two modified versions of mmfo. The
function works by creating a differential between mmfo and mmf2
and by applying the resulting patch to mmf1. Because of this
sequence, mmf1 changes will be privileged against the ones of
mmf2. The ecb will be used by the patch algorithm to create the
result file while the rjecb will be used to emit all differen-
tial chunks that cannot be applied. Like explained above, call-
backs are always called with entire records to guarantee atomic-
ity of the resulting output. The function returns 0 if suc-
ceeded or -1 if an error occurred during the patch operation.
int xdl_bdiff(mmfile_t *mmf1, mmfile_t *mmf2, bdiffparam_t const *bdp,
xdemitcb_t *ecb);
Request to create the difference between the two text memory
files mmf1 and mmf2. The mmf1 memory files is considered the
"old" file while mmf2 is considered the "new" file. So the func-
tion will create a patch file that once applied to mmf1 will
give mmf2 as result. Files mmf1 and mmf2 must be compact to make
it easy and faster to perform the difference operation. Func-
tions are available to check for compactness ( xdl_mmfile_iscom-
pact() ) and to make compact a non-compact file (
xdl_mmfile_compact() ). An example of how to create a compact
memory file (described inside the test subdirectory) is :
int xdlt_load_mmfile(char const *fname, mmfile_t *mf, int binmode) {
char cc;
int fd;
long size, bsize;
char *blk;
if (xdl_init_mmfile(mf, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0)
return -1;
if ((fd = open(fname, O_RDONLY)) == -1) {
perror(fname);
xdl_free_mmfile(mf);
return -1;
}
if ((size = bsize = lseek(fd, 0, SEEK_END)) > 0 && !binmode) {
if (lseek(fd, -1, SEEK_END) != (off_t) -1 &&
read(fd, &cc, 1) && cc != '\n')
bsize++;
}
lseek(fd, 0, SEEK_SET);
if (!(blk = (char *) xdl_mmfile_writeallocate(mf, bsize))) {
xdl_free_mmfile(mf);
close(fd);
return -1;
}
if (read(fd, blk, (size_t) size) != (size_t) size) {
perror(fname);
xdl_free_mmfile(mf);
close(fd);
return -1;
}
close(fd);
if (bsize > size)
blk[size] = '\n';
return 0;
}
The bdp parameter points to a structure :
typedef struct s_bdiffparam {
long bsize;
} bdiffparam_t;
that is used to pass information to the binary file differential
algorithm. The bsize parameter specify the size of the block
that will be used to decompose mmf1 during the block classifica-
tion phase of the algorithm (see MacDonald paper). Suggested
values go from 16 to 64, with a preferred power of two charac-
teristic. The ecb parameter is used to pass the emission call-
back to the algorithm responsible of the output file creation.
The function returns 0 if succeede or -1 if an error is
occurred.
int xdl_bdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, bdiffparam_t const
*bdp, xdemitcb_t *ecb);
Same as xdl_bdiff() but it works on memory buffer directly. The
xdl_bdiff() is implemented internally with a xdl_bdiff_mb()
after having setup the two memory buffers from the passed memory
files (that must be compact, as described above). The memory
buffer structure is defined as :
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
An empty memory buffer is specified by setting the ptr member as
NULL and the size member as zero. The reason of having this
function is to avoid the memory file preparation, that might
involve copying memory from other sources. Using the
xdl_bdiff_mb(), the caller can setup the two memory buffer by
using, for example, mmap(2), and hence avoiding unnecessary mem-
ory copies. The other parameters and the return value of the
function xdl_bdiff_mb() are the same as the ones already
described in xdl_bdiff().
int xdl_rabdiff(mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb);
Request to create the difference between the two text memory
files mmf1 and mmf2 using the Rabin's polynomial fingerprinting
algorithm. This algorithm typically performs faster and produces
smaller deltas, when compared to the XDelta-like one. The mmf1
memory files is considered the "old" file while mmf2 is consid-
ered the "new" file. So the function will create a patch file
that once applied to mmf1 will give mmf2 as result. Files mmf1
and mmf2 must be compact to make it easy and faster to perform
the difference operation. Functions are available to check for
compactness ( xdl_mmfile_iscompact() ) and to make compact a
non-compact file ( xdl_mmfile_compact() ). The ecb parameter is
used to pass the emission callback to the algorithm responsible
of the output file creation. The function returns 0 if succeede
or -1 if an error is occurred.
int xdl_rabdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, xdemitcb_t
*ecb);
Same as xdl_rabdiff() but it works on memory buffer directly.
The memory buffer structure is defined as :
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
An empty memory buffer is specified by setting the ptr member as
NULL and the size member as zero. The reason of having this
function is to avoid the memory file preparation, that might
involve copying memory from other sources. Using the xdl_rabd-
iff_mb(), the caller can setup the two memory buffer by using,
for example, mmap(2), and hence avoiding unnecessary memory
copies. The other parameters and the return value of the func-
tion xdl_rabdiff_mb() are the same as the ones already described
in xdl_rabdiff().
long xdl_bdiff_tgsize(mmfile_t *mmfp);
Given a binary memory file patch, it returns the size that the
result file will have once the patch is applied to the target
file. It can be used to pre-allocate (or write-allocate) a mem-
ory block to store the patch result so that a compact file will
be available at the end of the operation. The function returns
the requested size, or -1 if an error occurred during the opera-
tion.
int xdl_bpatch(mmfile_t *mmf, mmfile_t *mmfp, xdemitcb_t *ecb);
Request to patch the binary memory file mmf using the binary
patch file stored in mmfp. The mmf memory file is not changed
during the operation and can be considered as read only. The
binary patch algorithm has no notion of context, so the patch
operation cannot be partial (either success or failure). The ecb
parameter contain the callabck (see above for description) used
by the binary patch algorithm to emit the result file. The func-
tion returns 0 if succeeded or -1 if an error occurred during
the patch operation.
SEE ALSO
Two papers drove the content of this library and these are :
o File System Support for Delta Compression by Joshua P. MacDonald
http://www.xmailserver.org/xdfs.pdf
o Fingerprinting by Random Polynomials by Michael O. Rabin
http://www.xmailserver.org/rabin.pdf
o An O(ND) Difference Algorithm and Its Variations by Eugene W.
Myers http://www.xmailserver.org/diff2.pdf
Also usefull information can be looked up inside the diffutil GNU pack-
age :
http://www.gnu.org/software/diffutils/diffutils.html
LICENSE
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. A copy of the license is available
at :
http://www.gnu.org/copyleft/lesser.html
AUTHOR
Developed by Davide Libenzi <davidel@xmailserver.org>
AVAILABILITY
The latest version of LibXDiff can be found at :
http://www.xmailserver.org/xdiff-lib.html
BUGS
There are no known bugs. Bug reports and comments to Davide Libenzi
<davidel@xmailserver.org>
GNU 0.23 LibXDiff(3)

View File

@@ -0,0 +1,202 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include "xdiff.h"
#include "xtestutils.h"
static int xdlt_outf(void *priv, mmbuffer_t *mb, int nbuf) {
int i;
for (i = 0; i < nbuf; i++)
if (!fwrite(mb[i].ptr, mb[i].size, 1, (FILE *) priv))
return -1;
return 0;
}
void usage(char const *prg) {
fprintf(stderr,
"use: %s --diff [-C N] from-file to-file\n"
" %s --patch orig-file patch-file\n"
" %s --bdiff [-B N] from-file to-file\n"
" %s --rabdiff from-file to-file\n"
" %s --bpatch orig-file patch-file\n",
prg, prg, prg, prg, prg);
}
static void *wrap_malloc(void *priv, unsigned int size) {
return malloc(size);
}
static void wrap_free(void *priv, void *ptr) {
free(ptr);
}
static void *wrap_realloc(void *priv, void *ptr, unsigned int size) {
return realloc(ptr, size);
}
int main(int argc, char *argv[]) {
int i = 1, ctxlen = 3, bsize = 16, do_diff, do_patch, do_bdiff, do_bpatch, do_rabdiff;
memallocator_t malt;
mmfile_t mf1, mf2;
xpparam_t xpp;
xdemitconf_t xecfg;
bdiffparam_t bdp;
xdemitcb_t ecb, rjecb;
if (argc < 4) {
usage(argv[0]);
return 1;
}
malt.priv = NULL;
malt.malloc = wrap_malloc;
malt.free = wrap_free;
malt.realloc = wrap_realloc;
xdl_set_allocator(&malt);
do_diff = do_patch = do_bdiff = do_bpatch = do_rabdiff = 0;
if (!strcmp(argv[i], "--diff")) {
i++;
do_diff = 1;
for (; i < argc; i++) {
if (strcmp(argv[i], "-C") == 0) {
if (++i < argc)
ctxlen = atoi(argv[i]);
} else
break;
}
} else if (!strcmp(argv[i], "--patch")) {
i++;
do_patch = 1;
} else if (!strcmp(argv[i], "--bdiff")) {
i++;
do_bdiff = 1;
for (; i < argc; i++) {
if (strcmp(argv[i], "-B") == 0) {
if (++i < argc)
bsize = atoi(argv[i]);
} else
break;
}
} else if (!strcmp(argv[i], "--rabdiff")) {
i++;
do_rabdiff = 1;
} else if (!strcmp(argv[i], "--bpatch")) {
i++;
do_bpatch = 1;
} else {
usage(argv[0]);
return 1;
}
if (argc - i < 2) {
usage(argv[0]);
return 1;
}
xpp.flags = 0;
xecfg.ctxlen = ctxlen;
bdp.bsize = bsize;
if (xdlt_load_mmfile(argv[i], &mf1, do_bdiff || do_bpatch) < 0) {
return 2;
}
if (xdlt_load_mmfile(argv[i + 1], &mf2, do_bdiff || do_bpatch) < 0) {
xdl_free_mmfile(&mf1);
return 2;
}
if (do_diff) {
ecb.priv = stdout;
ecb.outf = xdlt_outf;
if (xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 3;
}
} else if (do_bdiff) {
ecb.priv = stdout;
ecb.outf = xdlt_outf;
if (xdl_bdiff(&mf1, &mf2, &bdp, &ecb) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 4;
}
} else if (do_rabdiff) {
ecb.priv = stdout;
ecb.outf = xdlt_outf;
if (xdl_rabdiff(&mf1, &mf2, &ecb) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 4;
}
} else if (do_bpatch) {
ecb.priv = stdout;
ecb.outf = xdlt_outf;
if (xdl_bpatch(&mf1, &mf2, &ecb) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 5;
}
} else if (do_patch) {
ecb.priv = stdout;
ecb.outf = xdlt_outf;
rjecb.priv = stderr;
rjecb.outf = xdlt_outf;
if (xdl_patch(&mf1, &mf2, XDL_PATCH_NORMAL, &ecb, &rjecb) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 6;
}
}
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 0;
}

View File

@@ -0,0 +1,132 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#include "xdiff.h"
#include "xtestutils.h"
static void *wrap_malloc(void *priv, unsigned int size) {
return malloc(size);
}
static void wrap_free(void *priv, void *ptr) {
free(ptr);
}
static void *wrap_realloc(void *priv, void *ptr, unsigned int size) {
return realloc(ptr, size);
}
int main(int argc, char *argv[]) {
int i, chmax = 64;
long size = 1024 * 100;
double rmod = 0.1;
xpparam_t xpp;
xdemitconf_t xecfg;
bdiffparam_t bdp;
memallocator_t malt;
malt.priv = NULL;
malt.malloc = wrap_malloc;
malt.free = wrap_free;
malt.realloc = wrap_realloc;
xdl_set_allocator(&malt);
xpp.flags = 0;
xecfg.ctxlen = 3;
bdp.bsize = 16;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--size")) {
if (++i < argc)
size = atol(argv[i]);
} else if (!strcmp(argv[i], "--rmod")) {
if (++i < argc)
rmod = atof(argv[i]);
} else if (!strcmp(argv[i], "--ctxlen")) {
if (++i < argc)
xecfg.ctxlen = atol(argv[i]);
} else if (!strcmp(argv[i], "--chmax")) {
if (++i < argc)
chmax = atoi(argv[i]);
}
}
srand(time(NULL));
for (i = 0;; i++) {
fprintf(stderr, "Running TEXT test : %d ... ", i);
if (xdlt_auto_regress(&xpp, &xecfg, size, rmod, chmax) < 0) {
fprintf(stderr, "FAIL\n");
break;
} else {
fprintf(stderr, "OK\n");
}
fprintf(stderr, "Running BIN test : %d ... ", i);
if (xdlt_auto_binregress(&bdp, size, rmod, chmax) < 0) {
fprintf(stderr, "FAIL\n");
break;
} else {
fprintf(stderr, "OK\n");
}
fprintf(stderr, "Running RBIN test : %d ... ", i);
if (xdlt_auto_rabinregress(size, rmod, chmax) < 0) {
fprintf(stderr, "FAIL\n");
break;
} else {
fprintf(stderr, "OK\n");
}
fprintf(stderr, "Running MBIN test : %d ... ", i);
if (xdlt_auto_mbinregress(&bdp, size, rmod, chmax, 32) != 0) {
fprintf(stderr, "FAIL\n");
break;
} else {
fprintf(stderr, "OK\n");
}
}
return 0;
}

View File

@@ -0,0 +1,631 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined(WIN32) || defined(_WIN32)
#include <io.h>
#define write _write
#define read _read
#define lseek _lseek
#define close _close
#define O_CREAT _O_CREAT
#define O_WRONLY _O_WRONLY
#define O_RDONLY _O_RDONLY
#define off_t long
#define size_t int
#else /* #if defined(WIN32) || defined(_WIN32) */
#include <unistd.h>
#endif /* #if defined(WIN32) || defined(_WIN32) */
#include "xmacros.h"
#include "xdiff.h"
#include "xtestutils.h"
#define XDLT_STD_BLKSIZE (1024 * 8)
#define XDLT_MAX_LINE_SIZE 80
static int xdlt_mmfile_outf(void *priv, mmbuffer_t *mb, int nbuf);
int xdlt_dump_mmfile(char const *fname, mmfile_t *mmf) {
int fd;
long size;
char *blk;
if ((fd = open(fname, O_CREAT | O_WRONLY, 0644)) == -1) {
perror(fname);
return -1;
}
if ((blk = (char *) xdl_mmfile_first(mmf, &size)) != NULL) {
do {
if (write(fd, blk, (size_t) size) != (size_t) size) {
perror(fname);
close(fd);
return -1;
}
} while ((blk = (char *) xdl_mmfile_next(mmf, &size)) != NULL);
}
close(fd);
return 0;
}
int xdlt_load_mmfile(char const *fname, mmfile_t *mf, int binmode) {
char cc;
int fd;
long size;
char *blk;
if (xdl_init_mmfile(mf, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
if ((fd = open(fname, O_RDONLY)) == -1) {
perror(fname);
xdl_free_mmfile(mf);
return -1;
}
size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
if (!(blk = (char *) xdl_mmfile_writeallocate(mf, size))) {
xdl_free_mmfile(mf);
close(fd);
return -1;
}
if (read(fd, blk, (size_t) size) != (size_t) size) {
perror(fname);
xdl_free_mmfile(mf);
close(fd);
return -1;
}
close(fd);
return 0;
}
static int xdlt_mmfile_outf(void *priv, mmbuffer_t *mb, int nbuf) {
mmfile_t *mmf = priv;
if (xdl_writem_mmfile(mmf, mb, nbuf) < 0) {
return -1;
}
return 0;
}
int xdlt_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, mmfile_t *mfp) {
xdemitcb_t ecb;
if (xdl_init_mmfile(mfp, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
ecb.priv = mfp;
ecb.outf = xdlt_mmfile_outf;
if (xdl_diff(mf1, mf2, xpp, xecfg, &ecb) < 0) {
xdl_free_mmfile(mfp);
return -1;
}
return 0;
}
int xdlt_do_patch(mmfile_t *mfo, mmfile_t *mfp, int mode, mmfile_t *mfr) {
xdemitcb_t ecb, rjecb;
mmfile_t mmfrj;
if (xdl_init_mmfile(mfr, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
if (xdl_init_mmfile(&mmfrj, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
xdl_free_mmfile(mfr);
return -1;
}
ecb.priv = mfr;
ecb.outf = xdlt_mmfile_outf;
rjecb.priv = &mmfrj;
rjecb.outf = xdlt_mmfile_outf;
if (xdl_patch(mfo, mfp, mode, &ecb, &rjecb) < 0) {
xdl_free_mmfile(&mmfrj);
xdl_free_mmfile(mfr);
return -1;
}
if (mmfrj.fsize > 0) {
#if 1
xdlt_dump_mmfile("xregr.orig", mfo);
xdlt_dump_mmfile("xregr.patch", mfp);
xdlt_dump_mmfile("xregr.rej", &mmfrj);
#endif
xdl_free_mmfile(&mmfrj);
xdl_free_mmfile(mfr);
return -1;
}
xdl_free_mmfile(&mmfrj);
return 0;
}
int xdlt_do_regress(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg) {
mmfile_t mfp, mfr;
if (xdlt_do_diff(mf1, mf2, xpp, xecfg, &mfp) < 0) {
return -1;
}
if (xdlt_do_patch(mf1, &mfp, XDL_PATCH_NORMAL, &mfr) < 0) {
xdl_free_mmfile(&mfp);
return -1;
}
if (xdl_mmfile_cmp(&mfr, mf2)) {
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return -1;
}
xdl_free_mmfile(&mfr);
if (xdlt_do_patch(mf2, &mfp, XDL_PATCH_REVERSE, &mfr) < 0) {
xdl_free_mmfile(&mfp);
return -1;
}
if (xdl_mmfile_cmp(&mfr, mf1)) {
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return -1;
}
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return 0;
}
long xdlt_gen_line(char *buf, long msize) {
long i, size;
static const char ab[] =
"zxcvbnmlkjhgfdsaqwertyuiop"
"ZXCVBNMLKJHGFDSAQWERTYUIOP"
"0123456789 ";
static const int absize = sizeof(ab) - 1;
if (msize > 0)
size = rand() % (msize - 1);
else
size = -msize - 1;
for (i = 0; i < size; i++)
buf[i] = ab[rand() % absize];
buf[i] = '\n';
return size + 1;
}
int xdlt_create_file(mmfile_t *mf, long size) {
long lnsize, csize;
char *data;
if (xdl_init_mmfile(mf, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
if (!(data = xdl_mmfile_writeallocate(mf, size))) {
xdl_free_mmfile(mf);
return -1;
}
for (csize = 0; size - csize > XDLT_MAX_LINE_SIZE;) {
lnsize = xdlt_gen_line(data, XDLT_MAX_LINE_SIZE);
data += lnsize;
csize += lnsize;
}
if (csize < size)
xdlt_gen_line(data, -(size - csize));
return 0;
}
int xdlt_change_file(mmfile_t *mfo, mmfile_t *mfr, double rmod,
int chmax) {
long skipln, lnsize, bsize;
char const *blk, *cur, *top, *eol;
char lnbuf[XDLT_MAX_LINE_SIZE + 1];
if (xdl_init_mmfile(mfr, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
if ((blk = xdl_mmfile_first(mfo, &bsize)) != NULL) {
for (cur = blk, top = blk + bsize, skipln = 0;;) {
if (cur >= top) {
if ((blk = xdl_mmfile_next(mfo, &bsize)) == NULL)
break;
cur = blk;
top = blk + bsize;
}
if (!(eol = memchr(cur, '\n', top - cur)))
eol = top;
if (!skipln) {
if (DBL_RAND() < rmod) {
skipln = rand() % chmax;
if (rand() & 1) {
for (; skipln > 0; skipln--) {
lnsize = xdlt_gen_line(lnbuf, XDLT_MAX_LINE_SIZE);
if (xdl_write_mmfile(mfr, lnbuf, lnsize) != lnsize) {
xdl_free_mmfile(mfr);
return -1;
}
}
}
} else {
lnsize = (eol - cur) + 1;
if (xdl_write_mmfile(mfr, cur, lnsize) != lnsize) {
xdl_free_mmfile(mfr);
return -1;
}
}
} else
skipln--;
cur = eol + 1;
}
}
return 0;
}
int xdlt_auto_regress(xpparam_t const *xpp, xdemitconf_t const *xecfg, long size,
double rmod, int chmax) {
mmfile_t mf1, mf2;
if (xdlt_create_file(&mf1, size) < 0) {
return -1;
}
if (xdlt_change_file(&mf1, &mf2, rmod, chmax) < 0) {
xdl_free_mmfile(&mf1);
return -1;
}
if (xdlt_do_regress(&mf1, &mf2, xpp, xecfg) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return -1;
}
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return 0;
}
int xdlt_do_bindiff(mmfile_t *mf1, mmfile_t *mf2, bdiffparam_t const *bdp, mmfile_t *mfp) {
xdemitcb_t ecb;
if (xdl_init_mmfile(mfp, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
ecb.priv = mfp;
ecb.outf = xdlt_mmfile_outf;
if (xdl_bdiff(mf1, mf2, bdp, &ecb) < 0) {
xdl_free_mmfile(mfp);
return -1;
}
return 0;
}
int xdlt_do_rabdiff(mmfile_t *mf1, mmfile_t *mf2, mmfile_t *mfp) {
xdemitcb_t ecb;
if (xdl_init_mmfile(mfp, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
ecb.priv = mfp;
ecb.outf = xdlt_mmfile_outf;
if (xdl_rabdiff(mf1, mf2, &ecb) < 0) {
xdl_free_mmfile(mfp);
return -1;
}
return 0;
}
int xdlt_do_binpatch(mmfile_t *mf, mmfile_t *mfp, mmfile_t *mfr) {
xdemitcb_t ecb;
if (xdl_init_mmfile(mfr, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
ecb.priv = mfr;
ecb.outf = xdlt_mmfile_outf;
if (xdl_bpatch(mf, mfp, &ecb) < 0) {
xdl_free_mmfile(mfr);
return -1;
}
return 0;
}
int xdlt_do_binregress(mmfile_t *mf1, mmfile_t *mf2, bdiffparam_t const *bdp) {
mmfile_t mfp, mfr;
if (xdlt_do_bindiff(mf1, mf2, bdp, &mfp) < 0) {
return -1;
}
if (xdlt_do_binpatch(mf1, &mfp, &mfr) < 0) {
xdl_free_mmfile(&mfp);
return -1;
}
if (xdl_mmfile_cmp(&mfr, mf2)) {
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return -1;
}
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return 0;
}
int xdlt_do_rabinregress(mmfile_t *mf1, mmfile_t *mf2) {
mmfile_t mfp, mfr;
if (xdlt_do_rabdiff(mf1, mf2, &mfp) < 0) {
return -1;
}
if (xdlt_do_binpatch(mf1, &mfp, &mfr) < 0) {
xdl_free_mmfile(&mfp);
return -1;
}
if (xdl_mmfile_cmp(&mfr, mf2)) {
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return -1;
}
xdl_free_mmfile(&mfr);
xdl_free_mmfile(&mfp);
return 0;
}
int xdlt_auto_binregress(bdiffparam_t const *bdp, long size,
double rmod, int chmax) {
mmfile_t mf1, mf2, mf2c;
if (xdlt_create_file(&mf1, size) < 0) {
return -1;
}
if (xdlt_change_file(&mf1, &mf2, rmod, chmax) < 0) {
xdl_free_mmfile(&mf1);
return -1;
}
if (xdl_mmfile_compact(&mf2, &mf2c, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return -1;
}
xdl_free_mmfile(&mf2);
if (xdlt_do_binregress(&mf1, &mf2c, bdp) < 0) {
xdl_free_mmfile(&mf2c);
xdl_free_mmfile(&mf1);
return -1;
}
xdl_free_mmfile(&mf2c);
xdl_free_mmfile(&mf1);
return 0;
}
int xdlt_auto_rabinregress(long size, double rmod, int chmax) {
mmfile_t mf1, mf2, mf2c;
if (xdlt_create_file(&mf1, size) < 0) {
return -1;
}
if (xdlt_change_file(&mf1, &mf2, rmod, chmax) < 0) {
xdl_free_mmfile(&mf1);
return -1;
}
if (xdl_mmfile_compact(&mf2, &mf2c, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
xdl_free_mmfile(&mf2);
xdl_free_mmfile(&mf1);
return -1;
}
xdl_free_mmfile(&mf2);
if (xdlt_do_rabinregress(&mf1, &mf2c) < 0) {
xdl_free_mmfile(&mf2c);
xdl_free_mmfile(&mf1);
return -1;
}
xdl_free_mmfile(&mf2c);
xdl_free_mmfile(&mf1);
return 0;
}
int xdlt_auto_mbinregress(bdiffparam_t const *bdp, long size,
double rmod, int chmax, int n) {
int i, res;
mmbuffer_t *mbb;
mmfile_t *mf, *mfc, *mfx;
mmfile_t mfn, mff, mfd, mfb;
xdemitcb_t ecb;
if ((mbb = (mmbuffer_t *) xdl_malloc((n + 2) * sizeof(mmbuffer_t))) == NULL) {
return -1;
}
if ((mf = mfc = (mmfile_t *) xdl_malloc((n + 2) * sizeof(mmfile_t))) == NULL) {
xdl_free(mbb);
return -1;
}
if (xdlt_create_file(mfc, size) < 0) {
xdl_free(mf);
xdl_free(mbb);
return -1;
}
mbb[0].ptr = (char *) xdl_mmfile_first(mfc, &mbb[0].size);
mfc++;
mfx = mf;
for (i = 0; i < n; i++) {
if (xdlt_change_file(mfx, &mfn, rmod, chmax) < 0) {
if (mfx != mf) xdl_free_mmfile(mfx);
for (; i >= 0; i--)
xdl_free_mmfile(mf + i);
xdl_free(mf);
xdl_free(mbb);
return -1;
}
if (xdl_mmfile_compact(&mfn, &mff, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
xdl_free_mmfile(&mfn);
if (mfx != mf) xdl_free_mmfile(mfx);
for (; i >= 0; i--)
xdl_free_mmfile(mf + i);
xdl_free(mf);
xdl_free(mbb);
return -1;
}
xdl_free_mmfile(&mfn);
if (xdlt_do_bindiff(mfx, &mff, bdp, &mfd) < 0) {
xdl_free_mmfile(&mff);
if (mfx != mf) xdl_free_mmfile(mfx);
for (; i >= 0; i--)
xdl_free_mmfile(mf + i);
xdl_free(mf);
xdl_free(mbb);
return -1;
}
if (mfx != mf) xdl_free_mmfile(mfx);
mfx = &mfb;
*mfx = mff;
if (xdl_mmfile_compact(&mfd, mfc, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
xdl_free_mmfile(&mfd);
xdl_free_mmfile(mfx);
for (; i >= 0; i--)
xdl_free_mmfile(mf + i);
xdl_free(mf);
xdl_free(mbb);
return -1;
}
mbb[i + 1].ptr = (char *) xdl_mmfile_first(mfc, &mbb[i + 1].size);
mfc++;
xdl_free_mmfile(&mfd);
}
if (xdl_init_mmfile(mfc, XDLT_STD_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
xdl_free_mmfile(mfx);
for (i = n; i >= 0; i--)
xdl_free_mmfile(mf + i);
xdl_free(mf);
xdl_free(mbb);
return -1;
}
ecb.priv = mfc;
ecb.outf = xdlt_mmfile_outf;
if ((res = xdl_bpatch_multi(&mbb[0], &mbb[1], n, &ecb)) == 0)
res = xdl_mmfile_cmp(mfx, mfc);
xdl_free_mmfile(mfx);
for (i = n + 1; i >= 0; i--)
xdl_free_mmfile(mf + i);
xdl_free(mf);
xdl_free(mbb);
return res;
}

View File

@@ -0,0 +1,57 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XTESTUTILS_H)
#define XTESTUTILS_H
#define DBL_RAND() (((double) rand()) / (1.0 + (double) RAND_MAX))
int xdlt_dump_mmfile(char const *fname, mmfile_t *mmf);
int xdlt_load_mmfile(char const *fname, mmfile_t *mf, int binmode);
int xdlt_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, mmfile_t *mfp);
int xdlt_do_patch(mmfile_t *mfo, mmfile_t *mfp, int mode, mmfile_t *mfr);
int xdlt_do_regress(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg);
long xdlt_gen_line(char *buf, long msize);
int xdlt_create_file(mmfile_t *mf, long size);
int xdlt_change_file(mmfile_t *mfo, mmfile_t *mfr, double rmod, int chmax);
int xdlt_auto_regress(xpparam_t const *xpp, xdemitconf_t const *xecfg, long size,
double rmod, int chmax);
int xdlt_do_bindiff(mmfile_t *mf1, mmfile_t *mf2, bdiffparam_t const *bdp, mmfile_t *mfp);
int xdlt_do_rabdiff(mmfile_t *mf1, mmfile_t *mf2, mmfile_t *mfp);
int xdlt_do_binpatch(mmfile_t *mf, mmfile_t *mfp, mmfile_t *mfr);
int xdlt_do_binregress(mmfile_t *mf1, mmfile_t *mf2, bdiffparam_t const *bdp);
int xdlt_do_rabinregress(mmfile_t *mf1, mmfile_t *mf2);
int xdlt_auto_binregress(bdiffparam_t const *bdp, long size,
double rmod, int chmax);
int xdlt_auto_rabinregress(long size, double rmod, int chmax);
int xdlt_auto_mbinregress(bdiffparam_t const *bdp, long size,
double rmod, int chmax, int n);
#endif /* #if !defined(XTESTUTILS_H) */

View File

@@ -0,0 +1,296 @@
/*
* xrabin by Davide Libenzi (Rabin's polynomial generator)
* Copyright (C) 2006 Davide Libenzi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*
* Hints, ideas and code for the implementation came from:
*
* Rabin's original paper: http://www.xmailserver.org/rabin.pdf
* Chan & Lu's paper: http://www.xmailserver.org/rabin_impl.pdf
* Broder's paper: http://www.xmailserver.org/rabin_apps.pdf
* LBFS source code: http://www.fs.net/sfswww/lbfs/
* Geert Bosch's post: http://marc.theaimsgroup.com/?l=git&m=114565424620771&w=2
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(WIN32)
#define strtoll _strtoi64
#endif
#if !defined(XRAB_WORD_TYPE)
#if defined(WIN32)
#define XRAB_WORD_TYPE __int64
#else /* #if defined(WIN32) */
#define XRAB_WORD_TYPE long long
#endif /* #if defined(WIN32) */
#endif /* #if !defined(XRAB_WORD_TYPE) */
#if !defined(XRAB_WORD_PFMT)
#if defined(WIN32)
#define XRAB_WORD_PFMT "0x%I64x"
#else /* #if defined(WIN32) */
#define XRAB_WORD_PFMT "0x%llx"
#endif /* #if defined(WIN32) */
#endif /* #if !defined(XRAB_WORD_PFMT) */
#define XPLYW_BITS (sizeof(xply_word) * 8)
#define XPLYW_MSB ((xply_word) 1 << (sizeof(xply_word) * 8 - 1))
typedef unsigned XRAB_WORD_TYPE xply_word;
static int xrab_isprime(int n) {
if (n > 3) {
if (n & 1) {
int i, hn = n / 2;
for (i = 3; i < hn; i += 2)
if (!(n % i))
return 0;
} else
return 0;
}
return 1;
}
static int xrab_fls(xply_word v) {
int r, s;
xply_word mask = ~(((xply_word) 1 << (XPLYW_BITS / 2)) - 1);
if (v == 0)
return 0;
for (r = XPLYW_BITS, s = r / 2; s != 0;) {
if ((v & mask) == 0) {
v <<= s;
r -= s;
}
s /= 2;
mask <<= s;
}
return r;
}
static xply_word xrab_polymod(xply_word nh, xply_word nl, xply_word d) {
int i, k = xrab_fls(d) - 1;
d <<= (XPLYW_BITS - 1) - k;
if (nh) {
if (nh & XPLYW_MSB)
nh ^= d;
for (i = XPLYW_BITS - 2; i >= 0; i--)
if (nh & ((xply_word) 1) << i) {
nh ^= d >> (XPLYW_BITS - 1) - i;
nl ^= d << i + 1;
}
}
for (i = XPLYW_BITS - 1; i >= k; i--)
if (nl & ((xply_word) 1 << i))
nl ^= d >> (XPLYW_BITS - 1) - i;
return nl;
}
static xply_word xrab_polygcd(xply_word x, xply_word y) {
for (;;) {
if (!y)
return x;
x = xrab_polymod(0, x, y);
if (!x)
return y;
y = xrab_polymod(0, y, x);
}
}
static void xrab_polymult(xply_word *php, xply_word *plp, xply_word x,
xply_word y) {
int i;
xply_word ph = 0, pl = 0;
if (x & 1)
pl = y;
for (i = 1; i < XPLYW_BITS; i++)
if (x & (((xply_word) 1) << i)) {
ph ^= y >> (XPLYW_BITS - i);
pl ^= y << i;
}
if (php)
*php = ph;
if (plp)
*plp = pl;
}
static xply_word xrab_polymmult(xply_word x, xply_word y, xply_word d) {
xply_word h, l;
xrab_polymult(&h, &l, x, y);
return xrab_polymod(h, l, d);
}
static int xrab_polyirreducible(xply_word f) {
xply_word u = 2;
int i, m = (xrab_fls(f) - 1) >> 1;
for (i = 0; i < m; i++) {
u = xrab_polymmult(u, u, f);
if (xrab_polygcd(f, u ^ 2) != 1)
return 0;
}
return 1;
}
static void xrab_rndgen(xply_word *f) {
unsigned int i;
xply_word g;
for (i = 0, g = 0; i < sizeof(xply_word); i++)
g ^= (g << 11) + (unsigned int) rand() + (g >> 7);
*f = g;
}
static int xrab_polygen(int degree, xply_word *ply) {
xply_word msb, mask, f;
if (degree <= 0 || degree >= XPLYW_BITS)
return -1;
msb = ((xply_word) 1) << degree;
mask = msb - 1;
srand(time(NULL));
do {
xrab_rndgen(&f);
f = (f & mask) | msb;
} while (!xrab_polyirreducible(f));
*ply = f;
return 0;
}
static int xarb_calc_tu(xply_word poly, int size, xply_word *t, xply_word *u) {
int j, xshift, shift;
xply_word t1, ssh;
xshift = xrab_fls(poly) - 1;
shift = xshift - 8;
if (shift < 0)
return -1;
t1 = xrab_polymod(0, ((xply_word) 1) << xshift, poly);
for (j = 0; j < 256; j++)
t[j] = xrab_polymmult(j, t1, poly) | ((xply_word) j << xshift);
for (j = 1, ssh = 1; j < size; j++)
ssh = (ssh << 8) ^ t[ssh >> shift];
for (j = 0; j < 256; j++)
u[j] = xrab_polymmult(j, ssh, poly);
return 0;
}
int main(int ac, char **av) {
int i, size = 20, degree = 0, shift;
xply_word ply = 0, t[256], u[256];
for (i = 1; i < ac; i++) {
if (strcmp(av[i], "-s") == 0) {
if (++i < ac)
size = atol(av[i]);
} else if (strcmp(av[i], "-p") == 0) {
if (++i < ac)
ply = (xply_word) strtoll(av[i], NULL, 16);
} else if (strcmp(av[i], "-d") == 0) {
if (++i < ac)
degree = atol(av[i]);
}
}
if (degree && (degree < 8 || degree >= XPLYW_BITS)) {
fprintf(stderr, "degree (%d) out of bound for the poly word size (8..%u)\n",
degree, XPLYW_BITS);
return 1;
}
if (degree == 0)
for (degree = XPLYW_BITS - 1; !xrab_isprime(degree); degree--);
if (ply == 0 && xrab_polygen(degree, &ply) < 0)
return 2;
shift = (xrab_fls(ply) - 1) - 8;
fprintf(stderr, "found poly = " XRAB_WORD_PFMT " (shift %d)\n",
ply, shift);
if (xarb_calc_tu(ply, size, t, u) < 0)
return 3;
fprintf(stdout, "#if defined(XRABPLY_TYPE%d)\n\n", XPLYW_BITS);
fprintf(stdout, "#if !defined(XV%d)\n", XPLYW_BITS);
fprintf(stdout, "#define XV%d(v) ((xply_word) v ## ULL)\n", XPLYW_BITS);
fprintf(stdout, "#endif\n\n");
fprintf(stdout, "#define XRAB_ROOTPOLY XV%d(" XRAB_WORD_PFMT ")\n\n",
XPLYW_BITS, ply);
fprintf(stdout, "#define XRAB_SHIFT %d\n", shift);
fprintf(stdout, "#define XRAB_WNDSIZE %d\n\n", size);
fprintf(stdout, "typedef unsigned XRABPLY_TYPE%d xply_word;\n\n", XPLYW_BITS);
fprintf(stdout, "static const xply_word T[256] = {\n");
for (i = 0; i < 256; i++) {
if (i) {
fputs(",", stdout);
if (i % 4 == 0)
fputs("\n\t", stdout);
else
fputs(" ", stdout);
} else
fputs("\t", stdout);
fprintf(stdout, "XV%d(" XRAB_WORD_PFMT ")", XPLYW_BITS, t[i]);
}
fprintf(stdout, "\n};\n\n");
fprintf(stdout, "static const xply_word U[256] = {\n");
for (i = 0; i < 256; i++) {
if (i) {
fputs(",", stdout);
if (i % 4 == 0)
fputs("\n\t", stdout);
else
fputs(" ", stdout);
} else
fputs("\t", stdout);
fprintf(stdout, "XV%d(" XRAB_WORD_PFMT ")", XPLYW_BITS, u[i]);
}
fprintf(stdout, "\n};\n\n");
fprintf(stdout, "#endif /* if defined(XRABPLY_TYPE%d) */\n\n", XPLYW_BITS);
return 0;
}

View File

@@ -0,0 +1,70 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
/* largest prime smaller than 65536 */
#define BASE 65521L
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
#define NMAX 5552
#define DO1(buf, i) { s1 += buf[i]; s2 += s1; }
#define DO2(buf, i) DO1(buf, i); DO1(buf, i + 1);
#define DO4(buf, i) DO2(buf, i); DO2(buf, i + 2);
#define DO8(buf, i) DO4(buf, i); DO4(buf, i + 4);
#define DO16(buf) DO8(buf, 0); DO8(buf, 8);
unsigned long xdl_adler32(unsigned long adler, unsigned char const *buf,
unsigned int len) {
int k;
unsigned long s1 = adler & 0xffff;
unsigned long s2 = (adler >> 16) & 0xffff;
if (!buf)
return 1;
while (len > 0) {
k = len < NMAX ? len :NMAX;
len -= k;
while (k >= 16) {
DO16(buf);
buf += 16;
k -= 16;
}
if (k != 0)
do {
s1 += *buf++;
s2 += s1;
} while (--k);
s1 %= BASE;
s2 %= BASE;
}
return (s2 << 16) | s1;
}

View File

@@ -0,0 +1,34 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XADLER32_H)
#define XADLER32_H
unsigned long xdl_adler32(unsigned long adler, unsigned char const *buf,
unsigned int len);
#endif /* #if !defined(XADLER32_H) */

View File

@@ -0,0 +1,51 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
static memallocator_t xmalt = {NULL, NULL, NULL};
int xdl_set_allocator(memallocator_t const *malt) {
xmalt = *malt;
return 0;
}
void *xdl_malloc(unsigned int size) {
return xmalt.malloc ? xmalt.malloc(xmalt.priv, size): NULL;
}
void xdl_free(void *ptr) {
if (xmalt.free)
xmalt.free(xmalt.priv, ptr);
}
void *xdl_realloc(void *ptr, unsigned int size) {
return xmalt.realloc ? xmalt.realloc(xmalt.priv, ptr, size): NULL;
}

View File

@@ -0,0 +1,315 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
typedef struct s_bdrecord {
struct s_bdrecord *next;
unsigned long fp;
char const *ptr;
} bdrecord_t;
typedef struct s_bdfile {
char const *data, *top;
chastore_t cha;
unsigned int fphbits;
bdrecord_t **fphash;
} bdfile_t;
static int xdl_prepare_bdfile(mmbuffer_t *mmb, long fpbsize, bdfile_t *bdf) {
unsigned int fphbits;
long i, size, hsize;
char const *base, *data, *top;
bdrecord_t *brec;
bdrecord_t **fphash;
fphbits = xdl_hashbits((unsigned int) (mmb->size / fpbsize) + 1);
hsize = 1 << fphbits;
if (!(fphash = (bdrecord_t **) xdl_malloc(hsize * sizeof(bdrecord_t *)))) {
return -1;
}
for (i = 0; i < hsize; i++)
fphash[i] = NULL;
if (xdl_cha_init(&bdf->cha, sizeof(bdrecord_t), hsize / 4 + 1) < 0) {
xdl_free(fphash);
return -1;
}
if (!(size = mmb->size)) {
bdf->data = bdf->top = NULL;
} else {
bdf->data = data = base = mmb->ptr;
bdf->top = top = mmb->ptr + mmb->size;
if ((data += (size / fpbsize) * fpbsize) == top)
data -= fpbsize;
for (; data >= base; data -= fpbsize) {
if (!(brec = (bdrecord_t *) xdl_cha_alloc(&bdf->cha))) {
xdl_cha_free(&bdf->cha);
xdl_free(fphash);
return -1;
}
brec->fp = xdl_adler32(0, (unsigned char const *) data,
XDL_MIN(fpbsize, (long) (top - data)));
brec->ptr = data;
i = (long) XDL_HASHLONG(brec->fp, fphbits);
brec->next = fphash[i];
fphash[i] = brec;
}
}
bdf->fphbits = fphbits;
bdf->fphash = fphash;
return 0;
}
static void xdl_free_bdfile(bdfile_t *bdf) {
xdl_free(bdf->fphash);
xdl_cha_free(&bdf->cha);
}
unsigned long xdl_mmb_adler32(mmbuffer_t *mmb) {
return mmb->size ? xdl_adler32(0, (unsigned char const *) mmb->ptr, mmb->size): 0;
}
unsigned long xdl_mmf_adler32(mmfile_t *mmf) {
unsigned long fp = 0;
long size;
char const *blk;
if ((blk = (char const *) xdl_mmfile_first(mmf, &size)) != NULL) {
do {
fp = xdl_adler32(fp, (unsigned char const *) blk, size);
} while ((blk = (char const *) xdl_mmfile_next(mmf, &size)) != NULL);
}
return fp;
}
int xdl_bdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, bdiffparam_t const *bdp, xdemitcb_t *ecb) {
long i, rsize, size, bsize, csize, msize, moff;
unsigned long fp;
char const *blk, *base, *data, *top, *ptr1, *ptr2;
bdrecord_t *brec;
bdfile_t bdf;
mmbuffer_t mb[2];
unsigned char cpybuf[32];
if ((bsize = bdp->bsize) < XDL_MIN_BLKSIZE)
bsize = XDL_MIN_BLKSIZE;
if (xdl_prepare_bdfile(mmb1, bsize, &bdf) < 0) {
return -1;
}
/*
* Prepare and emit the binary patch file header. It will be used
* to verify that that file being patched matches in size and fingerprint
* the one that generated the patch.
*/
fp = xdl_mmb_adler32(mmb1);
size = mmb1->size;
XDL_LE32_PUT(cpybuf, fp);
XDL_LE32_PUT(cpybuf + 4, size);
mb[0].ptr = (char *) cpybuf;
mb[0].size = 4 + 4;
if (ecb->outf(ecb->priv, mb, 1) < 0) {
xdl_free_bdfile(&bdf);
return -1;
}
if ((blk = (char const *) mmb2->ptr) != NULL) {
size = mmb2->size;
for (base = data = blk, top = data + size; data < top;) {
rsize = XDL_MIN(bsize, (long) (top - data));
fp = xdl_adler32(0, (unsigned char const *) data, rsize);
i = (long) XDL_HASHLONG(fp, bdf.fphbits);
for (msize = 0, brec = bdf.fphash[i]; brec; brec = brec->next)
if (brec->fp == fp) {
csize = XDL_MIN((long) (top - data), (long) (bdf.top - brec->ptr));
for (ptr1 = brec->ptr, ptr2 = data; csize && *ptr1 == *ptr2;
csize--, ptr1++, ptr2++);
if ((csize = (long) (ptr1 - brec->ptr)) > msize) {
moff = (long) (brec->ptr - bdf.data);
msize = csize;
}
}
if (msize < XDL_COPYOP_SIZE) {
data++;
} else {
if (data > base) {
i = (long) (data - base);
if (i > 255) {
cpybuf[0] = XDL_BDOP_INSB;
XDL_LE32_PUT(cpybuf + 1, i);
mb[0].ptr = (char *) cpybuf;
mb[0].size = XDL_INSBOP_SIZE;
} else {
cpybuf[0] = XDL_BDOP_INS;
cpybuf[1] = (unsigned char) i;
mb[0].ptr = (char *) cpybuf;
mb[0].size = 2;
}
mb[1].ptr = (char *) base;
mb[1].size = i;
if (ecb->outf(ecb->priv, mb, 2) < 0) {
xdl_free_bdfile(&bdf);
return -1;
}
}
data += msize;
cpybuf[0] = XDL_BDOP_CPY;
XDL_LE32_PUT(cpybuf + 1, moff);
XDL_LE32_PUT(cpybuf + 5, msize);
mb[0].ptr = (char *) cpybuf;
mb[0].size = XDL_COPYOP_SIZE;
if (ecb->outf(ecb->priv, mb, 1) < 0) {
xdl_free_bdfile(&bdf);
return -1;
}
base = data;
}
}
if (data > base) {
i = (long) (data - base);
if (i > 255) {
cpybuf[0] = XDL_BDOP_INSB;
XDL_LE32_PUT(cpybuf + 1, i);
mb[0].ptr = (char *) cpybuf;
mb[0].size = XDL_INSBOP_SIZE;
} else {
cpybuf[0] = XDL_BDOP_INS;
cpybuf[1] = (unsigned char) i;
mb[0].ptr = (char *) cpybuf;
mb[0].size = 2;
}
mb[1].ptr = (char *) base;
mb[1].size = i;
if (ecb->outf(ecb->priv, mb, 2) < 0) {
xdl_free_bdfile(&bdf);
return -1;
}
}
}
xdl_free_bdfile(&bdf);
return 0;
}
int xdl_bdiff(mmfile_t *mmf1, mmfile_t *mmf2, bdiffparam_t const *bdp, xdemitcb_t *ecb) {
mmbuffer_t mmb1, mmb2;
if (!xdl_mmfile_iscompact(mmf1) || !xdl_mmfile_iscompact(mmf2)) {
return -1;
}
if ((mmb1.ptr = (char *) xdl_mmfile_first(mmf1, &mmb1.size)) == NULL)
mmb1.size = 0;
if ((mmb2.ptr = (char *) xdl_mmfile_first(mmf2, &mmb2.size)) == NULL)
mmb2.size = 0;
return xdl_bdiff_mb(&mmb1, &mmb2, bdp, ecb);
}
long xdl_bdiff_tgsize(mmfile_t *mmfp) {
long tgsize = 0, size, off, csize;
char const *blk;
unsigned char const *data, *top;
if ((blk = (char const *) xdl_mmfile_first(mmfp, &size)) == NULL ||
size < XDL_BPATCH_HDR_SIZE) {
return -1;
}
blk += XDL_BPATCH_HDR_SIZE;
size -= XDL_BPATCH_HDR_SIZE;
do {
for (data = (unsigned char const *) blk, top = data + size;
data < top;) {
if (*data == XDL_BDOP_INS) {
data++;
csize = (long) *data++;
tgsize += csize;
data += csize;
} else if (*data == XDL_BDOP_INSB) {
data++;
XDL_LE32_GET(data, csize);
data += 4;
tgsize += csize;
data += csize;
} else if (*data == XDL_BDOP_CPY) {
data++;
XDL_LE32_GET(data, off);
data += 4;
XDL_LE32_GET(data, csize);
data += 4;
tgsize += csize;
} else {
return -1;
}
}
} while ((blk = (char const *) xdl_mmfile_next(mmfp, &size)) != NULL);
return tgsize;
}

View File

@@ -0,0 +1,40 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XBDIFF_H)
#define XBDIFF_H
#define XDL_BPATCH_HDR_SIZE (4 + 4)
#define XDL_MIN_BLKSIZE 16
#define XDL_INSBOP_SIZE (1 + 4)
#define XDL_COPYOP_SIZE (1 + 4 + 4)
unsigned long xdl_mmb_adler32(mmbuffer_t *mmb);
unsigned long xdl_mmf_adler32(mmfile_t *mmf);
#endif /* #if !defined(XBDIFF_H) */

View File

@@ -0,0 +1,336 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#define XDL_MOBF_MINALLOC 128
typedef struct s_mmoffbuffer {
long off, size;
char *ptr;
} mmoffbuffer_t;
static int xdl_copy_range(mmfile_t *mmf, long off, long size, xdemitcb_t *ecb) {
if (xdl_seek_mmfile(mmf, off) < 0) {
return -1;
}
if (xdl_copy_mmfile(mmf, size, ecb) != size) {
return -1;
}
return 0;
}
int xdl_bpatch(mmfile_t *mmf, mmfile_t *mmfp, xdemitcb_t *ecb) {
long size, off, csize, osize;
unsigned long fp, ofp;
char const *blk;
unsigned char const *data, *top;
mmbuffer_t mb;
if ((blk = (char const *) xdl_mmfile_first(mmfp, &size)) == NULL ||
size < XDL_BPATCH_HDR_SIZE) {
return -1;
}
ofp = xdl_mmf_adler32(mmf);
osize = xdl_mmfile_size(mmf);
XDL_LE32_GET(blk, fp);
XDL_LE32_GET(blk + 4, csize);
if (fp != ofp || csize != osize) {
return -1;
}
blk += XDL_BPATCH_HDR_SIZE;
size -= XDL_BPATCH_HDR_SIZE;
do {
for (data = (unsigned char const *) blk, top = data + size;
data < top;) {
if (*data == XDL_BDOP_INS) {
data++;
mb.size = (long) *data++;
mb.ptr = (char *) data;
data += mb.size;
if (ecb->outf(ecb->priv, &mb, 1) < 0) {
return -1;
}
} else if (*data == XDL_BDOP_INSB) {
data++;
XDL_LE32_GET(data, csize);
data += 4;
mb.size = csize;
mb.ptr = (char *) data;
data += mb.size;
if (ecb->outf(ecb->priv, &mb, 1) < 0) {
return -1;
}
} else if (*data == XDL_BDOP_CPY) {
data++;
XDL_LE32_GET(data, off);
data += 4;
XDL_LE32_GET(data, csize);
data += 4;
if (xdl_copy_range(mmf, off, csize, ecb) < 0) {
return -1;
}
} else {
return -1;
}
}
} while ((blk = (char const *) xdl_mmfile_next(mmfp, &size)) != NULL);
return 0;
}
static unsigned long xdl_mmob_adler32(mmoffbuffer_t *obf, int n) {
unsigned long ha;
for (ha = 0; n > 0; n--, obf++)
ha = xdl_adler32(ha, (unsigned char const *) obf->ptr, obf->size);
return ha;
}
static long xdl_mmob_size(mmoffbuffer_t *obf, int n) {
return n > 0 ? obf[n - 1].off + obf[n - 1].size: 0;
}
static mmoffbuffer_t *xdl_mmob_new(mmoffbuffer_t **probf, int *pnobf, int *paobf) {
int aobf;
mmoffbuffer_t *cobf, *rrobf;
if (*pnobf >= *paobf) {
aobf = 2 * (*paobf) + 1;
if ((rrobf = (mmoffbuffer_t *)
xdl_realloc(*probf, aobf * sizeof(mmoffbuffer_t))) == NULL) {
return NULL;
}
*probf = rrobf;
*paobf = aobf;
}
cobf = (*probf) + (*pnobf);
(*pnobf)++;
return cobf;
}
static int xdl_mmob_find_cntr(mmoffbuffer_t *obf, int n, long off) {
int i, lo, hi;
for (lo = -1, hi = n; hi - lo > 1;) {
i = (hi + lo) / 2;
if (off < obf[i].off)
hi = i;
else
lo = i;
}
return (lo >= 0 && off >= obf[lo].off && off < obf[lo].off + obf[lo].size) ? lo: -1;
}
static int xdl_bmerge(mmoffbuffer_t *obf, int n, mmbuffer_t *mbfp, mmoffbuffer_t **probf,
int *pnobf) {
int i, aobf, nobf;
long ooff, off, csize;
unsigned long fp, ofp;
unsigned char const *data, *top;
mmoffbuffer_t *robf, *cobf;
if (mbfp->size < XDL_BPATCH_HDR_SIZE) {
return -1;
}
data = (unsigned char const *) mbfp->ptr;
top = data + mbfp->size;
ofp = xdl_mmob_adler32(obf, n);
XDL_LE32_GET(data, fp);
data += 4;
XDL_LE32_GET(data, csize);
data += 4;
if (fp != ofp || csize != xdl_mmob_size(obf, n)) {
return -1;
}
aobf = XDL_MOBF_MINALLOC;
nobf = 0;
if ((robf = (mmoffbuffer_t *) xdl_malloc(aobf * sizeof(mmoffbuffer_t))) == NULL) {
return -1;
}
for (ooff = 0; data < top;) {
if (*data == XDL_BDOP_INS) {
data++;
if ((cobf = xdl_mmob_new(&robf, &nobf, &aobf)) == NULL) {
xdl_free(robf);
return -1;
}
cobf->off = ooff;
cobf->size = (long) *data++;
cobf->ptr = (char *) data;
data += cobf->size;
ooff += cobf->size;
} else if (*data == XDL_BDOP_INSB) {
data++;
XDL_LE32_GET(data, csize);
data += 4;
if ((cobf = xdl_mmob_new(&robf, &nobf, &aobf)) == NULL) {
xdl_free(robf);
return -1;
}
cobf->off = ooff;
cobf->size = csize;
cobf->ptr = (char *) data;
data += cobf->size;
ooff += cobf->size;
} else if (*data == XDL_BDOP_CPY) {
data++;
XDL_LE32_GET(data, off);
data += 4;
XDL_LE32_GET(data, csize);
data += 4;
if ((i = xdl_mmob_find_cntr(obf, n, off)) < 0) {
xdl_free(robf);
return -1;
}
off -= obf[i].off;
for (; i < n && csize > 0; i++, off = 0) {
if ((cobf = xdl_mmob_new(&robf, &nobf, &aobf)) == NULL) {
xdl_free(robf);
return -1;
}
cobf->off = ooff;
cobf->size = XDL_MIN(csize, obf[i].size - off);
cobf->ptr = obf[i].ptr + off;
ooff += cobf->size;
csize -= cobf->size;
}
if (csize > 0) {
xdl_free(robf);
return -1;
}
} else {
xdl_free(robf);
return -1;
}
}
*probf = robf;
*pnobf = nobf;
return 0;
}
static int xdl_bmerge_synt(mmoffbuffer_t *obf, int n, xdemitcb_t *ecb) {
int i;
mmbuffer_t *mb;
if ((mb = (mmbuffer_t *) xdl_malloc(n * sizeof(mmbuffer_t))) == NULL) {
return -1;
}
for (i = 0; i < n; i++) {
mb[i].ptr = obf[i].ptr;
mb[i].size = obf[i].size;
}
if (ecb->outf(ecb->priv, mb, n) < 0) {
xdl_free(mb);
return -1;
}
xdl_free(mb);
return 0;
}
int xdl_bpatch_multi(mmbuffer_t *base, mmbuffer_t *mbpch, int n, xdemitcb_t *ecb) {
int i, nobf, fnobf;
mmoffbuffer_t *obf, *fobf;
nobf = 1;
if ((obf = (mmoffbuffer_t *) xdl_malloc(nobf * sizeof(mmoffbuffer_t))) == NULL) {
return -1;
}
obf->off = 0;
obf->ptr = base->ptr;
obf->size = base->size;
for (i = 0; i < n; i++) {
if (xdl_bmerge(obf, nobf, &mbpch[i], &fobf, &fnobf) < 0) {
xdl_free(obf);
return -1;
}
xdl_free(obf);
obf = fobf;
nobf = fnobf;
}
if (xdl_bmerge_synt(obf, nobf, ecb) < 0) {
xdl_free(obf);
return -1;
}
xdl_free(obf);
return 0;
}

View File

@@ -0,0 +1,144 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XDIFF_H)
#define XDIFF_H
#ifdef __cplusplus
extern "C" {
#endif /* #ifdef __cplusplus */
#ifdef _WIN32
#ifdef LIBXDIFF_DLL_EXPORT
#define LIBXDIFF_EXPORT __declspec(dllexport)
#else
#define LIBXDIFF_EXPORT __declspec(dllimport)
#endif
#else
#define LIBXDIFF_EXPORT
#endif
#define XDF_NEED_MINIMAL (1 << 1)
#define XDL_PATCH_NORMAL '-'
#define XDL_PATCH_REVERSE '+'
#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
#define XDL_PATCH_IGNOREBSPACE (1 << 8)
#define XDL_MMB_READONLY (1 << 0)
#define XDL_MMF_ATOMIC (1 << 0)
#define XDL_BDOP_INS 1
#define XDL_BDOP_CPY 2
#define XDL_BDOP_INSB 3
LIBXDIFF_EXPORT typedef struct s_memallocator {
void *priv;
void *(*malloc)(void *, unsigned int);
void (*free)(void *, void *);
void *(*realloc)(void *, void *, unsigned int);
} memallocator_t;
LIBXDIFF_EXPORT typedef struct s_mmblock {
struct s_mmblock *next;
unsigned long flags;
long size, bsize;
char *ptr;
} mmblock_t;
LIBXDIFF_EXPORT typedef struct s_mmfile {
unsigned long flags;
mmblock_t *head, *tail;
long bsize, fsize, rpos;
mmblock_t *rcur, *wcur;
} mmfile_t;
LIBXDIFF_EXPORT typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
LIBXDIFF_EXPORT typedef struct s_xpparam {
unsigned long flags;
} xpparam_t;
LIBXDIFF_EXPORT typedef struct s_xdemitcb {
void *priv;
int (*outf)(void *, mmbuffer_t *, int);
} xdemitcb_t;
LIBXDIFF_EXPORT typedef struct s_xdemitconf {
long ctxlen;
} xdemitconf_t;
LIBXDIFF_EXPORT typedef struct s_bdiffparam {
long bsize;
} bdiffparam_t;
LIBXDIFF_EXPORT int xdl_set_allocator(memallocator_t const *malt);
LIBXDIFF_EXPORT void *xdl_malloc(unsigned int size);
LIBXDIFF_EXPORT void xdl_free(void *ptr);
LIBXDIFF_EXPORT void *xdl_realloc(void *ptr, unsigned int size);
LIBXDIFF_EXPORT int xdl_init_mmfile(mmfile_t *mmf, long bsize, unsigned long flags);
LIBXDIFF_EXPORT void xdl_free_mmfile(mmfile_t *mmf);
LIBXDIFF_EXPORT int xdl_mmfile_iscompact(mmfile_t *mmf);
LIBXDIFF_EXPORT int xdl_seek_mmfile(mmfile_t *mmf, long off);
LIBXDIFF_EXPORT long xdl_read_mmfile(mmfile_t *mmf, void *data, long size);
LIBXDIFF_EXPORT long xdl_write_mmfile(mmfile_t *mmf, void const *data, long size);
LIBXDIFF_EXPORT long xdl_writem_mmfile(mmfile_t *mmf, mmbuffer_t *mb, int nbuf);
LIBXDIFF_EXPORT void *xdl_mmfile_writeallocate(mmfile_t *mmf, long size);
LIBXDIFF_EXPORT long xdl_mmfile_ptradd(mmfile_t *mmf, char *ptr, long size, unsigned long flags);
LIBXDIFF_EXPORT long xdl_copy_mmfile(mmfile_t *mmf, long size, xdemitcb_t *ecb);
LIBXDIFF_EXPORT void *xdl_mmfile_first(mmfile_t *mmf, long *size);
LIBXDIFF_EXPORT void *xdl_mmfile_next(mmfile_t *mmf, long *size);
LIBXDIFF_EXPORT long xdl_mmfile_size(mmfile_t *mmf);
LIBXDIFF_EXPORT int xdl_mmfile_cmp(mmfile_t *mmf1, mmfile_t *mmf2);
LIBXDIFF_EXPORT int xdl_mmfile_compact(mmfile_t *mmfo, mmfile_t *mmfc, long bsize, unsigned long flags);
LIBXDIFF_EXPORT int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, xdemitcb_t *ecb);
LIBXDIFF_EXPORT int xdl_patch(mmfile_t *mf, mmfile_t *mfp, int mode, xdemitcb_t *ecb,
xdemitcb_t *rjecb);
LIBXDIFF_EXPORT int xdl_merge3(mmfile_t *mmfo, mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb,
xdemitcb_t *rjecb);
LIBXDIFF_EXPORT int xdl_bdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, bdiffparam_t const *bdp, xdemitcb_t *ecb);
LIBXDIFF_EXPORT int xdl_bdiff(mmfile_t *mmf1, mmfile_t *mmf2, bdiffparam_t const *bdp, xdemitcb_t *ecb);
LIBXDIFF_EXPORT int xdl_rabdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, xdemitcb_t *ecb);
LIBXDIFF_EXPORT int xdl_rabdiff(mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb);
LIBXDIFF_EXPORT long xdl_bdiff_tgsize(mmfile_t *mmfp);
LIBXDIFF_EXPORT int xdl_bpatch(mmfile_t *mmf, mmfile_t *mmfp, xdemitcb_t *ecb);
LIBXDIFF_EXPORT int xdl_bpatch_multi(mmbuffer_t *base, mmbuffer_t *mbpch, int n, xdemitcb_t *ecb);
#ifdef __cplusplus
}
#endif /* #ifdef __cplusplus */
#endif /* #if !defined(XDIFF_H) */

View File

@@ -0,0 +1,556 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#define XDL_MAX_COST_MIN 256
#define XDL_HEUR_MIN_COST 256
#define XDL_LINE_MAX (long)((1UL << (8 * sizeof(long) - 1)) - 1)
#define XDL_SNAKE_CNT 20
#define XDL_K_HEUR 4
typedef struct s_xdpsplit {
long i1, i2;
int min_lo, min_hi;
} xdpsplit_t;
/*
* See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
* Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
* the forward diagonal starting from (off1, off2) and the backward diagonal
* starting from (lim1, lim2). If the K values on the same diagonal crosses
* returns the furthest point of reach. We might end up having to expensive
* cases using this algorithm is full, so a little bit of heuristic is needed
* to cut the search and to return a suboptimal point.
*/
static long xdl_split(unsigned long const *ha1, long off1, long lim1,
unsigned long const *ha2, long off2, long lim2,
long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
xdalgoenv_t *xenv) {
long dmin = off1 - lim2, dmax = lim1 - off2;
long fmid = off1 - off2, bmid = lim1 - lim2;
long odd = (fmid - bmid) & 1;
long fmin = fmid, fmax = fmid;
long bmin = bmid, bmax = bmid;
long ec, d, i1, i2, prev1, best, dd, v, k;
/*
* Set initial diagonal values for both forward and backward path.
*/
kvdf[fmid] = off1;
kvdb[bmid] = lim1;
for (ec = 1;; ec++) {
int got_snake = 0;
/*
* We need to extent the diagonal "domain" by one. If the next
* values exits the box boundaries we need to change it in the
* opposite direction because (max - min) must be a power of two.
* Also we initialize the extenal K value to -1 so that we can
* avoid extra conditions check inside the core loop.
*/
if (fmin > dmin)
kvdf[--fmin - 1] = -1;
else
++fmin;
if (fmax < dmax)
kvdf[++fmax + 1] = -1;
else
--fmax;
for (d = fmax; d >= fmin; d -= 2) {
if (kvdf[d - 1] >= kvdf[d + 1])
i1 = kvdf[d - 1] + 1;
else
i1 = kvdf[d + 1];
prev1 = i1;
i2 = i1 - d;
for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);
if (i1 - prev1 > xenv->snake_cnt)
got_snake = 1;
kvdf[d] = i1;
if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {
spl->i1 = i1;
spl->i2 = i2;
spl->min_lo = spl->min_hi = 1;
return ec;
}
}
/*
* We need to extent the diagonal "domain" by one. If the next
* values exits the box boundaries we need to change it in the
* opposite direction because (max - min) must be a power of two.
* Also we initialize the extenal K value to -1 so that we can
* avoid extra conditions check inside the core loop.
*/
if (bmin > dmin)
kvdb[--bmin - 1] = XDL_LINE_MAX;
else
++bmin;
if (bmax < dmax)
kvdb[++bmax + 1] = XDL_LINE_MAX;
else
--bmax;
for (d = bmax; d >= bmin; d -= 2) {
if (kvdb[d - 1] < kvdb[d + 1])
i1 = kvdb[d - 1];
else
i1 = kvdb[d + 1] - 1;
prev1 = i1;
i2 = i1 - d;
for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);
if (prev1 - i1 > xenv->snake_cnt)
got_snake = 1;
kvdb[d] = i1;
if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {
spl->i1 = i1;
spl->i2 = i2;
spl->min_lo = spl->min_hi = 1;
return ec;
}
}
if (need_min)
continue;
/*
* If the edit cost is above the heuristic trigger and if
* we got a good snake, we sample current diagonals to see
* if some of the, have reached an "interesting" path. Our
* measure is a function of the distance from the diagonal
* corner (i1 + i2) penalized with the distance from the
* mid diagonal itself. If this value is above the current
* edit cost times a magic factor (XDL_K_HEUR) we consider
* it interesting.
*/
if (got_snake && ec > xenv->heur_min) {
for (best = 0, d = fmax; d >= fmin; d -= 2) {
dd = d > fmid ? d - fmid: fmid - d;
i1 = kvdf[d];
i2 = i1 - d;
v = (i1 - off1) + (i2 - off2) - dd;
if (v > XDL_K_HEUR * ec && v > best &&
off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
off2 + xenv->snake_cnt <= i2 && i2 < lim2) {
for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
if (k == xenv->snake_cnt) {
best = v;
spl->i1 = i1;
spl->i2 = i2;
break;
}
}
}
if (best > 0) {
spl->min_lo = 1;
spl->min_hi = 0;
return ec;
}
for (best = 0, d = bmax; d >= bmin; d -= 2) {
dd = d > bmid ? d - bmid: bmid - d;
i1 = kvdb[d];
i2 = i1 - d;
v = (lim1 - i1) + (lim2 - i2) - dd;
if (v > XDL_K_HEUR * ec && v > best &&
off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&
off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {
for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
if (k == xenv->snake_cnt - 1) {
best = v;
spl->i1 = i1;
spl->i2 = i2;
break;
}
}
}
if (best > 0) {
spl->min_lo = 0;
spl->min_hi = 1;
return ec;
}
}
/*
* Enough is enough. We spent too much time here and now we collect
* the furthest reaching path using the (i1 + i2) measure.
*/
if (ec >= xenv->mxcost) {
long fbest, fbest1, bbest, bbest1;
fbest = -1;
for (d = fmax; d >= fmin; d -= 2) {
i1 = XDL_MIN(kvdf[d], lim1);
i2 = i1 - d;
if (lim2 < i2)
i1 = lim2 + d, i2 = lim2;
if (fbest < i1 + i2) {
fbest = i1 + i2;
fbest1 = i1;
}
}
bbest = XDL_LINE_MAX;
for (d = bmax; d >= bmin; d -= 2) {
i1 = XDL_MAX(off1, kvdb[d]);
i2 = i1 - d;
if (i2 < off2)
i1 = off2 + d, i2 = off2;
if (i1 + i2 < bbest) {
bbest = i1 + i2;
bbest1 = i1;
}
}
if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {
spl->i1 = fbest1;
spl->i2 = fbest - fbest1;
spl->min_lo = 1;
spl->min_hi = 0;
} else {
spl->i1 = bbest1;
spl->i2 = bbest - bbest1;
spl->min_lo = 0;
spl->min_hi = 1;
}
return ec;
}
}
return -1;
}
/*
* Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling
* the box splitting function. Note that the real job (marking changed lines)
* is done in the two boundary reaching checks.
*/
int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
diffdata_t *dd2, long off2, long lim2,
long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
/*
* Shrink the box by walking through each diagonal snake (SW and NE).
*/
for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);
for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);
/*
* If one dimension is empty, then all records on the other one must
* be obviously changed.
*/
if (off1 == lim1) {
char *rchg2 = dd2->rchg;
long *rindex2 = dd2->rindex;
for (; off2 < lim2; off2++)
rchg2[rindex2[off2]] = 1;
} else if (off2 == lim2) {
char *rchg1 = dd1->rchg;
long *rindex1 = dd1->rindex;
for (; off1 < lim1; off1++)
rchg1[rindex1[off1]] = 1;
} else {
long ec;
xdpsplit_t spl;
/*
* Divide ...
*/
if ((ec = xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
need_min, &spl, xenv)) < 0) {
return -1;
}
/*
* ... et Impera.
*/
if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,
kvdf, kvdb, spl.min_lo, xenv) < 0 ||
xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,
kvdf, kvdb, spl.min_hi, xenv) < 0) {
return -1;
}
}
return 0;
}
int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe) {
long ndiags;
long *kvd, *kvdf, *kvdb;
xdalgoenv_t xenv;
diffdata_t dd1, dd2;
if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
return -1;
}
/*
* Allocate and setup K vectors to be used by the differential algorithm.
* One is to store the forward path and one to store the backward path.
*/
ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
xdl_free_env(xe);
return -1;
}
kvdf = kvd;
kvdb = kvdf + ndiags;
kvdf += xe->xdf2.nreff + 1;
kvdb += xe->xdf2.nreff + 1;
xenv.mxcost = xdl_bogosqrt(ndiags);
if (xenv.mxcost < XDL_MAX_COST_MIN)
xenv.mxcost = XDL_MAX_COST_MIN;
xenv.snake_cnt = XDL_SNAKE_CNT;
xenv.heur_min = XDL_HEUR_MIN_COST;
dd1.nrec = xe->xdf1.nreff;
dd1.ha = xe->xdf1.ha;
dd1.rchg = xe->xdf1.rchg;
dd1.rindex = xe->xdf1.rindex;
dd2.nrec = xe->xdf2.nreff;
dd2.ha = xe->xdf2.ha;
dd2.rchg = xe->xdf2.rchg;
dd2.rindex = xe->xdf2.rindex;
if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
xdl_free(kvd);
xdl_free_env(xe);
return -1;
}
xdl_free(kvd);
return 0;
}
static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {
xdchange_t *xch;
if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))
return NULL;
xch->next = xscr;
xch->i1 = i1;
xch->i2 = i2;
xch->chg1 = chg1;
xch->chg2 = chg2;
return xch;
}
static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo) {
long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
xrecord_t **recs = xdf->recs;
/*
* This is the same of what GNU diff does. Move back and forward
* change groups for a consistent and pretty diff output. This also
* helps in finding joineable change groups and reduce the diff size.
*/
for (ix = ixo = 0;;) {
/*
* Find the first changed line in the to-be-compacted file.
* We need to keep track of both indexes, so if we find a
* changed lines group on the other file, while scanning the
* to-be-compacted file, we need to skip it properly. Note
* that loops that are testing for changed lines on rchg* do
* not need index bounding since the array is prepared with
* a zero at position -1 and N.
*/
for (; ix < nrec && !rchg[ix]; ix++)
while (rchgo[ixo++]);
if (ix == nrec)
break;
/*
* Record the start of a changed-group in the to-be-compacted file
* and find the end of it, on both to-be-compacted and other file
* indexes (ix and ixo).
*/
ixs = ix;
for (ix++; rchg[ix]; ix++);
for (; rchgo[ixo]; ixo++);
do {
grpsiz = ix - ixs;
/*
* If the line before the current change group, is equal to
* the last line of the current change group, shift backward
* the group.
*/
while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
XDL_RECMATCH(recs[ixs - 1], recs[ix - 1])) {
rchg[--ixs] = 1;
rchg[--ix] = 0;
/*
* This change might have joined two change groups,
* so we try to take this scenario in account by moving
* the start index accordingly (and so the other-file
* end-of-group index).
*/
for (; rchg[ixs - 1]; ixs--);
while (rchgo[--ixo]);
}
/*
* Record the end-of-group position in case we are matched
* with a group of changes in the other file (that is, the
* change record before the enf-of-group index in the other
* file is set).
*/
ixref = rchgo[ixo - 1] ? ix: nrec;
/*
* If the first line of the current change group, is equal to
* the line next of the current change group, shift forward
* the group.
*/
while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
XDL_RECMATCH(recs[ixs], recs[ix])) {
rchg[ixs++] = 0;
rchg[ix++] = 1;
/*
* This change might have joined two change groups,
* so we try to take this scenario in account by moving
* the start index accordingly (and so the other-file
* end-of-group index). Keep tracking the reference
* index in case we are shifting together with a
* corresponding group of changes in the other file.
*/
for (; rchg[ix]; ix++);
while (rchgo[++ixo])
ixref = ix;
}
} while (grpsiz != ix - ixs);
/*
* Try to move back the possibly merged group of changes, to match
* the recorded postion in the other file.
*/
while (ixref < ix) {
rchg[--ixs] = 1;
rchg[--ix] = 0;
while (rchgo[--ixo]);
}
}
return 0;
}
int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
xdchange_t *cscr = NULL, *xch;
char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
long i1, i2, l1, l2;
/*
* Trivial. Collects "groups" of changes and creates an edit script.
*/
for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
for (l1 = i1; rchg1[i1 - 1]; i1--);
for (l2 = i2; rchg2[i2 - 1]; i2--);
if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {
xdl_free_script(cscr);
return -1;
}
cscr = xch;
}
*xscr = cscr;
return 0;
}
void xdl_free_script(xdchange_t *xscr) {
xdchange_t *xch;
while ((xch = xscr) != NULL) {
xscr = xscr->next;
xdl_free(xch);
}
}
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
xdchange_t *xscr;
xdfenv_t xe;
if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
return -1;
}
if (xdl_change_compact(&xe.xdf1, &xe.xdf2) < 0 ||
xdl_change_compact(&xe.xdf2, &xe.xdf1) < 0 ||
xdl_build_script(&xe, &xscr) < 0) {
xdl_free_env(&xe);
return -1;
}
if (xscr) {
if (xdl_emit_diff(&xe, xscr, ecb, xecfg) < 0) {
xdl_free_script(xscr);
xdl_free_env(&xe);
return -1;
}
xdl_free_script(xscr);
}
xdl_free_env(&xe);
return 0;
}

View File

@@ -0,0 +1,60 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XDIFFI_H)
#define XDIFFI_H
typedef struct s_diffdata {
long nrec;
unsigned long const *ha;
long *rindex;
char *rchg;
} diffdata_t;
typedef struct s_xdalgoenv {
long mxcost;
long snake_cnt;
long heur_min;
} xdalgoenv_t;
typedef struct s_xdchange {
struct s_xdchange *next;
long i1, i2;
long chg1, chg2;
} xdchange_t;
int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
diffdata_t *dd2, long off2, long lim2,
long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe);
int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
void xdl_free_script(xdchange_t *xscr);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
#endif /* #if !defined(XDIFFI_H) */

View File

@@ -0,0 +1,132 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
*rec = xdf->recs[ri]->ptr;
return xdf->recs[ri]->size;
}
static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
long size, psize = strlen(pre);
char const *rec;
size = xdl_get_rec(xdf, ri, &rec);
if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
return -1;
}
return 0;
}
/*
* Starting at the passed change atom, find the latest change atom to be included
* inside the differential hunk according to the specified configuration.
*/
static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
xdchange_t *xch, *xchp;
for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
if (xch->i1 - (xchp->i1 + xchp->chg1) > 2 * xecfg->ctxlen)
break;
return xchp;
}
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg) {
long s1, s2, e1, e2, lctx;
xdchange_t *xch, *xche;
for (xch = xche = xscr; xch; xch = xche->next) {
xche = xdl_get_hunk(xch, xecfg);
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
lctx = xecfg->ctxlen;
lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
e1 = xche->i1 + xche->chg1 + lctx;
e2 = xche->i2 + xche->chg2 + lctx;
/*
* Emit current hunk header.
*/
if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, ecb) < 0)
return -1;
/*
* Emit pre-context.
*/
for (; s1 < xch->i1; s1++)
if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
return -1;
for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
/*
* Merge previous with current change atom.
*/
for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
return -1;
/*
* Removes lines from the first file.
*/
for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
return -1;
/*
* Adds lines from the second file.
*/
for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
return -1;
if (xch == xche)
break;
s1 = xch->i1 + xch->chg1;
s2 = xch->i2 + xch->chg2;
}
/*
* Emit post-context.
*/
for (s1 = xche->i1 + xche->chg1; s1 < e1; s1++)
if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
return -1;
}
return 0;
}

View File

@@ -0,0 +1,34 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XEMIT_H)
#define XEMIT_H
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
#endif /* #if !defined(XEMIT_H) */

View File

@@ -0,0 +1,71 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XINCLUDE_H)
#define XINCLUDE_H
#if defined(HAVE_WINCONFIG_H)
#include "winconfig.h"
#endif /* #if defined(HAVE_CONFIG_H) */
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif /* #if defined(HAVE_CONFIG_H) */
#if defined(HAVE_STDIO_H)
#include <stdio.h>
#endif /* #if defined(HAVE_STDIO_H) */
#if defined(HAVE_STDLIB_H)
#include <stdlib.h>
#endif /* #if defined(HAVE_STDLIB_H) */
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif /* #if defined(HAVE_UNISTD_H) */
#if defined(HAVE_STRING_H)
#include <string.h>
#endif /* #if defined(HAVE_STRING_H) */
#if defined(HAVE_LIMITS_H)
#include <limits.h>
#endif /* #if defined(HAVE_LIMITS_H) */
#include "xmacros.h"
#include "xmissing.h"
#include "xdiff.h"
#include "xtypes.h"
#include "xutils.h"
#include "xadler32.h"
#include "xprepare.h"
#include "xdiffi.h"
#include "xemit.h"
#include "xbdiff.h"
#endif /* #if !defined(XINCLUDE_H) */

View File

@@ -0,0 +1,51 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XMACROS_H)
#define XMACROS_H
#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
#define XDL_ADDBITS(v, b) ((v) + ((v) >> (b)))
#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
#define XDL_HASHLONG(v, b) (XDL_ADDBITS((unsigned long) (v), b) & XDL_MASKBITS(b))
#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
#define XDL_RECMATCH(r1, r2) ((r1)->size == (r2)->size && memcmp((r1)->ptr, (r2)->ptr, (r1)->size) == 0)
#define XDL_LE32_PUT(p, v) do { \
unsigned char *__p = (unsigned char *) (p); \
*__p++ = (unsigned char) (v); \
*__p++ = (unsigned char) ((v) >> 8); \
*__p++ = (unsigned char) ((v) >> 16); \
*__p = (unsigned char) ((v) >> 24); \
} while (0)
#define XDL_LE32_GET(p, v) do { \
unsigned char const *__p = (unsigned char const *) (p); \
(v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
} while (0)
#endif /* #if !defined(XMACROS_H) */

View File

@@ -0,0 +1,66 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#define XDL_MERGE3_BLKSIZE (1024 * 8)
#define XDL_MERGE3_CTXLEN 3
int xdl_merge3(mmfile_t *mmfo, mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb,
xdemitcb_t *rjecb) {
xpparam_t xpp;
xdemitconf_t xecfg;
xdemitcb_t xecb;
mmfile_t mmfp;
if (xdl_init_mmfile(&mmfp, XDL_MERGE3_BLKSIZE, XDL_MMF_ATOMIC) < 0) {
return -1;
}
xpp.flags = 0;
xecfg.ctxlen = XDL_MERGE3_CTXLEN;
xecb.priv = &mmfp;
xecb.outf = xdl_mmfile_outf;
if (xdl_diff(mmfo, mmf2, &xpp, &xecfg, &xecb) < 0) {
xdl_free_mmfile(&mmfp);
return -1;
}
if (xdl_patch(mmf1, &mmfp, XDL_PATCH_NORMAL, ecb, rjecb) < 0) {
xdl_free_mmfile(&mmfp);
return -1;
}
xdl_free_mmfile(&mmfp);
return 0;
}

View File

@@ -0,0 +1,92 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#if !defined(HAVE_MEMCHR)
void *memchr(void const *p, int c, long n) {
char const *pc = p;
for (; n; n--, pc++)
if (*pc == (char) c)
return pc;
return NULL;
}
#endif /* #if !defined(HAVE_MEMCHR) */
#if !defined(HAVE_MEMCMP)
int memcmp(void const *p1, void const *p2, long n) {
char const *pc1 = p1, *pc2 = p2;
for (; n; n--, pc1++, pc2++)
if (*pc1 != *pc2)
return *pc1 - *pc2;
return 0;
}
#endif /* #if !defined(HAVE_MEMCMP) */
#if !defined(HAVE_MEMCPY)
void *memcpy(void *d, void const *s, long n) {
char *dc = d;
char const *sc = s;
for (; n; n--, dc++, sc++)
*dc = *sc;
return d;
}
#endif /* #if !defined(HAVE_MEMCPY) */
#if !defined(HAVE_MEMSET)
void *memset(void *d, int c, long n) {
char *dc = d;
for (; n; n--, dc++)
*dc = (char) c;
return d;
}
#endif /* #if !defined(HAVE_MEMSET) */
#if !defined(HAVE_STRLEN)
long strlen(char const *s) {
char const *tmp;
for (tmp = s; *s; s++);
return (long) (s - tmp);
}
#endif /* #if !defined(HAVE_STRLEN) */

View File

@@ -0,0 +1,56 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XMISSING_H)
#define XMISSING_H
#if !defined(CHAR_BIT)
#define CHAR_BIT 8
#endif /* #if !defined(CHAR_BIT) */
#if !defined(HAVE_MEMCHR)
void *memchr(void const *p, int c, long n);
#endif /* #if !defined(HAVE_MEMCHR) */
#if !defined(HAVE_MEMCMP)
int memcmp(void const *p1, void const *p2, long n);
#endif /* #if !defined(HAVE_MEMCMP) */
#if !defined(HAVE_MEMCPY)
void *memcpy(void *d, void const *s, long n);
#endif /* #if !defined(HAVE_MEMCPY) */
#if !defined(HAVE_MEMSET)
void *memset(void *d, int c, long n);
#endif /* #if !defined(HAVE_MEMSET) */
#if !defined(HAVE_STRLEN)
long strlen(char const *s);
#endif /* #if !defined(HAVE_STRLEN) */
#endif /* #if !defined(XMISSING_H) */

View File

@@ -0,0 +1,641 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#define XDL_MAX_FUZZ 3
#define XDL_MIN_SYNCLINES 4
typedef struct s_recinfo {
char const *ptr;
long size;
} recinfo_t;
typedef struct s_recfile {
mmfile_t *mf;
long nrec;
recinfo_t *recs;
} recfile_t;
typedef struct s_hunkinfo {
long s1, s2;
long c1, c2;
long cmn, radd, rdel, pctx, sctx;
} hunkinfo_t;
typedef struct s_patchstats {
long adds, dels;
} patchstats_t;
typedef struct s_patch {
recfile_t rf;
hunkinfo_t hi;
long hkrec;
long hklen;
long flags;
patchstats_t ps;
int fuzzies;
} patch_t;
static int xdl_load_hunk_info(char const *line, long size, hunkinfo_t *hki);
static int xdl_init_recfile(mmfile_t *mf, int ispatch, recfile_t *rf);
static void xdl_free_recfile(recfile_t *rf);
static char const *xdl_recfile_get(recfile_t *rf, long irec, long *size);
static int xdl_init_patch(mmfile_t *mf, long flags, patch_t *pch);
static void xdl_free_patch(patch_t *pch);
static int xdl_load_hunk(patch_t *pch, long hkrec);
static int xdl_first_hunk(patch_t *pch);
static int xdl_next_hunk(patch_t *pch);
static int xdl_line_match(patch_t *pch, const char *s, long ns, char const *m, long nm);
static int xdl_hunk_match(recfile_t *rf, long irec, patch_t *pch, int mode, int fuzz);
static int xdl_find_hunk(recfile_t *rf, long ibase, patch_t *pch, int mode,
int fuzz, long *hkpos, int *exact);
static int xdl_emit_rfile_line(recfile_t *rf, long line, xdemitcb_t *ecb);
static int xdl_flush_section(recfile_t *rf, long start, long top, xdemitcb_t *ecb);
static int xdl_apply_hunk(recfile_t *rf, long hkpos, patch_t *pch, int mode,
long *ibase, xdemitcb_t *ecb);
static int xdl_reject_hunk(recfile_t *rf, patch_t *pch, int mode,
xdemitcb_t *rjecb);
static int xdl_process_hunk(recfile_t *rff, patch_t *pch, long *ibase, int mode,
xdemitcb_t *ecb, xdemitcb_t *rjecb);
static int xdl_load_hunk_info(char const *line, long size, hunkinfo_t *hki) {
char const *next;
/*
* The diff header format should be:
*
* @@ -OP,OC +NP,NC @@
*
* Unfortunately some software avoid to emit OP or/and NP in case
* of not existing old or new file (it should be mitted as zero).
* We need to handle both syntaxes.
*/
if (memcmp(line, "@@ -", 4))
return -1;
line += 4;
size -= 4;
if (!size || !XDL_ISDIGIT(*line))
return -1;
hki->s1 = xdl_atol(line, &next);
size -= next - line;
line = next;
if (!size)
return -1;
if (*line == ',') {
size--, line++;
if (!size || !XDL_ISDIGIT(*line))
return -1;
hki->c1 = xdl_atol(line, &next);
size -= next - line;
line = next;
if (!size || *line != ' ')
return -1;
size--, line++;
} else if (*line == ' ') {
size--, line++;
hki->c1 = hki->s1;
hki->s1 = 0;
} else
return -1;
if (!size || *line != '+')
return -1;
size--, line++;
if (!size || !XDL_ISDIGIT(*line))
return -1;
hki->s2 = xdl_atol(line, &next);
size -= next - line;
line = next;
if (!size)
return -1;
if (*line == ',') {
size--, line++;
if (!size || !XDL_ISDIGIT(*line))
return -1;
hki->c2 = xdl_atol(line, &next);
size -= next - line;
line = next;
if (!size || *line != ' ')
return -1;
size--, line++;
} else if (*line == ' ') {
size--, line++;
hki->c2 = hki->s2;
hki->s2 = 0;
} else
return -1;
if (size < 2 || memcmp(line, "@@", 2) != 0)
return -1;
/*
* We start from zero, so decrement by one unless it's the special position
* '0' inside the unified diff (new or deleted file).
*/
if (hki->s1 > 0 && hki->c1 > 0)
hki->s1--;
if (hki->s2 > 0 && hki->c2 > 0)
hki->s2--;
return 0;
}
static int xdl_init_recfile(mmfile_t *mf, int ispatch, recfile_t *rf) {
long narec, nrec, bsize;
recinfo_t *recs, *rrecs;
char const *blk, *cur, *top, *eol;
narec = xdl_guess_lines(mf);
if (!(recs = (recinfo_t *) xdl_malloc(narec * sizeof(recinfo_t)))) {
return -1;
}
nrec = 0;
if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
for (top = blk + bsize;;) {
if (cur >= top) {
if (!(cur = blk = xdl_mmfile_next(mf, &bsize)))
break;
top = blk + bsize;
}
if (nrec >= narec) {
narec *= 2;
if (!(rrecs = (recinfo_t *)
xdl_realloc(recs, narec * sizeof(recinfo_t)))) {
xdl_free(recs);
return -1;
}
recs = rrecs;
}
recs[nrec].ptr = cur;
if (!(eol = memchr(cur, '\n', top - cur)))
eol = top - 1;
recs[nrec].size = (long) (eol - cur) + 1;
if (ispatch && *cur == '\\' && nrec > 0 && recs[nrec - 1].size > 0 &&
recs[nrec - 1].ptr[recs[nrec - 1].size - 1] == '\n')
recs[nrec - 1].size--;
else
nrec++;
cur = eol + 1;
}
}
rf->mf = mf;
rf->nrec = nrec;
rf->recs = recs;
return 0;
}
static void xdl_free_recfile(recfile_t *rf) {
xdl_free(rf->recs);
}
static char const *xdl_recfile_get(recfile_t *rf, long irec, long *size) {
if (irec < 0 || irec >= rf->nrec)
return NULL;
*size = rf->recs[irec].size;
return rf->recs[irec].ptr;
}
static int xdl_init_patch(mmfile_t *mf, long flags, patch_t *pch) {
if (xdl_init_recfile(mf, 1, &pch->rf) < 0) {
return -1;
}
pch->hkrec = 0;
pch->hklen = 0;
pch->flags = flags;
pch->ps.adds = pch->ps.dels = 0;
pch->fuzzies = 0;
return 0;
}
static void xdl_free_patch(patch_t *pch) {
xdl_free_recfile(&pch->rf);
}
static int xdl_load_hunk(patch_t *pch, long hkrec) {
long size, i, nb;
char const *line;
for (;; hkrec++) {
pch->hkrec = hkrec;
if (!(line = xdl_recfile_get(&pch->rf, pch->hkrec, &size)))
return 0;
if (*line == '@')
break;
}
if (xdl_load_hunk_info(line, size, &pch->hi) < 0) {
return -1;
}
pch->hi.cmn = pch->hi.radd = pch->hi.rdel = pch->hi.pctx = pch->hi.sctx = 0;
for (i = pch->hkrec + 1, nb = 0;
(line = xdl_recfile_get(&pch->rf, i, &size)) != NULL; i++) {
if (*line == '@' || *line == '\n')
break;
if (*line == ' ') {
nb++;
pch->hi.cmn++;
} else if (*line == '+') {
if (pch->hi.radd + pch->hi.rdel == 0)
pch->hi.pctx = nb;
nb = 0;
pch->hi.radd++;
} else if (*line == '-') {
if (pch->hi.radd + pch->hi.rdel == 0)
pch->hi.pctx = nb;
nb = 0;
pch->hi.rdel++;
} else {
return -1;
}
}
pch->hi.sctx = nb;
if (pch->hi.cmn + pch->hi.radd != pch->hi.c2 ||
pch->hi.cmn + pch->hi.rdel != pch->hi.c1) {
return -1;
}
pch->hklen = i - pch->hkrec - 1;
return 1;
}
static int xdl_first_hunk(patch_t *pch) {
return xdl_load_hunk(pch, 0);
}
static int xdl_next_hunk(patch_t *pch) {
return xdl_load_hunk(pch, pch->hkrec + pch->hklen + 1);
}
static int xdl_line_match(patch_t *pch, const char *s, long ns, char const *m, long nm) {
for (; ns > 0 && (s[ns - 1] == '\r' || s[ns - 1] == '\n'); ns--);
for (; nm > 0 && (m[nm - 1] == '\r' || m[nm - 1] == '\n'); nm--);
if (pch->flags & XDL_PATCH_IGNOREBSPACE) {
for (; ns > 0 && (*s == ' ' || *s == '\t'); ns--, s++);
for (; ns > 0 && (s[ns - 1] == ' ' || s[ns - 1] == '\t'); ns--);
for (; nm > 0 && (*m == ' ' || *m == '\t'); nm--, m++);
for (; nm > 0 && (m[nm - 1] == ' ' || m[nm - 1] == '\t'); nm--);
}
return ns == nm && memcmp(s, m, ns) == 0;
}
static int xdl_hunk_match(recfile_t *rf, long irec, patch_t *pch, int mode, int fuzz) {
long i, j, z, fsize, psize, ptop, pfuzz, sfuzz, misses;
char const *fline, *pline;
/*
* Limit fuzz to not be greater than the prefix and suffix context.
*/
pfuzz = fuzz < pch->hi.pctx ? fuzz: pch->hi.pctx;
sfuzz = fuzz < pch->hi.sctx ? fuzz: pch->hi.sctx;
/*
* First loop through the prefix fuzz area. In this loop we simply
* note mismatching lines. We allow missing lines here, that is,
* some prefix context lines are missing.
*/
for (z = pfuzz, misses = 0, i = irec, j = pch->hkrec + 1,
ptop = pch->hkrec + 1 + pch->hklen - sfuzz;
z > 0 && i < rf->nrec && j < ptop; i++, j++, z--) {
if (!(pline = xdl_recfile_get(&pch->rf, j, &psize)))
return 0;
if (!(fline = xdl_recfile_get(rf, i, &fsize)) ||
!xdl_line_match(pch, fline, fsize, pline + 1, psize - 1))
misses++;
}
if (misses > fuzz)
return 0;
/*
* Strict match loop.
*/
for (; i < rf->nrec && j < ptop; i++, j++) {
for (; j < ptop; j++) {
if (!(pline = xdl_recfile_get(&pch->rf, j, &psize)))
return 0;
if (*pline == ' ' || *pline == mode)
break;
}
if (j == ptop)
break;
if (!(fline = xdl_recfile_get(rf, i, &fsize)) ||
!xdl_line_match(pch, fline, fsize, pline + 1, psize - 1))
return 0;
}
for (; j < ptop; j++)
if (!(pline = xdl_recfile_get(&pch->rf, j, &psize)) ||
*pline == ' ' || *pline == mode)
return 0;
/*
* Finally loop through the suffix fuzz area. In this loop we simply
* note mismatching lines. We allow missing lines here, that is,
* some suffix context lines are missing.
*/
for (z = sfuzz; z > 0 && i < rf->nrec; i++, j++, z--) {
if (!(pline = xdl_recfile_get(&pch->rf, j, &psize)))
return 0;
if (!(fline = xdl_recfile_get(rf, i, &fsize)) ||
!xdl_line_match(pch, fline, fsize, pline + 1, psize - 1))
misses++;
}
return misses <= fuzz;
}
static int xdl_find_hunk(recfile_t *rf, long ibase, patch_t *pch, int mode,
int fuzz, long *hkpos, int *exact) {
long hpos, hlen, i, j;
long pos[2];
hpos = mode == '-' ? pch->hi.s1: pch->hi.s2;
hlen = mode == '-' ? pch->hi.cmn + pch->hi.rdel: pch->hi.cmn + pch->hi.radd;
if (xdl_hunk_match(rf, hpos, pch, mode, fuzz)) {
*hkpos = hpos;
*exact = 1;
return 1;
}
for (i = 1;; i++) {
/*
* We allow a negative starting hunk position, up to the
* number of prefix context lines.
*/
j = 0;
if (hpos - i >= ibase - pch->hi.pctx)
pos[j++] = hpos - i;
if (hpos + i + hlen <= rf->nrec)
pos[j++] = hpos + i;
if (!j)
break;
for (j--; j >= 0; j--)
if (xdl_hunk_match(rf, pos[j], pch, mode, fuzz)) {
*hkpos = pos[j];
*exact = 0;
return 1;
}
}
return 0;
}
static int xdl_emit_rfile_line(recfile_t *rf, long line, xdemitcb_t *ecb) {
mmbuffer_t mb;
if (!(mb.ptr = (char *) xdl_recfile_get(rf, line, &mb.size)) ||
ecb->outf(ecb->priv, &mb, 1) < 0) {
return -1;
}
return 0;
}
static int xdl_flush_section(recfile_t *rf, long start, long top, xdemitcb_t *ecb) {
long i;
for (i = start; i <= top; i++) {
if (xdl_emit_rfile_line(rf, i, ecb) < 0) {
return -1;
}
}
return 0;
}
static int xdl_apply_hunk(recfile_t *rf, long hkpos, patch_t *pch, int mode,
long *ibase, xdemitcb_t *ecb) {
long j, psize, ptop;
char const *pline;
mmbuffer_t mb;
/*
* The hunk starting position (hkpos) can be negative, up to the number
* of prefix context lines. Since this function only emit the core of
* the hunk (the remaining lines are flushed by xdl_flush_section() calls)
* we need to normalize it by adding the number of prefix context lines.
* The normalized value of the starting position is then greater/equal
* to zero.
*/
hkpos += pch->hi.pctx;
if (xdl_flush_section(rf, *ibase, hkpos - 1, ecb) < 0) {
return -1;
}
*ibase = hkpos;
for (j = pch->hkrec + 1 + pch->hi.pctx,
ptop = pch->hkrec + 1 + pch->hklen - pch->hi.sctx; j < ptop; j++) {
if (!(pline = xdl_recfile_get(&pch->rf, j, &psize))) {
return -1;
}
if (*pline == ' ') {
if (xdl_emit_rfile_line(rf, *ibase, ecb) < 0) {
return -1;
}
(*ibase)++;
} else if (*pline != mode) {
mb.ptr = (char *) pline + 1;
mb.size = psize - 1;
if (ecb->outf(ecb->priv, &mb, 1) < 0) {
return -1;
}
pch->ps.adds++;
} else {
(*ibase)++;
pch->ps.dels++;
}
}
return 0;
}
static int xdl_reject_hunk(recfile_t *rf, patch_t *pch, int mode,
xdemitcb_t *rjecb) {
long i, size, s1, s2, c1, c2;
char const *line, *pre;
mmbuffer_t mb;
if (mode == '-') {
s1 = pch->hi.s1;
s2 = pch->hi.s2;
c1 = pch->hi.c1;
c2 = pch->hi.c2;
} else {
s1 = pch->hi.s2;
s2 = pch->hi.s1;
c1 = pch->hi.c2;
c2 = pch->hi.c1;
}
s1 += pch->ps.adds - pch->ps.dels;
if (xdl_emit_hunk_hdr(s1 + 1, c1, s2 + 1, c2, rjecb) < 0) {
return -1;
}
for (i = pch->hkrec + 1;
(line = xdl_recfile_get(&pch->rf, i, &size)) != NULL; i++) {
if (*line == '@' || *line == '\n')
break;
if (mode == '-' || *line == ' ') {
mb.ptr = (char *) line;
mb.size = size;
if (rjecb->outf(rjecb->priv, &mb, 1) < 0) {
return -1;
}
} else {
pre = *line == '+' ? "-": "+";
if (xdl_emit_diffrec(line + 1, size - 1, pre, strlen(pre),
rjecb) < 0) {
return -1;
}
}
}
return 0;
}
static int xdl_process_hunk(recfile_t *rff, patch_t *pch, long *ibase, int mode,
xdemitcb_t *ecb, xdemitcb_t *rjecb) {
int fuzz, exact, hlen, maxfuzz;
long hkpos;
hlen = mode == '-' ? pch->hi.cmn + pch->hi.rdel: pch->hi.cmn + pch->hi.radd;
maxfuzz = XDL_MAX_FUZZ;
if (hlen - maxfuzz < XDL_MIN_SYNCLINES)
maxfuzz = hlen - XDL_MIN_SYNCLINES;
if (maxfuzz < 0)
maxfuzz = 0;
for (fuzz = 0; fuzz <= maxfuzz; fuzz++) {
if (xdl_find_hunk(rff, *ibase, pch, mode, fuzz,
&hkpos, &exact)) {
if (xdl_apply_hunk(rff, hkpos, pch, mode,
ibase, ecb) < 0) {
return -1;
}
if (!exact || fuzz)
pch->fuzzies++;
return 0;
}
}
if (xdl_reject_hunk(rff, pch, mode, rjecb) < 0) {
return -1;
}
return 0;
}
int xdl_patch(mmfile_t *mf, mmfile_t *mfp, int mode, xdemitcb_t *ecb,
xdemitcb_t *rjecb) {
int hkres, exact;
long hkpos, ibase;
recfile_t rff;
patch_t pch;
if (xdl_init_recfile(mf, 0, &rff) < 0) {
return -1;
}
if (xdl_init_patch(mfp, mode & ~XDL_PATCH_MODEMASK, &pch) < 0) {
xdl_free_recfile(&rff);
return -1;
}
mode &= XDL_PATCH_MODEMASK;
ibase = 0;
if ((hkres = xdl_first_hunk(&pch)) > 0) {
do {
if (xdl_process_hunk(&rff, &pch, &ibase, mode,
ecb, rjecb) < 0) {
xdl_free_patch(&pch);
xdl_free_recfile(&rff);
return -1;
}
} while ((hkres = xdl_next_hunk(&pch)) > 0);
}
if (hkres < 0) {
xdl_free_patch(&pch);
xdl_free_recfile(&rff);
return -1;
}
if (xdl_flush_section(&rff, ibase, rff.nrec - 1, ecb) < 0) {
xdl_free_patch(&pch);
xdl_free_recfile(&rff);
return -1;
}
xdl_free_patch(&pch);
xdl_free_recfile(&rff);
return pch.fuzzies;
}

View File

@@ -0,0 +1,456 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#define XDL_KPDIS_RUN 4
#define XDL_MAX_EQLIMIT 1024
#define XDL_SIMSCAN_WINDOWN 100
typedef struct s_xdlclass {
struct s_xdlclass *next;
unsigned long ha;
char const *line;
long size;
long idx;
} xdlclass_t;
typedef struct s_xdlclassifier {
unsigned int hbits;
long hsize;
xdlclass_t **rchash;
chastore_t ncha;
long count;
} xdlclassifier_t;
static int xdl_init_classifier(xdlclassifier_t *cf, long size) {
long i;
cf->hbits = xdl_hashbits((unsigned int) size);
cf->hsize = 1 << cf->hbits;
if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
return -1;
}
if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
xdl_cha_free(&cf->ncha);
return -1;
}
for (i = 0; i < cf->hsize; i++)
cf->rchash[i] = NULL;
cf->count = 0;
return 0;
}
static void xdl_free_classifier(xdlclassifier_t *cf) {
xdl_free(cf->rchash);
xdl_cha_free(&cf->ncha);
}
static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned int hbits,
xrecord_t *rec) {
long hi;
char const *line;
xdlclass_t *rcrec;
line = rec->ptr;
hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
if (rcrec->ha == rec->ha && rcrec->size == rec->size &&
!memcmp(line, rcrec->line, rec->size))
break;
if (!rcrec) {
if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
return -1;
}
rcrec->idx = cf->count++;
rcrec->line = line;
rcrec->size = rec->size;
rcrec->ha = rec->ha;
rcrec->next = cf->rchash[hi];
cf->rchash[hi] = rcrec;
}
rec->ha = (unsigned long) rcrec->idx;
hi = (long) XDL_HASHLONG(rec->ha, hbits);
rec->next = rhash[hi];
rhash[hi] = rec;
return 0;
}
static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
xdlclassifier_t *cf, xdfile_t *xdf) {
unsigned int hbits;
long i, nrec, hsize, bsize;
unsigned long hav;
char const *blk, *cur, *top, *prev;
xrecord_t *crec;
xrecord_t **recs, **rrecs;
xrecord_t **rhash;
unsigned long *ha;
char *rchg;
long *rindex;
if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) {
return -1;
}
if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) {
xdl_cha_free(&xdf->rcha);
return -1;
}
hbits = xdl_hashbits((unsigned int) narec);
hsize = 1 << hbits;
if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) {
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
for (i = 0; i < hsize; i++)
rhash[i] = NULL;
nrec = 0;
if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
for (top = blk + bsize;;) {
if (cur >= top) {
if (!(cur = blk = xdl_mmfile_next(mf, &bsize)))
break;
top = blk + bsize;
}
prev = cur;
hav = xdl_hash_record(&cur, top);
if (nrec >= narec) {
narec *= 2;
if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) {
xdl_free(rhash);
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
recs = rrecs;
}
if (!(crec = xdl_cha_alloc(&xdf->rcha))) {
xdl_free(rhash);
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
crec->ptr = prev;
crec->size = (long) (cur - prev);
crec->ha = hav;
recs[nrec++] = crec;
if (xdl_classify_record(cf, rhash, hbits, crec) < 0) {
xdl_free(rhash);
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
}
}
if (!(rchg = (char *) xdl_malloc(nrec + 2))) {
xdl_free(rhash);
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
memset(rchg, 0, nrec + 2);
if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) {
xdl_free(rchg);
xdl_free(rhash);
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) {
xdl_free(rindex);
xdl_free(rchg);
xdl_free(rhash);
xdl_free(recs);
xdl_cha_free(&xdf->rcha);
return -1;
}
xdf->nrec = nrec;
xdf->recs = recs;
xdf->hbits = hbits;
xdf->rhash = rhash;
xdf->rchg = rchg + 1;
xdf->rindex = rindex;
xdf->nreff = 0;
xdf->ha = ha;
xdf->dstart = 0;
xdf->dend = nrec - 1;
return 0;
}
static void xdl_free_ctx(xdfile_t *xdf) {
xdl_free(xdf->rhash);
xdl_free(xdf->rindex);
xdl_free(xdf->rchg - 1);
xdl_free(xdf->ha);
xdl_free(xdf->recs);
xdl_cha_free(&xdf->rcha);
}
static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
long r, rdis0, rpdis0, rdis1, rpdis1;
/*
* Limits the window the is examined during the similar-lines
* scan. The loops below stops when dis[i - r] == 1 (line that
* has no match), but there are corner cases where the loop
* proceed all the way to the extremities by causing huge
* performance penalties in case of big files.
*/
if (i - s > XDL_SIMSCAN_WINDOWN)
s = i - XDL_SIMSCAN_WINDOWN;
if (e - i > XDL_SIMSCAN_WINDOWN)
e = i + XDL_SIMSCAN_WINDOWN;
/*
* Scans the lines before 'i' to find a run of lines that either
* have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
* Note that we always call this function with dis[i] > 1, so the
* current line (i) is already a multimatch line.
*/
for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
if (!dis[i - r])
rdis0++;
else if (dis[i - r] == 2)
rpdis0++;
else
break;
}
/*
* If the run before the line 'i' found only multimatch lines, we
* return 0 and hence we don't make the current line (i) discarded.
* We want to discard multimatch lines only when they appear in the
* middle of runs with nomatch lines (dis[j] == 0).
*/
if (rdis0 == 0)
return 0;
for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
if (!dis[i + r])
rdis1++;
else if (dis[i + r] == 2)
rpdis1++;
else
break;
}
/*
* If the run after the line 'i' found only multimatch lines, we
* return 0 and hence we don't make the current line (i) discarded.
*/
if (rdis1 == 0)
return 0;
rdis1 += rdis0;
rpdis1 += rpdis0;
return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
}
/*
* Try to reduce the problem complexity, discard records that have no
* matches on the other file. Also, lines that have multiple matches
* might be potentially discarded if they happear in a run of discardable.
*/
static int xdl_cleanup_records(xdfile_t *xdf1, xdfile_t *xdf2) {
long i, nm, rhi, nreff, mlim;
unsigned long hav;
xrecord_t **recs;
xrecord_t *rec;
char *dis, *dis1, *dis2;
if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
return -1;
}
memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
dis1 = dis;
dis2 = dis1 + xdf1->nrec + 1;
if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
mlim = XDL_MAX_EQLIMIT;
for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
hav = (*recs)->ha;
rhi = (long) XDL_HASHLONG(hav, xdf2->hbits);
for (nm = 0, rec = xdf2->rhash[rhi]; rec; rec = rec->next)
if (rec->ha == hav && ++nm == mlim)
break;
dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
}
if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
mlim = XDL_MAX_EQLIMIT;
for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
hav = (*recs)->ha;
rhi = (long) XDL_HASHLONG(hav, xdf1->hbits);
for (nm = 0, rec = xdf1->rhash[rhi]; rec; rec = rec->next)
if (rec->ha == hav && ++nm == mlim)
break;
dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
}
for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
i <= xdf1->dend; i++, recs++) {
if (dis1[i] == 1 ||
(dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
xdf1->rindex[nreff] = i;
xdf1->ha[nreff] = (*recs)->ha;
nreff++;
} else
xdf1->rchg[i] = 1;
}
xdf1->nreff = nreff;
for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
i <= xdf2->dend; i++, recs++) {
if (dis2[i] == 1 ||
(dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
xdf2->rindex[nreff] = i;
xdf2->ha[nreff] = (*recs)->ha;
nreff++;
} else
xdf2->rchg[i] = 1;
}
xdf2->nreff = nreff;
xdl_free(dis);
return 0;
}
/*
* Early trim initial and terminal matching records.
*/
static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
long i, lim;
xrecord_t **recs1, **recs2;
recs1 = xdf1->recs;
recs2 = xdf2->recs;
for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
i++, recs1++, recs2++)
if ((*recs1)->ha != (*recs2)->ha)
break;
xdf1->dstart = xdf2->dstart = i;
recs1 = xdf1->recs + xdf1->nrec - 1;
recs2 = xdf2->recs + xdf2->nrec - 1;
for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
if ((*recs1)->ha != (*recs2)->ha)
break;
xdf1->dend = xdf1->nrec - i - 1;
xdf2->dend = xdf2->nrec - i - 1;
return 0;
}
static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2) {
if (xdl_trim_ends(xdf1, xdf2) < 0 ||
xdl_cleanup_records(xdf1, xdf2) < 0) {
return -1;
}
return 0;
}
int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe) {
long enl1, enl2;
xdlclassifier_t cf;
enl1 = xdl_guess_lines(mf1) + 1;
enl2 = xdl_guess_lines(mf2) + 1;
if (xdl_init_classifier(&cf, enl1 + enl2 + 1) < 0) {
return -1;
}
if (xdl_prepare_ctx(mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
xdl_free_classifier(&cf);
return -1;
}
if (xdl_prepare_ctx(mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
xdl_free_ctx(&xe->xdf1);
xdl_free_classifier(&cf);
return -1;
}
xdl_free_classifier(&cf);
if (xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
xdl_free_ctx(&xe->xdf2);
xdl_free_ctx(&xe->xdf1);
return -1;
}
return 0;
}
void xdl_free_env(xdfenv_t *xe) {
xdl_free_ctx(&xe->xdf2);
xdl_free_ctx(&xe->xdf1);
}

View File

@@ -0,0 +1,35 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XPREPARE_H)
#define XPREPARE_H
int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe);
void xdl_free_env(xdfenv_t *xe);
#endif /* #if !defined(XPREPARE_H) */

View File

@@ -0,0 +1,381 @@
/*
* xrabdiff by Davide Libenzi (Rabin's polynomial fingerprint based delta generator)
* Copyright (C) 2006 Davide Libenzi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*
* Hints, ideas and code for the implementation came from:
*
* Rabin's original paper: http://www.xmailserver.org/rabin.pdf
* Chan & Lu's paper: http://www.xmailserver.org/rabin_impl.pdf
* Broder's paper: http://www.xmailserver.org/rabin_apps.pdf
* LBFS source code: http://www.fs.net/sfswww/lbfs/
* Geert Bosch's post: http://marc.theaimsgroup.com/?l=git&m=114565424620771&w=2
*
*/
#include "xinclude.h"
#if !defined(XRABPLY_TYPE32) && !defined(XRABPLY_TYPE64)
#define XRABPLY_TYPE64 long long
#define XV64(v) ((xply_word) v ## ULL)
#endif
#include "xrabply.c"
#define XRAB_SLIDE(v, c) do { \
if (++wpos == XRAB_WNDSIZE) wpos = 0; \
v ^= U[wbuf[wpos]]; \
wbuf[wpos] = (c); \
v = ((v << 8) | (c)) ^ T[v >> XRAB_SHIFT]; \
} while (0)
#define XRAB_MINCPYSIZE 12
#define XRAB_WBITS (sizeof(xply_word) * 8)
typedef struct s_xrabctx {
long idxsize;
long *idx;
unsigned char const *data;
long size;
} xrabctx_t;
typedef struct s_xrabcpyi {
long src;
long tgt;
long len;
} xrabcpyi_t;
typedef struct s_xrabcpyi_arena {
long cnt, size;
xrabcpyi_t *acpy;
} xrabcpyi_arena_t;
static void xrab_init_cpyarena(xrabcpyi_arena_t *aca) {
aca->cnt = aca->size = 0;
aca->acpy = NULL;
}
static void xrab_free_cpyarena(xrabcpyi_arena_t *aca) {
xdl_free(aca->acpy);
}
static int xrab_add_cpy(xrabcpyi_arena_t *aca, xrabcpyi_t const *rcpy) {
long size;
xrabcpyi_t *acpy;
if (aca->cnt >= aca->size) {
size = 2 * aca->size + 1024;
if ((acpy = (xrabcpyi_t *)
xdl_realloc(aca->acpy, size * sizeof(xrabcpyi_t))) == NULL)
return -1;
aca->acpy = acpy;
aca->size = size;
}
aca->acpy[aca->cnt++] = *rcpy;
return 0;
}
static long xrab_cmnseq(unsigned char const *data, long start, long size) {
unsigned char ch = data[start];
unsigned char const *ptr, *top;
for (ptr = data + start + 1, top = data + size; ptr < top && ch == *ptr; ptr++);
return (long) (ptr - (data + start + 1));
}
static int xrab_build_ctx(unsigned char const *data, long size, xrabctx_t *ctx) {
long i, isize, idxsize, seq, wpos = 0;
xply_word fp = 0, mask;
unsigned char ch;
unsigned char const *ptr, *eot;
long *idx;
unsigned char wbuf[XRAB_WNDSIZE];
long maxoffs[256];
long maxseq[256];
xply_word maxfp[256];
memset(wbuf, 0, sizeof(wbuf));
memset(maxseq, 0, sizeof(maxseq));
isize = 2 * (size / XRAB_WNDSIZE);
for (idxsize = 1; idxsize < isize; idxsize <<= 1);
mask = (xply_word) (idxsize - 1);
if ((idx = (long *) xdl_malloc(idxsize * sizeof(long))) == NULL)
return -1;
memset(idx, 0, idxsize * sizeof(long));
for (i = 0; i + XRAB_WNDSIZE < size; i += XRAB_WNDSIZE) {
/*
* Generate a brand new hash for the current window. Here we could
* try to perform pseudo-loop unroll by 4 blocks if necessary, and
* if we force XRAB_WNDSIZE to be a multiple of 4, we could reduce
* the branch occurence inside XRAB_SLIDE by a factor of 4.
*/
for (ptr = data + i, eot = ptr + XRAB_WNDSIZE; ptr < eot; ptr++)
XRAB_SLIDE(fp, *ptr);
/*
* Try to scan for single value scans, and store them in the
* array according to the longest one. Before we do a fast check
* to avoid calling xrab_cmnseq() when not necessary.
*/
if ((ch = data[i]) == data[i + XRAB_WNDSIZE - 1] &&
(seq = xrab_cmnseq(data, i, size)) > XRAB_WNDSIZE &&
seq > maxseq[ch]) {
maxseq[ch] = seq;
maxfp[ch] = fp;
maxoffs[ch] = i + XRAB_WNDSIZE;
seq = (seq / XRAB_WNDSIZE) * XRAB_WNDSIZE;
i += seq - XRAB_WNDSIZE;
} else
idx[fp & mask] = i + XRAB_WNDSIZE;
}
/*
* Restore back the logest sequences by overwriting target hash buckets.
*/
for (i = 0; i < 256; i++)
if (maxseq[i])
idx[maxfp[i] & mask] = maxoffs[i];
ctx->idxsize = idxsize;
ctx->idx = idx;
ctx->data = data;
ctx->size = size;
return 0;
}
static void xrab_free_ctx(xrabctx_t *ctx) {
xdl_free(ctx->idx);
}
static int xrab_diff(unsigned char const *data, long size, xrabctx_t *ctx,
xrabcpyi_arena_t *aca) {
long i, offs, ssize, src, tgt, esrc, etgt, wpos = 0;
xply_word fp = 0, mask;
long const *idx;
unsigned char const *sdata;
xrabcpyi_t rcpy;
unsigned char wbuf[XRAB_WNDSIZE];
xrab_init_cpyarena(aca);
memset(wbuf, 0, sizeof(wbuf));
for (i = 0; i < XRAB_WNDSIZE - 1 && i < size; i++)
XRAB_SLIDE(fp, data[i]);
idx = ctx->idx;
sdata = ctx->data;
ssize = ctx->size;
mask = (xply_word) (ctx->idxsize - 1);
while (i < size) {
unsigned char ch = data[i++];
XRAB_SLIDE(fp, ch);
offs = idx[fp & mask];
/*
* Fast check here to probabilistically reduce false positives
* that would trigger the slow path below.
*/
if (offs == 0 || ch != sdata[offs - 1])
continue;
/*
* Stretch the match both sides as far as possible.
*/
src = offs - 1;
tgt = i - 1;
for (; tgt > 0 && src > 0 && data[tgt - 1] == sdata[src - 1];
tgt--, src--);
esrc = offs;
etgt = i;
for (; etgt < size && esrc < ssize && data[etgt] == sdata[esrc];
etgt++, esrc++);
/*
* Avoid considering copies smaller than the XRAB_MINCPYSIZE
* threshold.
*/
if (etgt - tgt >= XRAB_MINCPYSIZE) {
rcpy.src = src;
rcpy.tgt = tgt;
rcpy.len = etgt - tgt;
if (xrab_add_cpy(aca, &rcpy) < 0) {
xrab_free_cpyarena(aca);
return -1;
}
/*
* Fill up the new window and exit with 'i' properly set on exit.
*/
for (i = etgt - XRAB_WNDSIZE; i < etgt; i++)
XRAB_SLIDE(fp, data[i]);
}
}
return 0;
}
static int xrab_tune_cpyarena(unsigned char const *data, long size, xrabctx_t *ctx,
xrabcpyi_arena_t *aca) {
long i, cpos;
xrabcpyi_t *rcpy;
for (cpos = size, i = aca->cnt - 1; i >= 0; i--) {
rcpy = aca->acpy + i;
if (rcpy->tgt >= cpos)
rcpy->len = 0;
else if (rcpy->tgt + rcpy->len > cpos) {
if ((rcpy->len = cpos - rcpy->tgt) >= XRAB_MINCPYSIZE)
cpos = rcpy->tgt;
else
rcpy->len = 0;
} else
cpos = rcpy->tgt;
}
return 0;
}
int xdl_rabdiff_mb(mmbuffer_t *mmb1, mmbuffer_t *mmb2, xdemitcb_t *ecb) {
long i, cpos, size;
unsigned long fp;
xrabcpyi_t *rcpy;
xrabctx_t ctx;
xrabcpyi_arena_t aca;
mmbuffer_t mb[2];
unsigned char cpybuf[32];
fp = xdl_mmb_adler32(mmb1);
if (xrab_build_ctx((unsigned char const *) mmb1->ptr, mmb1->size,
&ctx) < 0)
return -1;
if (xrab_diff((unsigned char const *) mmb2->ptr, mmb2->size, &ctx,
&aca) < 0) {
xrab_free_ctx(&ctx);
return -1;
}
xrab_tune_cpyarena((unsigned char const *) mmb2->ptr, mmb2->size, &ctx,
&aca);
xrab_free_ctx(&ctx);
/*
* Prepare and emit the binary patch file header. It will be used
* to verify that that file being patched matches in size and fingerprint
* the one that generated the patch.
*/
size = mmb1->size;
XDL_LE32_PUT(cpybuf, fp);
XDL_LE32_PUT(cpybuf + 4, size);
mb[0].ptr = (char *) cpybuf;
mb[0].size = 4 + 4;
if (ecb->outf(ecb->priv, mb, 1) < 0) {
xrab_free_cpyarena(&aca);
return -1;
}
for (cpos = 0, i = 0; i < aca.cnt; i++) {
rcpy = aca.acpy + i;
if (rcpy->len == 0)
continue;
if (cpos < rcpy->tgt) {
size = rcpy->tgt - cpos;
if (size > 255) {
cpybuf[0] = XDL_BDOP_INSB;
XDL_LE32_PUT(cpybuf + 1, size);
mb[0].ptr = (char *) cpybuf;
mb[0].size = XDL_INSBOP_SIZE;
} else {
cpybuf[0] = XDL_BDOP_INS;
cpybuf[1] = (unsigned char) size;
mb[0].ptr = (char *) cpybuf;
mb[0].size = 2;
}
mb[1].ptr = mmb2->ptr + cpos;
mb[1].size = size;
if (ecb->outf(ecb->priv, mb, 2) < 0) {
xrab_free_cpyarena(&aca);
return -1;
}
cpos = rcpy->tgt;
}
cpybuf[0] = XDL_BDOP_CPY;
XDL_LE32_PUT(cpybuf + 1, rcpy->src);
XDL_LE32_PUT(cpybuf + 5, rcpy->len);
mb[0].ptr = (char *) cpybuf;
mb[0].size = XDL_COPYOP_SIZE;
if (ecb->outf(ecb->priv, mb, 1) < 0) {
xrab_free_cpyarena(&aca);
return -1;
}
cpos += rcpy->len;
}
xrab_free_cpyarena(&aca);
if (cpos < mmb2->size) {
size = mmb2->size - cpos;
if (size > 255) {
cpybuf[0] = XDL_BDOP_INSB;
XDL_LE32_PUT(cpybuf + 1, size);
mb[0].ptr = (char *) cpybuf;
mb[0].size = XDL_INSBOP_SIZE;
} else {
cpybuf[0] = XDL_BDOP_INS;
cpybuf[1] = (unsigned char) size;
mb[0].ptr = (char *) cpybuf;
mb[0].size = 2;
}
mb[1].ptr = mmb2->ptr + cpos;
mb[1].size = size;
if (ecb->outf(ecb->priv, mb, 2) < 0)
return -1;
}
return 0;
}
int xdl_rabdiff(mmfile_t *mmf1, mmfile_t *mmf2, xdemitcb_t *ecb) {
mmbuffer_t mmb1, mmb2;
if (!xdl_mmfile_iscompact(mmf1) || !xdl_mmfile_iscompact(mmf2))
return -1;
if ((mmb1.ptr = (char *) xdl_mmfile_first(mmf1, &mmb1.size)) == NULL)
mmb1.size = 0;
if ((mmb2.ptr = (char *) xdl_mmfile_first(mmf2, &mmb2.size)) == NULL)
mmb2.size = 0;
return xdl_rabdiff_mb(&mmb1, &mmb2, ecb);
}

View File

@@ -0,0 +1,298 @@
#if defined(XRABPLY_TYPE64)
#if !defined(XV64)
#define XV64(v) ((xply_word) v ## ULL)
#endif
#define XRAB_ROOTPOLY XV64(0x36f7381af4d70d33)
#define XRAB_SHIFT 53
#define XRAB_WNDSIZE 20
typedef unsigned XRABPLY_TYPE64 xply_word;
static const xply_word T[256] = {
XV64(0x0), XV64(0x36f7381af4d70d33), XV64(0x5b19482f1d791755), XV64(0x6dee7035e9ae1a66),
XV64(0x80c5a844ce252399), XV64(0xb632905e3af22eaa), XV64(0xdbdce06bd35c34cc), XV64(0xed2bd871278b39ff),
XV64(0x18b50899c4a4732), XV64(0x377c6893689d4a01), XV64(0x5a9218a681335067), XV64(0x6c6520bc75e45d54),
XV64(0x814ef8cd526f64ab), XV64(0xb7b9c0d7a6b86998), XV64(0xda57b0e24f1673fe), XV64(0xeca088f8bbc17ecd),
XV64(0x316a11338948e64), XV64(0x35e19909cc438357), XV64(0x580fe93c25ed9931), XV64(0x6ef8d126d13a9402),
XV64(0x83d30957f6b1adfd), XV64(0xb524314d0266a0ce), XV64(0xd8ca4178ebc8baa8), XV64(0xee3d79621f1fb79b),
XV64(0x29df19aa4dec956), XV64(0x346ac9805009c465), XV64(0x5984b9b5b9a7de03), XV64(0x6f7381af4d70d330),
XV64(0x825859de6afbeacf), XV64(0xb4af61c49e2ce7fc), XV64(0xd94111f17782fd9a), XV64(0xefb629eb8355f0a9),
XV64(0x62d422671291cc8), XV64(0x30da7a3c85fe11fb), XV64(0x5d340a096c500b9d), XV64(0x6bc33213988706ae),
XV64(0x86e8ea62bf0c3f51), XV64(0xb01fd2784bdb3262), XV64(0xddf1a24da2752804), XV64(0xeb069a5756a22537),
XV64(0x7a612afed635bfa), XV64(0x31512ab519b456c9), XV64(0x5cbf5a80f01a4caf), XV64(0x6a48629a04cd419c),
XV64(0x8763baeb23467863), XV64(0xb19482f1d7917550), XV64(0xdc7af2c43e3f6f36), XV64(0xea8dcadecae86205),
XV64(0x53be33549bd92ac), XV64(0x33ccdb2fbd6a9f9f), XV64(0x5e22ab1a54c485f9), XV64(0x68d59300a01388ca),
XV64(0x85fe4b718798b135), XV64(0xb309736b734fbc06), XV64(0xdee7035e9ae1a660), XV64(0xe8103b446e36ab53),
XV64(0x4b0b3bcd5f7d59e), XV64(0x32478ba62120d8ad), XV64(0x5fa9fb93c88ec2cb), XV64(0x695ec3893c59cff8),
XV64(0x84751bf81bd2f607), XV64(0xb28223e2ef05fb34), XV64(0xdf6c53d706abe152), XV64(0xe99b6bcdf27cec61),
XV64(0xc5a844ce2523990), XV64(0x3aadbc56168534a3), XV64(0x5743cc63ff2b2ec5), XV64(0x61b4f4790bfc23f6),
XV64(0x8c9f2c082c771a09), XV64(0xba681412d8a0173a), XV64(0xd7866427310e0d5c), XV64(0xe1715c3dc5d9006f),
XV64(0xdd1d4c57e187ea2), XV64(0x3b26ecdf8acf7391), XV64(0x56c89cea636169f7), XV64(0x603fa4f097b664c4),
XV64(0x8d147c81b03d5d3b), XV64(0xbbe3449b44ea5008), XV64(0xd60d34aead444a6e), XV64(0xe0fa0cb45993475d),
XV64(0xf4c255fdac6b7f4), XV64(0x39bb1d452e11bac7), XV64(0x54556d70c7bfa0a1), XV64(0x62a2556a3368ad92),
XV64(0x8f898d1b14e3946d), XV64(0xb97eb501e034995e), XV64(0xd490c534099a8338), XV64(0xe267fd2efd4d8e0b),
XV64(0xec775d6468cf0c6), XV64(0x38304dccb25bfdf5), XV64(0x55de3df95bf5e793), XV64(0x632905e3af22eaa0),
XV64(0x8e02dd9288a9d35f), XV64(0xb8f5e5887c7ede6c), XV64(0xd51b95bd95d0c40a), XV64(0xe3ecada76107c939),
XV64(0xa77c66a937b2558), XV64(0x3c80fe7067ac286b), XV64(0x516e8e458e02320d), XV64(0x6799b65f7ad53f3e),
XV64(0x8ab26e2e5d5e06c1), XV64(0xbc455634a9890bf2), XV64(0xd1ab260140271194), XV64(0xe75c1e1bb4f01ca7),
XV64(0xbfc96e30f31626a), XV64(0x3d0baef9fbe66f59), XV64(0x50e5decc1248753f), XV64(0x6612e6d6e69f780c),
XV64(0x8b393ea7c11441f3), XV64(0xbdce06bd35c34cc0), XV64(0xd0207688dc6d56a6), XV64(0xe6d74e9228ba5b95),
XV64(0x9616779abefab3c), XV64(0x3f965f635f38a60f), XV64(0x52782f56b696bc69), XV64(0x648f174c4241b15a),
XV64(0x89a4cf3d65ca88a5), XV64(0xbf53f727911d8596), XV64(0xd2bd871278b39ff0), XV64(0xe44abf088c6492c3),
XV64(0x8ea37f037a5ec0e), XV64(0x3e1d0feac372e13d), XV64(0x53f37fdf2adcfb5b), XV64(0x650447c5de0bf668),
XV64(0x882f9fb4f980cf97), XV64(0xbed8a7ae0d57c2a4), XV64(0xd336d79be4f9d8c2), XV64(0xe5c1ef81102ed5f1),
XV64(0x18b50899c4a47320), XV64(0x2e42308330737e13), XV64(0x43ac40b6d9dd6475), XV64(0x755b78ac2d0a6946),
XV64(0x9870a0dd0a8150b9), XV64(0xae8798c7fe565d8a), XV64(0xc369e8f217f847ec), XV64(0xf59ed0e8e32f4adf),
XV64(0x193e581058ee3412), XV64(0x2fc9600aac393921), XV64(0x4227103f45972347), XV64(0x74d02825b1402e74),
XV64(0x99fbf05496cb178b), XV64(0xaf0cc84e621c1ab8), XV64(0xc2e2b87b8bb200de), XV64(0xf41580617f650ded),
XV64(0x1ba3a98afc30fd44), XV64(0x2d54919008e7f077), XV64(0x40bae1a5e149ea11), XV64(0x764dd9bf159ee722),
XV64(0x9b6601ce3215dedd), XV64(0xad9139d4c6c2d3ee), XV64(0xc07f49e12f6cc988), XV64(0xf68871fbdbbbc4bb),
XV64(0x1a28f903607aba76), XV64(0x2cdfc11994adb745), XV64(0x4131b12c7d03ad23), XV64(0x77c6893689d4a010),
XV64(0x9aed5147ae5f99ef), XV64(0xac1a695d5a8894dc), XV64(0xc1f41968b3268eba), XV64(0xf703217247f18389),
XV64(0x1e984abfb58d6fe8), XV64(0x286f72a5415a62db), XV64(0x45810290a8f478bd), XV64(0x73763a8a5c23758e),
XV64(0x9e5de2fb7ba84c71), XV64(0xa8aadae18f7f4142), XV64(0xc544aad466d15b24), XV64(0xf3b392ce92065617),
XV64(0x1f131a3629c728da), XV64(0x29e4222cdd1025e9), XV64(0x440a521934be3f8f), XV64(0x72fd6a03c06932bc),
XV64(0x9fd6b272e7e20b43), XV64(0xa9218a6813350670), XV64(0xc4cffa5dfa9b1c16), XV64(0xf238c2470e4c1125),
XV64(0x1d8eebac8d19e18c), XV64(0x2b79d3b679ceecbf), XV64(0x4697a3839060f6d9), XV64(0x70609b9964b7fbea),
XV64(0x9d4b43e8433cc215), XV64(0xabbc7bf2b7ebcf26), XV64(0xc6520bc75e45d540), XV64(0xf0a533ddaa92d873),
XV64(0x1c05bb251153a6be), XV64(0x2af2833fe584ab8d), XV64(0x471cf30a0c2ab1eb), XV64(0x71ebcb10f8fdbcd8),
XV64(0x9cc01361df768527), XV64(0xaa372b7b2ba18814), XV64(0xc7d95b4ec20f9272), XV64(0xf12e635436d89f41),
XV64(0x14ef8cd526f64ab0), XV64(0x2218b4cfd2214783), XV64(0x4ff6c4fa3b8f5de5), XV64(0x7901fce0cf5850d6),
XV64(0x942a2491e8d36929), XV64(0xa2dd1c8b1c04641a), XV64(0xcf336cbef5aa7e7c), XV64(0xf9c454a4017d734f),
XV64(0x1564dc5cbabc0d82), XV64(0x2393e4464e6b00b1), XV64(0x4e7d9473a7c51ad7), XV64(0x788aac69531217e4),
XV64(0x95a1741874992e1b), XV64(0xa3564c02804e2328), XV64(0xceb83c3769e0394e), XV64(0xf84f042d9d37347d),
XV64(0x17f92dc61e62c4d4), XV64(0x210e15dceab5c9e7), XV64(0x4ce065e9031bd381), XV64(0x7a175df3f7ccdeb2),
XV64(0x973c8582d047e74d), XV64(0xa1cbbd982490ea7e), XV64(0xcc25cdadcd3ef018), XV64(0xfad2f5b739e9fd2b),
XV64(0x16727d4f822883e6), XV64(0x2085455576ff8ed5), XV64(0x4d6b35609f5194b3), XV64(0x7b9c0d7a6b869980),
XV64(0x96b7d50b4c0da07f), XV64(0xa040ed11b8daad4c), XV64(0xcdae9d245174b72a), XV64(0xfb59a53ea5a3ba19),
XV64(0x12c2cef357df5678), XV64(0x2435f6e9a3085b4b), XV64(0x49db86dc4aa6412d), XV64(0x7f2cbec6be714c1e),
XV64(0x920766b799fa75e1), XV64(0xa4f05ead6d2d78d2), XV64(0xc91e2e98848362b4), XV64(0xffe9168270546f87),
XV64(0x13499e7acb95114a), XV64(0x25bea6603f421c79), XV64(0x4850d655d6ec061f), XV64(0x7ea7ee4f223b0b2c),
XV64(0x938c363e05b032d3), XV64(0xa57b0e24f1673fe0), XV64(0xc8957e1118c92586), XV64(0xfe62460bec1e28b5),
XV64(0x11d46fe06f4bd81c), XV64(0x272357fa9b9cd52f), XV64(0x4acd27cf7232cf49), XV64(0x7c3a1fd586e5c27a),
XV64(0x9111c7a4a16efb85), XV64(0xa7e6ffbe55b9f6b6), XV64(0xca088f8bbc17ecd0), XV64(0xfcffb79148c0e1e3),
XV64(0x105f3f69f3019f2e), XV64(0x26a8077307d6921d), XV64(0x4b467746ee78887b), XV64(0x7db14f5c1aaf8548),
XV64(0x909a972d3d24bcb7), XV64(0xa66daf37c9f3b184), XV64(0xcb83df02205dabe2), XV64(0xfd74e718d48aa6d1)
};
static const xply_word U[256] = {
XV64(0x0), XV64(0x1c3eb44b122426b2), XV64(0xe8a508cd09f4057), XV64(0x12b4e4c7c2bb66e5),
XV64(0x1d14a119a13e80ae), XV64(0x12a1552b31aa61c), XV64(0x139ef19571a1c0f9), XV64(0xfa045de6385e64b),
XV64(0xcde7a29b6aa0c6f), XV64(0x10e0ce62a48e2add), XV64(0x2542aa566354c38), XV64(0x1e6a9eee74116a8a),
XV64(0x11cadb3017948cc1), XV64(0xdf46f7b05b0aa73), XV64(0x1f408bbcc70bcc96), XV64(0x37e3ff7d52fea24),
XV64(0x19bcf4536d5418de), XV64(0x58240187f703e6c), XV64(0x1736a4dfbdcb5889), XV64(0xb081094afef7e3b),
XV64(0x4a8554acc6a9870), XV64(0x1896e101de4ebec2), XV64(0xa2205c61cf5d827), XV64(0x161cb18d0ed1fe95),
XV64(0x15628e7adbfe14b1), XV64(0x95c3a31c9da3203), XV64(0x1be8def60b6154e6), XV64(0x7d66abd19457254),
XV64(0x8762f637ac0941f), XV64(0x14489b2868e4b2ad), XV64(0x6fc7fefaa5fd448), XV64(0x1ac2cba4b87bf2fa),
XV64(0x58ed0bc2e7f3c8f), XV64(0x19b064f73c5b1a3d), XV64(0xb048030fee07cd8), XV64(0x173a347becc45a6a),
XV64(0x189a71a58f41bc21), XV64(0x4a4c5ee9d659a93), XV64(0x161021295fdefc76), XV64(0xa2e95624dfadac4),
XV64(0x950aa9598d530e0), XV64(0x156e1ede8af11652), XV64(0x7dafa19484a70b7), XV64(0x1be44e525a6e5605),
XV64(0x14440b8c39ebb04e), XV64(0x87abfc72bcf96fc), XV64(0x1ace5b00e974f019), XV64(0x6f0ef4bfb50d6ab),
XV64(0x1c3224ef432b2451), XV64(0xc90a4510f02e3), XV64(0x12b8746393b46406), XV64(0xe86c028819042b4),
XV64(0x12685f6e215a4ff), XV64(0x1d1831bdf031824d), XV64(0xfacd57a328ae4a8), XV64(0x1392613120aec21a),
XV64(0x10ec5ec6f581283e), XV64(0xcd2ea8de7a50e8c), XV64(0x1e660e4a251e6869), XV64(0x258ba01373a4edb),
XV64(0xdf8ffdf54bfa890), XV64(0x11c64b94469b8e22), XV64(0x372af538420e8c7), XV64(0x1f4c1b189604ce75),
XV64(0xb1da1785cfe791e), XV64(0x172315334eda5fac), XV64(0x597f1f48c613949), XV64(0x19a945bf9e451ffb),
XV64(0x16090061fdc0f9b0), XV64(0xa37b42aefe4df02), XV64(0x188350ed2d5fb9e7), XV64(0x4bde4a63f7b9f55),
XV64(0x7c3db51ea547571), XV64(0x1bfd6f1af87053c3), XV64(0x9498bdd3acb3526), XV64(0x15773f9628ef1394),
XV64(0x1ad77a484b6af5df), XV64(0x6e9ce03594ed36d), XV64(0x145d2ac49bf5b588), XV64(0x8639e8f89d1933a),
XV64(0x12a1552b31aa61c0), XV64(0xe9fe160238e4772), XV64(0x1c2b05a7e1352197), XV64(0x15b1ecf3110725),
XV64(0xfb5f4329094e16e), XV64(0x138b407982b0c7dc), XV64(0x13fa4be400ba139), XV64(0x1d0110f5522f878b),
XV64(0x1e7f2f0287006daf), XV64(0x2419b4995244b1d), XV64(0x10f57f8e579f2df8), XV64(0xccbcbc545bb0b4a),
XV64(0x36b8e1b263eed01), XV64(0x1f553a50341acbb3), XV64(0xde1de97f6a1ad56), XV64(0x11df6adce4858be4),
XV64(0xe9371c472814591), XV64(0x12adc58f60a56323), XV64(0x192148a21e05c6), XV64(0x1c279503b03a2374),
XV64(0x1387d0ddd3bfc53f), XV64(0xfb96496c19be38d), XV64(0x1d0d805103208568), XV64(0x133341a1104a3da),
XV64(0x24d0bedc42b49fe), XV64(0x1e73bfa6d60f6f4c), XV64(0xcc75b6114b409a9), XV64(0x10f9ef2a06902f1b),
XV64(0x1f59aaf46515c950), XV64(0x3671ebf7731efe2), XV64(0x11d3fa78b58a8907), XV64(0xded4e33a7aeafb5),
XV64(0x172f85971fd55d4f), XV64(0xb1131dc0df17bfd), XV64(0x19a5d51bcf4a1d18), XV64(0x59b6150dd6e3baa),
XV64(0xa3b248ebeebdde1), XV64(0x160590c5accffb53), XV64(0x4b174026e749db6), XV64(0x188fc0497c50bb04),
XV64(0x1bf1ffbea97f5120), XV64(0x7cf4bf5bb5b7792), XV64(0x157baf3279e01177), XV64(0x9451b796bc437c5),
XV64(0x6e55ea70841d18e), XV64(0x1adbeaec1a65f73c), XV64(0x86f0e2bd8de91d9), XV64(0x1451ba60cafab76b),
XV64(0x163b42f0b9fcf23c), XV64(0xa05f6bbabd8d48e), XV64(0x18b1127c6963b26b), XV64(0x48fa6377b4794d9),
XV64(0xb2fe3e918c27292), XV64(0x171157a20ae65420), XV64(0x5a5b365c85d32c5), XV64(0x199b072eda791477),
XV64(0x1ae538d90f56fe53), XV64(0x6db8c921d72d8e1), XV64(0x146f6855dfc9be04), XV64(0x851dc1ecded98b6),
XV64(0x7f199c0ae687efd), XV64(0x1bcf2d8bbc4c584f), XV64(0x97bc94c7ef73eaa), XV64(0x15457d076cd31818),
XV64(0xf87b6a3d4a8eae2), XV64(0x13b902e8c68ccc50), XV64(0x10de62f0437aab5), XV64(0x1d33526416138c07),
XV64(0x129317ba75966a4c), XV64(0xeada3f167b24cfe), XV64(0x1c194736a5092a1b), XV64(0x27f37db72d0ca9),
XV64(0x359cc8a6202e68d), XV64(0x1f6778c17026c03f), XV64(0xdd39c06b29da6da), XV64(0x11ed284da0b98068),
XV64(0x1e4d6d93c33c6623), XV64(0x273d9d8d1184091), XV64(0x10c73d1f13a32674), XV64(0xcf98954018700c6),
XV64(0x13b5924c9783ceb3), XV64(0xf8b260785a7e801), XV64(0x1d3fc2c0471c8ee4), XV64(0x101768b5538a856),
XV64(0xea1335536bd4e1d), XV64(0x129f871e249968af), XV64(0x2b63d9e6220e4a), XV64(0x1c15d792f40628f8),
XV64(0x1f6be8652129c2dc), XV64(0x3555c2e330de46e), XV64(0x11e1b8e9f1b6828b), XV64(0xddf0ca2e392a439),
XV64(0x27f497c80174272), XV64(0x1e41fd37923364c0), XV64(0xcf519f050880225), XV64(0x10cbadbb42ac2497),
XV64(0xa09661ffad7d66d), XV64(0x1637d254e8f3f0df), XV64(0x48336932a48963a), XV64(0x18bd82d8386cb088),
XV64(0x171dc7065be956c3), XV64(0xb23734d49cd7071), XV64(0x1997978a8b761694), XV64(0x5a923c199523026),
XV64(0x6d71c364c7dda02), XV64(0x1ae9a87d5e59fcb0), XV64(0x85d4cba9ce29a55), XV64(0x1463f8f18ec6bce7),
XV64(0x1bc3bd2fed435aac), XV64(0x7fd0964ff677c1e), XV64(0x1549eda33ddc1afb), XV64(0x97759e82ff83c49),
XV64(0x1d26e388e5028b22), XV64(0x11857c3f726ad90), XV64(0x13acb304359dcb75), XV64(0xf92074f27b9edc7),
XV64(0x324291443c0b8c), XV64(0x1c0cf6da56182d3e), XV64(0xeb8121d94a34bdb), XV64(0x1286a65686876d69),
XV64(0x11f899a153a8874d), XV64(0xdc62dea418ca1ff), XV64(0x1f72c92d8337c71a), XV64(0x34c7d669113e1a8),
XV64(0xcec38b8f29607e3), XV64(0x10d28cf3e0b22151), XV64(0x2666834220947b4), XV64(0x1e58dc7f302d6106),
XV64(0x49a17db885693fc), XV64(0x18a4a3909a72b54e), XV64(0xa10475758c9d3ab), XV64(0x162ef31c4aedf519),
XV64(0x198eb6c229681352), XV64(0x5b002893b4c35e0), XV64(0x1704e64ef9f75305), XV64(0xb3a5205ebd375b7),
XV64(0x8446df23efc9f93), XV64(0x147ad9b92cd8b921), XV64(0x6ce3d7eee63dfc4), XV64(0x1af08935fc47f976),
XV64(0x1550cceb9fc21f3d), XV64(0x96e78a08de6398f), XV64(0x1bda9c674f5d5f6a), XV64(0x7e4282c5d7979d8),
XV64(0x18a83334cb7db7ad), XV64(0x496877fd959911f), XV64(0x162263b81be2f7fa), XV64(0xa1cd7f309c6d148),
XV64(0x5bc922d6a433703), XV64(0x19822666786711b1), XV64(0xb36c2a1badc7754), XV64(0x170876eaa8f851e6),
XV64(0x1476491d7dd7bbc2), XV64(0x848fd566ff39d70), XV64(0x1afc1991ad48fb95), XV64(0x6c2addabf6cdd27),
XV64(0x962e804dce93b6c), XV64(0x155c5c4fcecd1dde), XV64(0x7e8b8880c767b3b), XV64(0x1bd60cc31e525d89),
XV64(0x114c767a629af73), XV64(0x1d2a732cb40d89c1), XV64(0xf9e97eb76b6ef24), XV64(0x13a023a06492c996),
XV64(0x1c00667e07172fdd), XV64(0x3ed2351533096f), XV64(0x128a36f2d7886f8a), XV64(0xeb482b9c5ac4938),
XV64(0xdcabd4e1083a31c), XV64(0x11f4090502a785ae), XV64(0x340edc2c01ce34b), XV64(0x1f7e5989d238c5f9),
XV64(0x10de1c57b1bd23b2), XV64(0xce0a81ca3990500), XV64(0x1e544cdb612263e5), XV64(0x26af89073064557)
};
#endif /* if defined(XRABPLY_TYPE64) */
#if defined(XRABPLY_TYPE32)
#if !defined(XV32)
#define XV32(v) ((xply_word) v ## ULL)
#endif
#define XRAB_ROOTPOLY XV32(0xabd1f37b)
#define XRAB_SHIFT 23
#define XRAB_WNDSIZE 20
typedef unsigned XRABPLY_TYPE32 xply_word;
static const xply_word T[256] = {
XV32(0x0), XV32(0xabd1f37b), XV32(0x57a3e6f6), XV32(0xfc72158d),
XV32(0x4963e97), XV32(0xaf47cdec), XV32(0x5335d861), XV32(0xf8e42b1a),
XV32(0x92c7d2e), XV32(0xa2fd8e55), XV32(0x5e8f9bd8), XV32(0xf55e68a3),
XV32(0xdba43b9), XV32(0xa66bb0c2), XV32(0x5a19a54f), XV32(0xf1c85634),
XV32(0x1258fa5c), XV32(0xb9890927), XV32(0x45fb1caa), XV32(0xee2aefd1),
XV32(0x16cec4cb), XV32(0xbd1f37b0), XV32(0x416d223d), XV32(0xeabcd146),
XV32(0x1b748772), XV32(0xb0a57409), XV32(0x4cd76184), XV32(0xe70692ff),
XV32(0x1fe2b9e5), XV32(0xb4334a9e), XV32(0x48415f13), XV32(0xe390ac68),
XV32(0x24b1f4b8), XV32(0x8f6007c3), XV32(0x7312124e), XV32(0xd8c3e135),
XV32(0x2027ca2f), XV32(0x8bf63954), XV32(0x77842cd9), XV32(0xdc55dfa2),
XV32(0x2d9d8996), XV32(0x864c7aed), XV32(0x7a3e6f60), XV32(0xd1ef9c1b),
XV32(0x290bb701), XV32(0x82da447a), XV32(0x7ea851f7), XV32(0xd579a28c),
XV32(0x36e90ee4), XV32(0x9d38fd9f), XV32(0x614ae812), XV32(0xca9b1b69),
XV32(0x327f3073), XV32(0x99aec308), XV32(0x65dcd685), XV32(0xce0d25fe),
XV32(0x3fc573ca), XV32(0x941480b1), XV32(0x6866953c), XV32(0xc3b76647),
XV32(0x3b534d5d), XV32(0x9082be26), XV32(0x6cf0abab), XV32(0xc72158d0),
XV32(0x4963e970), XV32(0xe2b21a0b), XV32(0x1ec00f86), XV32(0xb511fcfd),
XV32(0x4df5d7e7), XV32(0xe624249c), XV32(0x1a563111), XV32(0xb187c26a),
XV32(0x404f945e), XV32(0xeb9e6725), XV32(0x17ec72a8), XV32(0xbc3d81d3),
XV32(0x44d9aac9), XV32(0xef0859b2), XV32(0x137a4c3f), XV32(0xb8abbf44),
XV32(0x5b3b132c), XV32(0xf0eae057), XV32(0xc98f5da), XV32(0xa74906a1),
XV32(0x5fad2dbb), XV32(0xf47cdec0), XV32(0x80ecb4d), XV32(0xa3df3836),
XV32(0x52176e02), XV32(0xf9c69d79), XV32(0x5b488f4), XV32(0xae657b8f),
XV32(0x56815095), XV32(0xfd50a3ee), XV32(0x122b663), XV32(0xaaf34518),
XV32(0x6dd21dc8), XV32(0xc603eeb3), XV32(0x3a71fb3e), XV32(0x91a00845),
XV32(0x6944235f), XV32(0xc295d024), XV32(0x3ee7c5a9), XV32(0x953636d2),
XV32(0x64fe60e6), XV32(0xcf2f939d), XV32(0x335d8610), XV32(0x988c756b),
XV32(0x60685e71), XV32(0xcbb9ad0a), XV32(0x37cbb887), XV32(0x9c1a4bfc),
XV32(0x7f8ae794), XV32(0xd45b14ef), XV32(0x28290162), XV32(0x83f8f219),
XV32(0x7b1cd903), XV32(0xd0cd2a78), XV32(0x2cbf3ff5), XV32(0x876ecc8e),
XV32(0x76a69aba), XV32(0xdd7769c1), XV32(0x21057c4c), XV32(0x8ad48f37),
XV32(0x7230a42d), XV32(0xd9e15756), XV32(0x259342db), XV32(0x8e42b1a0),
XV32(0x3916219b), XV32(0x92c7d2e0), XV32(0x6eb5c76d), XV32(0xc5643416),
XV32(0x3d801f0c), XV32(0x9651ec77), XV32(0x6a23f9fa), XV32(0xc1f20a81),
XV32(0x303a5cb5), XV32(0x9bebafce), XV32(0x6799ba43), XV32(0xcc484938),
XV32(0x34ac6222), XV32(0x9f7d9159), XV32(0x630f84d4), XV32(0xc8de77af),
XV32(0x2b4edbc7), XV32(0x809f28bc), XV32(0x7ced3d31), XV32(0xd73cce4a),
XV32(0x2fd8e550), XV32(0x8409162b), XV32(0x787b03a6), XV32(0xd3aaf0dd),
XV32(0x2262a6e9), XV32(0x89b35592), XV32(0x75c1401f), XV32(0xde10b364),
XV32(0x26f4987e), XV32(0x8d256b05), XV32(0x71577e88), XV32(0xda868df3),
XV32(0x1da7d523), XV32(0xb6762658), XV32(0x4a0433d5), XV32(0xe1d5c0ae),
XV32(0x1931ebb4), XV32(0xb2e018cf), XV32(0x4e920d42), XV32(0xe543fe39),
XV32(0x148ba80d), XV32(0xbf5a5b76), XV32(0x43284efb), XV32(0xe8f9bd80),
XV32(0x101d969a), XV32(0xbbcc65e1), XV32(0x47be706c), XV32(0xec6f8317),
XV32(0xfff2f7f), XV32(0xa42edc04), XV32(0x585cc989), XV32(0xf38d3af2),
XV32(0xb6911e8), XV32(0xa0b8e293), XV32(0x5ccaf71e), XV32(0xf71b0465),
XV32(0x6d35251), XV32(0xad02a12a), XV32(0x5170b4a7), XV32(0xfaa147dc),
XV32(0x2456cc6), XV32(0xa9949fbd), XV32(0x55e68a30), XV32(0xfe37794b),
XV32(0x7075c8eb), XV32(0xdba43b90), XV32(0x27d62e1d), XV32(0x8c07dd66),
XV32(0x74e3f67c), XV32(0xdf320507), XV32(0x2340108a), XV32(0x8891e3f1),
XV32(0x7959b5c5), XV32(0xd28846be), XV32(0x2efa5333), XV32(0x852ba048),
XV32(0x7dcf8b52), XV32(0xd61e7829), XV32(0x2a6c6da4), XV32(0x81bd9edf),
XV32(0x622d32b7), XV32(0xc9fcc1cc), XV32(0x358ed441), XV32(0x9e5f273a),
XV32(0x66bb0c20), XV32(0xcd6aff5b), XV32(0x3118ead6), XV32(0x9ac919ad),
XV32(0x6b014f99), XV32(0xc0d0bce2), XV32(0x3ca2a96f), XV32(0x97735a14),
XV32(0x6f97710e), XV32(0xc4468275), XV32(0x383497f8), XV32(0x93e56483),
XV32(0x54c43c53), XV32(0xff15cf28), XV32(0x367daa5), XV32(0xa8b629de),
XV32(0x505202c4), XV32(0xfb83f1bf), XV32(0x7f1e432), XV32(0xac201749),
XV32(0x5de8417d), XV32(0xf639b206), XV32(0xa4ba78b), XV32(0xa19a54f0),
XV32(0x597e7fea), XV32(0xf2af8c91), XV32(0xedd991c), XV32(0xa50c6a67),
XV32(0x469cc60f), XV32(0xed4d3574), XV32(0x113f20f9), XV32(0xbaeed382),
XV32(0x420af898), XV32(0xe9db0be3), XV32(0x15a91e6e), XV32(0xbe78ed15),
XV32(0x4fb0bb21), XV32(0xe461485a), XV32(0x18135dd7), XV32(0xb3c2aeac),
XV32(0x4b2685b6), XV32(0xe0f776cd), XV32(0x1c856340), XV32(0xb754903b)
};
static const xply_word U[256] = {
XV32(0x0), XV32(0x5ce33923), XV32(0x1217813d), XV32(0x4ef4b81e),
XV32(0x242f027a), XV32(0x78cc3b59), XV32(0x36388347), XV32(0x6adbba64),
XV32(0x485e04f4), XV32(0x14bd3dd7), XV32(0x5a4985c9), XV32(0x6aabcea),
XV32(0x6c71068e), XV32(0x30923fad), XV32(0x7e6687b3), XV32(0x2285be90),
XV32(0x3b6dfa93), XV32(0x678ec3b0), XV32(0x297a7bae), XV32(0x7599428d),
XV32(0x1f42f8e9), XV32(0x43a1c1ca), XV32(0xd5579d4), XV32(0x51b640f7),
XV32(0x7333fe67), XV32(0x2fd0c744), XV32(0x61247f5a), XV32(0x3dc74679),
XV32(0x571cfc1d), XV32(0xbffc53e), XV32(0x450b7d20), XV32(0x19e84403),
XV32(0x76dbf526), XV32(0x2a38cc05), XV32(0x64cc741b), XV32(0x382f4d38),
XV32(0x52f4f75c), XV32(0xe17ce7f), XV32(0x40e37661), XV32(0x1c004f42),
XV32(0x3e85f1d2), XV32(0x6266c8f1), XV32(0x2c9270ef), XV32(0x707149cc),
XV32(0x1aaaf3a8), XV32(0x4649ca8b), XV32(0x8bd7295), XV32(0x545e4bb6),
XV32(0x4db60fb5), XV32(0x11553696), XV32(0x5fa18e88), XV32(0x342b7ab),
XV32(0x69990dcf), XV32(0x357a34ec), XV32(0x7b8e8cf2), XV32(0x276db5d1),
XV32(0x5e80b41), XV32(0x590b3262), XV32(0x17ff8a7c), XV32(0x4b1cb35f),
XV32(0x21c7093b), XV32(0x7d243018), XV32(0x33d08806), XV32(0x6f33b125),
XV32(0x46661937), XV32(0x1a852014), XV32(0x5471980a), XV32(0x892a129),
XV32(0x62491b4d), XV32(0x3eaa226e), XV32(0x705e9a70), XV32(0x2cbda353),
XV32(0xe381dc3), XV32(0x52db24e0), XV32(0x1c2f9cfe), XV32(0x40cca5dd),
XV32(0x2a171fb9), XV32(0x76f4269a), XV32(0x38009e84), XV32(0x64e3a7a7),
XV32(0x7d0be3a4), XV32(0x21e8da87), XV32(0x6f1c6299), XV32(0x33ff5bba),
XV32(0x5924e1de), XV32(0x5c7d8fd), XV32(0x4b3360e3), XV32(0x17d059c0),
XV32(0x3555e750), XV32(0x69b6de73), XV32(0x2742666d), XV32(0x7ba15f4e),
XV32(0x117ae52a), XV32(0x4d99dc09), XV32(0x36d6417), XV32(0x5f8e5d34),
XV32(0x30bdec11), XV32(0x6c5ed532), XV32(0x22aa6d2c), XV32(0x7e49540f),
XV32(0x1492ee6b), XV32(0x4871d748), XV32(0x6856f56), XV32(0x5a665675),
XV32(0x78e3e8e5), XV32(0x2400d1c6), XV32(0x6af469d8), XV32(0x361750fb),
XV32(0x5cccea9f), XV32(0x2fd3bc), XV32(0x4edb6ba2), XV32(0x12385281),
XV32(0xbd01682), XV32(0x57332fa1), XV32(0x19c797bf), XV32(0x4524ae9c),
XV32(0x2fff14f8), XV32(0x731c2ddb), XV32(0x3de895c5), XV32(0x610bace6),
XV32(0x438e1276), XV32(0x1f6d2b55), XV32(0x5199934b), XV32(0xd7aaa68),
XV32(0x67a1100c), XV32(0x3b42292f), XV32(0x75b69131), XV32(0x2955a812),
XV32(0x271dc115), XV32(0x7bfef836), XV32(0x350a4028), XV32(0x69e9790b),
XV32(0x332c36f), XV32(0x5fd1fa4c), XV32(0x11254252), XV32(0x4dc67b71),
XV32(0x6f43c5e1), XV32(0x33a0fcc2), XV32(0x7d5444dc), XV32(0x21b77dff),
XV32(0x4b6cc79b), XV32(0x178ffeb8), XV32(0x597b46a6), XV32(0x5987f85),
XV32(0x1c703b86), XV32(0x409302a5), XV32(0xe67babb), XV32(0x52848398),
XV32(0x385f39fc), XV32(0x64bc00df), XV32(0x2a48b8c1), XV32(0x76ab81e2),
XV32(0x542e3f72), XV32(0x8cd0651), XV32(0x4639be4f), XV32(0x1ada876c),
XV32(0x70013d08), XV32(0x2ce2042b), XV32(0x6216bc35), XV32(0x3ef58516),
XV32(0x51c63433), XV32(0xd250d10), XV32(0x43d1b50e), XV32(0x1f328c2d),
XV32(0x75e93649), XV32(0x290a0f6a), XV32(0x67feb774), XV32(0x3b1d8e57),
XV32(0x199830c7), XV32(0x457b09e4), XV32(0xb8fb1fa), XV32(0x576c88d9),
XV32(0x3db732bd), XV32(0x61540b9e), XV32(0x2fa0b380), XV32(0x73438aa3),
XV32(0x6aabcea0), XV32(0x3648f783), XV32(0x78bc4f9d), XV32(0x245f76be),
XV32(0x4e84ccda), XV32(0x1267f5f9), XV32(0x5c934de7), XV32(0x7074c4),
XV32(0x22f5ca54), XV32(0x7e16f377), XV32(0x30e24b69), XV32(0x6c01724a),
XV32(0x6dac82e), XV32(0x5a39f10d), XV32(0x14cd4913), XV32(0x482e7030),
XV32(0x617bd822), XV32(0x3d98e101), XV32(0x736c591f), XV32(0x2f8f603c),
XV32(0x4554da58), XV32(0x19b7e37b), XV32(0x57435b65), XV32(0xba06246),
XV32(0x2925dcd6), XV32(0x75c6e5f5), XV32(0x3b325deb), XV32(0x67d164c8),
XV32(0xd0adeac), XV32(0x51e9e78f), XV32(0x1f1d5f91), XV32(0x43fe66b2),
XV32(0x5a1622b1), XV32(0x6f51b92), XV32(0x4801a38c), XV32(0x14e29aaf),
XV32(0x7e3920cb), XV32(0x22da19e8), XV32(0x6c2ea1f6), XV32(0x30cd98d5),
XV32(0x12482645), XV32(0x4eab1f66), XV32(0x5fa778), XV32(0x5cbc9e5b),
XV32(0x3667243f), XV32(0x6a841d1c), XV32(0x2470a502), XV32(0x78939c21),
XV32(0x17a02d04), XV32(0x4b431427), XV32(0x5b7ac39), XV32(0x5954951a),
XV32(0x338f2f7e), XV32(0x6f6c165d), XV32(0x2198ae43), XV32(0x7d7b9760),
XV32(0x5ffe29f0), XV32(0x31d10d3), XV32(0x4de9a8cd), XV32(0x110a91ee),
XV32(0x7bd12b8a), XV32(0x273212a9), XV32(0x69c6aab7), XV32(0x35259394),
XV32(0x2ccdd797), XV32(0x702eeeb4), XV32(0x3eda56aa), XV32(0x62396f89),
XV32(0x8e2d5ed), XV32(0x5401ecce), XV32(0x1af554d0), XV32(0x46166df3),
XV32(0x6493d363), XV32(0x3870ea40), XV32(0x7684525e), XV32(0x2a676b7d),
XV32(0x40bcd119), XV32(0x1c5fe83a), XV32(0x52ab5024), XV32(0xe486907)
};
#endif /* if defined(XRABPLY_TYPE32) */

View File

@@ -0,0 +1,68 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XTYPES_H)
#define XTYPES_H
typedef struct s_chanode {
struct s_chanode *next;
long icurr;
} chanode_t;
typedef struct s_chastore {
chanode_t *head, *tail;
long isize, nsize;
chanode_t *ancur;
chanode_t *sncur;
long scurr;
} chastore_t;
typedef struct s_xrecord {
struct s_xrecord *next;
char const *ptr;
long size;
unsigned long ha;
} xrecord_t;
typedef struct s_xdfile {
chastore_t rcha;
long nrec;
unsigned int hbits;
xrecord_t **rhash;
long dstart, dend;
xrecord_t **recs;
char *rchg;
long *rindex;
long nreff;
unsigned long *ha;
} xdfile_t;
typedef struct s_xdfenv {
xdfile_t xdf1, xdf2;
} xdfenv_t;
#endif /* #if !defined(XTYPES_H) */

View File

@@ -0,0 +1,580 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
#define XDL_GUESS_NLINES 256
long xdl_bogosqrt(long n) {
long i;
/*
* Classical integer square root approximation using shifts.
*/
for (i = 1; n > 0; n >>= 2)
i <<= 1;
return i;
}
int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
xdemitcb_t *ecb) {
int i = 2;
mmbuffer_t mb[3];
mb[0].ptr = (char *) pre;
mb[0].size = psize;
mb[1].ptr = (char *) rec;
mb[1].size = size;
if (size > 0 && rec[size - 1] != '\n') {
mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
mb[2].size = strlen(mb[2].ptr);
i++;
}
if (ecb->outf(ecb->priv, mb, i) < 0) {
return -1;
}
return 0;
}
int xdl_init_mmfile(mmfile_t *mmf, long bsize, unsigned long flags) {
mmf->flags = flags;
mmf->head = mmf->tail = NULL;
mmf->bsize = bsize;
mmf->fsize = 0;
mmf->rcur = mmf->wcur = NULL;
mmf->rpos = 0;
return 0;
}
void xdl_free_mmfile(mmfile_t *mmf) {
mmblock_t *cur, *tmp;
for (cur = mmf->head; (tmp = cur) != NULL;) {
cur = cur->next;
xdl_free(tmp);
}
}
int xdl_mmfile_iscompact(mmfile_t *mmf) {
return mmf->head == mmf->tail;
}
int xdl_seek_mmfile(mmfile_t *mmf, long off) {
long bsize;
if (xdl_mmfile_first(mmf, &bsize)) {
do {
if (off < bsize) {
mmf->rpos = off;
return 0;
}
off -= bsize;
} while (xdl_mmfile_next(mmf, &bsize));
}
return -1;
}
long xdl_read_mmfile(mmfile_t *mmf, void *data, long size) {
long rsize, csize;
char *ptr = data;
mmblock_t *rcur;
for (rsize = 0, rcur = mmf->rcur; rcur && rsize < size;) {
if (mmf->rpos >= rcur->size) {
if (!(mmf->rcur = rcur = rcur->next))
break;
mmf->rpos = 0;
}
csize = XDL_MIN(size - rsize, rcur->size - mmf->rpos);
memcpy(ptr, rcur->ptr + mmf->rpos, csize);
rsize += csize;
ptr += csize;
mmf->rpos += csize;
}
return rsize;
}
long xdl_write_mmfile(mmfile_t *mmf, void const *data, long size) {
long wsize, bsize, csize;
mmblock_t *wcur;
for (wsize = 0; wsize < size;) {
wcur = mmf->wcur;
if (wcur && (wcur->flags & XDL_MMB_READONLY))
return wsize;
if (!wcur || wcur->size == wcur->bsize ||
(mmf->flags & XDL_MMF_ATOMIC && wcur->size + size > wcur->bsize)) {
bsize = XDL_MAX(mmf->bsize, size);
if (!(wcur = (mmblock_t *) xdl_malloc(sizeof(mmblock_t) + bsize))) {
return wsize;
}
wcur->flags = 0;
wcur->ptr = (char *) wcur + sizeof(mmblock_t);
wcur->size = 0;
wcur->bsize = bsize;
wcur->next = NULL;
if (!mmf->head)
mmf->head = wcur;
if (mmf->tail)
mmf->tail->next = wcur;
mmf->tail = wcur;
mmf->wcur = wcur;
}
csize = XDL_MIN(size - wsize, wcur->bsize - wcur->size);
memcpy(wcur->ptr + wcur->size, (char const *) data + wsize, csize);
wsize += csize;
wcur->size += csize;
mmf->fsize += csize;
}
return size;
}
long xdl_writem_mmfile(mmfile_t *mmf, mmbuffer_t *mb, int nbuf) {
int i;
long size;
char *data;
for (i = 0, size = 0; i < nbuf; i++)
size += mb[i].size;
if (!(data = (char *) xdl_mmfile_writeallocate(mmf, size)))
return -1;
for (i = 0; i < nbuf; i++) {
memcpy(data, mb[i].ptr, mb[i].size);
data += mb[i].size;
}
return size;
}
void *xdl_mmfile_writeallocate(mmfile_t *mmf, long size) {
long bsize;
mmblock_t *wcur;
char *blk;
if (!(wcur = mmf->wcur) || wcur->size + size > wcur->bsize) {
bsize = XDL_MAX(mmf->bsize, size);
if (!(wcur = (mmblock_t *) xdl_malloc(sizeof(mmblock_t) + bsize))) {
return NULL;
}
wcur->flags = 0;
wcur->ptr = (char *) wcur + sizeof(mmblock_t);
wcur->size = 0;
wcur->bsize = bsize;
wcur->next = NULL;
if (!mmf->head)
mmf->head = wcur;
if (mmf->tail)
mmf->tail->next = wcur;
mmf->tail = wcur;
mmf->wcur = wcur;
}
blk = wcur->ptr + wcur->size;
wcur->size += size;
mmf->fsize += size;
return blk;
}
long xdl_mmfile_ptradd(mmfile_t *mmf, char *ptr, long size, unsigned long flags) {
mmblock_t *wcur;
if (!(wcur = (mmblock_t *) xdl_malloc(sizeof(mmblock_t)))) {
return -1;
}
wcur->flags = flags;
wcur->ptr = ptr;
wcur->size = wcur->bsize = size;
wcur->next = NULL;
if (!mmf->head)
mmf->head = wcur;
if (mmf->tail)
mmf->tail->next = wcur;
mmf->tail = wcur;
mmf->wcur = wcur;
mmf->fsize += size;
return size;
}
long xdl_copy_mmfile(mmfile_t *mmf, long size, xdemitcb_t *ecb) {
long rsize, csize;
mmblock_t *rcur;
mmbuffer_t mb;
for (rsize = 0, rcur = mmf->rcur; rcur && rsize < size;) {
if (mmf->rpos >= rcur->size) {
if (!(mmf->rcur = rcur = rcur->next))
break;
mmf->rpos = 0;
}
csize = XDL_MIN(size - rsize, rcur->size - mmf->rpos);
mb.ptr = rcur->ptr + mmf->rpos;
mb.size = csize;
if (ecb->outf(ecb->priv, &mb, 1) < 0) {
return rsize;
}
rsize += csize;
mmf->rpos += csize;
}
return rsize;
}
void *xdl_mmfile_first(mmfile_t *mmf, long *size) {
if (!(mmf->rcur = mmf->head))
return NULL;
*size = mmf->rcur->size;
return mmf->rcur->ptr;
}
void *xdl_mmfile_next(mmfile_t *mmf, long *size) {
if (!mmf->rcur || !(mmf->rcur = mmf->rcur->next))
return NULL;
*size = mmf->rcur->size;
return mmf->rcur->ptr;
}
long xdl_mmfile_size(mmfile_t *mmf) {
return mmf->fsize;
}
int xdl_mmfile_cmp(mmfile_t *mmf1, mmfile_t *mmf2) {
int cres;
long size, bsize1, bsize2, size1, size2;
char const *blk1, *cur1, *top1;
char const *blk2, *cur2, *top2;
if ((cur1 = blk1 = xdl_mmfile_first(mmf1, &bsize1)) != NULL)
top1 = blk1 + bsize1;
if ((cur2 = blk2 = xdl_mmfile_first(mmf2, &bsize2)) != NULL)
top2 = blk2 + bsize2;
if (!cur1) {
if (!cur2 || xdl_mmfile_size(mmf2) == 0)
return 0;
return -*cur2;
} else if (!cur2)
return xdl_mmfile_size(mmf1) ? *cur1: 0;
for (;;) {
if (cur1 >= top1) {
if ((cur1 = blk1 = xdl_mmfile_next(mmf1, &bsize1)) != NULL)
top1 = blk1 + bsize1;
}
if (cur2 >= top2) {
if ((cur2 = blk2 = xdl_mmfile_next(mmf2, &bsize2)) != NULL)
top2 = blk2 + bsize2;
}
if (!cur1) {
if (!cur2)
break;
return -*cur2;
} else if (!cur2)
return *cur1;
size1 = top1 - cur1;
size2 = top2 - cur2;
size = XDL_MIN(size1, size2);
if ((cres = memcmp(cur1, cur2, size)) != 0)
return cres;
cur1 += size;
cur2 += size;
}
return 0;
}
int xdl_mmfile_compact(mmfile_t *mmfo, mmfile_t *mmfc, long bsize, unsigned long flags) {
long fsize = xdl_mmfile_size(mmfo), size;
char *data;
char const *blk;
if (xdl_init_mmfile(mmfc, bsize, flags) < 0) {
return -1;
}
if (!(data = (char *) xdl_mmfile_writeallocate(mmfc, fsize))) {
xdl_free_mmfile(mmfc);
return -1;
}
if ((blk = (char const *) xdl_mmfile_first(mmfo, &size)) != NULL) {
do {
memcpy(data, blk, size);
data += size;
} while ((blk = (char const *) xdl_mmfile_next(mmfo, &size)) != NULL);
}
return 0;
}
int xdl_mmfile_outf(void *priv, mmbuffer_t *mb, int nbuf) {
mmfile_t *mmf = priv;
if (xdl_writem_mmfile(mmf, mb, nbuf) < 0) {
return -1;
}
return 0;
}
int xdl_cha_init(chastore_t *cha, long isize, long icount) {
cha->head = cha->tail = NULL;
cha->isize = isize;
cha->nsize = icount * isize;
cha->ancur = cha->sncur = NULL;
cha->scurr = 0;
return 0;
}
void xdl_cha_free(chastore_t *cha) {
chanode_t *cur, *tmp;
for (cur = cha->head; (tmp = cur) != NULL;) {
cur = cur->next;
xdl_free(tmp);
}
}
void *xdl_cha_alloc(chastore_t *cha) {
chanode_t *ancur;
void *data;
if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
return NULL;
}
ancur->icurr = 0;
ancur->next = NULL;
if (cha->tail)
cha->tail->next = ancur;
if (!cha->head)
cha->head = ancur;
cha->tail = ancur;
cha->ancur = ancur;
}
data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
ancur->icurr += cha->isize;
return data;
}
void *xdl_cha_first(chastore_t *cha) {
chanode_t *sncur;
if (!(cha->sncur = sncur = cha->head))
return NULL;
cha->scurr = 0;
return (char *) sncur + sizeof(chanode_t) + cha->scurr;
}
void *xdl_cha_next(chastore_t *cha) {
chanode_t *sncur;
if (!(sncur = cha->sncur))
return NULL;
cha->scurr += cha->isize;
if (cha->scurr == sncur->icurr) {
if (!(sncur = cha->sncur = sncur->next))
return NULL;
cha->scurr = 0;
}
return (char *) sncur + sizeof(chanode_t) + cha->scurr;
}
long xdl_guess_lines(mmfile_t *mf) {
long nl = 0, size, tsize = 0;
char const *data, *cur, *top;
if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
for (top = data + size; nl < XDL_GUESS_NLINES;) {
if (cur >= top) {
tsize += (long) (cur - data);
if (!(cur = data = xdl_mmfile_next(mf, &size)))
break;
top = data + size;
}
nl++;
if (!(cur = memchr(cur, '\n', top - cur)))
cur = top;
else
cur++;
}
tsize += (long) (cur - data);
}
if (nl && tsize)
nl = xdl_mmfile_size(mf) / (tsize / nl);
return nl + 1;
}
unsigned long xdl_hash_record(char const **data, char const *top) {
unsigned long ha = 5381;
char const *ptr = *data;
for (; ptr < top && *ptr != '\n'; ptr++) {
ha += (ha << 5);
ha ^= (unsigned long) *ptr;
}
*data = ptr < top ? ptr + 1: ptr;
return ha;
}
unsigned int xdl_hashbits(unsigned int size) {
unsigned int val = 1, bits = 0;
for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
return bits ? bits: 1;
}
int xdl_num_out(char *out, long val) {
char *ptr, *str = out;
char buf[32];
ptr = buf + sizeof(buf) - 1;
*ptr = '\0';
if (val < 0) {
*--ptr = '-';
val = -val;
}
for (; val && ptr > buf; val /= 10)
*--ptr = "0123456789"[val % 10];
if (*ptr)
for (; *ptr; ptr++, str++)
*str = *ptr;
else
*str++ = '0';
*str = '\0';
return str - out;
}
long xdl_atol(char const *str, char const **next) {
long val, base;
char const *top;
for (top = str; XDL_ISDIGIT(*top); top++);
if (next)
*next = top;
for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
val += base * (long)(*top - '0');
return val;
}
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, xdemitcb_t *ecb) {
int nb = 0;
mmbuffer_t mb;
char buf[128];
memcpy(buf, "@@ -", 4);
nb += 4;
nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
memcpy(buf + nb, ",", 1);
nb += 1;
nb += xdl_num_out(buf + nb, c1);
memcpy(buf + nb, " +", 2);
nb += 2;
nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
memcpy(buf + nb, ",", 1);
nb += 1;
nb += xdl_num_out(buf + nb, c2);
memcpy(buf + nb, " @@\n", 4);
nb += 4;
mb.ptr = buf;
mb.size = nb;
if (ecb->outf(ecb->priv, &mb, 1) < 0)
return -1;
return 0;
}

View File

@@ -0,0 +1,47 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XUTILS_H)
#define XUTILS_H
long xdl_bogosqrt(long n);
int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
xdemitcb_t *ecb);
int xdl_mmfile_outf(void *priv, mmbuffer_t *mb, int nbuf);
int xdl_cha_init(chastore_t *cha, long isize, long icount);
void xdl_cha_free(chastore_t *cha);
void *xdl_cha_alloc(chastore_t *cha);
void *xdl_cha_first(chastore_t *cha);
void *xdl_cha_next(chastore_t *cha);
long xdl_guess_lines(mmfile_t *mf);
unsigned long xdl_hash_record(char const **data, char const *top);
unsigned int xdl_hashbits(unsigned int size);
int xdl_num_out(char *out, long val);
long xdl_atol(char const *str, char const **next);
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, xdemitcb_t *ecb);
#endif /* #if !defined(XUTILS_H) */

View File

@@ -0,0 +1,27 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
char libxdiff_version[] = "LibXDiff v" PACKAGE_VERSION " by Davide Libenzi <davide@xmailserver.org>";

View File

@@ -0,0 +1,27 @@
module XDiff
VERSION = "0.0.1"
def self.validate_arg(method, name, arg)
raise ArgumentError.new(
"(#{method}) '#{name}' cannot be nil"
) if arg.nil?
raise ArgumentError.new(
"(#{method}) '#{name}' must be String (is #{arg.class})"
) unless arg.is_a?(String)
end
def self.diff(a, b)
validate_arg("diff", "a", a)
validate_arg("diff", "b", b)
Native.diff(a, b)
end
def self.patch(a, b)
validate_arg("patch", "a", a)
validate_arg("patch", "b", b)
Native.patch(a, b)
end
end
# XDiff::Native methods defined by extension.cc
require "xdiff/extension"
XDiff::Native.init

View File

@@ -0,0 +1,32 @@
require "minitest/autorun"
require "xdiff"
class TestXDiff < Minitest::Test
def assert_patches(a, b)
diff = XDiff.diff(a, b)
diff_hex = diff.unpack("H*").first
puts "diff (#{diff.size}): #{diff_hex}"
refute_equal "", diff
stitched = XDiff.patch(a, diff)
assert_equal b, stitched
end
def test_raises
assert_raises(ArgumentError) { XDiff.patch(nil, "") }
assert_raises(ArgumentError) { XDiff.patch("", nil) }
assert_raises(ArgumentError) { XDiff.patch("", 123) }
assert_raises(ArgumentError) { XDiff.patch(123, "") }
end
def test_patch
assert_patches("foo bar", "foo bar")
assert_patches("", "")
assert_patches("foo", "")
assert_patches("", "foo")
assert_patches("abc\x00123", "123")
assert_patches("abc\x00123", "\x00")
assert_patches("\x00", "\x00")
end
end

View File

@@ -0,0 +1,23 @@
require "rake"
Gem::Specification.new do |s|
s.name = "xdiff"
s.version = "0.0.1"
s.summary = "libxdiff bindings"
s.description = "A simple hello world gem"
s.authors = ["Dylan Knutson"]
s.email = "dymk@dymk.co"
s.files = FileList[
"lib/xdiff.rb",
"ext/xdiff/extconf.rb",
"ext/xdiff/extension.cc",
"ext/xdiff/xdiff.tar.gz",
]
s.homepage =
"https://rubygems.org/gems/xdiff"
s.license = "MIT"
s.extensions = %w[ext/xdiff/extconf.rb]
s.add_development_dependency "rake", ">= 1.9.1"
s.add_development_dependency "rake-compiler", ">= 0.8.3"
s.add_development_dependency "minitest"
end

View File

@@ -24,13 +24,7 @@
"react_ujs": "^2.6.2",
"shakapacker": "6.6.0",
"style-loader": "^3.3.2",
"terser-webpack-plugin": "5",
"typescript": "^5.0.3",
"webpack": "5",
"webpack-assets-manifest": "5",
"webpack-cli": "4",
"webpack-dev-server": "^4.13.2",
"webpack-merge": "5"
"terser-webpack-plugin": "5"
},
"version": "0.1.0",
"babel": {
@@ -46,6 +40,12 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@types/lodash": "^4.14.192",
"@types/react": "^18.0.33",
"react-refresh": "^0.14.0"
"react-refresh": "^0.14.0",
"typescript": "^5.0.3",
"webpack": "5",
"webpack-assets-manifest": "5",
"webpack-cli": "4",
"webpack-dev-server": "^4.13.2",
"webpack-merge": "5"
}
}