From 1d5257c7ef12f0bf60ae5f7531ffdb355cb13202 Mon Sep 17 00:00:00 2001 From: sliebig Date: Tue, 6 Nov 2007 08:49:04 +0000 Subject: Extended version of Joe Desbonnet´s (joe@galway.net) JBDiff library which is a is a Java translation of the bsdiff (v4.2) utility by Colin Percival. See http://www.daemonology.net/bsdiff/ --- bundles/ie.wombat.jbdiff/.classpath | 7 + bundles/ie.wombat.jbdiff/.project | 28 + .../.settings/org.eclipse.pde.core.prefs | 4 + bundles/ie.wombat.jbdiff/META-INF/MANIFEST.MF | 12 + bundles/ie.wombat.jbdiff/build.properties | 4 + bundles/ie.wombat.jbdiff/src/README.txt | 48 ++ bundles/ie.wombat.jbdiff/src/bsd-license.txt | 22 + .../src/ie/wombat/jbdiff/Activator.java | 34 ++ .../src/ie/wombat/jbdiff/JBDiff.java | 604 +++++++++++++++++++++ .../src/ie/wombat/jbdiff/JBPatch.java | 234 ++++++++ .../src/ie/wombat/jbdiff/Util.java | 109 ++++ bundles/ie.wombat.jbdiff/src/readme-more.txt | 10 + 12 files changed, 1116 insertions(+) create mode 100644 bundles/ie.wombat.jbdiff/.classpath create mode 100644 bundles/ie.wombat.jbdiff/.project create mode 100644 bundles/ie.wombat.jbdiff/.settings/org.eclipse.pde.core.prefs create mode 100644 bundles/ie.wombat.jbdiff/META-INF/MANIFEST.MF create mode 100644 bundles/ie.wombat.jbdiff/build.properties create mode 100644 bundles/ie.wombat.jbdiff/src/README.txt create mode 100644 bundles/ie.wombat.jbdiff/src/bsd-license.txt create mode 100644 bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Activator.java create mode 100644 bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBDiff.java create mode 100644 bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBPatch.java create mode 100644 bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Util.java create mode 100644 bundles/ie.wombat.jbdiff/src/readme-more.txt (limited to 'bundles/ie.wombat.jbdiff') diff --git a/bundles/ie.wombat.jbdiff/.classpath b/bundles/ie.wombat.jbdiff/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/ie.wombat.jbdiff/.project b/bundles/ie.wombat.jbdiff/.project new file mode 100644 index 000000000..76d444192 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/.project @@ -0,0 +1,28 @@ + + + ie.wombat.jbdiff + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/ie.wombat.jbdiff/.settings/org.eclipse.pde.core.prefs b/bundles/ie.wombat.jbdiff/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..950e19447 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +#Thu Nov 01 14:00:53 CET 2007 +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/bundles/ie.wombat.jbdiff/META-INF/MANIFEST.MF b/bundles/ie.wombat.jbdiff/META-INF/MANIFEST.MF new file mode 100644 index 000000000..53b210470 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Jbdiff Plug-in +Bundle-SymbolicName: ie.wombat.jbdiff +Bundle-Version: 0.1.0.1 +Bundle-Activator: ie.wombat.jbdiff.Activator +Import-Package: org.osgi.framework;version="1.3.0" +Eclipse-LazyStart: true +Require-Bundle: org.apache.tools.bzip2 +Export-Package: ie.wombat.jbdiff +Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1, + J2SE-1.4 diff --git a/bundles/ie.wombat.jbdiff/build.properties b/bundles/ie.wombat.jbdiff/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/bundles/ie.wombat.jbdiff/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/bundles/ie.wombat.jbdiff/src/README.txt b/bundles/ie.wombat.jbdiff/src/README.txt new file mode 100644 index 000000000..30ce0658e --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/README.txt @@ -0,0 +1,48 @@ +README file for JBDiff (Java Binary Diff) +Version: 0.1 +Release date: 8 Mar 2005. + +JBDiff (Java Binary Diff) utility is a Java translation of the bsdiff (v4.2) utility +by Colin Percival. See http://www.daemonology.net/bsdiff/ + +The file format is similar to, but currently not compatible with the bsdiff utility. +This is because bsdiff uses bzip2 for compression which is not available in the +standard Java libraries. Instead I use gzip (java.util.zip.*) + +The diff utility is very memory hungry. Attempting to diff very large files with +insufficient RAM may cause your computer to 'trash' (ie become unusably slow and may +require a reset to recover). Comparing two 20MB files will take approx 80 seconds +on a 2GHz Pentium 4 and will require a maximum heap size of at least 220 MBytes. The +maximum heap size can be specified using the -Xmx switch to the Java VM (see examples +below). The patch utility has more modest resource requirements. + +EXAMPLES: + +To compare old.bin with new.bin and produce diff file new-old.diff: + +java -Xmx200m -classpath jbdiff.jar ie.wombat.jbdiff.JBDiff old.bin new.bin new-old.diff + +To patch old.bin with new-old.diff to produce new.bin: + +java -Xmx200m -classpath jbdiff.jar ie.wombat.jbdiff.JBPatch old.bin new.bin new-old.diff + +TODO: + +This first release is a rather blind port of the bsdiff utility. A vast bulk of +the code ported to Java without any modification. There is scope +for optimization (the C bsdiff runs in approx 50% faster than JBDiff). + +Also it would be nice to be able to produce output that is compatible with bsdiff. +I need a bzip2 library for that. + +Any suggestions, feedback and bugs will be much appreciated. Please email +to joe@galway.net + +LICENSE: + +This software is distributed under the GPL v2 license (see LICENSE.txt file in +the distribution). If this license does not suit your needs, please contact me. + +Joe Desbonnet +joe@galway.net +8 Mar 2005 diff --git a/bundles/ie.wombat.jbdiff/src/bsd-license.txt b/bundles/ie.wombat.jbdiff/src/bsd-license.txt new file mode 100644 index 000000000..f94a2fd82 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/bsd-license.txt @@ -0,0 +1,22 @@ + +Copyright (c) , + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 COPYRIGHT OWNER OR +CONTRIBUTORS 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. diff --git a/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Activator.java b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Activator.java new file mode 100644 index 000000000..73b553185 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Activator.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2007 compeople AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * compeople AG (Stefan Liebig) - initial API and implementation + *******************************************************************************/ +package ie.wombat.jbdiff; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + /* + * (non-Javadoc) + * + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + } + + /* + * (non-Javadoc) + * + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + } + +} diff --git a/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBDiff.java b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBDiff.java new file mode 100644 index 000000000..cee25fe65 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBDiff.java @@ -0,0 +1,604 @@ +/* + 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 + */ + +package ie.wombat.jbdiff; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.tools.bzip2.CBZip2OutputStream; + +/** + * Java Binary Diff utility. Based on bsdiff (v4.2) by Colin Percival (see + * http://www.daemonology.net/bsdiff/ ) and distributed under BSD license. + * + *

+ * Running this on large files will probably require an increase of the default + * maximum heap size (use java -Xmx200m) + *

+ * + * @author Joe Desbonnet, joe@galway.net + * + */ +public class JBDiff { + + // JBDiff extensions by Stefan.Liebig@compeople.de: + // + // - uses an extended version of the org.apache.tools.bzip2 compressor to + // compress all of the blocks (ctrl,diff,extra). + // - added interfaces that allows using of JBDiff with streams and byte + // arrays. + + private static final String VERSION = "jbdiff-0.1.0"; + + // This is ´jbdiff40´. + private static final byte[] MAGIC_BYTES = new byte[] { 0x6a, 0x62, 0x64, + 0x69, 0x66, 0x66, 0x34, 0x30 }; + + private final static void split(int[] I, int[] V, int start, int len, int h) { + + int 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); + } + + } + + /** + * Fast suffix sporting. Larsson and Sadakane's qsufsort algorithm. See + * http://www.cs.lth.se/Research/Algorithms/Papers/jesper5.ps + * + * @param I + * @param V + * @param oldBuf + * @param oldsize + */ + private static void qsufsort(int[] I, int[] V, byte[] oldBuf, int oldsize) { + + // int oldsize = oldBuf.length; + int[] buckets = new int[256]; + + // No need to do that in Java. + // for ( int i = 0; i < 256; i++ ) { + // buckets[i] = 0; + // } + + for (int i = 0; i < oldsize; i++) { + buckets[oldBuf[i] & 0xff]++; + } + + for (int i = 1; i < 256; i++) { + buckets[i] += buckets[i - 1]; + } + + for (int i = 255; i > 0; i--) { + buckets[i] = buckets[i - 1]; + } + + buckets[0] = 0; + + for (int i = 0; i < oldsize; i++) { + I[++buckets[oldBuf[i] & 0xff]] = i; + } + + I[0] = oldsize; + for (int i = 0; i < oldsize; i++) { + V[i] = buckets[oldBuf[i] & 0xff]; + } + V[oldsize] = 0; + + for (int i = 1; i < 256; i++) { + if (buckets[i] == buckets[i - 1] + 1) { + I[buckets[i]] = -1; + } + } + + I[0] = -1; + + for (int h = 1; I[0] != -(oldsize + 1); h += h) { + int len = 0; + int i; + for (i = 0; i < oldsize + 1;) { + if (I[i] < 0) { + len -= I[i]; + i -= I[i]; + } else { + // if(len) I[i-len]=-len; + if (len != 0) { + I[i - len] = -len; + } + len = V[I[i]] + 1 - i; + split(I, V, i, len, h); + i += len; + len = 0; + } + + } + + if (len != 0) { + I[i - len] = -len; + } + } + + for (int i = 0; i < oldsize + 1; i++) { + I[V[i]] = i; + } + } + + /** + * Count the number of bytes that match in oldBuf (starting at offset + * oldOffset) and newBuf (starting at offset newOffset). + * + * @param oldBuf + * @param oldOffset + * @param newBuf + * @param newOffset + * @return + */ + private final static int matchlen(byte[] oldBuf, int oldSize, + int oldOffset, byte[] newBuf, int newSize, int newOffset) { + // int end = Math + // .min(oldBuf.length - oldOffset, newBuf.length - newOffset); + int end = Math.min(oldSize - oldOffset, newSize - newOffset); + for (int i = 0; i < end; i++) { + if (oldBuf[oldOffset + i] != newBuf[newOffset + i]) { + return i; + } + } + return end; + } + + private final static int search(int[] I, byte[] oldBuf, int oldSize, + byte[] newBuf, int newSize, int newBufOffset, int start, int end, + IntByRef pos) { + + if (end - start < 2) { + int x = matchlen(oldBuf, oldSize, I[start], newBuf, newSize, + newBufOffset); + int y = matchlen(oldBuf, oldSize, I[end], newBuf, newSize, + newBufOffset); + + if (x > y) { + pos.value = I[start]; + return x; + } else { + pos.value = I[end]; + return y; + } + } + + int x = start + (end - start) / 2; + if (Util.memcmp(oldBuf, oldSize, I[x], newBuf, newSize, newBufOffset) < 0) { + return search(I, oldBuf, oldSize, newBuf, newSize, newBufOffset, x, + end, pos); + } else { + return search(I, oldBuf, oldSize, newBuf, newSize, newBufOffset, + start, x, pos); + } + + } + + /** + * @param oldFile + * @param newFile + * @param diffFile + * @throws IOException + */ + public static void bsdiff(File oldFile, File newFile, File diffFile) + throws IOException { + InputStream oldInputStream = new BufferedInputStream( + new FileInputStream(oldFile)); + InputStream newInputStream = new BufferedInputStream( + new FileInputStream(newFile)); + OutputStream diffOutputStream = new FileOutputStream(diffFile); + + byte[] diffBytes = bsdiff(oldInputStream, (int) oldFile.length(), + newInputStream, (int) newFile.length()); + + diffOutputStream.write(diffBytes); + diffOutputStream.close(); + } + + /** + * @param oldInputStream + * @param oldsize + * @param newInputStream + * @param newsize + * @return + * @throws IOException + */ + public static byte[] bsdiff(InputStream oldInputStream, int oldsize, + InputStream newInputStream, int newsize) throws IOException { + + byte[] oldBuf = new byte[oldsize]; + + Util.readFromStream(oldInputStream, oldBuf, 0, oldsize); + oldInputStream.close(); + + byte[] newBuf = new byte[newsize]; + Util.readFromStream(newInputStream, newBuf, 0, newsize); + newInputStream.close(); + + return bsdiff(oldBuf, oldsize, newBuf, newsize); + } + + /** + * @param oldBuf + * @param oldsize + * @param newBuf + * @param newsize + * @return + * @throws IOException + */ + public static byte[] bsdiff(byte[] oldBuf, int oldsize, byte[] newBuf, + int newsize) throws IOException { + + int[] I = new int[oldsize + 1]; + qsufsort(I, new int[oldsize + 1], oldBuf, oldsize); + + // diff block + int dblen = 0; + byte[] db = new byte[newsize]; + + // extra block + int eblen = 0; + byte[] eb = new byte[newsize]; + + /* + * Diff file is composed as follows: + * + * Header (32 bytes) Data (from offset 32 to end of file) + * + * Header: Offset 0, length 8 bytes: file magic "jbdiff40" Offset 8, + * length 8 bytes: length of compressed ctrl block Offset 16, length 8 + * bytes: length of compressed diff block Offset 24, length 8 bytes: + * length of new file + * + * Data: 32 (length ctrlBlockLen): ctrlBlock (bzip2) 32+ctrlBlockLen + * (length diffBlockLen): diffBlock (bzip2) 32+ctrlBlockLen+diffBlockLen + * (to end of file): extraBlock (bzip2) + * + * ctrlBlock comprises a set of records, each record 12 bytes. A record + * comprises 3 x 32 bit integers. The ctrlBlock is not compressed. + */ + + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + DataOutputStream diffOut = new DataOutputStream(byteOut); + + /* + * Write as much of header as we have now. Size of ctrlBlock and + * diffBlock must be filled in later. + */ + diffOut.write(MAGIC_BYTES); + diffOut.writeLong(-1); // place holder for ctrlBlockLen + diffOut.writeLong(-1); // place holder for diffBlockLen + diffOut.writeLong(newsize); + diffOut.flush(); + + CBZip2OutputStream bzip2Out = new CBZip2OutputStream(diffOut); + DataOutputStream dataOut = new DataOutputStream(bzip2Out); + + int oldscore, scsc; + + int overlap, Ss, lens; + int i; + int scan = 0; + int len = 0; + int lastscan = 0; + int lastpos = 0; + int lastoffset = 0; + + IntByRef pos = new IntByRef(); + // int ctrlBlockLen = 0; + + while (scan < newsize) { + oldscore = 0; + + for (scsc = scan += len; scan < newsize; scan++) { + + len = search(I, oldBuf, oldsize, newBuf, newsize, scan, 0, + oldsize, pos); + + for (; scsc < scan + len; scsc++) { + if ((scsc + lastoffset < oldsize) + && (oldBuf[scsc + lastoffset] == newBuf[scsc])) { + oldscore++; + } + } + + if (((len == oldscore) && (len != 0)) || (len > oldscore + 8)) { + break; + } + + if ((scan + lastoffset < oldsize) + && (oldBuf[scan + lastoffset] == newBuf[scan])) { + oldscore--; + } + } + + if ((len != oldscore) || (scan == newsize)) { + int s = 0; + int Sf = 0; + int lenf = 0; + for (i = 0; (lastscan + i < scan) && (lastpos + i < oldsize);) { + if (oldBuf[lastpos + i] == newBuf[lastscan + i]) + s++; + i++; + if (s * 2 - i > Sf * 2 - lenf) { + Sf = s; + lenf = i; + } + } + + int lenb = 0; + if (scan < newsize) { + s = 0; + int Sb = 0; + for (i = 1; (scan >= lastscan + i) && (pos.value >= i); i++) { + if (oldBuf[pos.value - i] == newBuf[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 (newBuf[lastscan + lenf - overlap + i] == oldBuf[lastpos + + lenf - overlap + i]) { + s++; + } + if (newBuf[scan - lenb + i] == oldBuf[pos.value - lenb + + i]) { + s--; + } + if (s > Ss) { + Ss = s; + lens = i + 1; + } + } + + lenf += lens - overlap; + lenb -= lens; + } + + // ? byte casting introduced here -- might affect things + for (i = 0; i < lenf; i++) { + db[dblen + i] = (byte) (newBuf[lastscan + i] - oldBuf[lastpos + + i]); + } + + for (i = 0; i < (scan - lenb) - (lastscan + lenf); i++) { + eb[eblen + i] = newBuf[lastscan + lenf + i]; + } + + dblen += lenf; + eblen += (scan - lenb) - (lastscan + lenf); + + /* + * Write control block entry (3 x int) + */ + // diffOut.writeInt( lenf ); + // diffOut.writeInt( ( scan - lenb ) - ( lastscan + lenf ) ); + // diffOut.writeInt( ( pos[0] - lenb ) - ( lastpos + lenf ) ); + // ctrlBlockLen += 12; + dataOut.writeInt(lenf); + dataOut.writeInt((scan - lenb) - (lastscan + lenf)); + dataOut.writeInt((pos.value - lenb) - (lastpos + lenf)); + + lastscan = scan - lenb; + lastpos = pos.value - lenb; + lastoffset = pos.value - scan; + } // end if + } // end while loop + + dataOut.flush(); + bzip2Out.finish(); + + // now compressed ctrlBlockLen + int ctrlBlockLen = diffOut.size() - Util.HEADER_SIZE; + // System.err.println( "Diff: ctrlBlockLen=" + ctrlBlockLen ); + + // GZIPOutputStream gzOut; + + /* + * Write diff block + */ + // gzOut = new GZIPOutputStream( diffOut ); + bzip2Out = new CBZip2OutputStream(diffOut); + bzip2Out.write(db, 0, dblen); + bzip2Out.finish(); + bzip2Out.flush(); + int diffBlockLen = diffOut.size() - ctrlBlockLen - Util.HEADER_SIZE; + // System.err.println( "Diff: diffBlockLen=" + diffBlockLen ); + + /* + * Write extra block + */ + // gzOut = new GZIPOutputStream( diffOut ); + bzip2Out = new CBZip2OutputStream(diffOut); + bzip2Out.write(eb, 0, eblen); + bzip2Out.finish(); + bzip2Out.flush(); + // long extraBlockLen = diffOut.size() - diffBlockLen - ctrlBlockLen - + // HEADER_SIZE; + // System.err.println( "Diff: extraBlockLen=" + extraBlockLen ); + + diffOut.close(); + + /* + * Write missing header info. + */ + ByteArrayOutputStream byteHeaderOut = new ByteArrayOutputStream( + Util.HEADER_SIZE); + DataOutputStream headerOut = new DataOutputStream(byteHeaderOut); + headerOut.write(MAGIC_BYTES); + headerOut.writeLong(ctrlBlockLen); // place holder for ctrlBlockLen + headerOut.writeLong(diffBlockLen); // place holder for diffBlockLen + headerOut.writeLong(newsize); + headerOut.close(); + + // Copy header information into the diff + byte[] diffBytes = byteOut.toByteArray(); + byte[] headerBytes = byteHeaderOut.toByteArray(); + + System.arraycopy(headerBytes, 0, diffBytes, 0, headerBytes.length); + + return diffBytes; + // /* + // * Write missing header info. Need to reopen the file with + // RandomAccessFile + // * for this. + // */ + // RandomAccessFile diff = new RandomAccessFile( diffFile, "rw" ); + // diff.seek( 8 ); + // diff.writeLong( ctrlBlockLen ); // ctrlBlockLen (compressed) @offset + // 8 + // diff.writeLong( diffBlockLen ); // diffBlockLen (compressed) @offset + // 16 + // diff.close(); + } + + /** + * Run JBDiff from the command line. Params: oldfile newfile difffile. diff + * file will be created. + * + * @param arg + * @throws IOException + */ + public static void main(String[] arg) throws IOException { + + if (arg.length != 3) { + System.err + .println("usage example: java -Xmx200m ie.wombat.jbdiff.JBDiff oldfile newfile patchfile\n"); + return; + } + + File oldFile = new File(arg[0]); + File newFile = new File(arg[1]); + File diffFile = new File(arg[2]); + + bsdiff(oldFile, newFile, diffFile); + + } + + private static class IntByRef { + private int value; + } +} diff --git a/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBPatch.java b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBPatch.java new file mode 100644 index 000000000..550b25e96 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/JBPatch.java @@ -0,0 +1,234 @@ +/* + 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 + */ + +package ie.wombat.jbdiff; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.tools.bzip2.CBZip2InputStream; + +/** + * Java Binary patcher (based on bspatch by Colin Percival) + * + * @author Joe Desbonnet, joe@galway.net + */ +public class JBPatch { + + // JBPatch extensions by Stefan.Liebig@compeople.de: + // + // - uses an extended version of the org.apache.tools.bzip2 compressor to + // compress all of the blocks (ctrl,diff,extra). + // - added an interface that allows using of JBPatch with streams and byte + // arrays + + private static final String VERSION = "jbdiff-0.1.0"; + + /** + * Run JBPatch from the command line. Params: oldfile newfile patchfile. + * newfile will be created. + * + * @param arg + * @throws IOException + */ + public static void main(String[] arg) throws IOException { + + if (arg.length != 3) { + System.err + .println("usage example: java -Xmx200m ie.wombat.jbdiff.JBPatch oldfile newfile patchfile"); + } + + File oldFile = new File(arg[0]); + File newFile = new File(arg[1]); + File diffFile = new File(arg[2]); + + bspatch(oldFile, newFile, diffFile); + } + + /** + * @param oldFile + * @param newFile + * @param diffFile + * @throws IOException + */ + public static void bspatch(File oldFile, File newFile, File diffFile) + throws IOException { + InputStream oldInputStream = new BufferedInputStream( + new FileInputStream(oldFile)); + byte[] diffBytes = new byte[(int) diffFile.length()]; + InputStream diffInputStream = new FileInputStream(diffFile); + Util.readFromStream(diffInputStream, diffBytes, 0, diffBytes.length); + + byte[] newBytes = bspatch(oldInputStream, (int) oldFile.length(), + diffBytes); + + OutputStream newOutputStream = new FileOutputStream(newFile); + newOutputStream.write(newBytes); + newOutputStream.close(); + } + + /** + * @param oldInputStream + * @param diffInputStream + * @return + */ + public static byte[] bspatch(InputStream oldInputStream, int oldsize, + byte[] diffBytes) throws IOException { + /* + * Read in old file (file to be patched) to oldBuf + */ + // int oldsize = (int) oldFile.length(); + // byte[] oldBuf = new byte[oldsize + 1]; + byte[] oldBuf = new byte[oldsize]; + // InputStream oldIn = new FileInputStream( oldFile ); + Util.readFromStream(oldInputStream, oldBuf, 0, oldsize); + oldInputStream.close(); + // oldIn.close(); + + return JBPatch.bspatch(oldBuf, oldsize, diffBytes); + } + + /** + * @param oldBuf + * @param oldsize + * @param diffBytes + * @return + * @throws IOException + */ + public static byte[] bspatch(byte[] oldBuf, int oldsize, byte[] diffBytes) + throws IOException { + return bspatch(oldBuf, oldsize, diffBytes, diffBytes.length); + } + + /** + * @param oldBuf + * @param oldsize + * @param diffBuf + * @param diffSize + * @return + * @throws IOException + */ + public static byte[] bspatch(byte[] oldBuf, int oldsize, byte[] diffBuf, + int diffSize) throws IOException { + + DataInputStream diffIn = new DataInputStream(new ByteArrayInputStream( + diffBuf, 0, diffSize)); + + // skip headerMagic at header offset 0 (length 8 bytes) + diffIn.skip(8); + + // ctrlBlockLen after bzip2 compression at heater offset 8 (length 8 + // bytes) + long ctrlBlockLen = diffIn.readLong(); + + // diffBlockLen after bzip2 compression at header offset 16 (length 8 + // bytes) + long diffBlockLen = diffIn.readLong(); + + // size of new file at header offset 24 (length 8 bytes) + int newsize = (int) diffIn.readLong(); + + // System.err.println( "newsize=" + newsize ); + // System.err.println( "ctrlBlockLen=" + ctrlBlockLen ); + // System.err.println( "diffBlockLen=" + diffBlockLen ); + // System.err.println( "newsize=" + newsize ); + + InputStream in; + in = new ByteArrayInputStream(diffBuf, 0, diffSize); + in.skip(Util.HEADER_SIZE); + DataInputStream ctrlBlockIn = new DataInputStream( + new CBZip2InputStream(in)); + + in = new ByteArrayInputStream(diffBuf, 0, diffSize); + in.skip(ctrlBlockLen + Util.HEADER_SIZE); + InputStream diffBlockIn = new CBZip2InputStream(in); + + in = new ByteArrayInputStream(diffBuf, 0, diffSize); + in.skip(diffBlockLen + ctrlBlockLen + Util.HEADER_SIZE); + InputStream extraBlockIn = new CBZip2InputStream(in); + + // byte[] newBuf = new byte[newsize + 1]; + byte[] newBuf = new byte[newsize]; + + int oldpos = 0; + int newpos = 0; + int[] ctrl = new int[3]; + // int nbytes; + while (newpos < newsize) { + + for (int i = 0; i <= 2; i++) { + // ctrl[i] = diffIn.readInt(); + ctrl[i] = ctrlBlockIn.readInt(); + // System.err.println (" ctrl[" + i + "]=" + ctrl[i]); + } + + if (newpos + ctrl[0] > newsize) { + throw new IOException("Corrupt patch."); + } + + /* + * Read ctrl[0] bytes from diffBlock stream + */ + + Util.readFromStream(diffBlockIn, newBuf, newpos, ctrl[0]); + + for (int i = 0; i < ctrl[0]; i++) { + if ((oldpos + i >= 0) && (oldpos + i < oldsize)) { + newBuf[newpos + i] += oldBuf[oldpos + i]; + } + } + + newpos += ctrl[0]; + oldpos += ctrl[0]; + + if (newpos + ctrl[1] > newsize) { + throw new IOException("Corrupt patch."); + } + + Util.readFromStream(extraBlockIn, newBuf, newpos, ctrl[1]); + + newpos += ctrl[1]; + oldpos += ctrl[2]; + } + + // TODO: Check if at end of ctrlIn + // TODO: Check if at the end of diffIn + // TODO: Check if at the end of extraIn + + // This check is not needed since the byte array has been allocated with + // this constraint! + // if ( newBuf.length - 1 != newsize ) { + // throw new IOException( "Corrupt patch." ); + // } + + ctrlBlockIn.close(); + diffBlockIn.close(); + extraBlockIn.close(); + diffIn.close(); + + return newBuf; + // OutputStream out = new FileOutputStream( newFile ); + // out.write( newBuf, 0, newBuf.length - 1 ); + // out.close(); + } +} diff --git a/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Util.java b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Util.java new file mode 100644 index 000000000..175808e84 --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/ie/wombat/jbdiff/Util.java @@ -0,0 +1,109 @@ +/* + * Created on Feb 28, 2005 + */ +package ie.wombat.jbdiff; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author + * @author Joe Desbonnet, joe@galway.net + * + */ +public class Util { + + // JBDiff extensions by Stefan.Liebig@compeople.de: + // + // - introduced a HEADER_SIZE constant here + + /** + * Length of the diff file header. + */ + public static final int HEADER_SIZE = 32; + + /** + * Equiv of C library memcmp(). + * + * @param s1 + * @param s1offset + * @param s2 + * @param n + * @return + */ + /* + * public final static int memcmp(byte[] s1, int s1offset, byte[] s2, int + * s2offset, int n) { + * + * if ((s1offset + n) > s1.length) { n = s1.length - s1offset; } if + * ((s2offset + n) > s2.length) { n = s2.length - s2offset; } for (int i = + * 0; i < n; i++) { if (s1[i + s1offset] != s2[i + s2offset]) { return s1[i + + * s1offset] < s2[i + s2offset] ? -1 : 1; } } + * + * return 0; } + */ + + /** + * Equiv of C library memcmp(). + * + * @param s1 + * @param s1offset + * @param s2 + * @param n + * @return + */ + public final static int memcmp(byte[] s1, int s1Size, int s1offset, + byte[] s2, int s2Size, int s2offset) { + + int n = s1Size - s1offset; + + if (n > (s2Size - s2offset)) { + n = s2Size - s2offset; + } + for (int i = 0; i < n; i++) { + if (s1[i + s1offset] != s2[i + s2offset]) { + return s1[i + s1offset] < s2[i + s2offset] ? -1 : 1; + } + } + + return 0; + // int n = s1.length - s1offset; + // + // if (n > (s2.length - s2offset)) { + // n = s2.length - s2offset; + // } + // for (int i = 0; i < n; i++) { + // if (s1[i + s1offset] != s2[i + s2offset]) { + // return s1[i + s1offset] < s2[i + s2offset] ? -1 : 1; + // } + // } + // + // return 0; + } + + /** + * Read from input stream and fill the given buffer from the given offset up + * to length len. + * + * @param in + * @param buf + * @param offset + * @param len + * @throws IOException + */ + public static final void readFromStream(InputStream in, byte[] buf, + int offset, int len) throws IOException { + + int totalBytesRead = 0; + while (totalBytesRead < len) { + int bytesRead = in.read(buf, offset + totalBytesRead, len + - totalBytesRead); + if (bytesRead < 0) { + throw new IOException( + "Could not read expected number of bytes."); + } + totalBytesRead += bytesRead; + } + } + +} diff --git a/bundles/ie.wombat.jbdiff/src/readme-more.txt b/bundles/ie.wombat.jbdiff/src/readme-more.txt new file mode 100644 index 000000000..d822079cf --- /dev/null +++ b/bundles/ie.wombat.jbdiff/src/readme-more.txt @@ -0,0 +1,10 @@ +This library version is based on JBDiff (http://freshmeat.net/projects/jbdiff/) +which is also available under the OSI Approved :: BSD License (original) +http://www.opensource.org/licenses/bsd-license.html + +This library uses a modified version of the package org.apache.tools.bzip2 +of the Apache Ant project 1.7. + +Version 0.1.0.1 + - Extended interface so that it is not only file based and uses the extended CBZip2 as compressor. + -- cgit v1.2.3