8041972: Additional parse methods for Long/Integer
authorredestad
Sat, 12 Jul 2014 01:36:25 +0200
changeset 25653 41e5fa7ce490
parent 25652 3fa5768212c4
child 25654 bab08a81b601
8041972: Additional parse methods for Long/Integer Reviewed-by: mduigou, psandoz
jdk/src/share/classes/java/lang/Integer.java
jdk/src/share/classes/java/lang/Long.java
jdk/src/share/classes/java/lang/NumberFormatException.java
jdk/test/java/lang/Integer/ParsingTest.java
jdk/test/java/lang/Long/ParsingTest.java
--- a/jdk/src/share/classes/java/lang/Integer.java	Thu Jul 17 11:08:50 2014 -0700
+++ b/jdk/src/share/classes/java/lang/Integer.java	Sat Jul 12 01:36:25 2014 +0200
@@ -26,6 +26,7 @@
 package java.lang;
 
 import java.lang.annotation.Native;
+import java.util.Objects;
 
 /**
  * The {@code Integer} class wraps a value of the primitive type
@@ -549,12 +550,9 @@
                                             " greater than Character.MAX_RADIX");
         }
 
-        int result = 0;
         boolean negative = false;
         int i = 0, len = s.length();
         int limit = -Integer.MAX_VALUE;
-        int multmin;
-        int digit;
 
         if (len > 0) {
             char firstChar = s.charAt(0);
@@ -562,21 +560,21 @@
                 if (firstChar == '-') {
                     negative = true;
                     limit = Integer.MIN_VALUE;
-                } else if (firstChar != '+')
+                } else if (firstChar != '+') {
                     throw NumberFormatException.forInputString(s);
+                }
 
-                if (len == 1) // Cannot have lone "+" or "-"
+                if (len == 1) { // Cannot have lone "+" or "-"
                     throw NumberFormatException.forInputString(s);
+                }
                 i++;
             }
-            multmin = limit / radix;
+            int multmin = limit / radix;
+            int result = 0;
             while (i < len) {
                 // Accumulating negatively avoids surprises near MAX_VALUE
-                digit = Character.digit(s.charAt(i++),radix);
-                if (digit < 0) {
-                    throw NumberFormatException.forInputString(s);
-                }
-                if (result < multmin) {
+                int digit = Character.digit(s.charAt(i++), radix);
+                if (digit < 0 || result < multmin) {
                     throw NumberFormatException.forInputString(s);
                 }
                 result *= radix;
@@ -585,10 +583,126 @@
                 }
                 result -= digit;
             }
+            return negative ? result : -result;
         } else {
             throw NumberFormatException.forInputString(s);
         }
-        return negative ? result : -result;
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as a signed {@code int} in the
+     * specified {@code radix}, beginning at the specified {@code beginIndex}
+     * and extending to the end of the sequence.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the {@code int}
+     *                  representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @return     the signed {@code int} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable {@code int} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static int parseInt(CharSequence s, int radix, int beginIndex)
+                throws NumberFormatException {
+        // forces an implicit null check of s
+        return parseInt(s, radix, beginIndex, s.length());
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as a signed {@code int} in the
+     * specified {@code radix}, beginning at the specified {@code beginIndex}
+     * and extending to {@code endIndex - 1}.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the {@code int}
+     *                  representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @return     the signed {@code int} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code endIndex} or if {@code endIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable {@code int} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static int parseInt(CharSequence s, int radix, int beginIndex, int endIndex)
+                throws NumberFormatException {
+        s = Objects.requireNonNull(s);
+
+        if (beginIndex < 0 || beginIndex > endIndex || endIndex > s.length()) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (radix < Character.MIN_RADIX) {
+            throw new NumberFormatException("radix " + radix +
+                                            " less than Character.MIN_RADIX");
+        }
+        if (radix > Character.MAX_RADIX) {
+            throw new NumberFormatException("radix " + radix +
+                                            " greater than Character.MAX_RADIX");
+        }
+
+        boolean negative = false;
+        int i = beginIndex;
+        int limit = -Integer.MAX_VALUE;
+
+        if (i < endIndex) {
+            char firstChar = s.charAt(i);
+            if (firstChar < '0') { // Possible leading "+" or "-"
+                if (firstChar == '-') {
+                    negative = true;
+                    limit = Integer.MIN_VALUE;
+                } else if (firstChar != '+') {
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+                i++;
+                if (i == endIndex) { // Cannot have lone "+" or "-"
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+            }
+            int multmin = limit / radix;
+            int result = 0;
+            while (i < endIndex) {
+                // Accumulating negatively avoids surprises near MAX_VALUE
+                int digit = Character.digit(s.charAt(i++), radix);
+                if (digit < 0 || result < multmin) {
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+                result *= radix;
+                if (result < limit + digit) {
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+                result -= digit;
+            }
+            return negative ? result : -result;
+        } else {
+            throw NumberFormatException.forInputString("");
+        }
     }
 
     /**
@@ -689,6 +803,99 @@
     }
 
     /**
+     * Parses the {@link CharSequence} argument as an unsigned {@code int} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to the end of the sequence.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the unsigned
+     *                 {@code int} representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @return     the unsigned {@code int} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable unsigned {@code int} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static int parseUnsignedInt(CharSequence s, int radix, int beginIndex)
+                throws NumberFormatException {
+        // forces an implicit null check of s
+        return parseUnsignedInt(s, radix, beginIndex, s.length());
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as an unsigned {@code int} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to {@code endIndex - 1}.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the unsigned
+     *                 {@code int} representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @return     the unsigned {@code int} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code endIndex} or if {@code endIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable unsigned {@code int} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static int parseUnsignedInt(CharSequence s, int radix, int beginIndex, int endIndex)
+                throws NumberFormatException {
+        s = Objects.requireNonNull(s);
+
+        if (beginIndex < 0 || beginIndex > endIndex || endIndex > s.length()) {
+            throw new IndexOutOfBoundsException();
+        }
+        int start = beginIndex, len = endIndex - beginIndex;
+
+        if (len > 0) {
+            char firstChar = s.charAt(start);
+            if (firstChar == '-') {
+                throw new
+                    NumberFormatException(String.format("Illegal leading minus sign " +
+                                                       "on unsigned string %s.", s));
+            } else {
+                if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
+                        (radix == 10 && len <= 9)) { // Integer.MAX_VALUE in base 10 is 10 digits
+                    return parseInt(s, radix, start, start + len);
+                } else {
+                    long ell = Long.parseLong(s, radix, start, start + len);
+                    if ((ell & 0xffff_ffff_0000_0000L) == 0) {
+                        return (int) ell;
+                    } else {
+                        throw new
+                            NumberFormatException(String.format("String value %s exceeds " +
+                                                                "range of unsigned int.", s));
+                    }
+                }
+            }
+        } else {
+            throw new NumberFormatException("");
+        }
+    }
+
+    /**
      * Parses the string argument as an unsigned decimal integer. The
      * characters in the string must all be decimal digits, except
      * that the first character may be an an ASCII plus sign {@code
--- a/jdk/src/share/classes/java/lang/Long.java	Thu Jul 17 11:08:50 2014 -0700
+++ b/jdk/src/share/classes/java/lang/Long.java	Sat Jul 12 01:36:25 2014 +0200
@@ -27,6 +27,7 @@
 
 import java.lang.annotation.Native;
 import java.math.*;
+import java.util.Objects;
 
 
 /**
@@ -561,12 +562,9 @@
                                             " greater than Character.MAX_RADIX");
         }
 
-        long result = 0;
         boolean negative = false;
         int i = 0, len = s.length();
         long limit = -Long.MAX_VALUE;
-        long multmin;
-        int digit;
 
         if (len > 0) {
             char firstChar = s.charAt(0);
@@ -574,21 +572,21 @@
                 if (firstChar == '-') {
                     negative = true;
                     limit = Long.MIN_VALUE;
-                } else if (firstChar != '+')
+                } else if (firstChar != '+') {
                     throw NumberFormatException.forInputString(s);
+                }
 
-                if (len == 1) // Cannot have lone "+" or "-"
+                if (len == 1) { // Cannot have lone "+" or "-"
                     throw NumberFormatException.forInputString(s);
+                }
                 i++;
             }
-            multmin = limit / radix;
+            long multmin = limit / radix;
+            long result = 0;
             while (i < len) {
                 // Accumulating negatively avoids surprises near MAX_VALUE
-                digit = Character.digit(s.charAt(i++),radix);
-                if (digit < 0) {
-                    throw NumberFormatException.forInputString(s);
-                }
-                if (result < multmin) {
+                int digit = Character.digit(s.charAt(i++),radix);
+                if (digit < 0 || result < multmin) {
                     throw NumberFormatException.forInputString(s);
                 }
                 result *= radix;
@@ -597,10 +595,126 @@
                 }
                 result -= digit;
             }
+            return negative ? result : -result;
         } else {
             throw NumberFormatException.forInputString(s);
         }
-        return negative ? result : -result;
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as a signed {@code long} in
+     * the specified {@code radix}, beginning at the specified {@code beginIndex}
+     * and extending to the end of the sequence.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the {@code long}
+     *                  representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @return     the signed {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseLong(CharSequence s, int radix, int beginIndex)
+            throws NumberFormatException {
+        // forces a null check of s
+        return parseLong(s, radix, beginIndex, s.length());
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as a signed {@code long} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to {@code endIndex - 1}.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the {@code long}
+     *                  representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @return     the signed {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code endIndex} or if {@code endIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable {@code int} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseLong(CharSequence s, int radix, int beginIndex, int endIndex)
+                throws NumberFormatException {
+        s = Objects.requireNonNull(s);
+
+        if (beginIndex < 0 || beginIndex > endIndex || endIndex > s.length()) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (radix < Character.MIN_RADIX) {
+            throw new NumberFormatException("radix " + radix +
+                    " less than Character.MIN_RADIX");
+        }
+        if (radix > Character.MAX_RADIX) {
+            throw new NumberFormatException("radix " + radix +
+                    " greater than Character.MAX_RADIX");
+        }
+
+        boolean negative = false;
+        int i = beginIndex;
+        long limit = -Long.MAX_VALUE;
+
+        if (i < endIndex) {
+            char firstChar = s.charAt(i);
+            if (firstChar < '0') { // Possible leading "+" or "-"
+                if (firstChar == '-') {
+                    negative = true;
+                    limit = Long.MIN_VALUE;
+                } else if (firstChar != '+') {
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+                i++;
+            }
+            if (i >= endIndex) { // Cannot have lone "+", "-" or ""
+                throw NumberFormatException.forCharSequence(s, beginIndex,
+                        endIndex, i);
+            }
+            long multmin = limit / radix;
+            long result = 0;
+            while (i < endIndex) {
+                // Accumulating negatively avoids surprises near MAX_VALUE
+                int digit = Character.digit(s.charAt(i++), radix);
+                if (digit < 0 || result < multmin) {
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+                result *= radix;
+                if (result < limit + digit) {
+                    throw NumberFormatException.forCharSequence(s, beginIndex,
+                            endIndex, i);
+                }
+                result -= digit;
+            }
+            return negative ? result : -result;
+        } else {
+            throw new NumberFormatException("");
+        }
     }
 
     /**
@@ -694,7 +808,7 @@
                 }
 
                 // No need for range checks on len due to testing above.
-                long first = parseLong(s.substring(0, len - 1), radix);
+                long first = parseLong(s, radix, 0, len - 1);
                 int second = Character.digit(s.charAt(len - 1), radix);
                 if (second < 0) {
                     throw new NumberFormatException("Bad digit at end of " + s);
@@ -764,6 +878,155 @@
     }
 
     /**
+     * Parses the {@link CharSequence} argument as an unsigned {@code long} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to the end of the sequence.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the unsigned
+     *                 {@code long} representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @return     the unsigned {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable unsigned {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex)
+                throws NumberFormatException {
+        // forces a null check of s
+        return parseUnsignedLong(s, radix, beginIndex, s.length());
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as an unsigned {@code long} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to {@code endIndex - 1}.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the unsigned
+     *                 {@code long} representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @return     the unsigned {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, or if {@code beginIndex} is greater than
+     *             {@code endIndex} or if {@code endIndex} is greater than
+     *             {@code s.length()}.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable unsigned {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex, int endIndex)
+                throws NumberFormatException {
+        s = Objects.requireNonNull(s);
+
+        if (beginIndex < 0 || beginIndex > endIndex || endIndex > s.length()) {
+            throw new IndexOutOfBoundsException();
+        }
+        int start = beginIndex, len = endIndex - beginIndex;
+
+        if (len > 0) {
+            char firstChar = s.charAt(start);
+            if (firstChar == '-') {
+                throw new NumberFormatException(String.format("Illegal leading minus sign " +
+                        "on unsigned string %s.", s.subSequence(start, start + len)));
+            } else {
+                if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
+                    (radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
+                    return parseLong(s, radix, start, start + len);
+                }
+
+                // No need for range checks on end due to testing above.
+                long first = parseLong(s, radix, start, start + len - 1);
+                int second = Character.digit(s.charAt(start + len - 1), radix);
+                if (second < 0) {
+                    throw new NumberFormatException("Bad digit at end of " +
+                            s.subSequence(start, start + len));
+                }
+                long result = first * radix + second;
+
+                /*
+                 * Test leftmost bits of multiprecision extension of first*radix
+                 * for overflow. The number of bits needed is defined by
+                 * GUARD_BIT = ceil(log2(Character.MAX_RADIX)) + 1 = 7. Then
+                 * int guard = radix*(int)(first >>> (64 - GUARD_BIT)) and
+                 * overflow is tested by splitting guard in the ranges
+                 * guard < 92, 92 <= guard < 128, and 128 <= guard, where
+                 * 92 = 128 - Character.MAX_RADIX. Note that guard cannot take
+                 * on a value which does not include a prime factor in the legal
+                 * radix range.
+                 */
+                int guard = radix * (int) (first >>> 57);
+                if (guard >= 128 ||
+                        (result >= 0 && guard >= 128 - Character.MAX_RADIX)) {
+                    /*
+                     * For purposes of exposition, the programmatic statements
+                     * below should be taken to be multi-precision, i.e., not
+                     * subject to overflow.
+                     *
+                     * A) Condition guard >= 128:
+                     * If guard >= 128 then first*radix >= 2^7 * 2^57 = 2^64
+                     * hence always overflow.
+                     *
+                     * B) Condition guard < 92:
+                     * Define left7 = first >>> 57.
+                     * Given first = (left7 * 2^57) + (first & (2^57 - 1)) then
+                     * result <= (radix*left7)*2^57 + radix*(2^57 - 1) + second.
+                     * Thus if radix*left7 < 92, radix <= 36, and second < 36,
+                     * then result < 92*2^57 + 36*(2^57 - 1) + 36 = 2^64 hence
+                     * never overflow.
+                     *
+                     * C) Condition 92 <= guard < 128:
+                     * first*radix + second >= radix*left7*2^57 + second
+                     * so that first*radix + second >= 92*2^57 + 0 > 2^63
+                     *
+                     * D) Condition guard < 128:
+                     * radix*first <= (radix*left7) * 2^57 + radix*(2^57 - 1)
+                     * so
+                     * radix*first + second <= (radix*left7) * 2^57 + radix*(2^57 - 1) + 36
+                     * thus
+                     * radix*first + second < 128 * 2^57 + 36*2^57 - radix + 36
+                     * whence
+                     * radix*first + second < 2^64 + 2^6*2^57 = 2^64 + 2^63
+                     *
+                     * E) Conditions C, D, and result >= 0:
+                     * C and D combined imply the mathematical result
+                     * 2^63 < first*radix + second < 2^64 + 2^63. The lower
+                     * bound is therefore negative as a signed long, but the
+                     * upper bound is too small to overflow again after the
+                     * signed long overflows to positive above 2^64 - 1. Hence
+                     * result >= 0 implies overflow given C and D.
+                     */
+                    throw new NumberFormatException(String.format("String value %s exceeds " +
+                            "range of unsigned long.", s.subSequence(start, start + len)));
+                }
+                return result;
+            }
+        } else {
+            throw NumberFormatException.forInputString("");
+        }
+    }
+
+    /**
      * Parses the string argument as an unsigned decimal {@code long}. The
      * characters in the string must all be decimal digits, except
      * that the first character may be an an ASCII plus sign {@code
--- a/jdk/src/share/classes/java/lang/NumberFormatException.java	Thu Jul 17 11:08:50 2014 -0700
+++ b/jdk/src/share/classes/java/lang/NumberFormatException.java	Sat Jul 12 01:36:25 2014 +0200
@@ -56,7 +56,7 @@
     }
 
     /**
-     * Factory method for making a <code>NumberFormatException</code>
+     * Factory method for making a {@code NumberFormatException}
      * given the specified input which caused the error.
      *
      * @param   s   the input causing the error
@@ -64,4 +64,20 @@
     static NumberFormatException forInputString(String s) {
         return new NumberFormatException("For input string: \"" + s + "\"");
     }
+
+    /**
+     * Factory method for making a {@code NumberFormatException}
+     * given the specified input which caused the error.
+     *
+     * @param   s   the input causing the error
+     * @param   beginIndex   the beginning index, inclusive.
+     * @param   endIndex     the ending index, exclusive.
+     * @param   errorIndex   the index of the first error in s
+     */
+    static NumberFormatException forCharSequence(CharSequence s,
+            int beginIndex, int endIndex, int errorIndex) {
+        return new NumberFormatException("Error at index "
+                + (errorIndex - beginIndex) + " in: \""
+                + s.subSequence(beginIndex, endIndex) + "\"");
+    }
 }
--- a/jdk/test/java/lang/Integer/ParsingTest.java	Thu Jul 17 11:08:50 2014 -0700
+++ b/jdk/test/java/lang/Integer/ParsingTest.java	Sat Jul 12 01:36:25 2014 +0200
@@ -23,29 +23,37 @@
 
 /*
  * @test
- * @bug 5017980 6576055
+ * @bug 5017980 6576055 8041972
  * @summary Test parsing methods
  * @author Joseph D. Darcy
  */
 
+import java.lang.IllegalArgumentException;
+import java.lang.IndexOutOfBoundsException;
+import java.lang.NullPointerException;
+import java.lang.RuntimeException;
 
 /**
- * There are six methods in java.lang.Integer which transform strings
+ * There are eight methods in java.lang.Integer which transform strings
  * into an int or Integer value:
  *
  * public Integer(String s)
  * public static Integer decode(String nm)
+ * public static int parseInt(CharSequence s, int radix, int beginIndex, int endIndex)
+ * public static int parseInt(CharSequence s, int radix, int beginIndex)
  * public static int parseInt(String s, int radix)
  * public static int parseInt(String s)
  * public static Integer valueOf(String s, int radix)
  * public static Integer valueOf(String s)
  *
  * Besides decode, all the methods and constructor call down into
- * parseInt(String, int) to do the actual work.  Therefore, the
- * behavior of parseInt(String, int) will be tested here.
+ * parseInt(CharSequence, int, int, int) to do the actual work.  Therefore, the
+ * behavior of parseInt(CharSequence, int, int, int) will be tested here.
+ *
  */
 
 public class ParsingTest {
+
     public static void main(String... argv) {
         check("+100", +100);
         check("-100", -100);
@@ -55,10 +63,14 @@
         check("+00000", 0);
         check("-00000", 0);
 
+        check("+00000", 0, 0, 6);
+        check("-00000", 0, 0, 6);
+
         check("0", 0);
         check("1", 1);
         check("9", 9);
 
+        checkFailure("");
         checkFailure("\u0000");
         checkFailure("\u002f");
         checkFailure("+");
@@ -72,12 +84,45 @@
         checkFailure("+-6");
         checkFailure("-+6");
         checkFailure("*100");
+
+        check("test-00000", 0, 4, 10);
+        check("test-12345", -12345, 4, 10);
+        check("xx12345yy", 12345, 2, 7);
+
+        checkNumberFormatException("", 10, 0);
+        checkNumberFormatException("100", 10, 3);
+        checkNumberFormatException("+1000000", 10, 8);
+        checkNumberFormatException("-1000000", 10, 8);
+
+        checkNumberFormatException("", 10, 0, 0);
+        checkNumberFormatException("+-6", 10, 0, 3);
+        checkNumberFormatException("1000000", 10, 7);
+        checkNumberFormatException("1000000", 10, 7, 7);
+        checkNumberFormatException("1000000", Character.MAX_RADIX + 1, 0, 2);
+        checkNumberFormatException("1000000", Character.MIN_RADIX - 1, 0, 2);
+
+        checkIndexOutOfBoundsException("1000000", 10, 8);
+        checkIndexOutOfBoundsException("1000000", 10, -1);
+        checkIndexOutOfBoundsException("1000000", 10, 10, 4);
+        checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, -1, 2);
+        checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, -1, 2);
+        checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, 10, 2);
+        checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, 10, 2);
+        checkIndexOutOfBoundsException("-1", 10, 0, 3);
+        checkIndexOutOfBoundsException("-1", 10, 2, 3);
+        checkIndexOutOfBoundsException("-1", 10, -1, 2);
+
+        checkNull(10, 0, 1);
+        checkNull(10, -1, 0);
+        checkNull(10, 0, 0);
+        checkNull(10, 0, -1);
+        checkNull(-1, -1, -1);
     }
 
     private static void check(String val, int expected) {
         int n = Integer.parseInt(val);
         if (n != expected)
-            throw new RuntimeException("Integer.parsedInt failed. String:" +
+            throw new RuntimeException("Integer.parseInt failed. String:" +
                                                 val + " Result:" + n);
     }
 
@@ -91,4 +136,71 @@
             ; // Expected
         }
     }
+
+    private static void checkNumberFormatException(String val, int radix, int start) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(val, radix, start);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (NumberFormatException nfe) {
+            ; // Expected
+        }
+    }
+
+    private static void checkNumberFormatException(String val, int radix, int start, int end) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(val, radix, start, end);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start + ", " + end +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (NumberFormatException nfe) {
+            ; // Expected
+        }
+    }
+
+    private static void checkIndexOutOfBoundsException(String val, int radix, int start) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(val, radix, start);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (IndexOutOfBoundsException ioob) {
+            ; // Expected
+        }
+    }
+
+    private static void checkIndexOutOfBoundsException(String val, int radix, int start, int end) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(val, radix, start, end);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start + ", " + end +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (IndexOutOfBoundsException ioob) {
+            ; // Expected
+        }
+    }
+
+    private static void checkNull(int radix, int start, int end) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(null, 10, start, end);
+            System.err.println("parseInt(null, " + radix + ", " + start + ", " + end +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (NullPointerException npe) {
+            ; // Expected
+        }
+    }
+
+    private static void check(String val, int expected, int start, int end) {
+        int n = Integer.parseInt(val, 10, start, end);
+        if (n != expected)
+            throw new RuntimeException("Integer.parsedInt failed. String:" +
+                    val + ", start: " + start + ", end: " + end + " Result:" + n);
+    }
 }
--- a/jdk/test/java/lang/Long/ParsingTest.java	Thu Jul 17 11:08:50 2014 -0700
+++ b/jdk/test/java/lang/Long/ParsingTest.java	Sat Jul 12 01:36:25 2014 +0200
@@ -23,29 +23,31 @@
 
 /*
  * @test
- * @bug 5017980 6576055
+ * @bug 5017980 6576055 8041972
  * @summary Test parsing methods
  * @author Joseph D. Darcy
  */
 
-
 /**
- * There are six methods in java.lang.Long which transform strings
+ * There are eight methods in java.lang.Long which transform strings
  * into a long or Long value:
  *
  * public Long(String s)
  * public static Long decode(String nm)
+ * public static long parseLong(CharSequence s, int radix, int beginIndex, int endIndex)
+ * public static long parseLong(CharSequence s, int radix, int beginIndex)
  * public static long parseLong(String s, int radix)
  * public static long parseLong(String s)
  * public static Long valueOf(String s, int radix)
  * public static Long valueOf(String s)
  *
  * Besides decode, all the methods and constructor call down into
- * parseLong(String, int) to do the actual work.  Therefore, the
- * behavior of parseLong(String, int) will be tested here.
+ * parseLong(CharSequence, int, int, int) to do the actual work.  Therefore, the
+ * behavior of parseLong(CharSequence, int, int, int) will be tested here.
  */
 
 public class ParsingTest {
+
     public static void main(String... argv) {
         check("+100", +100L);
         check("-100", -100L);
@@ -59,6 +61,7 @@
         check("1", 1L);
         check("9", 9L);
 
+        checkFailure("");
         checkFailure("\u0000");
         checkFailure("\u002f");
         checkFailure("+");
@@ -72,12 +75,44 @@
         checkFailure("+-6");
         checkFailure("-+6");
         checkFailure("*100");
+
+        check("test-00000", 0L, 4, 10);
+        check("test-12345", -12345L, 4, 10);
+        check("xx12345yy", 12345L, 2, 7);
+        check("xx123456789012345yy", 123456789012345L, 2, 17);
+
+        checkNumberFormatException("100", 10, 3);
+        checkNumberFormatException("", 10, 0);
+        checkNumberFormatException("+1000000", 10, 8);
+        checkNumberFormatException("-1000000", 10, 8);
+
+        checkNumberFormatException("", 10, 0, 0);
+        checkNumberFormatException("+-6", 10, 0, 3);
+        checkNumberFormatException("1000000", 10, 7, 7);
+        checkNumberFormatException("1000000", Character.MAX_RADIX + 1, 0, 2);
+        checkNumberFormatException("1000000", Character.MIN_RADIX - 1, 0, 2);
+
+        checkIndexOutOfBoundsException("", 10, 1, 1);
+        checkIndexOutOfBoundsException("1000000", 10, 10, 4);
+        checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, 10, 2);
+        checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, 10, 2);
+        checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, -1, 2);
+        checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, -1, 2);
+        checkIndexOutOfBoundsException("-1", 10, 0, 3);
+        checkIndexOutOfBoundsException("-1", 10, 2, 3);
+        checkIndexOutOfBoundsException("-1", 10, -1, 2);
+
+        checkNull(10, 0, 1);
+        checkNull(10, -1, 0);
+        checkNull(10, 0, 0);
+        checkNull(10, 0, -1);
+        checkNull(-1, -1, -1);
     }
 
     private static void check(String val, long expected) {
         long n = Long.parseLong(val);
         if (n != expected)
-            throw new RuntimeException("Long.parsedLong failed. String:" +
+            throw new RuntimeException("Long.parseLong failed. String:" +
                                        val + " Result:" + n);
     }
 
@@ -91,4 +126,71 @@
             ; // Expected
         }
     }
+
+    private static void checkNumberFormatException(String val, int radix, int start) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(val, radix, start);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (NumberFormatException nfe) {
+            ; // Expected
+        }
+    }
+
+    private static void checkNumberFormatException(String val, int radix, int start, int end) {
+        long n = 0;
+        try {
+            n = Long.parseLong(val, radix, start, end);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start + ", " + end +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (NumberFormatException nfe) {
+            ; // Expected
+        }
+    }
+
+    private static void checkIndexOutOfBoundsException(String val, int radix, int start) {
+        int n = 0;
+        try {
+            n = Integer.parseInt(val, radix, start);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (IndexOutOfBoundsException ioob) {
+            ; // Expected
+        }
+    }
+
+    private static void checkIndexOutOfBoundsException(String val, int radix, int start, int end) {
+        long n = 0;
+        try {
+            n = Long.parseLong(val, radix, start, end);
+            System.err.println("parseInt(" + val + ", " + radix + ", " + start + ", " + end +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (IndexOutOfBoundsException ioob) {
+            ; // Expected
+        }
+    }
+
+    private static void checkNull(int radix, int start, int end) {
+        long n = 0;
+        try {
+            n = Long.parseLong(null, 10, start, end);
+            System.err.println("parseInt(null, " + radix + ", " + start + ", " + end +
+                    ") incorrectly returned " + n);
+            throw new RuntimeException();
+        } catch (NullPointerException npe) {
+            ; // Expected
+        }
+    }
+
+    private static void check(String val, long expected, int start, int end) {
+        long n = Long.parseLong(val, 10, start, end);
+        if (n != expected)
+            throw new RuntimeException("Long.parseLong failed. String:" +
+                    val + ", start: " + start + ", end: " + end + " Result:" + n);
+    }
 }