### Eclipse Workspace Patch 1.0 #P oak-core Index: src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java (revision 1746251) +++ src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java (working copy) @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; @@ -43,14 +44,48 @@ * Tests the revision class */ public class RevisionTest { + + @Test + public void invalid() { + // revisions need to start with "br" or "r" + for(String s : "1234,b,bb,".split(",")) { + try { + Revision.fromString(s); + fail("Expected: Invalid revision id exception for " + s); + } catch (Exception expected) { + // expected + } + } + } + + @Test + public void edgeCases() { + assertEquals("br0-0-0", new Revision(0, 0, 0, true).toString()); + Random rand = new Random(0); + for (int i = 0; i < 1000; i++) { + Revision r = new Revision(rand.nextLong(), rand.nextInt(), + rand.nextInt(), rand.nextBoolean()); + assertEquals(r.toString(), Revision.fromString(r.toString()).toString()); + } + for (int i = 0; i < 1000; i++) { + Revision r = new Revision(rand.nextInt(10), rand.nextInt(10), + rand.nextInt(10), rand.nextBoolean()); + assertEquals(r.toString(), Revision.fromString(r.toString()).toString()); + } + } @Test public void fromStringToString() { for (int i = 0; i < 10000; i++) { Revision r = Revision.newRevision(i); // System.out.println(r); - Revision r2 = Revision.fromString(r.toString()); - assertEquals(r.toString(), r2.toString()); + String rs = r.toString(); + Revision r2 = Revision.fromString(rs); + if(!rs.equals(r2.toString())) { + r2 = Revision.fromString(rs); + assertEquals(rs, r2.toString()); + } + assertEquals(rs, r2.toString()); assertEquals(r.hashCode(), r2.hashCode()); assertTrue(r.equals(r2)); } Index: src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java (revision 1746251) +++ src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java (working copy) @@ -122,17 +122,20 @@ @Ignore("Performance test") @Test public void performance_revisionToString() { - Revision r = new Revision(System.currentTimeMillis(), 0, 0); - // warm up - for (int i = 0; i < 1 * 1000 * 1000; i++) { - r.toString(); + for (int i = 0; i < 4; i++) { + performance_revisionToStringOne(); } + } + + private static void performance_revisionToStringOne() { + Revision r = new Revision(System.currentTimeMillis(), 0, 0); + int dummy = 0; long time = System.currentTimeMillis(); for (int i = 0; i < 30 * 1000 * 1000; i++) { - r.toString(); + dummy += r.toString().length(); } time = System.currentTimeMillis() - time; - System.out.println(time); + System.out.println("time: " + time + " dummy " + dummy); } @Test Index: src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java (revision 1746251) +++ src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java (working copy) @@ -27,6 +27,8 @@ */ public final class Revision implements CacheValue { + final static int REV_STRING_SIZE = Revision.newRevision(0).toString().length(); + static final int SHALLOW_MEMORY_USAGE = 32; private static volatile long lastTimestamp; @@ -220,34 +222,45 @@ } public static Revision fromString(String rev) { - boolean isBranch = false; - if (rev.startsWith("b")) { - isBranch = true; - rev = rev.substring(1); - } - if (!rev.startsWith("r")) { + boolean isBranch = rev.charAt(0) == 'b'; + int idx = isBranch ? 2 : 1; + if (rev.charAt(idx - 1) != 'r') { throw new IllegalArgumentException(rev); } - int idxCount = rev.indexOf('-'); - if (idxCount < 0) { - throw new IllegalArgumentException(rev); + int len = rev.length(); + // Parse timestamp + long timestamp = 0; + for (; idx < len; idx++) { + char c = rev.charAt(idx); + if (c == '-') { + break; + } + int digit = c >= 'a'? c - 'a' + 10 : c - '0'; + timestamp = (timestamp << 4) + digit; } - int idxClusterId = rev.indexOf('-', idxCount + 1); - if (idxClusterId < 0) { - throw new IllegalArgumentException(rev); + // Parse counter + int counter = 0; + for (idx++; idx < len; idx++) { + char c = rev.charAt(idx); + if (c == '-') { + break; + } + int digit = c >= 'a' ? c - 'a' + 10 : c - '0'; + counter = (counter << 4) + digit; + } + // Parse clusterId + int clusterId = 0; + for (idx++; idx < len; idx++) { + char c = rev.charAt(idx); + int digit = c >= 'a' ? c - 'a' + 10 : c - '0'; + clusterId = (clusterId << 4) + digit; } - String t = rev.substring(1, idxCount); - long timestamp = Long.parseLong(t, 16); - t = rev.substring(idxCount + 1, idxClusterId); - int c = Integer.parseInt(t, 16); - t = rev.substring(idxClusterId + 1); - int clusterId = Integer.parseInt(t, 16); - return new Revision(timestamp, c, clusterId, isBranch); + return new Revision(timestamp, counter, clusterId, isBranch); } @Override public String toString() { - return toStringBuilder(new StringBuilder()).toString(); + return toStringBuilder(new StringBuilder(REV_STRING_SIZE)).toString(); } /** @@ -262,18 +275,11 @@ sb.append('b'); } sb.append('r'); - sb.append(Long.toHexString(timestamp)).append('-'); - if (counter < 10) { - sb.append(counter); - } else { - sb.append(Integer.toHexString(counter)); - } + toHexString(sb, timestamp); sb.append('-'); - if (clusterId < 10) { - sb.append(clusterId); - } else { - sb.append(Integer.toHexString(clusterId)); - } + toHexString(sb, counter); + sb.append('-'); + toHexString(sb, clusterId); return sb; } @@ -371,4 +377,19 @@ public int getMemory() { return SHALLOW_MEMORY_USAGE; } + + private static void toHexString(StringBuilder sb, long x) { + int bitCount = (64 - Long.numberOfLeadingZeros(x)); + bitCount = Math.max(0, ((bitCount + 3) / 4 * 4) - 4); + for (int i = bitCount; i >= 0; i -= 4) { + int t = (int) (x >> i) & 15; + if (t > 9) { + t = t - 10 + 'a'; + } else { + t += '0'; + } + sb.append((char) t); + } + } + }