From 07871cd78aa7ee35762234112dfe46fe3ebc9a57 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 21 May 2025 22:39:11 +0000 Subject: [PATCH] 8357063: Document preconditions for DecimalDigits methods Reviewed-by: vyazici, liach, rriggs --- .../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/math/BigDecimal.java | 4 +- .../jdk/internal/util/DecimalDigits.java | 87 +++++++++++-------- .../patches/java.base/java/lang/Helper.java | 4 +- 7 files changed, 69 insertions(+), 58 deletions(-) diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index b63e9d09210..d317557cbb1 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -924,9 +924,9 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence int spaceNeeded = count + DecimalDigits.stringSize(i); byte[] value = ensureCapacitySameCoder(this.value, coder, spaceNeeded); if (isLatin1(coder)) { - DecimalDigits.getCharsLatin1(i, spaceNeeded, value); + DecimalDigits.uncheckedGetCharsLatin1(i, spaceNeeded, value); } else { - DecimalDigits.getCharsUTF16(i, spaceNeeded, value); + DecimalDigits.uncheckedGetCharsUTF16(i, spaceNeeded, value); } this.value = value; this.count = spaceNeeded; @@ -951,9 +951,9 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence int spaceNeeded = count + DecimalDigits.stringSize(l); byte[] value = ensureCapacitySameCoder(this.value, coder, spaceNeeded); if (isLatin1(coder)) { - DecimalDigits.getCharsLatin1(l, spaceNeeded, value); + DecimalDigits.uncheckedGetCharsLatin1(l, spaceNeeded, value); } else { - DecimalDigits.getCharsUTF16(l, spaceNeeded, value); + DecimalDigits.uncheckedGetCharsUTF16(l, spaceNeeded, value); } this.value = value; this.count = spaceNeeded; diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 1350a66b66c..a95149a3edd 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -432,11 +432,11 @@ public final class Integer extends Number int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; - DecimalDigits.getCharsLatin1(i, size, buf); + DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); return new String(buf, LATIN1); } else { byte[] buf = new byte[size * 2]; - DecimalDigits.getCharsUTF16(i, size, buf); + DecimalDigits.uncheckedGetCharsUTF16(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 3093f37e99a..20e07f41b9e 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -462,11 +462,11 @@ public final class Long extends Number int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; - DecimalDigits.getCharsLatin1(i, size, buf); + DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); return new String(buf, LATIN1); } else { byte[] buf = new byte[size * 2]; - DecimalDigits.getCharsUTF16(i, size, buf); + DecimalDigits.uncheckedGetCharsUTF16(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 f82e392a1fb..a5f6abbfdf1 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -315,12 +315,12 @@ final class StringConcatHelper { static long prepend(long indexCoder, byte[] buf, int value, String prefix) { int index = (int)indexCoder; if (indexCoder < UTF16) { - index = DecimalDigits.getCharsLatin1(value, index, buf); + index = DecimalDigits.uncheckedGetCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); return index; } else { - index = DecimalDigits.getCharsUTF16(value, index, buf); + index = DecimalDigits.uncheckedGetCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); return index | UTF16; @@ -341,12 +341,12 @@ final class StringConcatHelper { static long prepend(long indexCoder, byte[] buf, long value, String prefix) { int index = (int)indexCoder; if (indexCoder < UTF16) { - index = DecimalDigits.getCharsLatin1(value, index, buf); + index = DecimalDigits.uncheckedGetCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); return index; } else { - index = DecimalDigits.getCharsUTF16(value, index, buf); + index = DecimalDigits.uncheckedGetCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); return index | UTF16; @@ -713,11 +713,11 @@ final class StringConcatHelper { */ static int prepend(int index, byte coder, byte[] buf, int value, String prefix) { if (coder == String.LATIN1) { - index = DecimalDigits.getCharsLatin1(value, index, buf); + index = DecimalDigits.uncheckedGetCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); } else { - index = DecimalDigits.getCharsUTF16(value, index, buf); + index = DecimalDigits.uncheckedGetCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); } @@ -737,11 +737,11 @@ final class StringConcatHelper { */ static int prepend(int index, byte coder, byte[] buf, long value, String prefix) { if (coder == String.LATIN1) { - index = DecimalDigits.getCharsLatin1(value, index, buf); + index = DecimalDigits.uncheckedGetCharsLatin1(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.LATIN1); } else { - index = DecimalDigits.getCharsUTF16(value, index, buf); + index = DecimalDigits.uncheckedGetCharsUTF16(value, index, buf); index -= prefix.length(); prefix.getBytes(buf, index, String.UTF16); } diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index eec26ce496d..d40ec0b705a 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -4146,9 +4146,9 @@ public class BigDecimal extends Number implements Comparable { int highInt = (int)intCompact / 100; int highIntSize = DecimalDigits.stringSize(highInt); byte[] buf = new byte[highIntSize + 3]; - DecimalDigits.getCharsLatin1(highInt, highIntSize, buf); + DecimalDigits.uncheckedGetCharsLatin1(highInt, highIntSize, buf); buf[highIntSize] = '.'; - DecimalDigits.putPairLatin1(buf, highIntSize + 1, lowInt); + DecimalDigits.uncheckedPutPairLatin1(buf, highIntSize + 1, lowInt); try { return JLA.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); } catch (CharacterCodingException cce) { 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 d3df46dcd3c..6c0c745651e 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -143,14 +143,15 @@ public final class DecimalDigits { * values, to cover the Integer.MIN_VALUE case. Converting otherwise * (negative to positive) will expose -Integer.MIN_VALUE that overflows * integer. + *

+ * WARNING: This method does not perform any bound checks. * * @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. + public static int uncheckedGetCharsLatin1(int i, int index, byte[] buf) { int q; int charPos = index; @@ -163,20 +164,20 @@ public final class DecimalDigits { while (i <= -100) { q = i / 100; charPos -= 2; - putPairLatin1(buf, charPos, (q * 100) - i); + uncheckedPutPairLatin1(buf, charPos, (q * 100) - i); i = q; } // We know there are at most two digits left at this point. if (i <= -10) { charPos -= 2; - putPairLatin1(buf, charPos, -i); + uncheckedPutPairLatin1(buf, charPos, -i); } else { - putCharLatin1(buf, --charPos, '0' - i); + uncheckedPutCharLatin1(buf, --charPos, '0' - i); } if (negative) { - putCharLatin1(buf, --charPos, '-'); + uncheckedPutCharLatin1(buf, --charPos, '-'); } return charPos; } @@ -193,14 +194,15 @@ public final class DecimalDigits { * values, to cover the Long.MIN_VALUE case. Converting otherwise * (negative to positive) will expose -Long.MIN_VALUE that overflows * long. + *

+ * WARNING: This method does not perform any bound checks. * * @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. + public static int uncheckedGetCharsLatin1(long i, int index, byte[] buf) { long q; int charPos = index; @@ -213,7 +215,7 @@ public final class DecimalDigits { while (i < Integer.MIN_VALUE) { q = i / 100; charPos -= 2; - putPairLatin1(buf, charPos, (int)((q * 100) - i)); + uncheckedPutPairLatin1(buf, charPos, (int)((q * 100) - i)); i = q; } @@ -223,36 +225,37 @@ public final class DecimalDigits { while (i2 <= -100) { q2 = i2 / 100; charPos -= 2; - putPairLatin1(buf, charPos, (q2 * 100) - i2); + uncheckedPutPairLatin1(buf, charPos, (q2 * 100) - i2); i2 = q2; } // We know there are at most two digits left at this point. if (i2 <= -10) { charPos -= 2; - putPairLatin1(buf, charPos, -i2); + uncheckedPutPairLatin1(buf, charPos, -i2); } else { - putCharLatin1(buf, --charPos, '0' - i2); + uncheckedPutCharLatin1(buf, --charPos, '0' - i2); } if (negative) { - putCharLatin1(buf, --charPos, '-'); + uncheckedPutCharLatin1(buf, --charPos, '-'); } return charPos; } /** - * This is a variant of {@link DecimalDigits#getCharsLatin1(int, int, byte[])}, but for + * This is a variant of {@link DecimalDigits#uncheckedGetCharsLatin1(int, int, byte[])}, but for * UTF-16 coder. + *

+ * WARNING: This method does not perform any bound checks. * * @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. + public static int uncheckedGetCharsUTF16(int i, int index, byte[] buf) { int q; int charPos = index; @@ -265,36 +268,37 @@ public final class DecimalDigits { while (i <= -100) { q = i / 100; charPos -= 2; - putPairUTF16(buf, charPos, (q * 100) - i); + uncheckedPutPairUTF16(buf, charPos, (q * 100) - i); i = q; } // We know there are at most two digits left at this point. if (i <= -10) { charPos -= 2; - putPairUTF16(buf, charPos, -i); + uncheckedPutPairUTF16(buf, charPos, -i); } else { - putCharUTF16(buf, --charPos, '0' - i); + uncheckedPutCharUTF16(buf, --charPos, '0' - i); } if (negative) { - putCharUTF16(buf, --charPos, '-'); + uncheckedPutCharUTF16(buf, --charPos, '-'); } return charPos; } /** - * This is a variant of {@link DecimalDigits#getCharsLatin1(long, int, byte[])}, but for + * This is a variant of {@link DecimalDigits#uncheckedGetCharsLatin1(long, int, byte[])}, but for * UTF-16 coder. + *

+ * WARNING: This method does not perform any bound checks. * * @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. + public static int uncheckedGetCharsUTF16(long i, int index, byte[] buf) { long q; int charPos = index; @@ -307,7 +311,7 @@ public final class DecimalDigits { while (i < Integer.MIN_VALUE) { q = i / 100; charPos -= 2; - putPairUTF16(buf, charPos, (int)((q * 100) - i)); + uncheckedPutPairUTF16(buf, charPos, (int)((q * 100) - i)); i = q; } @@ -317,26 +321,26 @@ public final class DecimalDigits { while (i2 <= -100) { q2 = i2 / 100; charPos -= 2; - putPairUTF16(buf, charPos, (q2 * 100) - i2); + uncheckedPutPairUTF16(buf, charPos, (q2 * 100) - i2); i2 = q2; } // We know there are at most two digits left at this point. if (i2 <= -10) { charPos -= 2; - putPairUTF16(buf, charPos, -i2); + uncheckedPutPairUTF16(buf, charPos, -i2); } else { - putCharUTF16(buf, --charPos, '0' - i2); + uncheckedPutCharUTF16(buf, --charPos, '0' - i2); } if (negative) { - putCharUTF16(buf, --charPos, '-'); + uncheckedPutCharUTF16(buf, --charPos, '-'); } return charPos; } /** - * This is a variant of {@link DecimalDigits#getCharsUTF16(long, int, byte[])}, but for + * This is a variant of {@link DecimalDigits#uncheckedGetCharsUTF16(long, int, byte[])}, but for * UTF-16 coder. * * @param i value to convert @@ -345,7 +349,6 @@ public final class DecimalDigits { * @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; @@ -402,34 +405,42 @@ public final class DecimalDigits { /** * Insert the 2-bytes integer into the buf as 2 decimal digit ASCII bytes, * only least significant 16 bits of {@code v} are used. + *

+ * WARNING: This method does not perform any bound checks. + * * @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) { + public static void uncheckedPutPairLatin1(byte[] buf, int charPos, int v) { int packed = DIGITS[v & 0x7f]; - putCharLatin1(buf, charPos, packed & 0xFF); - putCharLatin1(buf, charPos + 1, packed >> 8); + uncheckedPutCharLatin1(buf, charPos, packed & 0xFF); + uncheckedPutCharLatin1(buf, charPos + 1, 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. + *

+ * WARNING: This method does not perform any bound checks. + * * @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) { + public static void uncheckedPutPairUTF16(byte[] buf, int charPos, int v) { int packed = DIGITS[v & 0x7f]; - putCharUTF16(buf, charPos, packed & 0xFF); - putCharUTF16(buf, charPos + 1, packed >> 8); + uncheckedPutCharUTF16(buf, charPos, packed & 0xFF); + uncheckedPutCharUTF16(buf, charPos + 1, packed >> 8); } - private static void putCharLatin1(byte[] buf, int charPos, int c) { + private static void uncheckedPutCharLatin1(byte[] buf, int charPos, int c) { + assert charPos >= 0 && charPos < buf.length; UNSAFE.putByte(buf, ARRAY_BYTE_BASE_OFFSET + charPos, (byte) c); } - private static void putCharUTF16(byte[] buf, int charPos, int c) { + private static void uncheckedPutCharUTF16(byte[] buf, int charPos, int c) { + assert charPos >= 0 && charPos < (buf.length >> 1); UNSAFE.putCharUnaligned(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c); } } 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 aef27bfb9a4..e6c8b68fc6f 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 @@ -120,14 +120,14 @@ public class Helper { public static int getChars(int i, int begin, int end, byte[] value) { StringUTF16.checkBoundsBeginEnd(begin, end, value); - int pos = DecimalDigits.getCharsUTF16(i, end, value); + int pos = DecimalDigits.uncheckedGetCharsUTF16(i, end, value); assert begin == pos; return pos; } public static int getChars(long l, int begin, int end, byte[] value) { StringUTF16.checkBoundsBeginEnd(begin, end, value); - int pos = DecimalDigits.getCharsUTF16(l, end, value); + int pos = DecimalDigits.uncheckedGetCharsUTF16(l, end, value); assert begin == pos; return pos; }