# HG changeset patch # User Artem Tikhomirov # Date 1313722772 -7200 # Node ID 0a2f445de774e242a9c60f1e9bb7f2cd48066d8a # Parent 3dd953c656193dfdc2478675af31ea03bd0bb781 Improve manifest parsing: reduce number of arrays instantiated for Nodeid diff -r 3dd953c65619 -r 0a2f445de774 src/org/tmatesoft/hg/core/Nodeid.java --- a/src/org/tmatesoft/hg/core/Nodeid.java Fri Aug 19 04:02:48 2011 +0200 +++ b/src/org/tmatesoft/hg/core/Nodeid.java Fri Aug 19 04:59:32 2011 +0200 @@ -20,6 +20,8 @@ import java.util.Arrays; +import org.tmatesoft.hg.internal.DigestHelper; + /** @@ -174,19 +176,7 @@ throw new IllegalArgumentException(); } byte[] data = new byte[20]; - boolean zeroBytes = true; - for (int i = 0, j = offset; i < data.length; i++) { - int b = asciiRepresentation[j++] & 0xCF; // -0x30 to get decimal digit out from their char, and to uppercase if a letter - int hiNibble = b > 64 ? b - 55 : b; - b = asciiRepresentation[j++] & 0xCF; - int lowNibble = b > 64 ? b - 55 : b; - if (hiNibble >= 16 || lowNibble >= 16) { - throw new IllegalArgumentException(String.format("Characters '%c%c' (%1$d and %2$d) at index %d are not valid hex digits", asciiRepresentation[j-2], asciiRepresentation[j-1], j-2)); - } - b = (((hiNibble << 4) | lowNibble) & 0xFF); - data[i] = (byte) b; - zeroBytes = zeroBytes && b == 0; - } + boolean zeroBytes = DigestHelper.ascii2bin(asciiRepresentation, offset, length, data); if (zeroBytes) { return NULL; } diff -r 3dd953c65619 -r 0a2f445de774 src/org/tmatesoft/hg/internal/DigestHelper.java --- a/src/org/tmatesoft/hg/internal/DigestHelper.java Fri Aug 19 04:02:48 2011 +0200 +++ b/src/org/tmatesoft/hg/internal/DigestHelper.java Fri Aug 19 04:59:32 2011 +0200 @@ -122,4 +122,24 @@ } return new String(result); } + + public static boolean ascii2bin(byte[] ascii, int offset, int len, byte[] binary) { + assert len % 2 == 0; + assert binary.length >= len >>> 1; + + boolean zeroBytes = true; + for (int i = 0, j = offset; i < len >>> 1; i++) { + int b = ascii[j++] & 0xCF; // -0x30 to get decimal digit out from their char, and to uppercase if a letter + int hiNibble = b > 64 ? b - 55 : b; + b = ascii[j++] & 0xCF; + int lowNibble = b > 64 ? b - 55 : b; + if (hiNibble >= 16 || lowNibble >= 16) { + throw new IllegalArgumentException(String.format("Characters '%c%c' (%1$d and %2$d) at index %d are not valid hex digits", ascii[j-2], ascii[j-1], j-2)); + } + b = (((hiNibble << 4) | lowNibble) & 0xFF); + binary[i] = (byte) b; + zeroBytes = zeroBytes && b == 0; + } + return zeroBytes; + } } diff -r 3dd953c65619 -r 0a2f445de774 src/org/tmatesoft/hg/repo/HgManifest.java --- a/src/org/tmatesoft/hg/repo/HgManifest.java Fri Aug 19 04:02:48 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgManifest.java Fri Aug 19 04:59:32 2011 +0200 @@ -26,6 +26,7 @@ import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.DataAccess; +import org.tmatesoft.hg.internal.DigestHelper; import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.Lifecycle; import org.tmatesoft.hg.internal.Pool; @@ -150,12 +151,14 @@ boolean end(int manifestRevision); } - private static class ManifestParser implements RevlogStream.Inspector/*, Lifecycle*/ { + private static class ManifestParser implements RevlogStream.Inspector/*, Lifecycle */{ private boolean gtg = true; // good to go private final Inspector inspector; private Pool2 nodeidPool, thisRevPool; private final Pool2 fnamePool; private final Pool flagsPool; + private final byte[] nodeidAsciiConvertBuffer = new byte[40]; + private byte[] nodeidLookupBuffer = new byte[20]; // get reassigned each time new Nodeid is added to pool public ManifestParser(Inspector delegate) { assert delegate != null; @@ -175,7 +178,6 @@ String fname = null; String flags = null; Nodeid nid = null; - final char[] nodeidConvertCache = new char[40]; String data = new String(da.byteArray()); final int dataLen = data.length(); // due to byte->char conversion, may be different for (int x = 0; gtg && x < dataLen; x++) { @@ -193,8 +195,19 @@ } z++; // cursor at first char of nodeid int nodeidLen = x-z < 40 ? x-z : 40; // if x-z > 40, there are flags - data.getChars(z, z+nodeidLen, nodeidConvertCache, 0); - nid = nodeidPool.unify(Nodeid.fromAscii(nodeidConvertCache, 0, nodeidLen)); + for (int k = 0; k < nodeidLen; k++) { + // intentionally didn't clear array as it shall be of length 40 (Nodeid.fromAscii won't stand anything but 40) + nodeidAsciiConvertBuffer[k] = (byte) data.charAt(z+k); + } + DigestHelper.ascii2bin(nodeidAsciiConvertBuffer, 0, nodeidLen, nodeidLookupBuffer); + nid = new Nodeid(nodeidLookupBuffer, false); // this Nodeid is for pool lookup only, mock object + Nodeid cached = nodeidPool.unify(nid); + if (cached == nid) { + // buffer now belongs to the cached nodeid + nodeidLookupBuffer = new byte[20]; + } else { + nid = cached; // use existing version, discard the lookup object + } thisRevPool.record(nid); // memorize revision for the next iteration. if (x-z > 40) { // 'x' and 'l' for executable bits and symlinks?