270 lines
9.9 KiB
Java
270 lines
9.9 KiB
Java
|
/* 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;
|
||
|
}
|
||
|
}
|
||
|
}
|