changeset 266:0a2f445de774

Improve manifest parsing: reduce number of arrays instantiated for Nodeid
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 19 Aug 2011 04:59:32 +0200
parents 3dd953c65619
children ec921ef0628e
files src/org/tmatesoft/hg/core/Nodeid.java src/org/tmatesoft/hg/internal/DigestHelper.java src/org/tmatesoft/hg/repo/HgManifest.java
diffstat 3 files changed, 40 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- 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;
 		}
--- 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;
+	}
 }
--- 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<Nodeid> nodeidPool, thisRevPool;
 		private final Pool2<String> fnamePool;
 		private final Pool<String> 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?