diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 41487a469b6a0..5b28f704d2585 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, 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); + } + + 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 2fb2d18a78c80..147defbc8a370 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 @@ -552,46 +553,49 @@ 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"); + int len; + byte[] value; + if (s == null || radix != 10 || (len = (value = s.value()).length) == 0 || !s.isLatin1()) { + return parseLong0(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]; + long 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 long parseLong0(String s, int radix) { + if (s == null) { + throw NumberFormatException.nullInput(); } - 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; - } - 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 parseLong(s, 0, len, radix); } /** diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index 7a317304f6e2c..cfa12fc9a740a 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 @@ -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 nullInput() { + return new NumberFormatException("Cannot parse null string"); + } } 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 6c0c745651e34..e1ec4123099c3 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) 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) { + // 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); + 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 diff --git a/test/jdk/com/sun/jdi/JdbExprTest.java b/test/jdk/com/sun/jdi/JdbExprTest.java index e73d332cdbf33..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 @@ -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"); 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) {