-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8347009: Speed up parseInt and parseLong #22919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
5bb9644
d24c635
288ac04
fa31ec4
aed2e3c
f555dae
5801b17
9ea0845
844780d
46bb40d
3b8d458
2c4eeb4
426d607
e99f5bb
c7db300
736bd9f
e010e97
52defbd
fd51c1c
f97093d
b52130b
eb86797
e2b228c
dc2a675
0bacfa0
284ad6a
0b04a70
a6d9846
1fb40bb
2f67c24
40b9faf
2702440
0c43e9a
c4863da
c8f514f
a4181b0
3a555c5
08535b4
047e170
25848bb
f2920e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
/* | ||
* Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
|
@@ -517,52 +518,53 @@ public static String toUnsignedString(int i) { | |
*/ | ||
public static int parseInt(String s, int radix) | ||
throws NumberFormatException { | ||
/* | ||
* WARNING: This method may be invoked early during VM initialization | ||
* before IntegerCache is initialized. Care must be taken to not use | ||
* the valueOf method. | ||
*/ | ||
|
||
if (s == null) { | ||
throw new NumberFormatException("Cannot parse null string"); | ||
int len; | ||
byte[] value; | ||
if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { | ||
return parseInt0(s, radix); | ||
} | ||
|
||
if (radix < Character.MIN_RADIX) { | ||
throw new NumberFormatException(String.format( | ||
"radix %s less than Character.MIN_RADIX", radix)); | ||
/* Accumulating negatively avoids surprises near MAX_VALUE */ | ||
int fc = value[0]; | ||
int result = Integer.isDigitLatin1(fc) | ||
? '0' - fc | ||
: len != 1 && (fc == '-' || fc == '+') | ||
? 0 | ||
: 1; // or any value > 0 | ||
int i = 1; | ||
int d; | ||
while (i + 1 < len | ||
&& (d = DecimalDigits.digit2(value, i)) != -1 | ||
&& MIN_VALUE / 100 <= result & result <= 0) { | ||
result = result * 100 - d; // overflow from d => result > 0 | ||
i += 2; | ||
} | ||
|
||
if (radix > Character.MAX_RADIX) { | ||
throw new NumberFormatException(String.format( | ||
"radix %s greater than Character.MAX_RADIX", radix)); | ||
if (i < len | ||
&& Integer.isDigitLatin1(d = value[i]) | ||
&& MIN_VALUE / 10 <= result & result <= 0) { | ||
result = result * 10 + '0' - d; // overflow from '0' - d => result > 0 | ||
i += 1; | ||
} | ||
|
||
int len = s.length(); | ||
if (len == 0) { | ||
throw NumberFormatException.forInputString("", radix); | ||
if (i == len | ||
& result <= 0 | ||
& (MIN_VALUE < result || fc == '-')) { | ||
return fc == '-' ? result : -result; | ||
} | ||
int digit = ~0xFF; | ||
int i = 0; | ||
char firstChar = s.charAt(i++); | ||
if (firstChar != '-' && firstChar != '+') { | ||
digit = digit(firstChar, radix); | ||
throw NumberFormatException.forInputString(s); | ||
} | ||
|
||
private static int parseInt0(String s, int radix) { | ||
if (s == null) { | ||
throw NumberFormatException.nullInput(); | ||
} | ||
if (digit >= 0 || digit == ~0xFF && len > 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 < 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; | ||
} | ||
int len; | ||
if ((len = s.length()) == 0) { | ||
throw NumberFormatException.forInputString(s); | ||
} | ||
throw NumberFormatException.forInputString(s, radix); | ||
return parseInt(s, 0, len, radix); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this call will result in different exception messages in some cases. Consolidating the exception messages between the String and the CharSequence parsing methods would likely allow for even more code simplification, but currently it appears there was effort to preserve the exact message text in exceptions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to keep the original error message completely, we need to keep the original implementation, which will lead to code duplication. |
||
} | ||
|
||
static boolean isDigitLatin1(int ch) { | ||
return CharacterDataLatin1.instance.isDigit(ch); | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
/* | ||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
|
@@ -132,6 +133,53 @@ public static int stringSize(long x) { | |
return 19 + d; | ||
} | ||
|
||
/** | ||
* Determine whether the two character in str are both digits. If they are, return (str[offset] - '0') * 10 + (str[offset + 1] - '0'), otherwise return -1 | ||
* @param str The input LATIN1 encoded String value | ||
* @param offset the offset | ||
* @return If both characters are digits, return (str[offset] - '0') * 10 + (str[offset + 1] - '0'), otherwise return -1 | ||
*/ | ||
public static int digit2(byte[] str, int offset) { | ||
wenshao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. | ||
/* | ||
Here we are doing a 2-Byte Vector operation on the short type. | ||
|
||
x & 0xF0 != 0x30 | ||
--------------- | ||
0 0b0011_0000 & 0b1111_0000 = 0b0011_0000 | ||
1 0b0011_0001 & 0b1111_0000 = 0b0011_0000 | ||
2 0b0011_0010 & 0b1111_0000 = 0b0011_0000 | ||
3 0b0011_0011 & 0b1111_0000 = 0b0011_0000 | ||
4 0b0011_0100 & 0b1111_0000 = 0b0011_0000 | ||
5 0b0011_0101 & 0b1111_0000 = 0b0011_0000 | ||
6 0b0011_0110 & 0b1111_0000 = 0b0011_0000 | ||
7 0b0011_0111 & 0b1111_0000 = 0b0011_0000 | ||
8 0b0011_1000 & 0b1111_0000 = 0b0011_0000 | ||
9 0b0011_1001 & 0b1111_0000 = 0b0011_0000 | ||
|
||
(((d = x & 0x0F) + 0x06) & 0xF0) != 0 | ||
--------------- | ||
0 ((0b0011_0000) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
1 ((0b0011_0001) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
2 ((0b0011_0010) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
3 ((0b0011_0011) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
4 ((0b0011_0100) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
5 ((0b0011_0101) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
6 ((0b0011_0110) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
7 ((0b0011_0111) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
8 ((0b0011_1000) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
9 ((0b0011_1001) & 0b0000_1111 + 0b0110_0000) & 0b1111_0000 = 0b0110_0000 | ||
*/ | ||
int d; | ||
short x = UNSAFE.getShortUnaligned(str, Unsafe.ARRAY_BYTE_BASE_OFFSET + offset, false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wenshao I'm a bit worried about the use of Unsafe here. This method is Did you consider safer alternatives, like usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using VarHandle in this scenario may affect JVM startup performance There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I concur that unsafe usage here is undesirable. Performance is not the only metric of interest when working on the JDK core libraries. |
||
if ((((x & 0xF0F0) - 0x3030) | ||
| (((d = x & 0x0F0F) + 0x0606) & 0xF0F0)) != 0 | ||
) { | ||
return -1; | ||
} | ||
return (d & 0xF) * 10 + (d >> 8); | ||
} | ||
|
||
/** | ||
* Places characters representing the integer i into the | ||
* character array buf. The characters are placed into | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
|
@@ -112,7 +112,7 @@ protected void runCases() { | |
execCommand(JdbCommand.set("JdbExprTestTarg.anInt", "0x80000000")) | ||
.shouldMatch("InvalidTypeException: .* convert 2147483648 to int"); | ||
execCommand(JdbCommand.set("JdbExprTestTarg.anInt", "0x8000000000000000L")) | ||
.shouldContain("java.lang.NumberFormatException: For input string: \"8000000000000000\""); | ||
.shouldContain("java.lang.NumberFormatException: Error at index 15 in: \"8000000000000000\""); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year should be adjusted. |
||
|
||
execCommand(JdbCommand.set("JdbExprTestTarg.anInt", "0x7fffffff")) | ||
.shouldContain("0x7fffffff = 2147483647"); | ||
|
Uh oh!
There was an error while loading. Please reload this page.