--- old/src/share/classes/java/lang/Integer.java 2013-02-12 14:23:44.645248903 -0800 +++ new/src/share/classes/java/lang/Integer.java 2013-02-12 14:23:44.405248891 -0800 @@ -26,7 +26,6 @@ package java.lang; import java.lang.annotation.Native; -import java.util.Properties; /** * The {@code Integer} class wraps a value of the primitive type @@ -308,8 +307,11 @@ * Convert the integer to an unsigned number. */ private static String toUnsignedString0(int i, int shift) { - char[] buf = new char[32]; - int charPos = 32; + // assert shift > 0 && shift <=5 : "Illegal shift value"; + int mag = Integer.SIZE - Long.numberOfLeadingZeros(i); + int chars = Math.max(((mag + (shift - 1)) / shift), 1); + char[] buf = new char[chars]; + int charPos = chars; int radix = 1 << shift; int mask = radix - 1; do { @@ -317,7 +319,8 @@ i >>>= shift; } while (i != 0); - return new String(buf, charPos, (32 - charPos)); + // Use special constructor which takes over "buf". + return new String(buf, true); } @@ -875,6 +878,7 @@ * Returns the value of this {@code Integer} as a {@code long} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions + * @see Integer#toUnsignedLong(int) */ public long longValue() { return (long)value; --- old/src/share/classes/java/lang/Long.java 2013-02-12 14:23:45.369248938 -0800 +++ new/src/share/classes/java/lang/Long.java 2013-02-12 14:23:45.133248927 -0800 @@ -28,6 +28,7 @@ import java.lang.annotation.Native; import java.math.*; + /** * The {@code Long} class wraps a value of the primitive type {@code * long} in an object. An object of type {@code Long} contains a @@ -344,18 +345,39 @@ } /** - * Convert the integer to an unsigned number. + * Format a long (treated as unsigned) into a String. + * @param val the value to format + * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) */ - private static String toUnsignedString0(long i, int shift) { - char[] buf = new char[64]; - int charPos = 64; + static String toUnsignedString0(long val, int shift) { + // assert shift > 0 && shift <=5 : "Illegal shift value"; + int mag = Long.SIZE - Long.numberOfLeadingZeros(val); + int chars = Math.max(((mag + (shift - 1)) / shift), 1); + char[] buf = new char[chars]; + + formatUnsignedLong(val, shift, buf, 0, chars); + return new String(buf, true); + } + + /** + * Format a long (treated as unsigned) into a character buffer. + * @param val the unsigned long to format + * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) + * @param buf the character buffer to write to + * @param offset the offset in the destination buffer to start at + * @param len the number of characters to write + * @return the lowest character location used + */ + static int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len) { + int charPos = len; int radix = 1 << shift; long mask = radix - 1; do { - buf[--charPos] = Integer.digits[(int)(i & mask)]; - i >>>= shift; - } while (i != 0); - return new String(buf, charPos, (64 - charPos)); + buf[offset + --charPos] = Integer.digits[(int)(val & mask)]; + val >>>= shift; + } while (val != 0 && charPos > 0); + + return charPos; } /** --- old/src/share/classes/java/lang/System.java 2013-02-12 14:23:46.109248974 -0800 +++ new/src/share/classes/java/lang/System.java 2013-02-12 14:23:45.877248963 -0800 @@ -1218,6 +1218,12 @@ public StackTraceElement getStackTraceElement(Throwable t, int i) { return t.getStackTraceElement(i); } + public String createStringSharedChars(char[] chars) { + return new String(chars, true); + } + public int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len) { + return Long.formatUnsignedLong(val, shift, buf, offset, len); + } }); } --- old/src/share/classes/java/util/UUID.java 2013-02-12 14:23:46.837249009 -0800 +++ new/src/share/classes/java/util/UUID.java 2013-02-12 14:23:46.597248997 -0800 @@ -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. @@ -74,6 +77,12 @@ */ 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. * @@ -178,7 +187,7 @@ * 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 @@ -188,26 +197,94 @@ * 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(); + + 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 = Long.decode(components[0]).longValue(); + 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 /** @@ -371,18 +448,37 @@ * * @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); } /** --- old/src/share/classes/sun/misc/JavaLangAccess.java 2013-02-12 14:23:47.553249044 -0800 +++ new/src/share/classes/sun/misc/JavaLangAccess.java 2013-02-12 14:23:47.313249032 -0800 @@ -97,4 +97,25 @@ * Returns the ith StackTraceElement for the given throwable. */ StackTraceElement getStackTraceElement(Throwable t, int i); + + /** + * Returns a string backed by the provided character array. The character + * array must not be modified after the string is created in order to + * fulfill String's contract. + * + * @param chars the character array to back the string. + * @return a newly created string whose content is the character array. + */ + String createStringSharedChars(char[] chars); + + /** + * Format a long (treated as unsigned) into a character buffer. + * @param val the unsigned long to format + * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) + * @param buf the character buffer to write to + * @param offset the offset in the destination buffer to start at + * @param len the number of characters to write + * @return the lowest character location used + */ + int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len); }