From 5bb9644202a8b961faad02439c95a81ca6862168 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 10:42:33 +0800 Subject: [PATCH 01/35] add benchmark --- test/micro/org/openjdk/bench/java/lang/Longs.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/micro/org/openjdk/bench/java/lang/Longs.java b/test/micro/org/openjdk/bench/java/lang/Longs.java index 765d00e9fb90e..f525d2b6038a5 100644 --- a/test/micro/org/openjdk/bench/java/lang/Longs.java +++ b/test/micro/org/openjdk/bench/java/lang/Longs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -78,6 +78,13 @@ public void toStringSmall(Blackhole bh) { } } + @Benchmark + public void parseLong(Blackhole bh) { + for (String s : strings) { + bh.consume(Long.parseLong(s)); + } + } + @Benchmark public void decode(Blackhole bh) { for (String s : strings) { From d24c6357376e823b95f2cbc02c8f0a6726101f01 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 13:56:07 +0800 Subject: [PATCH 02/35] optimize parseLong --- .../share/classes/java/lang/Long.java | 86 +++++++++++-------- .../java/lang/NumberFormatException.java | 14 +++ 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 822199bb09b51..ec71619dfc8c6 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -89,6 +89,9 @@ public final class Long extends Number */ @Native public static final long MAX_VALUE = 0x7fffffffffffffffL; + static final long MULT_MIN = -922337203685477580L; + static final long MULT_MIN_2 = -92233720368547758L; + /** * The {@code Class} instance representing the primitive type * {@code long}. @@ -552,46 +555,59 @@ public static String toUnsignedString(long i) { */ public static long parseLong(String s, int radix) throws NumberFormatException { - if (s == null) { - 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; + if (s == null || radix != 10 || (len = s.length()) == 0) { + return parseLong0(s, radix); } - - int len = s.length(); - if (len == 0) { - throw NumberFormatException.forInputString("", radix); - } - int digit = ~0xFF; int i = 0; - char firstChar = s.charAt(i++); - if (firstChar != '-' && firstChar != '+') { - digit = digit(firstChar, radix); + int neg = s.charAt(0) - '-'; + if (neg == 0 + || neg + 2 == 0 // firstChar == '+' + ) { + i = 1; } - if (digit >= 0 || digit == ~0xFF && len > 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 < len && (digit = digit(s.charAt(i++), radix)) >= 0 - && (inRange = result > multmin - || result == multmin && digit <= (int) (radix * multmin - limit))) { - result = radix * result - digit; + long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); + boolean inRange = true, isDigit = false; + long result = 0; + int c = 0, c1, digit; + while (i + 1 < len && (isDigit = isDigit((c = s.charAt(i)))) && isDigit(c1 = s.charAt(i + 1))) { + digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' + if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { + break; } - if (inRange && i == len && digit >= 0) { - return firstChar != '-' ? -result : result; + result = result * 100 - digit; + i += 2; + } + if (inRange) { + if (i + 1 == len) { + isDigit = isDigit((c = s.charAt(i))); + } + if (i != len && isDigit) { + digit = c - '0'; + inRange = result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); + result = result * 10 - digit; + i++; + } + if (i == len && result <= 0) { + return neg != 0 ? -result : result; } } - throw NumberFormatException.forInputString(s, radix); + throw NumberFormatException.forInputString(s); + } + + private static long parseLong0(String s, int radix) { + if (s == null) { + throw NumberFormatException.emptyInput(); + } + int len; + if ((len = s.length()) == 0) { + throw NumberFormatException.forInputString(s); + } + return parseLong(s, 0, len, radix); + } + + static boolean isDigit(int ch) { + return ch >= '0' && ch <= '9'; } /** diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index 7a317304f6e2c..bb8e5dd021650 100644 --- a/src/java.base/share/classes/java/lang/NumberFormatException.java +++ b/src/java.base/share/classes/java/lang/NumberFormatException.java @@ -54,6 +54,16 @@ public NumberFormatException (String s) { super (s); } + /** + * Factory method for making a {@code NumberFormatException} + * given the specified input which caused the error. + * + * @param s the input causing the error + */ + static NumberFormatException forInputString(String s) { + return forInputString(s, 10); + } + /** * Factory method for making a {@code NumberFormatException} * given the specified input which caused the error. @@ -82,4 +92,8 @@ static NumberFormatException forCharSequence(CharSequence s, + (errorIndex - beginIndex) + " in: \"" + s.subSequence(beginIndex, endIndex) + "\""); } + + static NumberFormatException emptyInput() { + return new NumberFormatException("Cannot parse null string"); + } } From 288ac043fb6f71f9b7728046e8b109e0ce267087 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 15:33:38 +0800 Subject: [PATCH 03/35] optimize parseInt --- .../share/classes/java/lang/Integer.java | 90 ++++++++++--------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index a6bf739220fc3..d67eb9697b41d 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -89,6 +89,9 @@ public final class Integer extends Number */ @Native public static final int MAX_VALUE = 0x7fffffff; + private static final int MULT_MIN = Integer.MIN_VALUE / 10; + private static final int MULT_MIN_2 = Integer.MIN_VALUE / 100; + /** * The {@code Class} instance representing the primitive type * {@code int}. @@ -516,52 +519,59 @@ 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"); - } - - 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) { - throw NumberFormatException.forInputString("", radix); + int len; + if (s == null || radix != 10 || (len = s.length()) == 0) { + return parseInt0(s, radix); } - int digit = ~0xFF; int i = 0; - char firstChar = s.charAt(i++); - if (firstChar != '-' && firstChar != '+') { - digit = digit(firstChar, radix); + int neg = s.charAt(0) - '-'; + if (neg == 0 + || neg + 2 == 0 // firstChar == '+' + ) { + i = 1; } - 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; + int limit = MIN_VALUE + (neg != 0 ? 1 : 0); + boolean inRange = true, isDigit = false; + int result = 0; + int c = 0, c1, digit; + while (i + 1 < len && (isDigit = isDigit((c = s.charAt(i)))) && isDigit(c1 = s.charAt(i + 1))) { + digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' + if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { + break; } - if (inRange && i == len && digit >= 0) { - return firstChar != '-' ? -result : result; + result = result * 100 - digit; + i += 2; + } + if (inRange) { + if (i + 1 == len) { + isDigit = isDigit((c = s.charAt(i))); + } + if (i != len && isDigit) { + digit = c - '0'; + inRange = result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); + result = result * 10 - digit; + i++; + } + if (i == len && result <= 0) { + return neg != 0 ? -result : result; } } - throw NumberFormatException.forInputString(s, radix); + throw NumberFormatException.forInputString(s); + } + + private static int parseInt0(String s, int radix) { + if (s == null) { + throw NumberFormatException.emptyInput(); + } + int len; + if ((len = s.length()) == 0) { + throw NumberFormatException.forInputString(s); + } + return parseInt(s, 0, len, radix); + } + + static boolean isDigit(int ch) { + return ch >= '0' && ch <= '9'; } /** From fa31ec4855a7dbfc3c525a10e3e5d5afa81dd5eb Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 15:54:25 +0800 Subject: [PATCH 04/35] reuse isDigit --- src/java.base/share/classes/java/lang/Long.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index ec71619dfc8c6..862cbe8d260de 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -570,7 +570,7 @@ public static long parseLong(String s, int radix) boolean inRange = true, isDigit = false; long result = 0; int c = 0, c1, digit; - while (i + 1 < len && (isDigit = isDigit((c = s.charAt(i)))) && isDigit(c1 = s.charAt(i + 1))) { + while (i + 1 < len && (isDigit = Integer.isDigit((c = s.charAt(i)))) && Integer.isDigit(c1 = s.charAt(i + 1))) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { break; @@ -580,7 +580,7 @@ public static long parseLong(String s, int radix) } if (inRange) { if (i + 1 == len) { - isDigit = isDigit((c = s.charAt(i))); + isDigit = Integer.isDigit((c = s.charAt(i))); } if (i != len && isDigit) { digit = c - '0'; @@ -606,10 +606,6 @@ private static long parseLong0(String s, int radix) { return parseLong(s, 0, len, radix); } - static boolean isDigit(int ch) { - return ch >= '0' && ch <= '9'; - } - /** * Parses the {@link CharSequence} argument as a signed {@code long} in * the specified {@code radix}, beginning at the specified From aed2e3c277d84ad57fb8de750fbab1bd04007452 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 17:17:19 +0800 Subject: [PATCH 05/35] optimize parseInt & parseLong --- .../share/classes/java/lang/Integer.java | 19 ++++++----- .../share/classes/java/lang/Long.java | 32 +++++++++++++------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index d67eb9697b41d..79ece3120c410 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -523,17 +523,20 @@ public static int parseInt(String s, int radix) if (s == null || radix != 10 || (len = s.length()) == 0) { return parseInt0(s, radix); } - int i = 0; - int neg = s.charAt(0) - '-'; - if (neg == 0 - || neg + 2 == 0 // firstChar == '+' + int result = 0, c = s.charAt(0), c1, digit; + boolean inRange = true, isDigit = false; + int neg = c - '-'; + if (neg != 0 + && neg + 2 != 0 // firstChar != '+' ) { - i = 1; + if (isDigit(c)) { + result = '0' - c; + } else { + inRange = false; + } } int limit = MIN_VALUE + (neg != 0 ? 1 : 0); - boolean inRange = true, isDigit = false; - int result = 0; - int c = 0, c1, digit; + int i = 1; while (i + 1 < len && (isDigit = isDigit((c = s.charAt(i)))) && isDigit(c1 = s.charAt(i + 1))) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 862cbe8d260de..4704f3a51dfe0 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -559,20 +559,24 @@ public static long parseLong(String s, int radix) if (s == null || radix != 10 || (len = s.length()) == 0) { return parseLong0(s, radix); } - int i = 0; - int neg = s.charAt(0) - '-'; - if (neg == 0 - || neg + 2 == 0 // firstChar == '+' + int c = s.charAt(0), c1, digit; + long result = 0; + boolean inRange = true, isDigit = false; + int neg = c - '-'; + if (neg != 0 + && neg + 2 != 0 // firstChar != '+' ) { - i = 1; + if (Integer.isDigit(c)) { + result = '0' - c; + } else { + inRange = false; + } } long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); - boolean inRange = true, isDigit = false; - long result = 0; - int c = 0, c1, digit; + int i = 1; while (i + 1 < len && (isDigit = Integer.isDigit((c = s.charAt(i)))) && Integer.isDigit(c1 = s.charAt(i + 1))) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { + if (!(inRange = inRange2(result, digit, limit))) { break; } result = result * 100 - digit; @@ -584,7 +588,7 @@ public static long parseLong(String s, int radix) } if (i != len && isDigit) { digit = c - '0'; - inRange = result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); + inRange = isInRange(result, digit, limit); result = result * 10 - digit; i++; } @@ -595,6 +599,14 @@ public static long parseLong(String s, int radix) throw NumberFormatException.forInputString(s); } + private static boolean isInRange(long result, int digit, long limit) { + return result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); + } + + private static boolean inRange2(long result, int digit, long limit) { + return result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)); + } + private static long parseLong0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); From f555dae25b8e2c1b0c96c3f14ddf1890e3ce7c52 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 17:56:49 +0800 Subject: [PATCH 06/35] optimize parseInt & parseLong --- src/java.base/share/classes/java/lang/Integer.java | 2 +- src/java.base/share/classes/java/lang/Long.java | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 79ece3120c410..ecb5614530f8f 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -555,7 +555,7 @@ public static int parseInt(String s, int radix) result = result * 10 - digit; i++; } - if (i == len && result <= 0) { + if (inRange && i == len && result <= 0) { return neg != 0 ? -result : result; } } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 4704f3a51dfe0..3e0060f0bd357 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -89,7 +89,6 @@ public final class Long extends Number */ @Native public static final long MAX_VALUE = 0x7fffffffffffffffL; - static final long MULT_MIN = -922337203685477580L; static final long MULT_MIN_2 = -92233720368547758L; /** @@ -576,7 +575,7 @@ public static long parseLong(String s, int radix) int i = 1; while (i + 1 < len && (isDigit = Integer.isDigit((c = s.charAt(i)))) && Integer.isDigit(c1 = s.charAt(i + 1))) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (!(inRange = inRange2(result, digit, limit))) { + if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { break; } result = result * 100 - digit; @@ -588,9 +587,9 @@ public static long parseLong(String s, int radix) } if (i != len && isDigit) { digit = c - '0'; - inRange = isInRange(result, digit, limit); result = result * 10 - digit; i++; + // max len is 20, No need to check inRange } if (i == len && result <= 0) { return neg != 0 ? -result : result; @@ -599,14 +598,6 @@ public static long parseLong(String s, int radix) throw NumberFormatException.forInputString(s); } - private static boolean isInRange(long result, int digit, long limit) { - return result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); - } - - private static boolean inRange2(long result, int digit, long limit) { - return result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)); - } - private static long parseLong0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); From 5801b17198485b1ac0a16cb773fb9565048aa38d Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 19:29:31 +0800 Subject: [PATCH 07/35] bug fix --- src/java.base/share/classes/java/lang/Integer.java | 4 ++-- src/java.base/share/classes/java/lang/Long.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index ecb5614530f8f..74dfdab1d9859 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -520,7 +520,7 @@ public static String toUnsignedString(int i) { public static int parseInt(String s, int radix) throws NumberFormatException { int len; - if (s == null || radix != 10 || (len = s.length()) == 0) { + if (s == null || radix != 10 || (len = s.length()) == 0 || !s.isLatin1()) { return parseInt0(s, radix); } int result = 0, c = s.charAt(0), c1, digit; @@ -539,7 +539,7 @@ public static int parseInt(String s, int radix) int i = 1; while (i + 1 < len && (isDigit = isDigit((c = s.charAt(i)))) && isDigit(c1 = s.charAt(i + 1))) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { + if (!(inRange = (result > MULT_MIN_2))) { break; } result = result * 100 - digit; diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 3e0060f0bd357..0335cdd4619c1 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -555,7 +555,7 @@ public static String toUnsignedString(long i) { public static long parseLong(String s, int radix) throws NumberFormatException { int len; - if (s == null || radix != 10 || (len = s.length()) == 0) { + if (s == null || radix != 10 || (len = s.length()) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } int c = s.charAt(0), c1, digit; From 9ea084555792fb3c870f6393ce53873e9bde73e6 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 20:05:20 +0800 Subject: [PATCH 08/35] use String::value --- src/java.base/share/classes/java/lang/Integer.java | 9 +++++---- src/java.base/share/classes/java/lang/Long.java | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 74dfdab1d9859..85a487a7ec215 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -520,10 +520,11 @@ public static String toUnsignedString(int i) { public static int parseInt(String s, int radix) throws NumberFormatException { int len; - if (s == null || radix != 10 || (len = s.length()) == 0 || !s.isLatin1()) { + byte[] value; + if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseInt0(s, radix); } - int result = 0, c = s.charAt(0), c1, digit; + int result = 0, c = value[0], c1, digit; boolean inRange = true, isDigit = false; int neg = c - '-'; if (neg != 0 @@ -537,7 +538,7 @@ public static int parseInt(String s, int radix) } int limit = MIN_VALUE + (neg != 0 ? 1 : 0); int i = 1; - while (i + 1 < len && (isDigit = isDigit((c = s.charAt(i)))) && isDigit(c1 = s.charAt(i + 1))) { + while (i + 1 < len && (isDigit = isDigit((c = value[i]))) && isDigit(c1 = value[i + 1])) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (!(inRange = (result > MULT_MIN_2))) { break; @@ -547,7 +548,7 @@ public static int parseInt(String s, int radix) } if (inRange) { if (i + 1 == len) { - isDigit = isDigit((c = s.charAt(i))); + isDigit = isDigit((c = value[i])); } if (i != len && isDigit) { digit = c - '0'; diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 0335cdd4619c1..8a4e3d9eba92a 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -89,7 +89,7 @@ public final class Long extends Number */ @Native public static final long MAX_VALUE = 0x7fffffffffffffffL; - static final long MULT_MIN_2 = -92233720368547758L; + private static final long MULT_MIN_2 = -92233720368547758L; /** * The {@code Class} instance representing the primitive type @@ -555,10 +555,11 @@ public static String toUnsignedString(long i) { public static long parseLong(String s, int radix) throws NumberFormatException { int len; - if (s == null || radix != 10 || (len = s.length()) == 0 || !s.isLatin1()) { + byte[] value; + if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } - int c = s.charAt(0), c1, digit; + int c = value[0], c1, digit; long result = 0; boolean inRange = true, isDigit = false; int neg = c - '-'; @@ -573,7 +574,7 @@ public static long parseLong(String s, int radix) } long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); int i = 1; - while (i + 1 < len && (isDigit = Integer.isDigit((c = s.charAt(i)))) && Integer.isDigit(c1 = s.charAt(i + 1))) { + while (i + 1 < len && (isDigit = Integer.isDigit((c = value[i]))) && Integer.isDigit(c1 = value[i + 1])) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { break; @@ -583,7 +584,7 @@ public static long parseLong(String s, int radix) } if (inRange) { if (i + 1 == len) { - isDigit = Integer.isDigit((c = s.charAt(i))); + isDigit = Integer.isDigit((c = value[i])); } if (i != len && isDigit) { digit = c - '0'; From 844780d886f866ea9fa471677eccf90c3771a8ed Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 20:46:09 +0800 Subject: [PATCH 09/35] bug fix --- src/java.base/share/classes/java/lang/Integer.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 85a487a7ec215..3861f615258bc 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -540,7 +540,7 @@ public static int parseInt(String s, int radix) int i = 1; while (i + 1 < len && (isDigit = isDigit((c = value[i]))) && isDigit(c1 = value[i + 1])) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (!(inRange = (result > MULT_MIN_2))) { + if (!(inRange = inRange2(result, digit, limit))) { break; } result = result * 100 - digit; @@ -552,7 +552,7 @@ public static int parseInt(String s, int radix) } if (i != len && isDigit) { digit = c - '0'; - inRange = result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); + inRange = inRange(result, digit, limit); result = result * 10 - digit; i++; } @@ -563,6 +563,14 @@ public static int parseInt(String s, int radix) throw NumberFormatException.forInputString(s); } + private static boolean inRange(int result, int digit, int limit) { + return result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); + } + + private static boolean inRange2(int result, int digit, int limit) { + return result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)); + } + private static int parseInt0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); From 46bb40d8531c5de7b1256363c37cff256f9b2823 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 22:06:30 +0800 Subject: [PATCH 10/35] bug fix --- src/java.base/share/classes/java/lang/Integer.java | 8 ++++---- src/java.base/share/classes/java/lang/Long.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 3861f615258bc..a8190db55119b 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -525,16 +525,16 @@ public static int parseInt(String s, int radix) return parseInt0(s, radix); } int result = 0, c = value[0], c1, digit; - boolean inRange = true, isDigit = false; + boolean inRange, isDigit = false; int neg = c - '-'; if (neg != 0 && neg + 2 != 0 // firstChar != '+' ) { - if (isDigit(c)) { + if (inRange = isDigit(c)) { result = '0' - c; - } else { - inRange = false; } + } else { + inRange = len != 1; } int limit = MIN_VALUE + (neg != 0 ? 1 : 0); int i = 1; diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 8a4e3d9eba92a..87b01c7a17365 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -561,16 +561,16 @@ public static long parseLong(String s, int radix) } int c = value[0], c1, digit; long result = 0; - boolean inRange = true, isDigit = false; + boolean inRange, isDigit = false; int neg = c - '-'; if (neg != 0 && neg + 2 != 0 // firstChar != '+' ) { - if (Integer.isDigit(c)) { + if (inRange = Integer.isDigit(c)) { result = '0' - c; - } else { - inRange = false; } + } else { + inRange = len != 1; } long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); int i = 1; From 3b8d45886ff9a309eec334e665389eb71898cbc2 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 4 Jan 2025 23:26:07 +0800 Subject: [PATCH 11/35] bug fix --- src/java.base/share/classes/java/lang/Integer.java | 9 ++++----- src/java.base/share/classes/java/lang/Long.java | 13 ++++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index a8190db55119b..56221dab58713 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -538,13 +538,12 @@ public static int parseInt(String s, int radix) } int limit = MIN_VALUE + (neg != 0 ? 1 : 0); int i = 1; - while (i + 1 < len && (isDigit = isDigit((c = value[i]))) && isDigit(c1 = value[i + 1])) { + while (i + 1 < len && inRange && (isDigit = isDigit((c = value[i]))) && isDigit(c1 = value[i + 1])) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (!(inRange = inRange2(result, digit, limit))) { - break; + if (inRange = inRange2(result, digit, limit)) { + result = result * 100 - digit; + i += 2; } - result = result * 100 - digit; - i += 2; } if (inRange) { if (i + 1 == len) { diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 87b01c7a17365..1327364c861eb 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -574,13 +574,12 @@ public static long parseLong(String s, int radix) } long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); int i = 1; - while (i + 1 < len && (isDigit = Integer.isDigit((c = value[i]))) && Integer.isDigit(c1 = value[i + 1])) { + while (i + 1 < len && inRange && (isDigit = Integer.isDigit((c = value[i]))) && Integer.isDigit(c1 = value[i + 1])) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (!(inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit))))) { - break; + if (inRange = inRange2(result, digit, limit)) { + result = result * 100 - digit; + i += 2; } - result = result * 100 - digit; - i += 2; } if (inRange) { if (i + 1 == len) { @@ -599,6 +598,10 @@ public static long parseLong(String s, int radix) throw NumberFormatException.forInputString(s); } + private static boolean inRange2(long result, int digit, long limit) { + return result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)); + } + private static long parseLong0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); From 2c4eeb4119c0f2b72726946fe3f06f543b21ccb7 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 00:41:33 +0800 Subject: [PATCH 12/35] code format --- .../share/classes/java/lang/Integer.java | 6 +++++- .../share/classes/java/lang/Long.java | 19 +++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 56221dab58713..d9c0301df2364 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -538,7 +538,11 @@ public static int parseInt(String s, int radix) } int limit = MIN_VALUE + (neg != 0 ? 1 : 0); int i = 1; - while (i + 1 < len && inRange && (isDigit = isDigit((c = value[i]))) && isDigit(c1 = value[i + 1])) { + while (inRange + && i + 1 < len + && (isDigit = isDigit((c = value[i]))) + && isDigit(c1 = value[i + 1]) + ) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (inRange = inRange2(result, digit, limit)) { result = result * 100 - digit; diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 1327364c861eb..aabfff2eebf5d 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -559,7 +559,7 @@ public static long parseLong(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } - int c = value[0], c1, digit; + int c = value[0], c1; long result = 0; boolean inRange, isDigit = false; int neg = c - '-'; @@ -574,9 +574,13 @@ public static long parseLong(String s, int radix) } long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); int i = 1; - while (i + 1 < len && inRange && (isDigit = Integer.isDigit((c = value[i]))) && Integer.isDigit(c1 = value[i + 1])) { - digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (inRange = inRange2(result, digit, limit)) { + while (inRange + && i + 1 < len + && (isDigit = Integer.isDigit((c = value[i]))) + && Integer.isDigit(c1 = value[i + 1]) + ) { + int digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' + if (inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)))) { result = result * 100 - digit; i += 2; } @@ -586,8 +590,7 @@ public static long parseLong(String s, int radix) isDigit = Integer.isDigit((c = value[i])); } if (i != len && isDigit) { - digit = c - '0'; - result = result * 10 - digit; + result = result * 10 - (c - '0'); i++; // max len is 20, No need to check inRange } @@ -598,10 +601,6 @@ public static long parseLong(String s, int radix) throw NumberFormatException.forInputString(s); } - private static boolean inRange2(long result, int digit, long limit) { - return result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)); - } - private static long parseLong0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); From 426d607e674c10a0321aa8a4a5c69576329ecf02 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 01:12:14 +0800 Subject: [PATCH 13/35] remove unused code --- src/java.base/share/classes/java/lang/Integer.java | 2 +- src/java.base/share/classes/java/lang/Long.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index d9c0301df2364..006d5587078ac 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -559,7 +559,7 @@ && isDigit(c1 = value[i + 1]) result = result * 10 - digit; i++; } - if (inRange && i == len && result <= 0) { + if (inRange && i == len) { return neg != 0 ? -result : result; } } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index aabfff2eebf5d..2c852e33b3752 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -594,7 +594,7 @@ public static long parseLong(String s, int radix) i++; // max len is 20, No need to check inRange } - if (i == len && result <= 0) { + if (i == len) { return neg != 0 ? -result : result; } } From e99f5bb7d48ff3007387057f5caef0e9f480cd00 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 01:22:25 +0800 Subject: [PATCH 14/35] reduce codeSize --- .../share/classes/java/lang/Integer.java | 28 ++++++++----------- .../share/classes/java/lang/Long.java | 15 ++++------ 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 006d5587078ac..0fd0e24877ecf 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -524,10 +524,10 @@ public static int parseInt(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseInt0(s, radix); } - int result = 0, c = value[0], c1, digit; - boolean inRange, isDigit = false; - int neg = c - '-'; - if (neg != 0 + int result = 0, c, c1, digit; + boolean inRange; + int neg; + if ((neg = (c = value[0]) - '-') != 0 && neg + 2 != 0 // firstChar != '+' ) { if (inRange = isDigit(c)) { @@ -540,24 +540,22 @@ public static int parseInt(String s, int radix) int i = 1; while (inRange && i + 1 < len - && (isDigit = isDigit((c = value[i]))) + && isDigit((c = value[i])) && isDigit(c1 = value[i + 1]) ) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (inRange = inRange2(result, digit, limit)) { + if (inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)))) { result = result * 100 - digit; i += 2; } } if (inRange) { - if (i + 1 == len) { - isDigit = isDigit((c = value[i])); - } - if (i != len && isDigit) { + if (i + 1 == len && isDigit((c = value[i]))) { digit = c - '0'; - inRange = inRange(result, digit, limit); - result = result * 10 - digit; - i++; + if (result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit))) { + result = result * 10 - digit; + i++; + } } if (inRange && i == len) { return neg != 0 ? -result : result; @@ -570,10 +568,6 @@ private static boolean inRange(int result, int digit, int limit) { return result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); } - private static boolean inRange2(int result, int digit, int limit) { - return result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)); - } - private static int parseInt0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 2c852e33b3752..e3cf94017c5ce 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -559,11 +559,11 @@ public static long parseLong(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } - int c = value[0], c1; + int c, c1; long result = 0; - boolean inRange, isDigit = false; - int neg = c - '-'; - if (neg != 0 + boolean inRange; + int neg; + if ((neg = (c = value[0]) - '-') != 0 && neg + 2 != 0 // firstChar != '+' ) { if (inRange = Integer.isDigit(c)) { @@ -576,7 +576,7 @@ public static long parseLong(String s, int radix) int i = 1; while (inRange && i + 1 < len - && (isDigit = Integer.isDigit((c = value[i]))) + && Integer.isDigit((c = value[i])) && Integer.isDigit(c1 = value[i + 1]) ) { int digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' @@ -586,10 +586,7 @@ public static long parseLong(String s, int radix) } } if (inRange) { - if (i + 1 == len) { - isDigit = Integer.isDigit((c = value[i])); - } - if (i != len && isDigit) { + if (i + 1 == len && Integer.isDigit((c = value[i]))) { result = result * 10 - (c - '0'); i++; // max len is 20, No need to check inRange From c7db300c8da875538bbcc75993e7043c0cc8b9f1 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 07:42:14 +0800 Subject: [PATCH 15/35] bug fix for bound check --- .../share/classes/java/lang/Integer.java | 14 +++++--------- src/java.base/share/classes/java/lang/Long.java | 16 ++++++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 0fd0e24877ecf..0700ebdb4dd53 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -89,8 +89,8 @@ public final class Integer extends Number */ @Native public static final int MAX_VALUE = 0x7fffffff; - private static final int MULT_MIN = Integer.MIN_VALUE / 10; - private static final int MULT_MIN_2 = Integer.MIN_VALUE / 100; + private static final int MULT_MIN_10 = Integer.MIN_VALUE / 10; + private static final int MULT_MIN_100 = Integer.MIN_VALUE / 100; /** * The {@code Class} instance representing the primitive type @@ -544,7 +544,7 @@ && isDigit((c = value[i])) && isDigit(c1 = value[i + 1]) ) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)))) { + if (inRange = (result > MULT_MIN_100 || (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)))) { result = result * 100 - digit; i += 2; } @@ -552,22 +552,18 @@ && isDigit(c1 = value[i + 1]) if (inRange) { if (i + 1 == len && isDigit((c = value[i]))) { digit = c - '0'; - if (result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit))) { + if (result > MULT_MIN_10 || (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit))) { result = result * 10 - digit; i++; } } - if (inRange && i == len) { + if (i == len) { return neg != 0 ? -result : result; } } throw NumberFormatException.forInputString(s); } - private static boolean inRange(int result, int digit, int limit) { - return result > MULT_MIN || (result == MULT_MIN && digit <= (MULT_MIN * 10 - limit)); - } - private static int parseInt0(String s, int radix) { if (s == null) { throw NumberFormatException.emptyInput(); diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index e3cf94017c5ce..a86b4027adfe8 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -89,7 +89,8 @@ public final class Long extends Number */ @Native public static final long MAX_VALUE = 0x7fffffffffffffffL; - private static final long MULT_MIN_2 = -92233720368547758L; + private static final long MULT_MIN_10 = MIN_VALUE / 10; + private static final long MULT_MIN_100 = MIN_VALUE / 100; /** * The {@code Class} instance representing the primitive type @@ -580,18 +581,21 @@ public static long parseLong(String s, int radix) && Integer.isDigit(c1 = value[i + 1]) ) { int digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (inRange = (result > MULT_MIN_2 || (result == MULT_MIN_2 && digit <= (MULT_MIN_2 * 100 - limit)))) { + if (inRange = (result > MULT_MIN_100 || (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)))) { result = result * 100 - digit; i += 2; } } if (inRange) { if (i + 1 == len && Integer.isDigit((c = value[i]))) { - result = result * 10 - (c - '0'); - i++; - // max len is 20, No need to check inRange + int digit = c - '0'; + // // max len is 20, No need to check inRange (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit)) + if (result >= MULT_MIN_10) { + result = result * 10 - digit; + i++; + } } - if (i == len) { + if (i == len && result <= 0) { return neg != 0 ? -result : result; } } From 736bd9f22b98162a76bc72662b003a485b75fa32 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 08:25:13 +0800 Subject: [PATCH 16/35] reduce codeSize --- src/java.base/share/classes/java/lang/Integer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 0700ebdb4dd53..e15655cffb7c4 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -544,12 +544,12 @@ && isDigit((c = value[i])) && isDigit(c1 = value[i + 1]) ) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' - if (inRange = (result > MULT_MIN_100 || (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)))) { + if (inRange = (result >= MULT_MIN_100)) { result = result * 100 - digit; i += 2; } } - if (inRange) { + if (inRange && result <= 0) { if (i + 1 == len && isDigit((c = value[i]))) { digit = c - '0'; if (result > MULT_MIN_10 || (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit))) { From e010e9727eaa0d8b938069e0edd9604397155f07 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 08:27:31 +0800 Subject: [PATCH 17/35] add comments --- src/java.base/share/classes/java/lang/Integer.java | 1 + src/java.base/share/classes/java/lang/Long.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index e15655cffb7c4..2c3714805a76d 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -544,6 +544,7 @@ && isDigit((c = value[i])) && isDigit(c1 = value[i + 1]) ) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' + // max digits is 19, no need to check inRange (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)) if (inRange = (result >= MULT_MIN_100)) { result = result * 100 - digit; i += 2; diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index a86b4027adfe8..9e06fff4eefb3 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -589,7 +589,7 @@ public static long parseLong(String s, int radix) if (inRange) { if (i + 1 == len && Integer.isDigit((c = value[i]))) { int digit = c - '0'; - // // max len is 20, No need to check inRange (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit)) + // max digits is 20, no need to check inRange (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit)) if (result >= MULT_MIN_10) { result = result * 10 - digit; i++; From 52defbd82af09418cddd01507b26cfde976755a6 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 11:08:24 +0800 Subject: [PATCH 18/35] use CharacterDataLatin1.instance::isDigit --- src/java.base/share/classes/java/lang/Integer.java | 12 ++++++------ src/java.base/share/classes/java/lang/Long.java | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 2c3714805a76d..6fecd99e60c0d 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -530,7 +530,7 @@ public static int parseInt(String s, int radix) if ((neg = (c = value[0]) - '-') != 0 && neg + 2 != 0 // firstChar != '+' ) { - if (inRange = isDigit(c)) { + if (inRange = isDigitLatin1(c)) { result = '0' - c; } } else { @@ -540,8 +540,8 @@ public static int parseInt(String s, int radix) int i = 1; while (inRange && i + 1 < len - && isDigit((c = value[i])) - && isDigit(c1 = value[i + 1]) + && isDigitLatin1((c = value[i])) + && isDigitLatin1(c1 = value[i + 1]) ) { digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' // max digits is 19, no need to check inRange (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)) @@ -551,7 +551,7 @@ && isDigit(c1 = value[i + 1]) } } if (inRange && result <= 0) { - if (i + 1 == len && isDigit((c = value[i]))) { + if (i + 1 == len && isDigitLatin1((c = value[i]))) { digit = c - '0'; if (result > MULT_MIN_10 || (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit))) { result = result * 10 - digit; @@ -576,8 +576,8 @@ private static int parseInt0(String s, int radix) { return parseInt(s, 0, len, radix); } - static boolean isDigit(int ch) { - return ch >= '0' && ch <= '9'; + static boolean isDigitLatin1(int ch) { + return CharacterDataLatin1.instance.isDigit(ch); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 9e06fff4eefb3..2acfdb1d810c6 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -567,7 +567,7 @@ public static long parseLong(String s, int radix) if ((neg = (c = value[0]) - '-') != 0 && neg + 2 != 0 // firstChar != '+' ) { - if (inRange = Integer.isDigit(c)) { + if (inRange = Integer.isDigitLatin1(c)) { result = '0' - c; } } else { @@ -577,8 +577,8 @@ public static long parseLong(String s, int radix) int i = 1; while (inRange && i + 1 < len - && Integer.isDigit((c = value[i])) - && Integer.isDigit(c1 = value[i + 1]) + && Integer.isDigitLatin1((c = value[i])) + && Integer.isDigitLatin1(c1 = value[i + 1]) ) { int digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (inRange = (result > MULT_MIN_100 || (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)))) { @@ -587,7 +587,7 @@ public static long parseLong(String s, int radix) } } if (inRange) { - if (i + 1 == len && Integer.isDigit((c = value[i]))) { + if (i + 1 == len && Integer.isDigitLatin1((c = value[i]))) { int digit = c - '0'; // max digits is 20, no need to check inRange (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit)) if (result >= MULT_MIN_10) { From fd51c1cea1be7e5e5fa3b30af09131fe87a356e6 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 12:16:52 +0800 Subject: [PATCH 19/35] emptyInput -> nullInput --- src/java.base/share/classes/java/lang/Integer.java | 2 +- src/java.base/share/classes/java/lang/Long.java | 2 +- .../share/classes/java/lang/NumberFormatException.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 6fecd99e60c0d..e1aa0a7aad610 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -567,7 +567,7 @@ && isDigitLatin1(c1 = value[i + 1]) private static int parseInt0(String s, int radix) { if (s == null) { - throw NumberFormatException.emptyInput(); + throw NumberFormatException.nullInput(); } int len; if ((len = s.length()) == 0) { diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 2acfdb1d810c6..8d29b45676b83 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -604,7 +604,7 @@ public static long parseLong(String s, int radix) private static long parseLong0(String s, int radix) { if (s == null) { - throw NumberFormatException.emptyInput(); + throw NumberFormatException.nullInput(); } int len; if ((len = s.length()) == 0) { diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index bb8e5dd021650..729788c3742d4 100644 --- a/src/java.base/share/classes/java/lang/NumberFormatException.java +++ b/src/java.base/share/classes/java/lang/NumberFormatException.java @@ -93,7 +93,7 @@ static NumberFormatException forCharSequence(CharSequence s, + s.subSequence(beginIndex, endIndex) + "\""); } - static NumberFormatException emptyInput() { + static NumberFormatException nullInput() { return new NumberFormatException("Cannot parse null string"); } } From f97093d324fbb7de322c86d52db11cafcff2de87 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 5 Jan 2025 18:57:21 +0800 Subject: [PATCH 20/35] vector digit2 --- .../share/classes/java/lang/Integer.java | 10 ++-- .../share/classes/java/lang/Long.java | 8 ++- .../jdk/internal/util/DecimalDigits.java | 51 +++++++++++++++++++ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index e1aa0a7aad610..11f47b0df05ed 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -524,7 +524,7 @@ public static int parseInt(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseInt0(s, radix); } - int result = 0, c, c1, digit; + int result = 0, c, digit; boolean inRange; int neg; if ((neg = (c = value[0]) - '-') != 0 @@ -540,10 +540,8 @@ public static int parseInt(String s, int radix) int i = 1; while (inRange && i + 1 < len - && isDigitLatin1((c = value[i])) - && isDigitLatin1(c1 = value[i + 1]) + && (digit = DecimalDigits.digit2(value, i)) != -1 ) { - digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' // max digits is 19, no need to check inRange (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)) if (inRange = (result >= MULT_MIN_100)) { result = result * 100 - digit; @@ -580,6 +578,10 @@ static boolean isDigitLatin1(int ch) { return CharacterDataLatin1.instance.isDigit(ch); } + static boolean isDigitLatin2(int ch) { + return CharacterDataLatin1.instance.isDigit(ch); + } + /** * Parses the {@link CharSequence} argument as a signed {@code int} in the * specified {@code radix}, beginning at the specified {@code beginIndex} diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 8d29b45676b83..4d9e29bc0d54b 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -560,7 +560,7 @@ public static long parseLong(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } - int c, c1; + int c, digit; long result = 0; boolean inRange; int neg; @@ -577,10 +577,8 @@ public static long parseLong(String s, int radix) int i = 1; while (inRange && i + 1 < len - && Integer.isDigitLatin1((c = value[i])) - && Integer.isDigitLatin1(c1 = value[i + 1]) + && (digit = DecimalDigits.digit2(value, i)) != -1 ) { - int digit = c * 10 + c1 - 528; // 528 = 48 * 11 = '0' * 10 + '0' if (inRange = (result > MULT_MIN_100 || (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)))) { result = result * 100 - digit; i += 2; @@ -588,7 +586,7 @@ public static long parseLong(String s, int radix) } if (inRange) { if (i + 1 == len && Integer.isDigitLatin1((c = value[i]))) { - int digit = c - '0'; + digit = c - '0'; // max digits is 20, no need to check inRange (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit)) if (result >= MULT_MIN_10) { result = result * 10 - digit; diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index 83438e59b8276..f97a710b40214 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -25,6 +25,8 @@ package jdk.internal.util; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; /** @@ -33,6 +35,7 @@ * @since 21 */ public final class DecimalDigits { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); /** * Each element of the array represents the packaging of two ascii characters based on little endian:

@@ -136,4 +139,52 @@ public static int stringSize(long x) { } return 19 + d; } + + /** + * Determine whether the two strings in bytes are both numbers. If they are, return d0 * 10 + d1, otherwise return -1 + * @param str The input LATIN1 encoded String value + * @param offset the offset + * @return If both characters are numbers, return d0 * 10 + d1, otherwise return -1 + */ + @ForceInline + public static int digit2(byte[] str, int offset) { + /* + Here we are doing a 2-Byte Vector operation on the short type. + + x & 0xF0 != 0xC0 + --------------- + 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); + if ((((x & 0xF0F0) - 0x3030) + | (((d = x & 0x0F0F) + 0x0606) & 0xF0F0)) != 0 + ) { + return -1; + } + return ((d & 0xF) << 3) + ((d & 0xF) << 1) // (d & 0xF) * 10 + + (d >> 8); + } } From b52130b053c5737e821146748d13273f593c35f2 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 14 Jan 2025 23:47:39 +0800 Subject: [PATCH 21/35] Update src/java.base/share/classes/jdk/internal/util/DecimalDigits.java Co-authored-by: Chen Liang --- .../share/classes/jdk/internal/util/DecimalDigits.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index f97a710b40214..5e0df8c878256 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -151,7 +151,7 @@ public static int digit2(byte[] str, int offset) { /* Here we are doing a 2-Byte Vector operation on the short type. - x & 0xF0 != 0xC0 + x & 0xF0 != 0x30 --------------- 0 0b0011_0000 & 0b1111_0000 = 0b0011_0000 1 0b0011_0001 & 0b1111_0000 = 0b0011_0000 From eb8679784163a477ae34c2538e33d9d8fc099ede Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 14 Jan 2025 23:50:09 +0800 Subject: [PATCH 22/35] remove unused --- src/java.base/share/classes/java/lang/Integer.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 11f47b0df05ed..5f08caa824230 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -578,10 +578,6 @@ static boolean isDigitLatin1(int ch) { return CharacterDataLatin1.instance.isDigit(ch); } - static boolean isDigitLatin2(int ch) { - return CharacterDataLatin1.instance.isDigit(ch); - } - /** * Parses the {@link CharSequence} argument as a signed {@code int} in the * specified {@code radix}, beginning at the specified {@code beginIndex} From e2b228cb63a2424509ff2a2527997a77df913848 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 25 Jan 2025 02:06:09 +0800 Subject: [PATCH 23/35] from @rgiulietti --- .../share/classes/java/lang/Integer.java | 58 +++++++----------- .../share/classes/java/lang/Long.java | 59 +++++++------------ 2 files changed, 44 insertions(+), 73 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 5f08caa824230..7d79777cee85c 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -89,9 +89,6 @@ public final class Integer extends Number */ @Native public static final int MAX_VALUE = 0x7fffffff; - private static final int MULT_MIN_10 = Integer.MIN_VALUE / 10; - private static final int MULT_MIN_100 = Integer.MIN_VALUE / 100; - /** * The {@code Class} instance representing the primitive type * {@code int}. @@ -524,41 +521,30 @@ public static int parseInt(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseInt0(s, radix); } - int result = 0, c, digit; - boolean inRange; - int neg; - if ((neg = (c = value[0]) - '-') != 0 - && neg + 2 != 0 // firstChar != '+' - ) { - if (inRange = isDigitLatin1(c)) { - result = '0' - c; - } - } else { - inRange = len != 1; - } - int limit = MIN_VALUE + (neg != 0 ? 1 : 0); + int fc = value[0]; + int result = Integer.isDigitLatin1(fc) + ? '0' - fc + : len != 1 && (fc == '-' || fc == '+') + ? 0 + : 1; // or any value > 0 int i = 1; - while (inRange - && i + 1 < len - && (digit = DecimalDigits.digit2(value, i)) != -1 - ) { - // max digits is 19, no need to check inRange (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)) - if (inRange = (result >= MULT_MIN_100)) { - result = result * 100 - digit; - i += 2; - } + 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 (inRange && result <= 0) { - if (i + 1 == len && isDigitLatin1((c = value[i]))) { - digit = c - '0'; - if (result > MULT_MIN_10 || (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit))) { - result = result * 10 - digit; - i++; - } - } - if (i == len) { - return neg != 0 ? -result : result; - } + 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; + } + if (i == len + & result <= 0 + & (MIN_VALUE < result || fc == '-')) { + return fc == '-' ? result : -result; } throw NumberFormatException.forInputString(s); } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 4d9e29bc0d54b..59e2b13e5cd3d 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -89,9 +89,6 @@ public final class Long extends Number */ @Native public static final long MAX_VALUE = 0x7fffffffffffffffL; - private static final long MULT_MIN_10 = MIN_VALUE / 10; - private static final long MULT_MIN_100 = MIN_VALUE / 100; - /** * The {@code Class} instance representing the primitive type * {@code long}. @@ -560,42 +557,30 @@ public static long parseLong(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } - int c, digit; - long result = 0; - boolean inRange; - int neg; - if ((neg = (c = value[0]) - '-') != 0 - && neg + 2 != 0 // firstChar != '+' - ) { - if (inRange = Integer.isDigitLatin1(c)) { - result = '0' - c; - } - } else { - inRange = len != 1; - } - long limit = MIN_VALUE + (neg != 0 ? 1L : 0L); + int fc = value[0]; + long result = Integer.isDigitLatin1(fc) + ? '0' - fc + : len != 1 && (fc == '-' || fc == '+') + ? 0 + : 1; // or any value > 0 int i = 1; - while (inRange - && i + 1 < len - && (digit = DecimalDigits.digit2(value, i)) != -1 - ) { - if (inRange = (result > MULT_MIN_100 || (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit)))) { - result = result * 100 - digit; - i += 2; - } + 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 (inRange) { - if (i + 1 == len && Integer.isDigitLatin1((c = value[i]))) { - digit = c - '0'; - // max digits is 20, no need to check inRange (result == MULT_MIN_10 && digit <= (MULT_MIN_10 * 10 - limit)) - if (result >= MULT_MIN_10) { - result = result * 10 - digit; - i++; - } - } - if (i == len && result <= 0) { - return neg != 0 ? -result : result; - } + 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; + } + if (i == len + & result <= 0 + & (MIN_VALUE < result || fc == '-')) { + return fc == '-' ? result : -result; } throw NumberFormatException.forInputString(s); } From dc2a6758f33daacde63189a2239b1fac1470a937 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 25 Jan 2025 09:16:17 +0800 Subject: [PATCH 24/35] use & --- src/java.base/share/classes/java/lang/Integer.java | 4 ++-- src/java.base/share/classes/java/lang/Long.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 7d79777cee85c..98be272a895dd 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -531,13 +531,13 @@ public static int parseInt(String s, int radix) int d; while (i + 1 < len && (d = DecimalDigits.digit2(value, i)) != -1 - && MIN_VALUE / 100 <= result && result <= 0) { + && MIN_VALUE / 100 <= result & result <= 0) { result = result * 100 - d; // overflow from d => result > 0 i += 2; } if (i < len && Integer.isDigitLatin1(d = value[i]) - && MIN_VALUE / 10 <= result && result <= 0) { + && MIN_VALUE / 10 <= result & result <= 0) { result = result * 10 + '0' - d; // overflow from '0' - d => result > 0 i += 1; } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 59e2b13e5cd3d..a2944e5269325 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -567,13 +567,13 @@ public static long parseLong(String s, int radix) int d; while (i + 1 < len && (d = DecimalDigits.digit2(value, i)) != -1 - && MIN_VALUE / 100 <= result && result <= 0) { + && MIN_VALUE / 100 <= result & result <= 0) { result = result * 100 - d; // overflow from d => result > 0 i += 2; } if (i < len && Integer.isDigitLatin1(d = value[i]) - && MIN_VALUE / 10 <= result && result <= 0) { + && MIN_VALUE / 10 <= result & result <= 0) { result = result * 10 + '0' - d; // overflow from '0' - d => result > 0 i += 1; } From 284ad6a03f8f8597d834b82b296ccd4d9005e37e Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 28 Jan 2025 09:19:46 +0800 Subject: [PATCH 25/35] error message --- .../share/classes/java/lang/Integer.java | 2 +- src/java.base/share/classes/java/lang/Long.java | 2 +- .../classes/java/lang/NumberFormatException.java | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 1cab590e5bb82..0356cb306cd7b 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -637,7 +637,7 @@ public static int parseInt(CharSequence s, int beginIndex, int endIndex, int rad } } throw NumberFormatException.forCharSequence(s, beginIndex, - endIndex, i - (digit < -1 ? 0 : 1)); + endIndex, i - (digit < -1 ? 0 : 1), radix); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index cee039345e002..4af383aaf19a4 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -669,7 +669,7 @@ public static long parseLong(CharSequence s, int beginIndex, int endIndex, int r } } throw NumberFormatException.forCharSequence(s, beginIndex, - endIndex, i - (digit < -1 ? 0 : 1)); + endIndex, i - (digit < -1 ? 0 : 1), radix); } /** diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index 729788c3742d4..9b03852525ff6 100644 --- a/src/java.base/share/classes/java/lang/NumberFormatException.java +++ b/src/java.base/share/classes/java/lang/NumberFormatException.java @@ -93,6 +93,22 @@ static NumberFormatException forCharSequence(CharSequence s, + s.subSequence(beginIndex, endIndex) + "\""); } + /** + * Factory method for making a {@code NumberFormatException} + * given the specified input which caused the error. + * + *

When {@code beginIndex} is 0 and {@code endIndex == s.length()}, this method + * behaves exactly like {@link #forInputString(String, int)} in error handling, + * including the format of thrown exceptions and diagnostic messages. + */ + static NumberFormatException forCharSequence(CharSequence s, + int beginIndex, int endIndex, int errorIndex, int radix) { + if (s instanceof String && beginIndex == 0 && endIndex == s.length()) { + return forInputString((String) s, radix); + } + return forCharSequence(s, beginIndex, endIndex, errorIndex); + } + static NumberFormatException nullInput() { return new NumberFormatException("Cannot parse null string"); } From 0b04a70609b3195aca6481c6f1c88b27c872a65b Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 30 Jan 2025 00:25:44 +0800 Subject: [PATCH 26/35] copyright --- src/java.base/share/classes/java/lang/Integer.java | 3 ++- src/java.base/share/classes/java/lang/Long.java | 1 + .../share/classes/java/lang/NumberFormatException.java | 2 +- .../share/classes/jdk/internal/util/DecimalDigits.java | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 0356cb306cd7b..2af44f876b1c6 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 4af383aaf19a4..66ee1ed98df48 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -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 diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index 9b03852525ff6..259a81231d3fe 100644 --- a/src/java.base/share/classes/java/lang/NumberFormatException.java +++ b/src/java.base/share/classes/java/lang/NumberFormatException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index 3568caceec8dc..8ac82e4b163c7 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 From a6d9846809d6213fd70e852773ff2f57b5bf0201 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 30 Jan 2025 12:33:22 +0800 Subject: [PATCH 27/35] multiply 10 --- .../share/classes/jdk/internal/util/DecimalDigits.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index 8ac82e4b163c7..65ca7444e99a6 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -178,8 +178,7 @@ public static int digit2(byte[] str, int offset) { ) { return -1; } - return ((d & 0xF) << 3) + ((d & 0xF) << 1) // (d & 0xF) * 10 - + (d >> 8); + return (d & 0xF) * 10 + (d >> 8); } /** From 2f67c246182de025fd15665e12bd7e44ebc1f4d8 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 5 Feb 2025 07:41:47 +0800 Subject: [PATCH 28/35] Update src/java.base/share/classes/java/lang/Integer.java Co-authored-by: Raffaello Giulietti --- src/java.base/share/classes/java/lang/Integer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 2af44f876b1c6..1539b2fb893a3 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -522,6 +522,7 @@ public static int parseInt(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseInt0(s, radix); } + /* Accumulating negatively avoids surprises near MAX_VALUE */ int fc = value[0]; int result = Integer.isDigitLatin1(fc) ? '0' - fc From 40b9fafaf6d418190d6b87aee68b7e42afbda365 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 5 Feb 2025 07:41:59 +0800 Subject: [PATCH 29/35] Update src/java.base/share/classes/java/lang/Long.java Co-authored-by: Raffaello Giulietti --- src/java.base/share/classes/java/lang/Long.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 66ee1ed98df48..de921ba51198e 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -558,6 +558,7 @@ public static long parseLong(String s, int radix) if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { return parseLong0(s, radix); } + /* Accumulating negatively avoids surprises near MAX_VALUE */ int fc = value[0]; long result = Integer.isDigitLatin1(fc) ? '0' - fc From 27024407a08c67369863708af5dddbbfc1fcc2ad Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 5 Feb 2025 08:15:43 +0800 Subject: [PATCH 30/35] fix JdbExprTest --- .../share/classes/java/lang/Integer.java | 2 +- src/java.base/share/classes/java/lang/Long.java | 2 +- .../classes/java/lang/NumberFormatException.java | 16 ---------------- test/jdk/com/sun/jdi/JdbExprTest.java | 2 +- 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 1539b2fb893a3..392cb620eab36 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -639,7 +639,7 @@ public static int parseInt(CharSequence s, int beginIndex, int endIndex, int rad } } throw NumberFormatException.forCharSequence(s, beginIndex, - endIndex, i - (digit < -1 ? 0 : 1), radix); + endIndex, i - (digit < -1 ? 0 : 1)); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index de921ba51198e..3db73a3580c69 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -671,7 +671,7 @@ public static long parseLong(CharSequence s, int beginIndex, int endIndex, int r } } throw NumberFormatException.forCharSequence(s, beginIndex, - endIndex, i - (digit < -1 ? 0 : 1), radix); + endIndex, i - (digit < -1 ? 0 : 1)); } /** diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index 259a81231d3fe..cfa12fc9a740a 100644 --- a/src/java.base/share/classes/java/lang/NumberFormatException.java +++ b/src/java.base/share/classes/java/lang/NumberFormatException.java @@ -93,22 +93,6 @@ static NumberFormatException forCharSequence(CharSequence s, + s.subSequence(beginIndex, endIndex) + "\""); } - /** - * Factory method for making a {@code NumberFormatException} - * given the specified input which caused the error. - * - *

When {@code beginIndex} is 0 and {@code endIndex == s.length()}, this method - * behaves exactly like {@link #forInputString(String, int)} in error handling, - * including the format of thrown exceptions and diagnostic messages. - */ - static NumberFormatException forCharSequence(CharSequence s, - int beginIndex, int endIndex, int errorIndex, int radix) { - if (s instanceof String && beginIndex == 0 && endIndex == s.length()) { - return forInputString((String) s, radix); - } - return forCharSequence(s, beginIndex, endIndex, errorIndex); - } - static NumberFormatException nullInput() { return new NumberFormatException("Cannot parse null string"); } diff --git a/test/jdk/com/sun/jdi/JdbExprTest.java b/test/jdk/com/sun/jdi/JdbExprTest.java index e73d332cdbf33..17e681f881160 100644 --- a/test/jdk/com/sun/jdi/JdbExprTest.java +++ b/test/jdk/com/sun/jdi/JdbExprTest.java @@ -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\""); execCommand(JdbCommand.set("JdbExprTestTarg.anInt", "0x7fffffff")) .shouldContain("0x7fffffff = 2147483647"); From 0c43e9ae5c1523ee7126c444d397d1a48c570c99 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 5 Feb 2025 20:55:11 +0800 Subject: [PATCH 31/35] copyright --- test/jdk/com/sun/jdi/JdbExprTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/com/sun/jdi/JdbExprTest.java b/test/jdk/com/sun/jdi/JdbExprTest.java index 17e681f881160..7aff1c449b0da 100644 --- a/test/jdk/com/sun/jdi/JdbExprTest.java +++ b/test/jdk/com/sun/jdi/JdbExprTest.java @@ -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 From c4863dac719e3801fdeff2a932a3dc32548c0464 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 6 Feb 2025 00:02:38 +0800 Subject: [PATCH 32/35] Update src/java.base/share/classes/jdk/internal/util/DecimalDigits.java Co-authored-by: Raffaello Giulietti --- src/java.base/share/classes/jdk/internal/util/DecimalDigits.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index ae6e9a4e57b1f..f4eb938a1a074 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -142,6 +142,7 @@ public static int stringSize(long x) { */ @ForceInline public static int digit2(byte[] str, int offset) { + // 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. From c8f514f261c8e5e4c53db4e7409e06b1661e1340 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 6 Feb 2025 00:07:32 +0800 Subject: [PATCH 33/35] fix comments --- .../share/classes/jdk/internal/util/DecimalDigits.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index f4eb938a1a074..c09aab46bd70a 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -135,10 +135,10 @@ public static int stringSize(long x) { } /** - * Determine whether the two strings in bytes are both numbers. If they are, return d0 * 10 + d1, otherwise return -1 + * Determine whether the two character in str are both digits. If they are, return (str[off] - '0') * 10 + (str[off] - '0'), otherwise return -1 * @param str The input LATIN1 encoded String value * @param offset the offset - * @return If both characters are numbers, return d0 * 10 + d1, otherwise return -1 + * @return If both characters are digits, return (str[off] - '0') * 10 + (str[off] - '0'), otherwise return -1 */ @ForceInline public static int digit2(byte[] str, int offset) { From a4181b08479106382ac33c02d06ea67bdac3ea2a Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 6 Feb 2025 00:58:05 +0800 Subject: [PATCH 34/35] fix comments --- .../share/classes/jdk/internal/util/DecimalDigits.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index c09aab46bd70a..43c7856f975fe 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -135,10 +135,10 @@ public static int stringSize(long x) { } /** - * Determine whether the two character in str are both digits. If they are, return (str[off] - '0') * 10 + (str[off] - '0'), otherwise return -1 + * 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[off] - '0') * 10 + (str[off] - '0'), otherwise return -1 + * @return If both characters are digits, return (str[offset] - '0') * 10 + (str[offset + 1] - '0'), otherwise return -1 */ @ForceInline public static int digit2(byte[] str, int offset) { From 3a555c5f42ed53cedc4fc11f036485916aadf805 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 6 Feb 2025 01:03:19 +0800 Subject: [PATCH 35/35] remove ForceInline --- .../share/classes/jdk/internal/util/DecimalDigits.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index 43c7856f975fe..5e74918b357ad 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -27,7 +27,6 @@ package jdk.internal.util; import jdk.internal.misc.Unsafe; -import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; @@ -140,7 +139,6 @@ public static int stringSize(long x) { * @param offset the offset * @return If both characters are digits, return (str[offset] - '0') * 10 + (str[offset + 1] - '0'), otherwise return -1 */ - @ForceInline public static int digit2(byte[] str, int offset) { // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. /*