/* * ==================================================================== * 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.Map; import org.tmatesoft.svn.core.internal.util.SVNHashMap; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNXDeltaAlgorithm extends SVNDeltaAlgorithm { private static final int MATCH_BLOCK_SIZE = 64; public void computeDelta(byte[] a, int aLength, byte[] b, int bLength) { if (bLength < MATCH_BLOCK_SIZE) { copyFromNewData(b, 0, bLength); return; } PseudoAdler32 bAdler = new PseudoAdler32(); Map aMatchesTable = createMatchesTable(a, aLength, MATCH_BLOCK_SIZE, bAdler); bAdler.reset(); bAdler.add(b, 0, MATCH_BLOCK_SIZE); int lo = 0; int size = bLength; Match previousInsertion = null; while(lo < size) { Match match = findMatch(aMatchesTable, bAdler, a, aLength, b, bLength, lo, previousInsertion); if (match == null) { if (previousInsertion != null && previousInsertion.length > 0) { previousInsertion.length++; } else { previousInsertion = new Match(lo, 1); } } else { if (previousInsertion != null && previousInsertion.length > 0) { copyFromNewData(b, previousInsertion.position, previousInsertion.length); previousInsertion = null; } copyFromSource(match.position, match.length); } int advance = match != null ? match.advance : 1; for (int next = lo; next < lo + advance; next++) { bAdler.remove(b[next]); if (next + MATCH_BLOCK_SIZE < bLength) { bAdler.add(b[next + MATCH_BLOCK_SIZE]); } } lo += advance; } if (previousInsertion != null && previousInsertion.length > 0) { copyFromNewData(b, previousInsertion.position, previousInsertion.length); previousInsertion = null; } } private static Match findMatch(Map matchesTable, PseudoAdler32 checksum, byte[] a, int aLength, byte[] b, int bLength, int bPos, Match previousInsertion) { Match existingMatch = (Match) matchesTable.get(new Integer(checksum.getValue())); if (existingMatch == null) { return null; } if (!equals(a, aLength, existingMatch.position, existingMatch.length, b, bLength, bPos)) { return null; } existingMatch = new Match(existingMatch.position, existingMatch.length); existingMatch.advance = existingMatch.length; // extend forward while(existingMatch.position + existingMatch.length < aLength && bPos + existingMatch.advance < bLength && a[existingMatch.position + existingMatch.length] == b[bPos + existingMatch.advance]) { existingMatch.length++; existingMatch.advance++; } // extend backward if (previousInsertion != null) { while(existingMatch.position > 0 && bPos > 0 && a[existingMatch.position - 1] == b[bPos -1] && previousInsertion.length != 0) { previousInsertion.length--; bPos--; existingMatch.position--; existingMatch.length++; } } return existingMatch; } private static Map createMatchesTable(byte[] data, int dataLength, int blockLength, PseudoAdler32 adler32) { Map matchesTable = new SVNHashMap(); for(int i = 0; i < dataLength; i+= blockLength) { int length = i + blockLength >= dataLength ? dataLength - i : blockLength; adler32.add(data, i, length); Integer checksum = new Integer(adler32.getValue()); if (!matchesTable.containsKey(checksum)) { matchesTable.put(checksum, new Match(i, length)); } adler32.reset(); } return matchesTable; } private static boolean equals(byte[] a, int aLength, int aPos, int length, byte[] b, int bLength, int bPos) { if (aPos + length - 1 > aLength || bPos + length > bLength) { return false; } for(int i = 0; i < length; i++) { if (a[aPos + i] != b[bPos + i]) { return false; } } return true; } private static class Match { public Match(int p, int l) { position = p; length = l; } public int position; public int length; public int advance; } private static int ADLER32_MASK = 0x0000FFFF; private static class PseudoAdler32 { private int myS1; private int myS2; private int myLength; public PseudoAdler32() { reset(); } public void add(byte b) { int z = b & 0x000000FF; myS1 = myS1 + z; myS1 = myS1 & ADLER32_MASK; myS2 = myS2 + myS1; myS2 = myS2 & ADLER32_MASK; myLength++; } public void remove(byte b) { int z = b & 0x000000FF; myS1 = myS1 - z; myS1 = myS1 & ADLER32_MASK; myS2 = myS2 - (myLength * z + 1); myS2 = myS2 & ADLER32_MASK; myLength--; } public void add(byte[] data, int offset, int length) { for (int i = offset; i < offset + length; i++) { add(data[i]); } } public int getValue() { return (myS2 << 16) | myS1; } public void reset() { myS1 = 1; myS2 = 0; myLength = 0; } } }