/* Taken from the javaxdelta project. http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/javaxdelta/javaxdelta/com/nothome/delta/Delta.java?rev=1.1.1.1&content-type=text/vnd.viewcvs-markup */ /* * * Copyright (c) 2001 Torgeir Veimo * Copyright (c) 2002 Nicolas PERIDONT * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * */ package org.thdl.util.javaxdelta; import java.io.*; import java.util.*; public class Delta { public final static int S = Checksum.S; public final static boolean debug = false; public final static int buff_size = 64*S; public Delta() { } public void computeDelta(File sourceFile, File targetFile, DiffWriter output) throws IOException { int targetLength = (int) targetFile.length(); int sourceLength = (int) sourceFile.length(); int targetidx = 0; Checksum checksum = new Checksum(); if (debug) { System.out.println("source len: " + sourceLength); System.out.println("target len: " + targetLength); System.out.println("using match length S = " + S); } checksum.generateChecksums(sourceFile, sourceLength); PushbackInputStream target = new PushbackInputStream(new BufferedInputStream(new FileInputStream(targetFile)),buff_size); RandomAccessFile source = new RandomAccessFile(sourceFile, "r"); boolean done = false; byte buf[] = new byte[S]; long hashf = 0; byte b[] = new byte[1]; byte sourcebyte[] = new byte[S]; if (targetLength-targetidx <= S) { System.err.println("too short input file"); return; } // initialize first complete checksum. target.read(buf, 0, S); targetidx += S; hashf = checksum.queryChecksum(buf, S); // The check for alternative hashf is only because I wanted to verify that the // update method really is correct. I will remove it shortly. long alternativehashf = hashf; if (debug) System.out.println("my hashf: " + hashf + ", adler32: " + alternativehashf); while (!done) { int index = checksum.findChecksumIndex(hashf); if (index != -1) { // possible match, need to check byte for byte boolean match = true; int offset = index * S; int length = S - 1; source.seek(offset); source.read(sourcebyte, 0, S); for (int ix = 0; ix < S; ix++) { if (sourcebyte[ix] != buf[ix]) { match = false; } } if (match) { //System.out.println("before targetidx : " + targetidx ); // The length of the match is determined by comparing bytes. long start = System.currentTimeMillis(); boolean ok = true; byte[] sourceBuff = new byte[buff_size]; byte[] targetBuff = new byte[buff_size]; int source_idx = 0; int target_idx = 0; do{ source_idx = source.read(sourceBuff, 0, buff_size); target_idx = target.read(targetBuff, 0, buff_size); int read_idx = Math.max(source_idx,target_idx); int i = 0; do { targetidx++; ++length; ok = sourceBuff[i] == targetBuff[i]; i++; if(!ok) { b[0] = targetBuff[i-1]; target.unread(targetBuff,i,target_idx-i); } } while(i < read_idx && ok); } while(ok && targetLength-targetidx > 0); // this is a insert instruction //System.out.println("output.addCopy("+offset+","+length+")"); output.addCopy(offset, length); if (targetLength-targetidx <= S) { // eof reached, special case for last bytes if (debug) System.out.println("last part of file"); buf[0] = b[0]; // don't loose this byte int remaining = targetLength-targetidx; target.read(buf, 1 , remaining); targetidx += remaining; for (int ix = 0; ix < (remaining + 1); ix++) output.addData(buf[ix]); done = true; } else { buf[0] = b[0]; target.read(buf, 1, S - 1); targetidx += S-1; alternativehashf = hashf = checksum.queryChecksum(buf, S); } continue; //continue loop } } if (targetLength-targetidx > 0) { // update the adler fingerpring with a single byte target.read(b, 0, 1); targetidx += 1; // insert instruction with the old byte we no longer use... output.addData(buf[0]); alternativehashf = checksum.incrementChecksum(alternativehashf, buf[0], b[0]); for (int j = 0; j < 15; j++) buf[j] = buf[j+1]; buf[15] = b[0]; hashf = checksum.queryChecksum(buf, S); if (debug) System.out.println("raw: " + Integer.toHexString((int)hashf) + ", incremental: " + Integer.toHexString((int)alternativehashf)); } else { for (int ix = 0; ix < S; ix++) output.addData(buf[ix]); done = true; } } output.close(); } // sample program to compute the difference between two input files. public static void main(String argv[]) { // DC: javaxdeltamain(argv); thdltoolsmain(argv); } public static void javaxdeltamain(String argv[]) { Delta delta = new Delta(); if (argv.length != 3) { System.err.println("usage Delta [-d] source target [output]"); System.err.println("either -d or an output filename must be specified."); System.err.println("aborting.."); return; } try { DiffWriter output = null; File sourceFile = null; File targetFile = null; if (argv[0].equals("-d")) { sourceFile = new File(argv[1]); targetFile = new File(argv[2]); output = new DebugDiffWriter(); } else { sourceFile = new File(argv[0]); targetFile = new File(argv[1]); output = new GDiffWriter(new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File(argv[2]))))); } if (sourceFile.length() > Integer.MAX_VALUE || targetFile.length() > Integer.MAX_VALUE) { System.err.println("source or target is too large, max length is " + Integer.MAX_VALUE); System.err.println("aborting.."); return; } //output.close(); delta.computeDelta(sourceFile, targetFile, output); output.flush(); output.close(); // DC: System.out.println("finished generating delta"); } catch (IOException ioe) { System.err.println("error while generating delta; " + ioe); } } public static void thdltoolsmain(String argv[]) { Delta delta = new Delta(); if (argv.length != 2) { System.err.println("usage Delta source target"); System.err.println("aborting.."); System.exit(-2); } int rc = Delta.areFilesDifferent(argv[0], argv[1]); if (rc == 1) System.out.println("files differ"); else if (rc == 0) System.out.println("files are the same"); else System.out.println("error comparing files"); System.exit(rc); } /** Returns 1 if source and target differ, 0 if they don't, -1 on error. David Chandler wrote this based on main(). */ public static int areFilesDifferent(String source, String target) { Delta delta = new Delta(); try { YesOrNoDiffWriter output = new YesOrNoDiffWriter(); File sourceFile = new File(source); File targetFile = new File(target); if (sourceFile.length() > Integer.MAX_VALUE || targetFile.length() > Integer.MAX_VALUE) { return -1; } delta.computeDelta(sourceFile, targetFile, output); return (output.areFilesDifferent()) ? 1 : 0; } catch (IOException ioe) { return -1; } } }