From e7aca754a61ed904adbd369f7ebee1211869348f Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 05:35:03 +0800 Subject: [PATCH 01/23] move getChars to DecimalDigits & deduplicate --- .../java/lang/AbstractStringBuilder.java | 8 +- .../share/classes/java/lang/Integer.java | 4 +- .../share/classes/java/lang/Long.java | 4 +- .../classes/java/lang/StringConcatHelper.java | 16 +- .../share/classes/java/lang/StringLatin1.java | 115 -------- .../share/classes/java/lang/StringUTF16.java | 107 +------ .../share/classes/java/lang/System.java | 8 - .../share/classes/java/math/BigDecimal.java | 127 ++------ .../jdk/internal/access/JavaLangAccess.java | 4 - .../jdk/internal/util/DecimalDigits.java | 279 ++++++++++++++++++ 10 files changed, 320 insertions(+), 352 deletions(-) diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index e0c3aa19f5437..94c4192ff5ef7 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -840,9 +840,9 @@ public AbstractStringBuilder append(int i) { int spaceNeeded = count + DecimalDigits.stringSize(i); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { - StringLatin1.getChars(i, spaceNeeded, value); + DecimalDigits.getCharsLatin1(i, spaceNeeded, value); } else { - StringUTF16.getChars(i, count, spaceNeeded, value); + DecimalDigits.getCharsUTF16(i, spaceNeeded, value); } this.count = spaceNeeded; return this; @@ -865,9 +865,9 @@ public AbstractStringBuilder append(long l) { int spaceNeeded = count + DecimalDigits.stringSize(l); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { - StringLatin1.getChars(l, spaceNeeded, value); + DecimalDigits.getCharsLatin1(l, spaceNeeded, value); } else { - StringUTF16.getChars(l, count, spaceNeeded, value); + DecimalDigits.getCharsUTF16(l, spaceNeeded, value); } this.count = spaceNeeded; return this; diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index eab0a942d9a9a..1d5a91e2586d5 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -433,11 +433,11 @@ public static String toString(int i) { int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; - StringLatin1.getChars(i, size, buf); + DecimalDigits.getCharsLatin1(i, size, buf); return new String(buf, LATIN1); } else { byte[] buf = new byte[size * 2]; - StringUTF16.getChars(i, size, buf); + DecimalDigits.getCharsUTF16(i, size, buf); return new String(buf, UTF16); } } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index f86e1622b3836..48d2bbc1bfebe 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -463,11 +463,11 @@ public static String toString(long i) { int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; - StringLatin1.getChars(i, size, buf); + DecimalDigits.getCharsLatin1(i, size, buf); return new String(buf, LATIN1); } else { byte[] buf = new byte[size * 2]; - StringUTF16.getChars(i, size, buf); + DecimalDigits.getCharsUTF16(i, size, buf); return new String(buf, UTF16); } } diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java index d0c558ed93a1a..13ada0637bae1 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -312,12 +312,12 @@ static long prepend(long indexCoder, byte[] buf, char value, String prefix) { static long prepend(long indexCoder, byte[] buf, int value, String prefix) { int index = (int)indexCoder; if (indexCoder < UTF16) { - index = StringLatin1.getChars(value, index, buf); + index = DecimalDigits.getCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); return index; } else { - index = StringUTF16.getChars(value, index, buf); + index = DecimalDigits.getCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); return index | UTF16; @@ -338,12 +338,12 @@ static long prepend(long indexCoder, byte[] buf, int value, String prefix) { static long prepend(long indexCoder, byte[] buf, long value, String prefix) { int index = (int)indexCoder; if (indexCoder < UTF16) { - index = StringLatin1.getChars(value, index, buf); + index = DecimalDigits.getCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); return index; } else { - index = StringUTF16.getChars(value, index, buf); + index = DecimalDigits.getCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); return index | UTF16; @@ -710,11 +710,11 @@ static int prepend(int index, byte coder, byte[] buf, char value, String prefix) */ static int prepend(int index, byte coder, byte[] buf, int value, String prefix) { if (coder == String.LATIN1) { - index = StringLatin1.getChars(value, index, buf); + index = DecimalDigits.getCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); } else { - index = StringUTF16.getChars(value, index, buf); + index = DecimalDigits.getCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); } @@ -734,11 +734,11 @@ static int prepend(int index, byte coder, byte[] buf, int value, String prefix) */ static int prepend(int index, byte coder, byte[] buf, long value, String prefix) { if (coder == String.LATIN1) { - index = StringLatin1.getChars(value, index, buf); + index = DecimalDigits.getCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); } else { - index = StringUTF16.getChars(value, index, buf); + index = DecimalDigits.getCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); } diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index abec36af1d9d7..633244db92411 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -33,7 +33,6 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import jdk.internal.util.ArraysSupport; -import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.IntrinsicCandidate; import static java.lang.String.LATIN1; @@ -83,120 +82,6 @@ public static byte[] inflate(byte[] value, int off, int len) { return ret; } - /** - * Places characters representing the integer i into the - * character array buf. The characters are placed into - * the buffer backwards starting with the least significant - * digit at the specified index (exclusive), and working - * backwards from there. - * - * @implNote This method converts positive inputs into negative - * values, to cover the Integer.MIN_VALUE case. Converting otherwise - * (negative to positive) will expose -Integer.MIN_VALUE that overflows - * integer. - * - * @param i value to convert - * @param index next index, after the least significant digit - * @param buf target buffer, Latin1-encoded - * @return index of the most significant digit or minus sign, if present - */ - static int getChars(int i, int index, byte[] buf) { - // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. - int q; - int charPos = index; - - boolean negative = i < 0; - if (!negative) { - i = -i; - } - - // Generate two digits per iteration - while (i <= -100) { - q = i / 100; - charPos -= 2; - writeDigitPair(buf, charPos, (q * 100) - i); - i = q; - } - - // We know there are at most two digits left at this point. - if (i < -9) { - charPos -= 2; - writeDigitPair(buf, charPos, -i); - } else { - buf[--charPos] = (byte)('0' - i); - } - - if (negative) { - buf[--charPos] = (byte)'-'; - } - return charPos; - } - - /** - * Places characters representing the long i into the - * character array buf. The characters are placed into - * the buffer backwards starting with the least significant - * digit at the specified index (exclusive), and working - * backwards from there. - * - * @implNote This method converts positive inputs into negative - * values, to cover the Long.MIN_VALUE case. Converting otherwise - * (negative to positive) will expose -Long.MIN_VALUE that overflows - * long. - * - * @param i value to convert - * @param index next index, after the least significant digit - * @param buf target buffer, Latin1-encoded - * @return index of the most significant digit or minus sign, if present - */ - static int getChars(long i, int index, byte[] buf) { - // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. - long q; - int charPos = index; - - boolean negative = (i < 0); - if (!negative) { - i = -i; - } - - // Get 2 digits/iteration using longs until quotient fits into an int - while (i <= Integer.MIN_VALUE) { - q = i / 100; - charPos -= 2; - writeDigitPair(buf, charPos, (int)((q * 100) - i)); - i = q; - } - - // Get 2 digits/iteration using ints - int q2; - int i2 = (int)i; - while (i2 <= -100) { - q2 = i2 / 100; - charPos -= 2; - writeDigitPair(buf, charPos, (q2 * 100) - i2); - i2 = q2; - } - - // We know there are at most two digits left at this point. - if (i2 < -9) { - charPos -= 2; - writeDigitPair(buf, charPos, -i2); - } else { - buf[--charPos] = (byte)('0' - i2); - } - - if (negative) { - buf[--charPos] = (byte)'-'; - } - return charPos; - } - - private static void writeDigitPair(byte[] buf, int charPos, int value) { - short pair = DecimalDigits.digitPair(value); - buf[charPos] = (byte)(pair); - buf[charPos + 1] = (byte)(pair >> 8); - } - public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); } diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 000483f29bc9e..a2c4e075c0564 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -1516,14 +1516,14 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { public static int getChars(int i, int begin, int end, byte[] value) { checkBoundsBeginEnd(begin, end, value); - int pos = getChars(i, end, value); + int pos = DecimalDigits.getCharsUTF16(i, end, value); assert begin == pos; return pos; } public static int getChars(long l, int begin, int end, byte[] value) { checkBoundsBeginEnd(begin, end, value); - int pos = getChars(l, end, value); + int pos = DecimalDigits.getCharsUTF16(l, end, value); assert begin == pos; return pos; } @@ -1670,109 +1670,6 @@ public static int lastIndexOfLatin1(byte[] src, int srcCount, static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; - // Used by trusted callers. Assumes all necessary bounds checks have - // been done by the caller. - - /** - * This is a variant of {@link StringLatin1#getChars(int, int, byte[])}, but for - * UTF-16 coder. - * - * @param i value to convert - * @param index next index, after the least significant digit - * @param buf target buffer, UTF16-coded. - * @return index of the most significant digit or minus sign, if present - */ - static int getChars(int i, int index, byte[] buf) { - // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. - int q, r; - int charPos = index; - - boolean negative = (i < 0); - if (!negative) { - i = -i; - } - - // Get 2 digits/iteration using ints - while (i <= -100) { - q = i / 100; - r = (q * 100) - i; - i = q; - charPos -= 2; - putPair(buf, charPos, r); - } - - // We know there are at most two digits left at this point. - if (i < -9) { - charPos -= 2; - putPair(buf, charPos, -i); - } else { - putChar(buf, --charPos, '0' - i); - } - - if (negative) { - putChar(buf, --charPos, '-'); - } - return charPos; - } - - /** - * This is a variant of {@link StringLatin1#getChars(long, int, byte[])}, but for - * UTF-16 coder. - * - * @param i value to convert - * @param index next index, after the least significant digit - * @param buf target buffer, UTF16-coded. - * @return index of the most significant digit or minus sign, if present - */ - static int getChars(long i, int index, byte[] buf) { - // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. - long q; - int charPos = index; - - boolean negative = (i < 0); - if (!negative) { - i = -i; - } - - // Get 2 digits/iteration using longs until quotient fits into an int - while (i <= Integer.MIN_VALUE) { - q = i / 100; - charPos -= 2; - putPair(buf, charPos, (int)((q * 100) - i)); - i = q; - } - - // Get 2 digits/iteration using ints - int q2; - int i2 = (int)i; - while (i2 <= -100) { - q2 = i2 / 100; - charPos -= 2; - putPair(buf, charPos, (q2 * 100) - i2); - i2 = q2; - } - - // We know there are at most two digits left at this point. - if (i2 < -9) { - charPos -= 2; - putPair(buf, charPos, -i2); - } else { - putChar(buf, --charPos, '0' - i2); - } - - if (negative) { - putChar(buf, --charPos, '-'); - } - return charPos; - } - - private static void putPair(byte[] buf, int charPos, int v) { - int packed = (int) DecimalDigits.digitPair(v); - putChar(buf, charPos, packed & 0xFF); - putChar(buf, charPos + 1, packed >> 8); - } - // End of trusted methods. - public static void checkIndex(int off, byte[] val) { String.checkIndex(off, length(val)); } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 930b6b7f611b5..b17a0d0b18915 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2651,14 +2651,6 @@ public byte stringCoder(String str) { return str.coder(); } - public int getCharsLatin1(long i, int index, byte[] buf) { - return StringLatin1.getChars(i, index, buf); - } - - public int getCharsUTF16(long i, int index, byte[] buf) { - return StringUTF16.getChars(i, index, buf); - } - public String join(String prefix, String suffix, String delimiter, String[] elements, int size) { return String.join(prefix, suffix, delimiter, elements, size); } diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index d915568a50212..e05548ea07bee 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -35,9 +35,15 @@ import java.io.ObjectInputStream; import java.io.ObjectStreamException; import java.io.StreamCorruptedException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; +import jdk.internal.util.DecimalDigits; + /** * Immutable, arbitrary-precision signed decimal numbers. A {@code * BigDecimal} consists of an arbitrary precision integer @@ -328,6 +334,8 @@ * @since 1.1 */ public class BigDecimal extends Number implements Comparable { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + /* * Let l = log_2(10). * Then, L < l < L + ulp(L) / 2, that is, L = roundTiesToEven(l). @@ -4168,103 +4176,6 @@ public BigDecimal ulp() { return BigDecimal.valueOf(1, this.scale(), 1); } - // Private class to build a string representation for BigDecimal object. The - // StringBuilder field acts as a buffer to hold the temporary representation - // of BigDecimal. The cmpCharArray holds all the characters for the compact - // representation of BigDecimal (except for '-' sign' if it is negative) if - // its intCompact field is not INFLATED. - static class StringBuilderHelper { - final StringBuilder sb; // Placeholder for BigDecimal string - final char[] cmpCharArray; // character array to place the intCompact - - StringBuilderHelper() { - sb = new StringBuilder(32); - // All non negative longs can be made to fit into 19 character array. - cmpCharArray = new char[19]; - } - - // Accessors. - StringBuilder getStringBuilder() { - sb.setLength(0); - return sb; - } - - char[] getCompactCharArray() { - return cmpCharArray; - } - - /** - * Places characters representing the intCompact in {@code long} into - * cmpCharArray and returns the offset to the array where the - * representation starts. - * - * @param intCompact the number to put into the cmpCharArray. - * @return offset to the array where the representation starts. - * Note: intCompact must be greater or equal to zero. - */ - int putIntCompact(long intCompact) { - assert intCompact >= 0; - - long q; - int r; - // since we start from the least significant digit, charPos points to - // the last character in cmpCharArray. - int charPos = cmpCharArray.length; - - // Get 2 digits/iteration using longs until quotient fits into an int - while (intCompact > Integer.MAX_VALUE) { - q = intCompact / 100; - r = (int)(intCompact - q * 100); - intCompact = q; - cmpCharArray[--charPos] = DIGIT_ONES[r]; - cmpCharArray[--charPos] = DIGIT_TENS[r]; - } - - // Get 2 digits/iteration using ints when i2 >= 100 - int q2; - int i2 = (int)intCompact; - while (i2 >= 100) { - q2 = i2 / 100; - r = i2 - q2 * 100; - i2 = q2; - cmpCharArray[--charPos] = DIGIT_ONES[r]; - cmpCharArray[--charPos] = DIGIT_TENS[r]; - } - - cmpCharArray[--charPos] = DIGIT_ONES[i2]; - if (i2 >= 10) - cmpCharArray[--charPos] = DIGIT_TENS[i2]; - - return charPos; - } - - static final char[] DIGIT_TENS = { - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', - '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', - '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', - '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', - '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', - '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', - '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', - '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', - '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', - }; - - static final char[] DIGIT_ONES = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - }; - } - /** * Lay out this {@code BigDecimal} into a {@code char[]} array. * The Java 1.2 equivalent to this was called {@code getValueString}. @@ -4275,6 +4186,8 @@ int putIntCompact(long intCompact) { * {@code BigDecimal} */ private String layoutChars(boolean sci) { + long intCompact = this.intCompact; + int scale = this.scale; if (scale == 0) // zero scale is trivial return (intCompact != INFLATED) ? Long.toString(intCompact): @@ -4284,18 +4197,24 @@ private String layoutChars(boolean sci) { // currency fast path int lowInt = (int)intCompact % 100; int highInt = (int)intCompact / 100; - return (Integer.toString(highInt) + '.' + - StringBuilderHelper.DIGIT_TENS[lowInt] + - StringBuilderHelper.DIGIT_ONES[lowInt]) ; + int highIntSize = DecimalDigits.stringSize(highInt); + byte[] buf = new byte[highIntSize + 3]; + DecimalDigits.putPairLatin1(buf, highIntSize + 1, lowInt); + buf[highIntSize] = '.'; + DecimalDigits.getCharsLatin1(highInt, highIntSize, buf); + try { + return JLA.newStringNoRepl(buf, StandardCharsets.ISO_8859_1); + } catch (CharacterCodingException cce) { + throw new AssertionError(cce); + } } - StringBuilderHelper sbHelper = new StringBuilderHelper(); char[] coeff; int offset; // offset is the starting index for coeff array // Get the significand as an absolute value if (intCompact != INFLATED) { - offset = sbHelper.putIntCompact(Math.abs(intCompact)); - coeff = sbHelper.getCompactCharArray(); + coeff = new char[19]; + offset = DecimalDigits.getChars(Math.abs(intCompact), coeff.length, coeff); } else { offset = 0; coeff = intVal.abs().toString().toCharArray(); @@ -4305,7 +4224,7 @@ private String layoutChars(boolean sci) { // If E-notation is needed, length will be: +1 if negative, +1 // if '.' needed, +2 for "E+", + up to 10 for adjusted exponent. // Otherwise it could have +1 if negative, plus leading "0.00000" - StringBuilder buf = sbHelper.getStringBuilder(); + StringBuilder buf = new StringBuilder(32);; if (signum() < 0) // prefix '-' if negative buf.append('-'); int coeffLen = coeff.length - offset; diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 0436cbb314f8e..ecfdbd28095d5 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -487,10 +487,6 @@ public interface JavaLangAccess { */ Object classData(Class c); - int getCharsLatin1(long i, int index, byte[] buf); - - int getCharsUTF16(long i, int index, byte[] buf); - /** * Returns the {@link NativeLibraries} object associated with the provided class loader. * This is used by {@link SymbolLookup#loaderLookup()}. 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..0189e859d8b12 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.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.vm.annotation.Stable; /** @@ -33,6 +35,7 @@ * @since 21 */ public final class DecimalDigits { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); /** * Each element of the array represents the packaging of two ascii characters based on little endian:

@@ -136,4 +139,280 @@ public static int stringSize(long x) { } return 19 + d; } + + // Used by trusted callers. Assumes all necessary bounds checks have + // been done by the caller. + /** + * Places characters representing the integer i into the + * character array buf. The characters are placed into + * the buffer backwards starting with the least significant + * digit at the specified index (exclusive), and working + * backwards from there. + * + * @implNote This method converts positive inputs into negative + * values, to cover the Integer.MIN_VALUE case. Converting otherwise + * (negative to positive) will expose -Integer.MIN_VALUE that overflows + * integer. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, Latin1-encoded + * @return index of the most significant digit or minus sign, if present + */ + public static int getCharsLatin1(int i, int index, byte[] buf) { + // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. + int q; + int charPos = index; + + boolean negative = i < 0; + if (!negative) { + i = -i; + } + + // Generate two digits per iteration + while (i <= -100) { + q = i / 100; + charPos -= 2; + putPairLatin1(buf, charPos, (q * 100) - i); + i = q; + } + + // We know there are at most two digits left at this point. + if (i < -9) { + charPos -= 2; + putPairLatin1(buf, charPos, -i); + } else { + buf[--charPos] = (byte)('0' - i); + } + + if (negative) { + buf[--charPos] = (byte)'-'; + } + return charPos; + } + + + /** + * Places characters representing the long i into the + * character array buf. The characters are placed into + * the buffer backwards starting with the least significant + * digit at the specified index (exclusive), and working + * backwards from there. + * + * @implNote This method converts positive inputs into negative + * values, to cover the Long.MIN_VALUE case. Converting otherwise + * (negative to positive) will expose -Long.MIN_VALUE that overflows + * long. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, Latin1-encoded + * @return index of the most significant digit or minus sign, if present + */ + public static int getCharsLatin1(long i, int index, byte[] buf) { + // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. + long q; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i <= Integer.MIN_VALUE) { + q = i / 100; + charPos -= 2; + putPairLatin1(buf, charPos, (int)((q * 100) - i)); + i = q; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 <= -100) { + q2 = i2 / 100; + charPos -= 2; + putPairLatin1(buf, charPos, (q2 * 100) - i2); + i2 = q2; + } + + // We know there are at most two digits left at this point. + if (i2 < -9) { + charPos -= 2; + putPairLatin1(buf, charPos, -i2); + } else { + buf[--charPos] = (byte)('0' - i2); + } + + if (negative) { + buf[--charPos] = (byte)'-'; + } + return charPos; + } + + + /** + * This is a variant of {@link DecimalDigits#getCharsLatin1(int, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + public static int getCharsUTF16(int i, int index, byte[] buf) { + // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. + int q, r; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using ints + while (i <= -100) { + q = i / 100; + r = (q * 100) - i; + i = q; + charPos -= 2; + putPairUTF16(buf, charPos, r); + } + + // We know there are at most two digits left at this point. + if (i < -9) { + charPos -= 2; + putPairUTF16(buf, charPos, -i); + } else { + JLA.putCharUTF16(buf, --charPos, '0' - i); + } + + if (negative) { + JLA.putCharUTF16(buf, --charPos, '-'); + } + return charPos; + } + + + /** + * This is a variant of {@link DecimalDigits#getCharsLatin1(long, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + public static int getCharsUTF16(long i, int index, byte[] buf) { + // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. + long q; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i <= Integer.MIN_VALUE) { + q = i / 100; + charPos -= 2; + putPairUTF16(buf, charPos, (int)((q * 100) - i)); + i = q; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 <= -100) { + q2 = i2 / 100; + charPos -= 2; + putPairUTF16(buf, charPos, (q2 * 100) - i2); + i2 = q2; + } + + // We know there are at most two digits left at this point. + if (i2 < -9) { + charPos -= 2; + putPairUTF16(buf, charPos, -i2); + } else { + JLA.putCharUTF16(buf, --charPos, '0' - i2); + } + + if (negative) { + JLA.putCharUTF16(buf, --charPos, '-'); + } + return charPos; + } + + /** + * This is a variant of {@link DecimalDigits#getCharsUTF16(long, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + public static int getChars(long i, int index, char[] buf) { + // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. + long q; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i <= Integer.MIN_VALUE) { + q = i / 100; + charPos -= 2; + putPair(buf, charPos, (int)((q * 100) - i)); + i = q; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 <= -100) { + q2 = i2 / 100; + charPos -= 2; + putPair(buf, charPos, (q2 * 100) - i2); + i2 = q2; + } + + // We know there are at most two digits left at this point. + if (i2 < -9) { + charPos -= 2; + putPair(buf, charPos, -i2); + } else { + buf[--charPos] = (char) ('0' - i2); + } + + if (negative) { + buf[--charPos] = '-'; + } + return charPos; + } + + public static void putPairLatin1(byte[] buf, int charPos, int v) { + int packed = digitPair(v); + buf[charPos ] = (byte) (packed); + buf[charPos + 1] = (byte) (packed >> 8); + } + + public static void putPairUTF16(byte[] buf, int charPos, int v) { + int packed = digitPair(v); + JLA.putCharUTF16(buf, charPos, packed & 0xFF); + JLA.putCharUTF16(buf, charPos + 1, packed >> 8); + } + + public static void putPair(char[] buf, int charPos, int v) { + int packed = digitPair(v); + buf[charPos ] = (char) (packed & 0xFF); + buf[charPos + 1] = (char) (packed >> 8); + } + // End of trusted methods. } From 2b7baadf2df5d37ec4b42d732aa429e57b643c43 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 08:27:49 +0800 Subject: [PATCH 02/23] add comments --- .../jdk/internal/util/DecimalDigits.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 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 0189e859d8b12..2319ddc1816e3 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -397,22 +397,43 @@ public static int getChars(long i, int index, char[] buf) { return charPos; } + /** + * Insert the 2-chars integer into the buf as 2 decimal digit ASCII chars, + * only least significant 16 bits of {@code v} are used. + * @param buf byte buffer to copy into + * @param charPos insert point + * @param v to convert + */ + public static void putPair(char[] buf, int charPos, int v) { + int packed = digitPair(v); + buf[charPos ] = (char) (packed & 0xFF); + buf[charPos + 1] = (char) (packed >> 8); + } + + /** + * Insert the 2-bytes integer into the buf as 2 decimal digit ASCII bytes, + * only least significant 16 bits of {@code v} are used. + * @param buf byte buffer to copy into + * @param charPos insert point + * @param v to convert + */ public static void putPairLatin1(byte[] buf, int charPos, int v) { int packed = digitPair(v); buf[charPos ] = (byte) (packed); buf[charPos + 1] = (byte) (packed >> 8); } + /** + * Insert the 2-chars integer into the buf as 2 decimal digit UTF16 bytes, + * only least significant 16 bits of {@code v} are used. + * @param buf byte buffer to copy into + * @param charPos insert point + * @param v to convert + */ public static void putPairUTF16(byte[] buf, int charPos, int v) { int packed = digitPair(v); JLA.putCharUTF16(buf, charPos, packed & 0xFF); JLA.putCharUTF16(buf, charPos + 1, packed >> 8); } - - public static void putPair(char[] buf, int charPos, int v) { - int packed = digitPair(v); - buf[charPos ] = (char) (packed & 0xFF); - buf[charPos + 1] = (char) (packed >> 8); - } // End of trusted methods. } From 7fbb3996ec476da989c35a0b02657a3fb8444bc4 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 08:58:01 +0800 Subject: [PATCH 03/23] remove unused code --- .../share/classes/java/lang/StringUTF16.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index a2c4e075c0564..99226ac1012ba 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -35,7 +35,6 @@ import jdk.internal.misc.Unsafe; import jdk.internal.util.ArraysSupport; -import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -1514,20 +1513,6 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { return codePointCount(val, beginIndex, endIndex, true /* checked */); } - public static int getChars(int i, int begin, int end, byte[] value) { - checkBoundsBeginEnd(begin, end, value); - int pos = DecimalDigits.getCharsUTF16(i, end, value); - assert begin == pos; - return pos; - } - - public static int getChars(long l, int begin, int end, byte[] value) { - checkBoundsBeginEnd(begin, end, value); - int pos = DecimalDigits.getCharsUTF16(l, end, value); - assert begin == pos; - return pos; - } - public static boolean contentEquals(byte[] v1, byte[] v2, int len) { checkBoundsOffCount(0, len, v2); for (int i = 0; i < len; i++) { From fb6c741183ef9a23a98d59a6fd4c1e5389da0d4a Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 09:41:56 +0800 Subject: [PATCH 04/23] remove JLA --- .../jdk/internal/util/DecimalDigits.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 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 2319ddc1816e3..121879e76fd68 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -25,17 +25,18 @@ package jdk.internal.util; -import jdk.internal.access.JavaLangAccess; -import jdk.internal.access.SharedSecrets; +import sun.misc.Unsafe; import jdk.internal.vm.annotation.Stable; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; + /** * Digits class for decimal digits. * * @since 21 */ public final class DecimalDigits { - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); /** * Each element of the array represents the packaging of two ascii characters based on little endian:

@@ -285,11 +286,11 @@ public static int getCharsUTF16(int i, int index, byte[] buf) { charPos -= 2; putPairUTF16(buf, charPos, -i); } else { - JLA.putCharUTF16(buf, --charPos, '0' - i); + putChar(buf, --charPos, '0' - i); } if (negative) { - JLA.putCharUTF16(buf, --charPos, '-'); + putChar(buf, --charPos, '-'); } return charPos; } @@ -337,11 +338,11 @@ public static int getCharsUTF16(long i, int index, byte[] buf) { charPos -= 2; putPairUTF16(buf, charPos, -i2); } else { - JLA.putCharUTF16(buf, --charPos, '0' - i2); + putChar(buf, --charPos, '0' - i2); } if (negative) { - JLA.putCharUTF16(buf, --charPos, '-'); + putChar(buf, --charPos, '-'); } return charPos; } @@ -432,8 +433,12 @@ public static void putPairLatin1(byte[] buf, int charPos, int v) { */ public static void putPairUTF16(byte[] buf, int charPos, int v) { int packed = digitPair(v); - JLA.putCharUTF16(buf, charPos, packed & 0xFF); - JLA.putCharUTF16(buf, charPos + 1, packed >> 8); + putChar(buf, charPos, packed & 0xFF); + putChar(buf, charPos + 1, packed >> 8); + } + + static void putChar(byte[] buf, int charPos, int c) { + UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + (charPos << 1), (char) c); } // End of trusted methods. } From fffa5283953ae59638842a6c2a19feca31f4268a Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 10:01:30 +0800 Subject: [PATCH 05/23] fix import --- .../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 121879e76fd68..f12171995453f 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -25,7 +25,7 @@ package jdk.internal.util; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import jdk.internal.vm.annotation.Stable; import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; From 060893fd7e1afbb8b531d3e78335069a7ccd0cce Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 10:13:41 +0800 Subject: [PATCH 06/23] remove digitPair --- .../classes/jdk/internal/util/DecimalDigits.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 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 f12171995453f..1667341c68204 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -80,15 +80,6 @@ public final class DecimalDigits { private DecimalDigits() { } - /** - * For values from 0 to 99 return a short encoding a pair of ASCII-encoded digit characters in little-endian - * @param i value to convert - * @return a short encoding a pair of ASCII-encoded digit characters - */ - public static short digitPair(int i) { - return DIGITS[i]; - } - /** * Returns the string representation size for a given int value. * @@ -406,7 +397,7 @@ public static int getChars(long i, int index, char[] buf) { * @param v to convert */ public static void putPair(char[] buf, int charPos, int v) { - int packed = digitPair(v); + int packed = DIGITS[v]; buf[charPos ] = (char) (packed & 0xFF); buf[charPos + 1] = (char) (packed >> 8); } @@ -419,7 +410,7 @@ public static void putPair(char[] buf, int charPos, int v) { * @param v to convert */ public static void putPairLatin1(byte[] buf, int charPos, int v) { - int packed = digitPair(v); + int packed = DIGITS[v]; buf[charPos ] = (byte) (packed); buf[charPos + 1] = (byte) (packed >> 8); } @@ -432,7 +423,7 @@ public static void putPairLatin1(byte[] buf, int charPos, int v) { * @param v to convert */ public static void putPairUTF16(byte[] buf, int charPos, int v) { - int packed = digitPair(v); + int packed = DIGITS[v]; putChar(buf, charPos, packed & 0xFF); putChar(buf, charPos + 1, packed >> 8); } From 4aa84d593345076c013e28bd6f48e3f8c80a6d17 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 10:35:28 +0800 Subject: [PATCH 07/23] unsafe putByte --- .../jdk/internal/util/DecimalDigits.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 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 1667341c68204..029b4cf026de1 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -174,11 +174,11 @@ public static int getCharsLatin1(int i, int index, byte[] buf) { charPos -= 2; putPairLatin1(buf, charPos, -i); } else { - buf[--charPos] = (byte)('0' - i); + putCharLatin1(buf, --charPos, '0' - i); } if (negative) { - buf[--charPos] = (byte)'-'; + putCharLatin1(buf, --charPos, '-'); } return charPos; } @@ -234,11 +234,11 @@ public static int getCharsLatin1(long i, int index, byte[] buf) { charPos -= 2; putPairLatin1(buf, charPos, -i2); } else { - buf[--charPos] = (byte)('0' - i2); + putCharLatin1(buf, --charPos, '0' - i2); } if (negative) { - buf[--charPos] = (byte)'-'; + putCharLatin1(buf, --charPos, '-'); } return charPos; } @@ -277,11 +277,11 @@ public static int getCharsUTF16(int i, int index, byte[] buf) { charPos -= 2; putPairUTF16(buf, charPos, -i); } else { - putChar(buf, --charPos, '0' - i); + putCharUTF16(buf, --charPos, '0' - i); } if (negative) { - putChar(buf, --charPos, '-'); + putCharUTF16(buf, --charPos, '-'); } return charPos; } @@ -329,11 +329,11 @@ public static int getCharsUTF16(long i, int index, byte[] buf) { charPos -= 2; putPairUTF16(buf, charPos, -i2); } else { - putChar(buf, --charPos, '0' - i2); + putCharUTF16(buf, --charPos, '0' - i2); } if (negative) { - putChar(buf, --charPos, '-'); + putCharUTF16(buf, --charPos, '-'); } return charPos; } @@ -411,8 +411,8 @@ public static void putPair(char[] buf, int charPos, int v) { */ public static void putPairLatin1(byte[] buf, int charPos, int v) { int packed = DIGITS[v]; - buf[charPos ] = (byte) (packed); - buf[charPos + 1] = (byte) (packed >> 8); + putCharLatin1(buf, charPos, packed & 0xFF); + putCharLatin1(buf, charPos + 1, packed >> 8); } /** @@ -424,11 +424,15 @@ public static void putPairLatin1(byte[] buf, int charPos, int v) { */ public static void putPairUTF16(byte[] buf, int charPos, int v) { int packed = DIGITS[v]; - putChar(buf, charPos, packed & 0xFF); - putChar(buf, charPos + 1, packed >> 8); + putCharUTF16(buf, charPos, packed & 0xFF); + putCharUTF16(buf, charPos + 1, packed >> 8); } - static void putChar(byte[] buf, int charPos, int c) { + private static void putCharLatin1(byte[] buf, int charPos, int c) { + UNSAFE.putByte(buf, ARRAY_BYTE_BASE_OFFSET + charPos, (byte) c); + } + + private static void putCharUTF16(byte[] buf, int charPos, int c) { UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + (charPos << 1), (char) c); } // End of trusted methods. From 04846840cf3c6e1d1cfe76905f899e8cf292273f Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 10:38:43 +0800 Subject: [PATCH 08/23] fix Helper --- .../jtreg/compiler/patches/java.base/java/lang/Helper.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index a24d7b98ada52..b0f99d7d935ef 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -23,6 +23,8 @@ package java.lang; +import jdk.internal.util.DecimalDigits; + /** * A helper class to get access to package-private members */ @@ -117,11 +119,11 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { } public static int getChars(int i, int begin, int end, byte[] value) { - return StringUTF16.getChars(i, begin, end, value); + return DecimalDigits.getCharsUTF16(i, end, value); } public static int getChars(long l, int begin, int end, byte[] value) { - return StringUTF16.getChars(l, begin, end, value); + return DecimalDigits.getCharsUTF16(l, begin, end, value); } public static boolean contentEquals(byte[] v1, byte[] v2, int len) { From 6324cf46b95397f919d34a5db62677c299c11e08 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 11:23:07 +0800 Subject: [PATCH 09/23] fix Helper --- .../jtreg/compiler/patches/java.base/java/lang/Helper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index b0f99d7d935ef..ff20cda80610f 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -123,7 +123,7 @@ public static int getChars(int i, int begin, int end, byte[] value) { } public static int getChars(long l, int begin, int end, byte[] value) { - return DecimalDigits.getCharsUTF16(l, begin, end, value); + return DecimalDigits.getCharsUTF16(l, end, value); } public static boolean contentEquals(byte[] v1, byte[] v2, int len) { From 65dbe6763e2c6657f13693f589d5502388738bc2 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 20 Oct 2024 12:41:40 +0800 Subject: [PATCH 10/23] fix Helper --- .../compiler/patches/java.base/java/lang/Helper.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index ff20cda80610f..540c0774fd01b 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -119,11 +119,17 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { } public static int getChars(int i, int begin, int end, byte[] value) { - return DecimalDigits.getCharsUTF16(i, end, value); + StringUTF16.checkBoundsBeginEnd(begin, end, value); + int pos = DecimalDigits.getCharsUTF16(i, end, value); + assert begin == pos; + return pos; } public static int getChars(long l, int begin, int end, byte[] value) { - return DecimalDigits.getCharsUTF16(l, end, value); + StringUTF16.checkBoundsBeginEnd(begin, end, value); + int pos = DecimalDigits.getCharsUTF16(l, end, value); + assert begin == pos; + return pos; } public static boolean contentEquals(byte[] v1, byte[] v2, int len) { From e97138dbcebfa84386d24b675ecf8bcec070f932 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 6 Nov 2024 13:13:34 +0800 Subject: [PATCH 11/23] remove comments, from @liach --- .../share/classes/jdk/internal/util/DecimalDigits.java | 3 --- 1 file changed, 3 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 029b4cf026de1..75e67e3f9cc90 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -132,8 +132,6 @@ public static int stringSize(long x) { return 19 + d; } - // Used by trusted callers. Assumes all necessary bounds checks have - // been done by the caller. /** * Places characters representing the integer i into the * character array buf. The characters are placed into @@ -435,5 +433,4 @@ private static void putCharLatin1(byte[] buf, int charPos, int c) { private static void putCharUTF16(byte[] buf, int charPos, int c) { UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + (charPos << 1), (char) c); } - // End of trusted methods. } From cf947f61d7fc45944ee10e98579cd9a472c17b4f Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 10 Nov 2024 16:54:16 +0800 Subject: [PATCH 12/23] add benchmark --- .../bench/java/lang/StringBuilders.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java index ed5c0d30db866..e41bd361ff5f3 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java +++ b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java @@ -54,6 +54,8 @@ public class StringBuilders { private StringBuilder sbLatin2; private StringBuilder sbUtf16; private StringBuilder sbUtf17; + private int[] intsArray; + private long[] longArray; @Setup public void setup() { @@ -69,6 +71,13 @@ public void setup() { sbLatin2 = new StringBuilder("Latin1 string"); sbUtf16 = new StringBuilder("UTF-\uFF11\uFF16 string"); sbUtf17 = new StringBuilder("UTF-\uFF11\uFF16 string"); + int size = 16; + intsArray = new int[size]; + longArray = new long[size]; + for (int i = 0; i < longArray.length; i++) { + intsArray[i] = ((100 * i + i) << 24) + 4543 + i * 4; + longArray[i] = ((100L * i + i) << 32) + 4543 + i * 4L; + } } @Benchmark @@ -224,6 +233,47 @@ public String toStringCharWithInt8() { return result.toString(); } + @Benchmark + public int appendWithIntLatin1() { + StringBuilder buf = sbLatin1; + buf.setLength(0); + for (long l : longArray) { + buf.append(l); + } + return buf.length(); + } + + @Benchmark + public int appendWithIntUtf16() { + StringBuilder buf = sbUtf16; + buf.setLength(0); + buf.setLength(0); + for (long l : longArray) { + buf.append(l); + } + return buf.length(); + } + + @Benchmark + public int appendWithLongLatin1() { + StringBuilder buf = sbLatin1; + buf.setLength(0); + for (long l : longArray) { + buf.append(l); + } + return buf.length(); + } + + @Benchmark + public int appendWithLongUtf16() { + StringBuilder buf = sbUtf16; + buf.setLength(0); + buf.setLength(0); + for (long l : longArray) { + buf.append(l); + } + return buf.length(); + } @Benchmark public int appendWithBool8Latin1() { From cd9ba30994340b18526e429d20637b8b467e0083 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Mon, 11 Nov 2024 23:24:52 +0800 Subject: [PATCH 13/23] fix unsafe address overflow --- .../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 75e67e3f9cc90..44e3ac18fac83 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -427,10 +427,10 @@ public static void putPairUTF16(byte[] buf, int charPos, int v) { } private static void putCharLatin1(byte[] buf, int charPos, int c) { - UNSAFE.putByte(buf, ARRAY_BYTE_BASE_OFFSET + charPos, (byte) c); + UNSAFE.putByte(buf, ARRAY_BYTE_BASE_OFFSET + (long) charPos, (byte) c); } private static void putCharUTF16(byte[] buf, int charPos, int c) { - UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + (charPos << 1), (char) c); + UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c); } } From 72ff93f2cd49040fecb7d31a63203c58fa0313a6 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 10 Dec 2024 22:38:35 +0800 Subject: [PATCH 14/23] form @cl4es --- src/java.base/share/classes/java/math/BigDecimal.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index b00970963b6b3..448b7ebf8458d 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -4195,9 +4195,9 @@ private String layoutChars(boolean sci) { int highInt = (int)intCompact / 100; int highIntSize = DecimalDigits.stringSize(highInt); byte[] buf = new byte[highIntSize + 3]; - DecimalDigits.putPairLatin1(buf, highIntSize + 1, lowInt); - buf[highIntSize] = '.'; DecimalDigits.getCharsLatin1(highInt, highIntSize, buf); + buf[highIntSize] = '.'; + DecimalDigits.putPairLatin1(buf, highIntSize + 1, lowInt); try { return JLA.newStringNoRepl(buf, StandardCharsets.ISO_8859_1); } catch (CharacterCodingException cce) { @@ -4209,6 +4209,7 @@ private String layoutChars(boolean sci) { int offset; // offset is the starting index for coeff array // Get the significand as an absolute value if (intCompact != INFLATED) { + // All non negative longs can be made to fit into 19 character array. coeff = new char[19]; offset = DecimalDigits.getChars(Math.abs(intCompact), coeff.length, coeff); } else { From 14499590a08f84673296614388a58f37fa55d0cf Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 17 Jan 2025 08:04:36 +0800 Subject: [PATCH 15/23] fix comment, from @rgiulietti --- .../classes/jdk/internal/util/DecimalDigits.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 44e3ac18fac83..c6686f4857f4c 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -42,19 +42,19 @@ public final class DecimalDigits { * Each element of the array represents the packaging of two ascii characters based on little endian:

*

      *      00 -> '0' | ('0' << 8) -> 0x3030
-     *      01 -> '1' | ('0' << 8) -> 0x3130
-     *      02 -> '2' | ('0' << 8) -> 0x3230
+     *      01 -> '1' | ('0' << 8) -> 0x3031
+     *      02 -> '2' | ('0' << 8) -> 0x3032
      *
      *     ...
      *
-     *      10 -> '0' | ('1' << 8) -> 0x3031
+     *      10 -> '0' | ('1' << 8) -> 0x3130
      *      11 -> '1' | ('1' << 8) -> 0x3131
-     *      12 -> '2' | ('1' << 8) -> 0x3231
+     *      12 -> '2' | ('1' << 8) -> 0x3132
      *
      *     ...
      *
-     *      97 -> '7' | ('9' << 8) -> 0x3739
-     *      98 -> '8' | ('9' << 8) -> 0x3839
+     *      97 -> '7' | ('9' << 8) -> 0x3937
+     *      98 -> '8' | ('9' << 8) -> 0x3938
      *      99 -> '9' | ('9' << 8) -> 0x3939
      * 
*/ From 3e23ab6ac133bc31a3b4202f7c389d9dd6c4e51e Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 18 Jan 2025 08:53:56 +0800 Subject: [PATCH 16/23] fix comment --- .../classes/jdk/internal/util/DecimalDigits.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 c6686f4857f4c..44e3ac18fac83 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -42,19 +42,19 @@ public final class DecimalDigits { * Each element of the array represents the packaging of two ascii characters based on little endian:

*

      *      00 -> '0' | ('0' << 8) -> 0x3030
-     *      01 -> '1' | ('0' << 8) -> 0x3031
-     *      02 -> '2' | ('0' << 8) -> 0x3032
+     *      01 -> '1' | ('0' << 8) -> 0x3130
+     *      02 -> '2' | ('0' << 8) -> 0x3230
      *
      *     ...
      *
-     *      10 -> '0' | ('1' << 8) -> 0x3130
+     *      10 -> '0' | ('1' << 8) -> 0x3031
      *      11 -> '1' | ('1' << 8) -> 0x3131
-     *      12 -> '2' | ('1' << 8) -> 0x3132
+     *      12 -> '2' | ('1' << 8) -> 0x3231
      *
      *     ...
      *
-     *      97 -> '7' | ('9' << 8) -> 0x3937
-     *      98 -> '8' | ('9' << 8) -> 0x3938
+     *      97 -> '7' | ('9' << 8) -> 0x3739
+     *      98 -> '8' | ('9' << 8) -> 0x3839
      *      99 -> '9' | ('9' << 8) -> 0x3939
      * 
*/ From 7df0c8bfb1d4dae89ec1440432da47308e0cb758 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 18 Jan 2025 09:16:47 +0800 Subject: [PATCH 17/23] Coding style consistency, from rgiulietti --- .../share/classes/jdk/internal/util/DecimalDigits.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 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 44e3ac18fac83..8ce0a4f90c971 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -168,7 +168,7 @@ public static int getCharsLatin1(int i, int index, byte[] buf) { } // We know there are at most two digits left at this point. - if (i < -9) { + if (i <= -10) { charPos -= 2; putPairLatin1(buf, charPos, -i); } else { @@ -228,7 +228,7 @@ public static int getCharsLatin1(long i, int index, byte[] buf) { } // We know there are at most two digits left at this point. - if (i2 < -9) { + if (i2 <= -10) { charPos -= 2; putPairLatin1(buf, charPos, -i2); } else { @@ -271,7 +271,7 @@ public static int getCharsUTF16(int i, int index, byte[] buf) { } // We know there are at most two digits left at this point. - if (i < -9) { + if (i <= -10) { charPos -= 2; putPairUTF16(buf, charPos, -i); } else { @@ -323,7 +323,7 @@ public static int getCharsUTF16(long i, int index, byte[] buf) { } // We know there are at most two digits left at this point. - if (i2 < -9) { + if (i2 <= -10) { charPos -= 2; putPairUTF16(buf, charPos, -i2); } else { @@ -374,7 +374,7 @@ public static int getChars(long i, int index, char[] buf) { } // We know there are at most two digits left at this point. - if (i2 < -9) { + if (i2 <= -10) { charPos -= 2; putPair(buf, charPos, -i2); } else { From 803a88aad967dc60386ada967ef3964cb8170c8f Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 18 Jan 2025 09:21:53 +0800 Subject: [PATCH 18/23] fix benchmark, from rgiulietti --- .../openjdk/bench/java/lang/StringBuilders.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java index e41bd361ff5f3..f857a77d80e66 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java +++ b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java @@ -54,7 +54,7 @@ public class StringBuilders { private StringBuilder sbLatin2; private StringBuilder sbUtf16; private StringBuilder sbUtf17; - private int[] intsArray; + private int[] intArray; private long[] longArray; @Setup @@ -72,10 +72,10 @@ public void setup() { sbUtf16 = new StringBuilder("UTF-\uFF11\uFF16 string"); sbUtf17 = new StringBuilder("UTF-\uFF11\uFF16 string"); int size = 16; - intsArray = new int[size]; + intArray = new int[size]; longArray = new long[size]; for (int i = 0; i < longArray.length; i++) { - intsArray[i] = ((100 * i + i) << 24) + 4543 + i * 4; + intArray[i] = ((100 * i + i) << 24) + 4543 + i * 4; longArray[i] = ((100L * i + i) << 32) + 4543 + i * 4L; } } @@ -237,8 +237,8 @@ public String toStringCharWithInt8() { public int appendWithIntLatin1() { StringBuilder buf = sbLatin1; buf.setLength(0); - for (long l : longArray) { - buf.append(l); + for (int i : intArray) { + buf.append(i); } return buf.length(); } @@ -247,9 +247,8 @@ public int appendWithIntLatin1() { public int appendWithIntUtf16() { StringBuilder buf = sbUtf16; buf.setLength(0); - buf.setLength(0); - for (long l : longArray) { - buf.append(l); + for (int i : intArray) { + buf.append(i); } return buf.length(); } @@ -268,7 +267,6 @@ public int appendWithLongLatin1() { public int appendWithLongUtf16() { StringBuilder buf = sbUtf16; buf.setLength(0); - buf.setLength(0); for (long l : longArray) { buf.append(l); } From d3d9631c02c1bdefb97daa261cdbe8cfef1aae98 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 18 Jan 2025 09:27:56 +0800 Subject: [PATCH 19/23] Coding style consistency, from rgiulietti --- .../share/classes/jdk/internal/util/DecimalDigits.java | 5 ++--- 1 file changed, 2 insertions(+), 3 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 8ce0a4f90c971..9a309969991a3 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -253,7 +253,7 @@ public static int getCharsLatin1(long i, int index, byte[] buf) { */ public static int getCharsUTF16(int i, int index, byte[] buf) { // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller. - int q, r; + int q; int charPos = index; boolean negative = (i < 0); @@ -264,10 +264,9 @@ public static int getCharsUTF16(int i, int index, byte[] buf) { // Get 2 digits/iteration using ints while (i <= -100) { q = i / 100; - r = (q * 100) - i; i = q; charPos -= 2; - putPairUTF16(buf, charPos, r); + putPairUTF16(buf, charPos, (q * 100) - i); } // We know there are at most two digits left at this point. From c8e4fe1067b4b5f5411c56c7e30c04bd137857bd Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 18 Jan 2025 09:59:20 +0800 Subject: [PATCH 20/23] from rgiulietti --- .../share/classes/jdk/internal/util/DecimalDigits.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 9a309969991a3..358dc119c308f 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -210,7 +210,7 @@ public static int getCharsLatin1(long i, int index, byte[] buf) { } // Get 2 digits/iteration using longs until quotient fits into an int - while (i <= Integer.MIN_VALUE) { + while (i < Integer.MIN_VALUE) { q = i / 100; charPos -= 2; putPairLatin1(buf, charPos, (int)((q * 100) - i)); @@ -304,7 +304,7 @@ public static int getCharsUTF16(long i, int index, byte[] buf) { } // Get 2 digits/iteration using longs until quotient fits into an int - while (i <= Integer.MIN_VALUE) { + while (i < Integer.MIN_VALUE) { q = i / 100; charPos -= 2; putPairUTF16(buf, charPos, (int)((q * 100) - i)); @@ -355,7 +355,7 @@ public static int getChars(long i, int index, char[] buf) { } // Get 2 digits/iteration using longs until quotient fits into an int - while (i <= Integer.MIN_VALUE) { + while (i < Integer.MIN_VALUE) { q = i / 100; charPos -= 2; putPair(buf, charPos, (int)((q * 100) - i)); From 34bd9d43a9a57d849e7f86e85e19f76ae7546fc7 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 18 Jan 2025 11:14:10 +0800 Subject: [PATCH 21/23] bug fix --- .../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 358dc119c308f..a65e27d7ad96a 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -264,9 +264,9 @@ public static int getCharsUTF16(int i, int index, byte[] buf) { // Get 2 digits/iteration using ints while (i <= -100) { q = i / 100; - i = q; charPos -= 2; putPairUTF16(buf, charPos, (q * 100) - i); + i = q; } // We know there are at most two digits left at this point. From 7eb89c912cfbf312bc8549a8f2145cba1d35da12 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 19 Jan 2025 14:56:32 +0800 Subject: [PATCH 22/23] use putCharUnaligned --- .../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 a65e27d7ad96a..3059bf95e794f 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -430,6 +430,6 @@ private static void putCharLatin1(byte[] buf, int charPos, int c) { } private static void putCharUTF16(byte[] buf, int charPos, int c) { - UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c); + UNSAFE.putCharUnaligned(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c); } } From 960e279e25667a9ad82e5855a8cdb391a49967a0 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Mon, 20 Jan 2025 21:50:23 +0800 Subject: [PATCH 23/23] fix comments --- .../classes/jdk/internal/util/DecimalDigits.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 3059bf95e794f..2c140bfbc134a 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -42,19 +42,19 @@ public final class DecimalDigits { * Each element of the array represents the packaging of two ascii characters based on little endian:

*

      *      00 -> '0' | ('0' << 8) -> 0x3030
-     *      01 -> '1' | ('0' << 8) -> 0x3130
-     *      02 -> '2' | ('0' << 8) -> 0x3230
+     *      01 -> '0' | ('1' << 8) -> 0x3130
+     *      02 -> '0' | ('2' << 8) -> 0x3230
      *
      *     ...
      *
-     *      10 -> '0' | ('1' << 8) -> 0x3031
+     *      10 -> '1' | ('0' << 8) -> 0x3031
      *      11 -> '1' | ('1' << 8) -> 0x3131
-     *      12 -> '2' | ('1' << 8) -> 0x3231
+     *      12 -> '1' | ('2' << 8) -> 0x3231
      *
      *     ...
      *
-     *      97 -> '7' | ('9' << 8) -> 0x3739
-     *      98 -> '8' | ('9' << 8) -> 0x3839
+     *      97 -> '9' | ('7' << 8) -> 0x3739
+     *      98 -> '9' | ('8' << 8) -> 0x3839
      *      99 -> '9' | ('9' << 8) -> 0x3939
      * 
*/