8317515: Unify the code of the parse*() families of methods in j.l.Integer and j.l.Long

Reviewed-by: redestad
This commit is contained in:
Raffaello Giulietti 2023-10-06 16:20:21 +00:00
parent a64794b1ed
commit b62e774e6a
2 changed files with 296 additions and 367 deletions

View File

@ -38,6 +38,7 @@ import java.lang.invoke.MethodHandles;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import static java.lang.Character.digit;
import static java.lang.String.COMPACT_STRINGS; import static java.lang.String.COMPACT_STRINGS;
import static java.lang.String.LATIN1; import static java.lang.String.LATIN1;
import static java.lang.String.UTF16; import static java.lang.String.UTF16;
@ -538,8 +539,7 @@ public final class Integer extends Number
* does not contain a parsable {@code int}. * does not contain a parsable {@code int}.
*/ */
public static int parseInt(String s, int radix) public static int parseInt(String s, int radix)
throws NumberFormatException throws NumberFormatException {
{
/* /*
* WARNING: This method may be invoked early during VM initialization * WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use * before IntegerCache is initialized. Care must be taken to not use
@ -551,52 +551,41 @@ public final class Integer extends Number
} }
if (radix < Character.MIN_RADIX) { if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" less than Character.MIN_RADIX"); "radix %s less than Character.MIN_RADIX", radix));
} }
if (radix > Character.MAX_RADIX) { if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" greater than Character.MAX_RADIX"); "radix %s greater than Character.MAX_RADIX", radix));
} }
boolean negative = false; int len = s.length();
int i = 0, len = s.length(); if (len == 0) {
int limit = -Integer.MAX_VALUE; throw new NumberFormatException("");
}
if (len > 0) { int digit = ~0xFF;
char firstChar = s.charAt(0); int i = 0;
if (firstChar < '0') { // Possible leading "+" or "-" char firstChar = s.charAt(i++);
if (firstChar == '-') { if (firstChar != '-' && firstChar != '+') {
negative = true; digit = digit(firstChar, radix);
limit = Integer.MIN_VALUE; }
} else if (firstChar != '+') { if (digit >= 0 || digit == ~0xFF && len > 1) {
throw NumberFormatException.forInputString(s, radix); int limit = firstChar != '-' ? MIN_VALUE + 1 : MIN_VALUE;
}
if (len == 1) { // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s, radix);
}
i++;
}
int multmin = limit / radix; int multmin = limit / radix;
int result = 0; int result = -(digit & 0xFF);
while (i < len) { boolean inRange = true;
// Accumulating negatively avoids surprises near MAX_VALUE /* Accumulating negatively avoids surprises near MAX_VALUE */
int digit = Character.digit(s.charAt(i++), radix); while (i < len && (digit = digit(s.charAt(i++), radix)) >= 0
if (digit < 0 || result < multmin) { && (inRange = result > multmin
throw NumberFormatException.forInputString(s, radix); || result == multmin && digit <= radix * multmin - limit)) {
} result = radix * result - digit;
result *= radix; }
if (result < limit + digit) { if (inRange && i == len && digit >= 0) {
throw NumberFormatException.forInputString(s, radix); return firstChar != '-' ? -result : result;
}
result -= digit;
} }
return negative ? result : -result;
} else {
throw NumberFormatException.forInputString(s, radix);
} }
throw NumberFormatException.forInputString(s, radix);
} }
/** /**
@ -632,55 +621,47 @@ public final class Integer extends Number
Objects.checkFromToIndex(beginIndex, endIndex, s.length()); Objects.checkFromToIndex(beginIndex, endIndex, s.length());
if (radix < Character.MIN_RADIX) { if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" less than Character.MIN_RADIX"); "radix %s less than Character.MIN_RADIX", radix));
} }
if (radix > Character.MAX_RADIX) { if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" greater than Character.MAX_RADIX"); "radix %s greater than Character.MAX_RADIX", radix));
} }
boolean negative = false; /*
* While s can be concurrently modified, it is ensured that each
* of its characters is read at most once, from lower to higher indices.
* This is obtained by reading them using the pattern s.charAt(i++),
* and by not updating i anywhere else.
*/
if (beginIndex == endIndex) {
throw new NumberFormatException("");
}
int digit = ~0xFF;
int i = beginIndex; int i = beginIndex;
int limit = -Integer.MAX_VALUE; char firstChar = s.charAt(i++);
if (firstChar != '-' && firstChar != '+') {
if (i < endIndex) { digit = digit(firstChar, radix);
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);
}
i++;
result -= digit;
}
return negative ? result : -result;
} else {
throw NumberFormatException.forInputString("", radix);
} }
if (digit >= 0 || digit == ~0xFF && endIndex - beginIndex > 1) {
int limit = firstChar != '-' ? MIN_VALUE + 1 : MIN_VALUE;
int multmin = limit / radix;
int result = -(digit & 0xFF);
boolean inRange = true;
/* Accumulating negatively avoids surprises near MAX_VALUE */
while (i < endIndex && (digit = digit(s.charAt(i++), radix)) >= 0
&& (inRange = result > multmin
|| result == multmin && digit <= radix * multmin - limit)) {
result = radix * result - digit;
}
if (inRange && i == endIndex && digit >= 0) {
return firstChar != '-' ? -result : result;
}
}
throw NumberFormatException.forCharSequence(s, beginIndex,
endIndex, i - (digit < -1 ? 0 : 1));
} }
/** /**
@ -701,7 +682,7 @@ public final class Integer extends Number
* parsable integer. * parsable integer.
*/ */
public static int parseInt(String s) throws NumberFormatException { public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10); return parseInt(s, 10);
} }
/** /**
@ -753,31 +734,48 @@ public final class Integer extends Number
throw new NumberFormatException("Cannot parse null string"); throw new NumberFormatException("Cannot parse null string");
} }
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException(String.format(
"radix %s less than Character.MIN_RADIX", radix));
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException(String.format(
"radix %s greater than Character.MAX_RADIX", radix));
}
int len = s.length(); int len = s.length();
if (len > 0) { if (len == 0) {
char firstChar = s.charAt(0);
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);
} else {
long ell = Long.parseLong(s, radix);
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 NumberFormatException.forInputString(s, radix); throw NumberFormatException.forInputString(s, radix);
} }
int i = 0;
char firstChar = s.charAt(i++);
if (firstChar == '-') {
throw new NumberFormatException(String.format(
"Illegal leading minus sign on unsigned string %s.", s));
}
int digit = ~0xFF;
if (firstChar != '+') {
digit = digit(firstChar, radix);
}
if (digit >= 0 || digit == ~0xFF && len > 1) {
int multmax = divideUnsigned(-1, radix); // -1 is max unsigned int
int result = digit & 0xFF;
boolean inRange = true;
while (i < len && (digit = digit(s.charAt(i++), radix)) >= 0
&& (inRange = compareUnsigned(result, multmax) < 0
|| result == multmax && digit < -radix * multmax)) {
result = radix * result + digit;
}
if (inRange && i == len && digit >= 0) {
return result;
}
}
if (digit < 0) {
throw NumberFormatException.forInputString(s, radix);
}
throw new NumberFormatException(String.format(
"String value %s exceeds range of unsigned int.", s));
} }
/** /**
@ -812,32 +810,54 @@ public final class Integer extends Number
Objects.requireNonNull(s); Objects.requireNonNull(s);
Objects.checkFromToIndex(beginIndex, endIndex, s.length()); Objects.checkFromToIndex(beginIndex, endIndex, s.length());
int start = beginIndex, len = endIndex - beginIndex; if (radix < Character.MIN_RADIX) {
throw new NumberFormatException(String.format(
"radix %s less than Character.MIN_RADIX", radix));
}
if (len > 0) { if (radix > Character.MAX_RADIX) {
char firstChar = s.charAt(start); throw new NumberFormatException(String.format(
if (firstChar == '-') { "radix %s greater than Character.MAX_RADIX", radix));
throw new }
NumberFormatException(String.format("Illegal leading minus sign " +
"on unsigned string %s.", s)); /*
} else { * While s can be concurrently modified, it is ensured that each
if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits * of its characters is read at most once, from lower to higher indices.
(radix == 10 && len <= 9)) { // Integer.MAX_VALUE in base 10 is 10 digits * This is obtained by reading them using the pattern s.charAt(i++),
return parseInt(s, start, start + len, radix); * and by not updating i anywhere else.
} else { */
long ell = Long.parseLong(s, start, start + len, radix); if (beginIndex == endIndex) {
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(""); throw new NumberFormatException("");
} }
int i = beginIndex;
char firstChar = s.charAt(i++);
if (firstChar == '-') {
throw new NumberFormatException(
"Illegal leading minus sign on unsigned string " + s + ".");
}
int digit = ~0xFF;
if (firstChar != '+') {
digit = digit(firstChar, radix);
}
if (digit >= 0 || digit == ~0xFF && endIndex - beginIndex > 1) {
int multmax = divideUnsigned(-1, radix); // -1 is max unsigned int
int result = digit & 0xFF;
boolean inRange = true;
while (i < endIndex && (digit = digit(s.charAt(i++), radix)) >= 0
&& (inRange = compareUnsigned(result, multmax) < 0
|| result == multmax && digit < -radix * multmax)) {
result = radix * result + digit;
}
if (inRange && i == endIndex && digit >= 0) {
return result;
}
}
if (digit < 0) {
throw NumberFormatException.forCharSequence(s, beginIndex,
endIndex, i - (digit < -1 ? 0 : 1));
}
throw new NumberFormatException(String.format(
"String value %s exceeds range of unsigned int.", s));
} }
/** /**

View File

@ -38,6 +38,7 @@ import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable; import jdk.internal.vm.annotation.Stable;
import static java.lang.Character.digit;
import static java.lang.String.COMPACT_STRINGS; import static java.lang.String.COMPACT_STRINGS;
import static java.lang.String.LATIN1; import static java.lang.String.LATIN1;
import static java.lang.String.UTF16; import static java.lang.String.UTF16;
@ -574,58 +575,47 @@ public final class Long extends Number
* parsable {@code long}. * parsable {@code long}.
*/ */
public static long parseLong(String s, int radix) public static long parseLong(String s, int radix)
throws NumberFormatException throws NumberFormatException {
{
if (s == null) { if (s == null) {
throw new NumberFormatException("Cannot parse null string"); throw new NumberFormatException("Cannot parse null string");
} }
if (radix < Character.MIN_RADIX) { if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" less than Character.MIN_RADIX"); "radix %s less than Character.MIN_RADIX", radix));
} }
if (radix > Character.MAX_RADIX) { if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" greater than Character.MAX_RADIX"); "radix %s greater than Character.MAX_RADIX", radix));
} }
boolean negative = false; int len = s.length();
int i = 0, len = s.length(); if (len == 0) {
long limit = -Long.MAX_VALUE; throw new NumberFormatException("");
}
if (len > 0) { int digit = ~0xFF;
char firstChar = s.charAt(0); int i = 0;
if (firstChar < '0') { // Possible leading "+" or "-" char firstChar = s.charAt(i++);
if (firstChar == '-') { if (firstChar != '-' && firstChar != '+') {
negative = true; digit = digit(firstChar, radix);
limit = Long.MIN_VALUE; }
} else if (firstChar != '+') { if (digit >= 0 || digit == ~0xFF && len > 1) {
throw NumberFormatException.forInputString(s, radix); long limit = firstChar != '-' ? MIN_VALUE + 1 : MIN_VALUE;
}
if (len == 1) { // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s, radix);
}
i++;
}
long multmin = limit / radix; long multmin = limit / radix;
long result = 0; long result = -(digit & 0xFF);
while (i < len) { boolean inRange = true;
// Accumulating negatively avoids surprises near MAX_VALUE /* Accumulating negatively avoids surprises near MAX_VALUE */
int digit = Character.digit(s.charAt(i++),radix); while (i < len && (digit = digit(s.charAt(i++), radix)) >= 0
if (digit < 0 || result < multmin) { && (inRange = result > multmin
throw NumberFormatException.forInputString(s, radix); || result == multmin && digit <= (int) (radix * multmin - limit))) {
} result = radix * result - digit;
result *= radix; }
if (result < limit + digit) { if (inRange && i == len && digit >= 0) {
throw NumberFormatException.forInputString(s, radix); return firstChar != '-' ? -result : result;
}
result -= digit;
} }
return negative ? result : -result;
} else {
throw NumberFormatException.forInputString(s, radix);
} }
throw NumberFormatException.forInputString(s, radix);
} }
/** /**
@ -661,55 +651,47 @@ public final class Long extends Number
Objects.checkFromToIndex(beginIndex, endIndex, s.length()); Objects.checkFromToIndex(beginIndex, endIndex, s.length());
if (radix < Character.MIN_RADIX) { if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" less than Character.MIN_RADIX"); "radix %s less than Character.MIN_RADIX", radix));
} }
if (radix > Character.MAX_RADIX) { if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + throw new NumberFormatException(String.format(
" greater than Character.MAX_RADIX"); "radix %s greater than Character.MAX_RADIX", radix));
} }
boolean negative = false; /*
int i = beginIndex; * While s can be concurrently modified, it is ensured that each
long limit = -Long.MAX_VALUE; * of its characters is read at most once, from lower to higher indices.
* This is obtained by reading them using the pattern s.charAt(i++),
if (i < endIndex) { * and by not updating i anywhere else.
char firstChar = s.charAt(i); */
if (firstChar < '0') { // Possible leading "+" or "-" if (beginIndex == endIndex) {
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);
}
i++;
result -= digit;
}
return negative ? result : -result;
} else {
throw new NumberFormatException(""); throw new NumberFormatException("");
} }
int digit = ~0xFF; // ~0xFF means firstChar char is sign
int i = beginIndex;
char firstChar = s.charAt(i++);
if (firstChar != '-' && firstChar != '+') {
digit = digit(firstChar, radix);
}
if (digit >= 0 || digit == ~0xFF && endIndex - beginIndex > 1) {
long limit = firstChar != '-' ? MIN_VALUE + 1 : MIN_VALUE;
long multmin = limit / radix;
long result = -(digit & 0xFF);
boolean inRange = true;
/* Accumulating negatively avoids surprises near MAX_VALUE */
while (i < endIndex && (digit = digit(s.charAt(i++), radix)) >= 0
&& (inRange = result > multmin
|| result == multmin && digit <= (int) (radix * multmin - limit))) {
result = radix * result - digit;
}
if (inRange && i == endIndex && digit >= 0) {
return firstChar != '-' ? -result : result;
}
}
throw NumberFormatException.forCharSequence(s, beginIndex,
endIndex, i - (digit < -1 ? 0 : 1));
} }
/** /**
@ -789,87 +771,48 @@ public final class Long extends Number
throw new NumberFormatException("Cannot parse null string"); throw new NumberFormatException("Cannot parse null string");
} }
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException(String.format(
"radix %s less than Character.MIN_RADIX", radix));
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException(String.format(
"radix %s greater than Character.MAX_RADIX", radix));
}
int len = s.length(); int len = s.length();
if (len > 0) { if (len == 0) {
char firstChar = s.charAt(0);
if (firstChar == '-') {
throw new
NumberFormatException(String.format("Illegal leading minus sign " +
"on unsigned string %s.", s));
} 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);
}
// No need for range checks on len due to testing above.
long first = parseLong(s, 0, len - 1, radix);
int second = Character.digit(s.charAt(len - 1), radix);
if (second < 0) {
throw new NumberFormatException("Bad digit at end of " + s);
}
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));
}
return result;
}
} else {
throw NumberFormatException.forInputString(s, radix); throw NumberFormatException.forInputString(s, radix);
} }
int i = 0;
char firstChar = s.charAt(i++);
if (firstChar == '-') {
throw new NumberFormatException(String.format(
"Illegal leading minus sign on unsigned string %s.", s));
}
int digit = ~0xFF;
if (firstChar != '+') {
digit = digit(firstChar, radix);
}
if (digit >= 0 || digit == ~0xFF && len > 1) {
long multmax = divideUnsigned(-1L, radix); // -1L is max unsigned long
long result = digit & 0xFF;
boolean inRange = true;
while (i < len && (digit = digit(s.charAt(i++), radix)) >= 0
&& (inRange = compareUnsigned(result, multmax) < 0
|| result == multmax && digit < (int) (-radix * multmax))) {
result = radix * result + digit;
}
if (inRange && i == len && digit >= 0) {
return result;
}
}
if (digit < 0) {
throw NumberFormatException.forInputString(s, radix);
}
throw new NumberFormatException(String.format(
"String value %s exceeds range of unsigned long.", s));
} }
/** /**
@ -904,88 +847,54 @@ public final class Long extends Number
Objects.requireNonNull(s); Objects.requireNonNull(s);
Objects.checkFromToIndex(beginIndex, endIndex, s.length()); Objects.checkFromToIndex(beginIndex, endIndex, s.length());
int start = beginIndex, len = endIndex - beginIndex; if (radix < Character.MIN_RADIX) {
throw new NumberFormatException(String.format(
"radix %s less than Character.MIN_RADIX", radix));
}
if (len > 0) { if (radix > Character.MAX_RADIX) {
char firstChar = s.charAt(start); throw new NumberFormatException(String.format(
if (firstChar == '-') { "radix %s greater than Character.MAX_RADIX", radix));
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, start, start + len, radix);
}
// No need for range checks on end due to testing above. /*
long first = parseLong(s, start, start + len - 1, radix); * While s can be concurrently modified, it is ensured that each
int second = Character.digit(s.charAt(start + len - 1), radix); * of its characters is read at most once, from lower to higher indices.
if (second < 0) { * This is obtained by reading them using the pattern s.charAt(i++),
throw new NumberFormatException("Bad digit at end of " + * and by not updating i anywhere else.
s.subSequence(start, start + len)); */
} if (beginIndex == endIndex) {
long result = first * radix + second; throw new NumberFormatException("");
}
/* int i = beginIndex;
* Test leftmost bits of multiprecision extension of first*radix char firstChar = s.charAt(i++);
* for overflow. The number of bits needed is defined by if (firstChar == '-') {
* GUARD_BIT = ceil(log2(Character.MAX_RADIX)) + 1 = 7. Then throw new NumberFormatException(
* int guard = radix*(int)(first >>> (64 - GUARD_BIT)) and "Illegal leading minus sign on unsigned string " + s + ".");
* overflow is tested by splitting guard in the ranges }
* guard < 92, 92 <= guard < 128, and 128 <= guard, where int digit = ~0xFF;
* 92 = 128 - Character.MAX_RADIX. Note that guard cannot take if (firstChar != '+') {
* on a value which does not include a prime factor in the legal digit = digit(firstChar, radix);
* radix range. }
*/ if (digit >= 0 || digit == ~0xFF && endIndex - beginIndex > 1) {
int guard = radix * (int) (first >>> 57); long multmax = divideUnsigned(-1L, radix); // -1L is max unsigned long
if (guard >= 128 || long result = digit & 0xFF;
(result >= 0 && guard >= 128 - Character.MAX_RADIX)) { boolean inRange = true;
/* while (i < endIndex && (digit = digit(s.charAt(i++), radix)) >= 0
* For purposes of exposition, the programmatic statements && (inRange = compareUnsigned(result, multmax) < 0
* below should be taken to be multi-precision, i.e., not || result == multmax && digit < (int) (-radix * multmax))) {
* subject to overflow. result = radix * result + digit;
* }
* A) Condition guard >= 128: if (inRange && i == endIndex && digit >= 0) {
* 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; return result;
} }
} else {
throw NumberFormatException.forInputString("", radix);
} }
if (digit < 0) {
throw NumberFormatException.forCharSequence(s, beginIndex,
endIndex, i - (digit < -1 ? 0 : 1));
}
throw new NumberFormatException(String.format(
"String value %s exceeds range of unsigned long.", s));
} }
/** /**