src/share/classes/java/util/UUID.java

Print this page
rev 6519 : 8006627: Performance improvements to UUID(String) and UUID.toString()
Contributed-by: Steven Schlansker <stevenschlansker@gmail.com>

@@ -25,10 +25,13 @@
 
 package java.util;
 
 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.
  *
  * <p> There exist different variants of these global identifiers.  The methods

@@ -72,10 +75,16 @@
     /**
      * Explicit serialVersionUID for interoperability.
      */
     private static final long serialVersionUID = -4856846361193249489L;
 
+    /**
+     * Provides access to String's non-public constructor that does not copy the char[]
+     */
+    private static final JavaLangAccess JAVA_LANG_ACCESS
+            = SharedSecrets.getJavaLangAccess();
+
     /*
      * The most significant 64 bits of this UUID.
      *
      * @serial
      */

@@ -176,40 +185,108 @@
 
     /**
      * Creates a {@code UUID} from the string standard representation as
      * described in the {@link #toString} method.
      *
-     * @param  name
+     * @param  str
      *         A string that specifies a {@code UUID}
      *
      * @return  A {@code UUID} with the specified value
      *
      * @throws  IllegalArgumentException
      *          If name does not conform to the string representation as
      *          described in {@link #toString}
      *
      */
-    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];
+    public static UUID fromString(String str) {
+        int dashCount = 4;
+        int [] dashPos = new int [6];
+        dashPos[0] = -1;
+        dashPos[5] = str.length();
 
-        long mostSigBits = Long.decode(components[0]).longValue();
+        for (int i = str.length()-1; i >= 0; i--) {
+            if (str.charAt(i) == '-') {
+                if (dashCount == 0) {
+                    throw new IllegalArgumentException("Invalid UUID string: " + str);
+                }
+                dashPos[dashCount--] = i;
+            }
+        }
+
+        if (dashCount > 0) {
+            throw new IllegalArgumentException("Invalid UUID string: " + str);
+        }
+
+        long mostSigBits = decode(str, dashPos, 0) & 0xffffffffL;
         mostSigBits <<= 16;
-        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits |= decode(str, dashPos, 1) & 0xffffL;
         mostSigBits <<= 16;
-        mostSigBits |= Long.decode(components[2]).longValue();
+        mostSigBits |= decode(str,  dashPos, 2) & 0xffffL;
 
-        long leastSigBits = Long.decode(components[3]).longValue();
+        long leastSigBits = decode(str,  dashPos, 3) & 0xffffL;
         leastSigBits <<= 48;
-        leastSigBits |= Long.decode(components[4]).longValue();
+        leastSigBits |= decode(str,  dashPos, 4) & 0xffffffffffffL;
 
         return new UUID(mostSigBits, leastSigBits);
     }
 
+    private static long decode(final String str, final int [] dashPos, final int field) {
+        int start = dashPos[field]+1;
+        int end = dashPos[field+1];
+        if (start >= end) {
+            throw new IllegalArgumentException("Invalid UUID string: " + str);
+        }
+        return decodeLongHexString(str, start, end);
+    }
+
+    private static final int NUM_ALPHA_DIFF = 'A' - '9' - 1;
+    private static final int LOWER_UPPER_DIFF = 'a' - 'A';
+
+    /**
+     * Given a hexadecimal string, decode a portion into a long.
+     * @param str the string to decode
+     * @param start the start of the substring to decode
+     * @param end the end of the substring to decode
+     */
+    public static long decodeLongHexString(CharSequence str, int start, int end)
+    {
+        long curr = 0;
+        for (int i = start; i < end; i++) {
+            int x = getNibbleFromHexChar(str.charAt(i));
+            curr <<= 4;
+            if (curr < 0) {
+                throw new NumberFormatException("long overflow");
+            }
+            curr |= x;
+        }
+        return curr;
+    }
+
+    /**
+     * Given a hexadecimal digit, return the decimal value.
+     * @param c the character to decode
+     */
+    public static int getNibbleFromHexChar(final char c)
+    {
+        int x = c - '0';
+        if (x > 9) {
+            x -= NUM_ALPHA_DIFF; // difference between '9' and 'A'
+            if (x > 15) {
+                x -= LOWER_UPPER_DIFF; // difference between 'a' and 'A'
+            }
+            if (x < 10) {
+                throw new IllegalArgumentException(c + " is not a valid character for a hex string");
+            }
+        }
+
+        if (x < 0 || x > 15) {
+            throw new IllegalArgumentException(c + " is not a valid character for a hex string");
+        }
+
+        return x;
+    }
+
     // Field Accessor Methods
 
     /**
      * Returns the least significant 64 bits of this UUID's 128 bit value.
      *

@@ -369,22 +446,41 @@
      *       | "A" | "B" | "C" | "D" | "E" | "F"
      * }</pre></blockquote>
      *
      * @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));
+    public String toString()
+    {
+        return toString(getMostSignificantBits(), getLeastSignificantBits());
+    }
+
+    /**
+     * Returns a {@code String} object representing a UUID passed in as
+     * two longs.  Usually you will use {@link #toString()} instead.
+     * @param msb the most significant bytes
+     * @param lsb the least significant bytes
+     */
+    private static String toString(long msb, long lsb)
+    {
+        char[] uuidChars = new char[36];
+
+        digits(uuidChars, 0, 8, msb >> 32);
+        uuidChars[8] = '-';
+        digits(uuidChars, 9, 4, msb >> 16);
+        uuidChars[13] = '-';
+        digits(uuidChars, 14, 4, msb);
+        uuidChars[18] = '-';
+        digits(uuidChars, 19, 4, lsb >> 48);
+        uuidChars[23] = '-';
+        digits(uuidChars, 24, 12, lsb);
+
+        return JAVA_LANG_ACCESS.createStringSharedChars(uuidChars);
     }
 
-    /** 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);
+    private static void digits(char[] dest, int offset, int digits, long val) {
+        long mask = 1L << (digits * 4);
+        JAVA_LANG_ACCESS.formatUnsignedLong(mask | (val & (mask - 1)), 4, dest, offset, digits);
     }
 
     /**
      * Returns a hash code for this {@code UUID}.
      *