/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.delta;
import java.util.Arrays;
/**
* @version 1.3
* @deprecated {@link SVNXDeltaAlgorithm} is used instead in all cases.
* @author TMate Software Ltd.
*/
public class SVNVDeltaAlgorithm extends SVNDeltaAlgorithm {
private static final int VD_KEY_SIZE = 4;
private SlotsTable mySlotsTable;
public void computeDelta(byte[] a, int aLength, byte[] b, int bLength) {
int dataLength;
byte[] data;
if (aLength > 0 && bLength > 0) {
// both are non-empty (reuse some local array).
data = new byte[(aLength + bLength)];
System.arraycopy(a, 0, data, 0, aLength);
System.arraycopy(b, 0, data, aLength, bLength);
dataLength = aLength + bLength;
} else if (aLength == 0) {
// a is empty
data = b;
dataLength = bLength;
} else {
// b is empty
data = a;
dataLength = aLength;
}
SlotsTable slotsTable = getSlotsTable(dataLength);
vdelta(slotsTable, data, 0, aLength, false);
vdelta(slotsTable, data, aLength, dataLength, true);
}
private SlotsTable getSlotsTable(int dataLength) {
if (mySlotsTable == null) {
mySlotsTable = new SlotsTable();
}
mySlotsTable.reset(dataLength);
return mySlotsTable;
}
private void vdelta(SlotsTable table, byte[] data, int start, int end, boolean doOutput) {
int here = start;
int insertFrom = -1;
while(true) {
if (end - here < VD_KEY_SIZE) {
int from = insertFrom >= 0 ? insertFrom : here;
if (doOutput && from < end) {
copyFromNewData(data, from, end - from);
}
return;
}
int currentMatch = -1;
int currentMatchLength = 0;
int key;
int slot;
boolean progress = false;
key = here;
do {
progress = false;
for(slot = table.getBucket(table.getBucketIndex(data, key)); slot >= 0; slot = table.mySlots[slot]) {
if (slot < key - here) {
continue;
}
int match = slot - (key - here);
int matchLength = findMatchLength(data, match, here, end);
if (match < start && match + matchLength > start) {
matchLength = start - match;
}
if (matchLength >= VD_KEY_SIZE && matchLength > currentMatchLength) {
currentMatch = match;
currentMatchLength = matchLength;
progress = true;
}
}
if (progress) {
key = here + currentMatchLength - (VD_KEY_SIZE - 1);
}
} while (progress && end - key >= VD_KEY_SIZE);
if (currentMatchLength < VD_KEY_SIZE) {
table.storeSlot(data, here);
if (insertFrom < 0) {
insertFrom = here;
}
here++;
continue;
} else if (doOutput) {
if (insertFrom >= 0) {
copyFromNewData(data, insertFrom, here - insertFrom);
insertFrom = -1;
}
if (currentMatch < start) {
copyFromSource(currentMatch, currentMatchLength);
} else {
copyFromTarget(currentMatch - start, currentMatchLength);
}
}
here += currentMatchLength;
if (end - here >= VD_KEY_SIZE) {
int last = here - (VD_KEY_SIZE - 1);
for(; last < here; ++last) {
table.storeSlot(data, last);
}
}
}
}
private int findMatchLength(byte[] data, int match, int from, int end) {
int here = from;
while(here < end && data[match] == data[here]) {
match++;
here++;
}
return here - from;
}
private static class SlotsTable {
private int[] mySlots;
private int[] myBuckets;
private int myBucketsCount;
public SlotsTable() {
}
public void reset(int dataLength) {
mySlots = allocate(mySlots, dataLength);
myBucketsCount = (dataLength / 3) | 1;
myBuckets = allocate(myBuckets, myBucketsCount);
Arrays.fill(mySlots, 0, dataLength, -1);
Arrays.fill(myBuckets, 0, myBucketsCount, -1);
}
public int getBucketIndex(byte[] data, int index) {
int hash = 0;
hash += (data[index] & 0xFF);
hash += hash*127 + (data[index + 1] & 0xFF);
hash += hash*127 + (data[index + 2] & 0xFF);
hash += hash*127 + (data[index + 3] & 0xFF);
hash = hash % myBucketsCount;
return hash < 0 ? -hash : hash;
}
public int getBucket(int bucketIndex) {
return myBuckets[bucketIndex];
}
public void storeSlot(byte[] data, int slotIndex) {
int bucketIndex = getBucketIndex(data, slotIndex);
mySlots[slotIndex] = myBuckets[bucketIndex];
myBuckets[bucketIndex] = slotIndex;
}
private static int[] allocate(int[] array, int length) {
if (array == null || array.length < length) {
return new int[length*3/2];
}
return array;
}
}
}