8006627: UUID to/from String performance should be improved by reducing object allocations
authorredestad
Mon, 21 Jul 2014 23:07:40 +0200
changeset 25668 a5d9c415d1fd
parent 25667 7428dc33b2bd
child 25669 daa21271c03b
8006627: UUID to/from String performance should be improved by reducing object allocations Reviewed-by: mduigou, plevart Contributed-by: Steven Schlansker <stevenschlansker@gmail.com>, Claes Redestad <claes.redestad@oracle.com>
jdk/src/share/classes/java/util/UUID.java
jdk/test/java/util/UUID/UUIDTest.java
--- a/jdk/src/share/classes/java/util/UUID.java	Tue Jul 22 13:13:27 2014 -0700
+++ b/jdk/src/share/classes/java/util/UUID.java	Mon Jul 21 23:07:40 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,9 @@
 
 import java.security.*;
 
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+
 /**
  * A class that represents an immutable universally unique identifier (UUID).
  * A UUID represents a 128-bit value.
@@ -88,6 +91,8 @@
      */
     private final long leastSigBits;
 
+    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+
     /*
      * The random number generator used by this class to create random
      * based UUIDs. In a holder class to defer initialization until needed.
@@ -189,21 +194,35 @@
      *
      */
     public static UUID fromString(String name) {
-        String[] components = name.split("-");
-        if (components.length != 5)
-            throw new IllegalArgumentException("Invalid UUID string: "+name);
-        for (int i=0; i<5; i++)
-            components[i] = "0x"+components[i];
+        if (name.length() > 36) {
+            throw new IllegalArgumentException("UUID string too large");
+        }
+
+        int dash1 = name.indexOf('-', 0);
+        int dash2 = name.indexOf('-', dash1 + 1);
+        int dash3 = name.indexOf('-', dash2 + 1);
+        int dash4 = name.indexOf('-', dash3 + 1);
+        int dash5 = name.indexOf('-', dash4 + 1);
 
-        long mostSigBits = Long.decode(components[0]).longValue();
+        // For any valid input, dash1 through dash4 will be positive and dash5
+        // negative, but it's enough to check dash4 and dash5:
+        // - if dash1 is -1, dash4 will be -1
+        // - if dash1 is positive but dash2 is -1, dash4 will be -1
+        // - if dash1 and dash2 is positive, dash3 will be -1, dash4 will be
+        //   positive, but so will dash5
+        if (dash4 < 0 || dash5 >= 0) {
+            throw new IllegalArgumentException("Invalid UUID string: " + name);
+        }
+
+        long mostSigBits = Long.parseLong(name, 16, 0, dash1) & 0xffffffffL;
         mostSigBits <<= 16;
-        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits |= Long.parseLong(name, 16, dash1 + 1, dash2) & 0xffffL;
         mostSigBits <<= 16;
-        mostSigBits |= Long.decode(components[2]).longValue();
+        mostSigBits |= Long.parseLong(name, 16, dash2 + 1, dash3) & 0xffffL;
 
-        long leastSigBits = Long.decode(components[3]).longValue();
+        long leastSigBits = Long.parseLong(name, 16, dash3 + 1, dash4) & 0xffffL;
         leastSigBits <<= 48;
-        leastSigBits |= Long.decode(components[4]).longValue();
+        leastSigBits |= Long.parseLong(name, 16, dash4 + 1) & 0xffffffffffffL;
 
         return new UUID(mostSigBits, leastSigBits);
     }
@@ -373,17 +392,17 @@
      * @return  A string representation of this {@code UUID}
      */
     public String toString() {
-        return (digits(mostSigBits >> 32, 8) + "-" +
-                digits(mostSigBits >> 16, 4) + "-" +
-                digits(mostSigBits, 4) + "-" +
-                digits(leastSigBits >> 48, 4) + "-" +
-                digits(leastSigBits, 12));
-    }
-
-    /** Returns val represented by the specified number of hex digits. */
-    private static String digits(long val, int digits) {
-        long hi = 1L << (digits * 4);
-        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
+        char[] chars = new char[36];
+        jla.formatUnsignedLong(mostSigBits >> 32, 4, chars, 0, 8);
+        chars[8] = '-';
+        jla.formatUnsignedLong(mostSigBits >> 16, 4, chars, 9, 4);
+        chars[13] = '-';
+        jla.formatUnsignedLong(mostSigBits, 4, chars, 14, 4);
+        chars[18] = '-';
+        jla.formatUnsignedLong(leastSigBits >> 48, 4, chars, 19, 4);
+        chars[23] = '-';
+        jla.formatUnsignedLong(leastSigBits, 4, chars, 24, 12);
+        return jla.newStringUnsafe(chars);
     }
 
     /**
--- a/jdk/test/java/util/UUID/UUIDTest.java	Tue Jul 22 13:13:27 2014 -0700
+++ b/jdk/test/java/util/UUID/UUIDTest.java	Mon Jul 21 23:07:40 2014 +0200
@@ -96,6 +96,23 @@
             if (!u1.equals(u2))
                 throw new Exception("UUID -> string -> UUID failed");
         }
+
+        testFromStringError("-0");
+        testFromStringError("x");
+        testFromStringError("----");
+        testFromStringError("-0-0-0-0");
+        testFromStringError("0-0-0-0-");
+        testFromStringError("0-0-0-0-0-");
+        testFromStringError("0-0-0-0-x");
+    }
+
+    private static void testFromStringError(String str) {
+        try {
+            UUID test = UUID.fromString(str);
+            throw new RuntimeException("Should have thrown IAE");
+        } catch (IllegalArgumentException iae) {
+            // pass
+        }
     }
 
     private static void versionTest() throws Exception {