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:
parent
a64794b1ed
commit
b62e774e6a
@ -38,6 +38,7 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.lang.Character.digit;
|
||||
import static java.lang.String.COMPACT_STRINGS;
|
||||
import static java.lang.String.LATIN1;
|
||||
import static java.lang.String.UTF16;
|
||||
@ -538,8 +539,7 @@ public final class Integer extends Number
|
||||
* does not contain a parsable {@code int}.
|
||||
*/
|
||||
public static int parseInt(String s, int radix)
|
||||
throws NumberFormatException
|
||||
{
|
||||
throws NumberFormatException {
|
||||
/*
|
||||
* WARNING: This method may be invoked early during VM initialization
|
||||
* 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) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" less than Character.MIN_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s less than Character.MIN_RADIX", radix));
|
||||
}
|
||||
|
||||
if (radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" greater than Character.MAX_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s greater than Character.MAX_RADIX", radix));
|
||||
}
|
||||
|
||||
boolean negative = false;
|
||||
int i = 0, len = s.length();
|
||||
int limit = -Integer.MAX_VALUE;
|
||||
|
||||
if (len > 0) {
|
||||
char firstChar = s.charAt(0);
|
||||
if (firstChar < '0') { // Possible leading "+" or "-"
|
||||
if (firstChar == '-') {
|
||||
negative = true;
|
||||
limit = Integer.MIN_VALUE;
|
||||
} else if (firstChar != '+') {
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
|
||||
if (len == 1) { // Cannot have lone "+" or "-"
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
int len = s.length();
|
||||
if (len == 0) {
|
||||
throw new NumberFormatException("");
|
||||
}
|
||||
int digit = ~0xFF;
|
||||
int i = 0;
|
||||
char firstChar = s.charAt(i++);
|
||||
if (firstChar != '-' && firstChar != '+') {
|
||||
digit = digit(firstChar, radix);
|
||||
}
|
||||
if (digit >= 0 || digit == ~0xFF && len > 1) {
|
||||
int limit = firstChar != '-' ? MIN_VALUE + 1 : MIN_VALUE;
|
||||
int multmin = limit / radix;
|
||||
int result = 0;
|
||||
while (i < len) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
int digit = Character.digit(s.charAt(i++), radix);
|
||||
if (digit < 0 || result < multmin) {
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
result *= radix;
|
||||
if (result < limit + digit) {
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
result -= digit;
|
||||
int result = -(digit & 0xFF);
|
||||
boolean inRange = true;
|
||||
/* Accumulating negatively avoids surprises near MAX_VALUE */
|
||||
while (i < len && (digit = digit(s.charAt(i++), radix)) >= 0
|
||||
&& (inRange = result > multmin
|
||||
|| result == multmin && digit <= radix * multmin - limit)) {
|
||||
result = radix * result - digit;
|
||||
}
|
||||
if (inRange && i == len && digit >= 0) {
|
||||
return firstChar != '-' ? -result : result;
|
||||
}
|
||||
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());
|
||||
|
||||
if (radix < Character.MIN_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" less than Character.MIN_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s less than Character.MIN_RADIX", radix));
|
||||
}
|
||||
|
||||
if (radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" greater than Character.MAX_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"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 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);
|
||||
}
|
||||
i++;
|
||||
result -= digit;
|
||||
}
|
||||
return negative ? result : -result;
|
||||
} else {
|
||||
throw NumberFormatException.forInputString("", radix);
|
||||
char firstChar = s.charAt(i++);
|
||||
if (firstChar != '-' && firstChar != '+') {
|
||||
digit = digit(firstChar, 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.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
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();
|
||||
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 {
|
||||
if (len == 0) {
|
||||
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.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) {
|
||||
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, start, start + len, radix);
|
||||
} else {
|
||||
long ell = Long.parseLong(s, start, start + len, 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 {
|
||||
if (radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s greater than Character.MAX_RADIX", radix));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,6 +38,7 @@ import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import static java.lang.Character.digit;
|
||||
import static java.lang.String.COMPACT_STRINGS;
|
||||
import static java.lang.String.LATIN1;
|
||||
import static java.lang.String.UTF16;
|
||||
@ -574,58 +575,47 @@ public final class Long extends Number
|
||||
* parsable {@code long}.
|
||||
*/
|
||||
public static long parseLong(String s, int radix)
|
||||
throws NumberFormatException
|
||||
{
|
||||
throws NumberFormatException {
|
||||
if (s == null) {
|
||||
throw new NumberFormatException("Cannot parse null string");
|
||||
}
|
||||
|
||||
if (radix < Character.MIN_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" less than Character.MIN_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s less than Character.MIN_RADIX", radix));
|
||||
}
|
||||
|
||||
if (radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" greater than Character.MAX_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s greater than Character.MAX_RADIX", radix));
|
||||
}
|
||||
|
||||
boolean negative = false;
|
||||
int i = 0, len = s.length();
|
||||
long limit = -Long.MAX_VALUE;
|
||||
|
||||
if (len > 0) {
|
||||
char firstChar = s.charAt(0);
|
||||
if (firstChar < '0') { // Possible leading "+" or "-"
|
||||
if (firstChar == '-') {
|
||||
negative = true;
|
||||
limit = Long.MIN_VALUE;
|
||||
} else if (firstChar != '+') {
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
|
||||
if (len == 1) { // Cannot have lone "+" or "-"
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
int len = s.length();
|
||||
if (len == 0) {
|
||||
throw new NumberFormatException("");
|
||||
}
|
||||
int digit = ~0xFF;
|
||||
int i = 0;
|
||||
char firstChar = s.charAt(i++);
|
||||
if (firstChar != '-' && firstChar != '+') {
|
||||
digit = digit(firstChar, radix);
|
||||
}
|
||||
if (digit >= 0 || digit == ~0xFF && len > 1) {
|
||||
long limit = firstChar != '-' ? MIN_VALUE + 1 : MIN_VALUE;
|
||||
long multmin = limit / radix;
|
||||
long result = 0;
|
||||
while (i < len) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
int digit = Character.digit(s.charAt(i++),radix);
|
||||
if (digit < 0 || result < multmin) {
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
result *= radix;
|
||||
if (result < limit + digit) {
|
||||
throw NumberFormatException.forInputString(s, radix);
|
||||
}
|
||||
result -= digit;
|
||||
long result = -(digit & 0xFF);
|
||||
boolean inRange = true;
|
||||
/* Accumulating negatively avoids surprises near MAX_VALUE */
|
||||
while (i < len && (digit = digit(s.charAt(i++), radix)) >= 0
|
||||
&& (inRange = result > multmin
|
||||
|| result == multmin && digit <= (int) (radix * multmin - limit))) {
|
||||
result = radix * result - digit;
|
||||
}
|
||||
if (inRange && i == len && digit >= 0) {
|
||||
return firstChar != '-' ? -result : result;
|
||||
}
|
||||
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());
|
||||
|
||||
if (radix < Character.MIN_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" less than Character.MIN_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s less than Character.MIN_RADIX", radix));
|
||||
}
|
||||
|
||||
if (radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException("radix " + radix +
|
||||
" greater than Character.MAX_RADIX");
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s greater than Character.MAX_RADIX", 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);
|
||||
}
|
||||
i++;
|
||||
result -= digit;
|
||||
}
|
||||
return negative ? result : -result;
|
||||
} else {
|
||||
/*
|
||||
* 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; // ~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");
|
||||
}
|
||||
|
||||
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();
|
||||
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 {
|
||||
if (len == 0) {
|
||||
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.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) {
|
||||
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, start, start + len, radix);
|
||||
}
|
||||
if (radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException(String.format(
|
||||
"radix %s greater than Character.MAX_RADIX", radix));
|
||||
}
|
||||
|
||||
// No need for range checks on end due to testing above.
|
||||
long first = parseLong(s, start, start + len - 1, radix);
|
||||
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)));
|
||||
}
|
||||
/*
|
||||
* 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 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) {
|
||||
long multmax = divideUnsigned(-1L, radix); // -1L is max unsigned long
|
||||
long result = digit & 0xFF;
|
||||
boolean inRange = true;
|
||||
while (i < endIndex && (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 == endIndex && digit >= 0) {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user