/*
* Syncany, www.syncany.org
* Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel@gmail.com>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.syncany.chunk;
/**
* A simple 32-bit "rolling" checksum. This checksum algorithm is based
* upon the algorithm outlined in the paper "The rsync algorithm" by
* Andrew Tridgell and Paul Mackerras. The algorithm works in such a way
* that if one knows the sum of a block
* <em>X<sub>k</sub>...X<sub>l</sub></em>, then it is a simple matter to
* compute the sum for <em>X<sub>k+1</sub>...X<sub>l+1</sub></em>.
*
* <p>The class has been adapted to work with the Syncany chunking classes.
*
* @author Casey Marshall
* @author Philipp C. Heckel <philipp.heckel@gmail.com>
* @version $Revision: 188 $
*/
public class Adler32Fingerprinter extends Fingerprinter {
protected final int charOffset;
/**
* The first half of the checksum.
*/
protected int a;
/**
* The second half of the checksum.
*/
protected int b;
/**
* The place from whence the current checksum has been computed.
*/
protected int pos;
/**
* The place to where the current checksum has been computed.
*/
protected int len;
/**
* The block from which the checksum is computed.
*/
protected byte[] block;
/**
* The index in {@link #newBlock} where the newest byte has
* been stored.
*/
protected int newIndex;
/**
* The block that is receiving new input.
*/
protected byte[] newBlock;
/**
* Creates a new rolling checksum. The <i>charOffset</i> argument
* affects the output of this checksum; rsync uses a char offset of
* 0, librsync 31.
*/
public Adler32Fingerprinter(int charOffset) {
this.charOffset = charOffset;
a = b = 0;
pos = 0;
}
public Adler32Fingerprinter() {
this(0);
}
@Override
public int getValue() {
return (a & 0xffff) | (b << 16);
}
@Override
public void reset() {
pos = 0;
a = b = 0;
len = 0;
}
/**
* "Roll" the checksum. This method takes a single byte as byte
* <em>X<sub>l+1</sub></em>, and recomputes the checksum for
* <em>X<sub>k+1</sub>...X<sub>l+1</sub></em>. This is the
* preferred method for updating the checksum.
*
* @param bt The next byte.
*/
@Override
public void roll(byte bt) {
a -= block[pos] + charOffset;
b -= len * (block[pos] + charOffset);
a += bt + charOffset;
b += a;
block[pos] = bt;
pos++;
if (pos == len) {
pos = 0;
}
}
/**
* Update the checksum by trimming off a byte only, not adding
* anything.
*/
public void trim() {
a -= block[pos % block.length] + charOffset;
b -= len * (block[pos % block.length] + charOffset);
pos++;
len--;
}
@Override
public void check(byte[] buf, int off, int len) {
block = new byte[len];
System.arraycopy(buf, off, block, 0, len);
reset();
this.len = block.length;
int i;
for (i = 0; i < block.length - 4; i += 4) {
b += 4 * (a + block[i]) + 3 * block[i + 1]
+ 2 * block[i + 2] + block[i + 3] + 10 * charOffset;
a += block[i] + block[i + 1] + block[i + 2]
+ block[i + 3] + 4 * charOffset;
}
for (; i < block.length; i++) {
a += block[i] + charOffset;
b += a;
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + a;
result = prime * result + b;
return result;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Adler32Fingerprinter) {
return ((Adler32Fingerprinter) o).a == a && ((Adler32Fingerprinter) o).b == b;
}
return false;
}
@Override
public String toString() {
return "Adler32";
}
}