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}.
*