Skip to content

Commit f446cef

Browse files
committedJan 25, 2025
8343962: [REDO] Move getChars to DecimalDigits
Reviewed-by: redestad, rgiulietti
1 parent 7c0985f commit f446cef

File tree

12 files changed

+409
-382
lines changed

12 files changed

+409
-382
lines changed
 

‎src/java.base/share/classes/java/lang/AbstractStringBuilder.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -830,9 +830,9 @@ public AbstractStringBuilder append(int i) {
830830
int spaceNeeded = count + DecimalDigits.stringSize(i);
831831
ensureCapacityInternal(spaceNeeded);
832832
if (isLatin1()) {
833-
StringLatin1.getChars(i, spaceNeeded, value);
833+
DecimalDigits.getCharsLatin1(i, spaceNeeded, value);
834834
} else {
835-
StringUTF16.getChars(i, count, spaceNeeded, value);
835+
DecimalDigits.getCharsUTF16(i, spaceNeeded, value);
836836
}
837837
this.count = spaceNeeded;
838838
return this;
@@ -855,9 +855,9 @@ public AbstractStringBuilder append(long l) {
855855
int spaceNeeded = count + DecimalDigits.stringSize(l);
856856
ensureCapacityInternal(spaceNeeded);
857857
if (isLatin1()) {
858-
StringLatin1.getChars(l, spaceNeeded, value);
858+
DecimalDigits.getCharsLatin1(l, spaceNeeded, value);
859859
} else {
860-
StringUTF16.getChars(l, count, spaceNeeded, value);
860+
DecimalDigits.getCharsUTF16(l, spaceNeeded, value);
861861
}
862862
this.count = spaceNeeded;
863863
return this;

‎src/java.base/share/classes/java/lang/Integer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -432,11 +432,11 @@ public static String toString(int i) {
432432
int size = DecimalDigits.stringSize(i);
433433
if (COMPACT_STRINGS) {
434434
byte[] buf = new byte[size];
435-
StringLatin1.getChars(i, size, buf);
435+
DecimalDigits.getCharsLatin1(i, size, buf);
436436
return new String(buf, LATIN1);
437437
} else {
438438
byte[] buf = new byte[size * 2];
439-
StringUTF16.getChars(i, size, buf);
439+
DecimalDigits.getCharsUTF16(i, size, buf);
440440
return new String(buf, UTF16);
441441
}
442442
}

‎src/java.base/share/classes/java/lang/Long.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -462,11 +462,11 @@ public static String toString(long i) {
462462
int size = DecimalDigits.stringSize(i);
463463
if (COMPACT_STRINGS) {
464464
byte[] buf = new byte[size];
465-
StringLatin1.getChars(i, size, buf);
465+
DecimalDigits.getCharsLatin1(i, size, buf);
466466
return new String(buf, LATIN1);
467467
} else {
468468
byte[] buf = new byte[size * 2];
469-
StringUTF16.getChars(i, size, buf);
469+
DecimalDigits.getCharsUTF16(i, size, buf);
470470
return new String(buf, UTF16);
471471
}
472472
}

‎src/java.base/share/classes/java/lang/StringConcatHelper.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,12 @@ static long prepend(long indexCoder, byte[] buf, char value, String prefix) {
298298
static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
299299
int index = (int)indexCoder;
300300
if (indexCoder < UTF16) {
301-
index = StringLatin1.getChars(value, index, buf);
301+
index = DecimalDigits.getCharsLatin1(value, index, buf);
302302
index -= prefix.length();
303303
prefix.getBytes(buf, index, String.LATIN1);
304304
return index;
305305
} else {
306-
index = StringUTF16.getChars(value, index, buf);
306+
index = DecimalDigits.getCharsUTF16(value, index, buf);
307307
index -= prefix.length();
308308
prefix.getBytes(buf, index, String.UTF16);
309309
return index | UTF16;
@@ -324,12 +324,12 @@ static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
324324
static long prepend(long indexCoder, byte[] buf, long value, String prefix) {
325325
int index = (int)indexCoder;
326326
if (indexCoder < UTF16) {
327-
index = StringLatin1.getChars(value, index, buf);
327+
index = DecimalDigits.getCharsLatin1(value, index, buf);
328328
index -= prefix.length();
329329
prefix.getBytes(buf, index, String.LATIN1);
330330
return index;
331331
} else {
332-
index = StringUTF16.getChars(value, index, buf);
332+
index = DecimalDigits.getCharsUTF16(value, index, buf);
333333
index -= prefix.length();
334334
prefix.getBytes(buf, index, String.UTF16);
335335
return index | UTF16;
@@ -682,11 +682,11 @@ static int prepend(int index, byte coder, byte[] buf, char value, String prefix)
682682
*/
683683
static int prepend(int index, byte coder, byte[] buf, int value, String prefix) {
684684
if (coder == String.LATIN1) {
685-
index = StringLatin1.getChars(value, index, buf);
685+
index = DecimalDigits.getCharsLatin1(value, index, buf);
686686
index -= prefix.length();
687687
prefix.getBytes(buf, index, String.LATIN1);
688688
} else {
689-
index = StringUTF16.getChars(value, index, buf);
689+
index = DecimalDigits.getCharsUTF16(value, index, buf);
690690
index -= prefix.length();
691691
prefix.getBytes(buf, index, String.UTF16);
692692
}
@@ -706,11 +706,11 @@ static int prepend(int index, byte coder, byte[] buf, int value, String prefix)
706706
*/
707707
static int prepend(int index, byte coder, byte[] buf, long value, String prefix) {
708708
if (coder == String.LATIN1) {
709-
index = StringLatin1.getChars(value, index, buf);
709+
index = DecimalDigits.getCharsLatin1(value, index, buf);
710710
index -= prefix.length();
711711
prefix.getBytes(buf, index, String.LATIN1);
712712
} else {
713-
index = StringUTF16.getChars(value, index, buf);
713+
index = DecimalDigits.getCharsUTF16(value, index, buf);
714714
index -= prefix.length();
715715
prefix.getBytes(buf, index, String.UTF16);
716716
}

‎src/java.base/share/classes/java/lang/StringLatin1.java

-115
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import java.util.stream.StreamSupport;
3535
import jdk.internal.misc.Unsafe;
3636
import jdk.internal.util.ArraysSupport;
37-
import jdk.internal.util.DecimalDigits;
3837
import jdk.internal.vm.annotation.IntrinsicCandidate;
3938

4039
import static java.lang.String.LATIN1;
@@ -86,120 +85,6 @@ public static byte[] inflate(byte[] value, int off, int len) {
8685
return ret;
8786
}
8887

89-
/**
90-
* Places characters representing the integer i into the
91-
* character array buf. The characters are placed into
92-
* the buffer backwards starting with the least significant
93-
* digit at the specified index (exclusive), and working
94-
* backwards from there.
95-
*
96-
* @implNote This method converts positive inputs into negative
97-
* values, to cover the Integer.MIN_VALUE case. Converting otherwise
98-
* (negative to positive) will expose -Integer.MIN_VALUE that overflows
99-
* integer.
100-
*
101-
* @param i value to convert
102-
* @param index next index, after the least significant digit
103-
* @param buf target buffer, Latin1-encoded
104-
* @return index of the most significant digit or minus sign, if present
105-
*/
106-
static int getChars(int i, int index, byte[] buf) {
107-
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
108-
int q;
109-
int charPos = index;
110-
111-
boolean negative = i < 0;
112-
if (!negative) {
113-
i = -i;
114-
}
115-
116-
// Generate two digits per iteration
117-
while (i <= -100) {
118-
q = i / 100;
119-
charPos -= 2;
120-
writeDigitPair(buf, charPos, (q * 100) - i);
121-
i = q;
122-
}
123-
124-
// We know there are at most two digits left at this point.
125-
if (i < -9) {
126-
charPos -= 2;
127-
writeDigitPair(buf, charPos, -i);
128-
} else {
129-
buf[--charPos] = (byte)('0' - i);
130-
}
131-
132-
if (negative) {
133-
buf[--charPos] = (byte)'-';
134-
}
135-
return charPos;
136-
}
137-
138-
/**
139-
* Places characters representing the long i into the
140-
* character array buf. The characters are placed into
141-
* the buffer backwards starting with the least significant
142-
* digit at the specified index (exclusive), and working
143-
* backwards from there.
144-
*
145-
* @implNote This method converts positive inputs into negative
146-
* values, to cover the Long.MIN_VALUE case. Converting otherwise
147-
* (negative to positive) will expose -Long.MIN_VALUE that overflows
148-
* long.
149-
*
150-
* @param i value to convert
151-
* @param index next index, after the least significant digit
152-
* @param buf target buffer, Latin1-encoded
153-
* @return index of the most significant digit or minus sign, if present
154-
*/
155-
static int getChars(long i, int index, byte[] buf) {
156-
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
157-
long q;
158-
int charPos = index;
159-
160-
boolean negative = (i < 0);
161-
if (!negative) {
162-
i = -i;
163-
}
164-
165-
// Get 2 digits/iteration using longs until quotient fits into an int
166-
while (i <= Integer.MIN_VALUE) {
167-
q = i / 100;
168-
charPos -= 2;
169-
writeDigitPair(buf, charPos, (int)((q * 100) - i));
170-
i = q;
171-
}
172-
173-
// Get 2 digits/iteration using ints
174-
int q2;
175-
int i2 = (int)i;
176-
while (i2 <= -100) {
177-
q2 = i2 / 100;
178-
charPos -= 2;
179-
writeDigitPair(buf, charPos, (q2 * 100) - i2);
180-
i2 = q2;
181-
}
182-
183-
// We know there are at most two digits left at this point.
184-
if (i2 < -9) {
185-
charPos -= 2;
186-
writeDigitPair(buf, charPos, -i2);
187-
} else {
188-
buf[--charPos] = (byte)('0' - i2);
189-
}
190-
191-
if (negative) {
192-
buf[--charPos] = (byte)'-';
193-
}
194-
return charPos;
195-
}
196-
197-
private static void writeDigitPair(byte[] buf, int charPos, int value) {
198-
short pair = DecimalDigits.digitPair(value);
199-
buf[charPos] = (byte)(pair);
200-
buf[charPos + 1] = (byte)(pair >> 8);
201-
}
202-
20388
public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
20489
inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
20590
}

‎src/java.base/share/classes/java/lang/StringUTF16.java

-118
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535

3636
import jdk.internal.misc.Unsafe;
3737
import jdk.internal.util.ArraysSupport;
38-
import jdk.internal.util.DecimalDigits;
3938
import jdk.internal.vm.annotation.ForceInline;
4039
import jdk.internal.vm.annotation.IntrinsicCandidate;
4140

@@ -1513,20 +1512,6 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
15131512
return codePointCount(val, beginIndex, endIndex, true /* checked */);
15141513
}
15151514

1516-
public static int getChars(int i, int begin, int end, byte[] value) {
1517-
checkBoundsBeginEnd(begin, end, value);
1518-
int pos = getChars(i, end, value);
1519-
assert begin == pos;
1520-
return pos;
1521-
}
1522-
1523-
public static int getChars(long l, int begin, int end, byte[] value) {
1524-
checkBoundsBeginEnd(begin, end, value);
1525-
int pos = getChars(l, end, value);
1526-
assert begin == pos;
1527-
return pos;
1528-
}
1529-
15301515
public static boolean contentEquals(byte[] v1, byte[] v2, int len) {
15311516
checkBoundsOffCount(0, len, v2);
15321517
for (int i = 0; i < len; i++) {
@@ -1662,109 +1647,6 @@ public static int lastIndexOfLatin1(byte[] src, int srcCount,
16621647

16631648
static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
16641649

1665-
// Used by trusted callers. Assumes all necessary bounds checks have
1666-
// been done by the caller.
1667-
1668-
/**
1669-
* This is a variant of {@link StringLatin1#getChars(int, int, byte[])}, but for
1670-
* UTF-16 coder.
1671-
*
1672-
* @param i value to convert
1673-
* @param index next index, after the least significant digit
1674-
* @param buf target buffer, UTF16-coded.
1675-
* @return index of the most significant digit or minus sign, if present
1676-
*/
1677-
static int getChars(int i, int index, byte[] buf) {
1678-
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
1679-
int q, r;
1680-
int charPos = index;
1681-
1682-
boolean negative = (i < 0);
1683-
if (!negative) {
1684-
i = -i;
1685-
}
1686-
1687-
// Get 2 digits/iteration using ints
1688-
while (i <= -100) {
1689-
q = i / 100;
1690-
r = (q * 100) - i;
1691-
i = q;
1692-
charPos -= 2;
1693-
putPair(buf, charPos, r);
1694-
}
1695-
1696-
// We know there are at most two digits left at this point.
1697-
if (i < -9) {
1698-
charPos -= 2;
1699-
putPair(buf, charPos, -i);
1700-
} else {
1701-
putChar(buf, --charPos, '0' - i);
1702-
}
1703-
1704-
if (negative) {
1705-
putChar(buf, --charPos, '-');
1706-
}
1707-
return charPos;
1708-
}
1709-
1710-
/**
1711-
* This is a variant of {@link StringLatin1#getChars(long, int, byte[])}, but for
1712-
* UTF-16 coder.
1713-
*
1714-
* @param i value to convert
1715-
* @param index next index, after the least significant digit
1716-
* @param buf target buffer, UTF16-coded.
1717-
* @return index of the most significant digit or minus sign, if present
1718-
*/
1719-
static int getChars(long i, int index, byte[] buf) {
1720-
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
1721-
long q;
1722-
int charPos = index;
1723-
1724-
boolean negative = (i < 0);
1725-
if (!negative) {
1726-
i = -i;
1727-
}
1728-
1729-
// Get 2 digits/iteration using longs until quotient fits into an int
1730-
while (i <= Integer.MIN_VALUE) {
1731-
q = i / 100;
1732-
charPos -= 2;
1733-
putPair(buf, charPos, (int)((q * 100) - i));
1734-
i = q;
1735-
}
1736-
1737-
// Get 2 digits/iteration using ints
1738-
int q2;
1739-
int i2 = (int)i;
1740-
while (i2 <= -100) {
1741-
q2 = i2 / 100;
1742-
charPos -= 2;
1743-
putPair(buf, charPos, (q2 * 100) - i2);
1744-
i2 = q2;
1745-
}
1746-
1747-
// We know there are at most two digits left at this point.
1748-
if (i2 < -9) {
1749-
charPos -= 2;
1750-
putPair(buf, charPos, -i2);
1751-
} else {
1752-
putChar(buf, --charPos, '0' - i2);
1753-
}
1754-
1755-
if (negative) {
1756-
putChar(buf, --charPos, '-');
1757-
}
1758-
return charPos;
1759-
}
1760-
1761-
private static void putPair(byte[] buf, int charPos, int v) {
1762-
int packed = (int) DecimalDigits.digitPair(v);
1763-
putChar(buf, charPos, packed & 0xFF);
1764-
putChar(buf, charPos + 1, packed >> 8);
1765-
}
1766-
// End of trusted methods.
1767-
17681650
public static void checkIndex(int off, byte[] val) {
17691651
String.checkIndex(off, length(val));
17701652
}

‎src/java.base/share/classes/java/lang/System.java

-8
Original file line numberDiff line numberDiff line change
@@ -2179,14 +2179,6 @@ public byte stringCoder(String str) {
21792179
return str.coder();
21802180
}
21812181

2182-
public int getCharsLatin1(long i, int index, byte[] buf) {
2183-
return StringLatin1.getChars(i, index, buf);
2184-
}
2185-
2186-
public int getCharsUTF16(long i, int index, byte[] buf) {
2187-
return StringUTF16.getChars(i, index, buf);
2188-
}
2189-
21902182
public String join(String prefix, String suffix, String delimiter, String[] elements, int size) {
21912183
return String.join(prefix, suffix, delimiter, elements, size);
21922184
}

‎src/java.base/share/classes/java/math/BigDecimal.java

+24-104
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,15 @@
3535
import java.io.ObjectInputStream;
3636
import java.io.ObjectStreamException;
3737
import java.io.StreamCorruptedException;
38+
import java.nio.charset.CharacterCodingException;
39+
import java.nio.charset.StandardCharsets;
3840
import java.util.Arrays;
3941
import java.util.Objects;
4042

43+
import jdk.internal.access.JavaLangAccess;
44+
import jdk.internal.access.SharedSecrets;
45+
import jdk.internal.util.DecimalDigits;
46+
4147
/**
4248
* Immutable, arbitrary-precision signed decimal numbers. A {@code
4349
* BigDecimal} consists of an arbitrary precision integer
@@ -328,6 +334,8 @@
328334
* @since 1.1
329335
*/
330336
public class BigDecimal extends Number implements Comparable<BigDecimal> {
337+
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
338+
331339
/*
332340
* Let l = log_2(10).
333341
* Then, L < l < L + ulp(L) / 2, that is, L = roundTiesToEven(l).
@@ -4164,103 +4172,6 @@ public BigDecimal ulp() {
41644172
return BigDecimal.valueOf(1, this.scale(), 1);
41654173
}
41664174

4167-
// Private class to build a string representation for BigDecimal object. The
4168-
// StringBuilder field acts as a buffer to hold the temporary representation
4169-
// of BigDecimal. The cmpCharArray holds all the characters for the compact
4170-
// representation of BigDecimal (except for '-' sign' if it is negative) if
4171-
// its intCompact field is not INFLATED.
4172-
static class StringBuilderHelper {
4173-
final StringBuilder sb; // Placeholder for BigDecimal string
4174-
final char[] cmpCharArray; // character array to place the intCompact
4175-
4176-
StringBuilderHelper() {
4177-
sb = new StringBuilder(32);
4178-
// All non negative longs can be made to fit into 19 character array.
4179-
cmpCharArray = new char[19];
4180-
}
4181-
4182-
// Accessors.
4183-
StringBuilder getStringBuilder() {
4184-
sb.setLength(0);
4185-
return sb;
4186-
}
4187-
4188-
char[] getCompactCharArray() {
4189-
return cmpCharArray;
4190-
}
4191-
4192-
/**
4193-
* Places characters representing the intCompact in {@code long} into
4194-
* cmpCharArray and returns the offset to the array where the
4195-
* representation starts.
4196-
*
4197-
* @param intCompact the number to put into the cmpCharArray.
4198-
* @return offset to the array where the representation starts.
4199-
* Note: intCompact must be greater or equal to zero.
4200-
*/
4201-
int putIntCompact(long intCompact) {
4202-
assert intCompact >= 0;
4203-
4204-
long q;
4205-
int r;
4206-
// since we start from the least significant digit, charPos points to
4207-
// the last character in cmpCharArray.
4208-
int charPos = cmpCharArray.length;
4209-
4210-
// Get 2 digits/iteration using longs until quotient fits into an int
4211-
while (intCompact > Integer.MAX_VALUE) {
4212-
q = intCompact / 100;
4213-
r = (int)(intCompact - q * 100);
4214-
intCompact = q;
4215-
cmpCharArray[--charPos] = DIGIT_ONES[r];
4216-
cmpCharArray[--charPos] = DIGIT_TENS[r];
4217-
}
4218-
4219-
// Get 2 digits/iteration using ints when i2 >= 100
4220-
int q2;
4221-
int i2 = (int)intCompact;
4222-
while (i2 >= 100) {
4223-
q2 = i2 / 100;
4224-
r = i2 - q2 * 100;
4225-
i2 = q2;
4226-
cmpCharArray[--charPos] = DIGIT_ONES[r];
4227-
cmpCharArray[--charPos] = DIGIT_TENS[r];
4228-
}
4229-
4230-
cmpCharArray[--charPos] = DIGIT_ONES[i2];
4231-
if (i2 >= 10)
4232-
cmpCharArray[--charPos] = DIGIT_TENS[i2];
4233-
4234-
return charPos;
4235-
}
4236-
4237-
static final char[] DIGIT_TENS = {
4238-
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
4239-
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
4240-
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
4241-
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
4242-
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
4243-
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
4244-
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
4245-
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
4246-
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
4247-
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
4248-
};
4249-
4250-
static final char[] DIGIT_ONES = {
4251-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4252-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4253-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4254-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4255-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4256-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4257-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4258-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4259-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4260-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
4261-
};
4262-
}
4263-
42644175
/**
42654176
* Lay out this {@code BigDecimal} into a {@code char[]} array.
42664177
* The Java 1.2 equivalent to this was called {@code getValueString}.
@@ -4271,6 +4182,8 @@ int putIntCompact(long intCompact) {
42714182
* {@code BigDecimal}
42724183
*/
42734184
private String layoutChars(boolean sci) {
4185+
long intCompact = this.intCompact;
4186+
int scale = this.scale;
42744187
if (scale == 0) // zero scale is trivial
42754188
return (intCompact != INFLATED) ?
42764189
Long.toString(intCompact):
@@ -4280,18 +4193,25 @@ private String layoutChars(boolean sci) {
42804193
// currency fast path
42814194
int lowInt = (int)intCompact % 100;
42824195
int highInt = (int)intCompact / 100;
4283-
return (Integer.toString(highInt) + '.' +
4284-
StringBuilderHelper.DIGIT_TENS[lowInt] +
4285-
StringBuilderHelper.DIGIT_ONES[lowInt]) ;
4196+
int highIntSize = DecimalDigits.stringSize(highInt);
4197+
byte[] buf = new byte[highIntSize + 3];
4198+
DecimalDigits.getCharsLatin1(highInt, highIntSize, buf);
4199+
buf[highIntSize] = '.';
4200+
DecimalDigits.putPairLatin1(buf, highIntSize + 1, lowInt);
4201+
try {
4202+
return JLA.newStringNoRepl(buf, StandardCharsets.ISO_8859_1);
4203+
} catch (CharacterCodingException cce) {
4204+
throw new AssertionError(cce);
4205+
}
42864206
}
42874207

4288-
StringBuilderHelper sbHelper = new StringBuilderHelper();
42894208
char[] coeff;
42904209
int offset; // offset is the starting index for coeff array
42914210
// Get the significand as an absolute value
42924211
if (intCompact != INFLATED) {
4293-
offset = sbHelper.putIntCompact(Math.abs(intCompact));
4294-
coeff = sbHelper.getCompactCharArray();
4212+
// All non negative longs can be made to fit into 19 character array.
4213+
coeff = new char[19];
4214+
offset = DecimalDigits.getChars(Math.abs(intCompact), coeff.length, coeff);
42954215
} else {
42964216
offset = 0;
42974217
coeff = intVal.abs().toString().toCharArray();
@@ -4301,7 +4221,7 @@ private String layoutChars(boolean sci) {
43014221
// If E-notation is needed, length will be: +1 if negative, +1
43024222
// if '.' needed, +2 for "E+", + up to 10 for adjusted exponent.
43034223
// Otherwise it could have +1 if negative, plus leading "0.00000"
4304-
StringBuilder buf = sbHelper.getStringBuilder();
4224+
StringBuilder buf = new StringBuilder(32);;
43054225
if (signum() < 0) // prefix '-' if negative
43064226
buf.append('-');
43074227
int coeffLen = coeff.length - offset;

‎src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java

-4
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,6 @@ public interface JavaLangAccess {
471471
*/
472472
Object classData(Class<?> c);
473473

474-
int getCharsLatin1(long i, int index, byte[] buf);
475-
476-
int getCharsUTF16(long i, int index, byte[] buf);
477-
478474
/**
479475
* Returns the {@link NativeLibraries} object associated with the provided class loader.
480476
* This is used by {@link SymbolLookup#loaderLookup()}.

‎src/java.base/share/classes/jdk/internal/util/DecimalDigits.java

+311-15
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,36 @@
2525

2626
package jdk.internal.util;
2727

28+
import jdk.internal.misc.Unsafe;
2829
import jdk.internal.vm.annotation.Stable;
2930

31+
import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
32+
3033
/**
3134
* Digits class for decimal digits.
3235
*
3336
* @since 21
3437
*/
3538
public final class DecimalDigits {
39+
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
3640

3741
/**
3842
* Each element of the array represents the packaging of two ascii characters based on little endian:<p>
3943
* <pre>
4044
* 00 -> '0' | ('0' << 8) -> 0x3030
41-
* 01 -> '1' | ('0' << 8) -> 0x3130
42-
* 02 -> '2' | ('0' << 8) -> 0x3230
45+
* 01 -> '0' | ('1' << 8) -> 0x3130
46+
* 02 -> '0' | ('2' << 8) -> 0x3230
4347
*
4448
* ...
4549
*
46-
* 10 -> '0' | ('1' << 8) -> 0x3031
50+
* 10 -> '1' | ('0' << 8) -> 0x3031
4751
* 11 -> '1' | ('1' << 8) -> 0x3131
48-
* 12 -> '2' | ('1' << 8) -> 0x3231
52+
* 12 -> '1' | ('2' << 8) -> 0x3231
4953
*
5054
* ...
5155
*
52-
* 97 -> '7' | ('9' << 8) -> 0x3739
53-
* 98 -> '8' | ('9' << 8) -> 0x3839
56+
* 97 -> '9' | ('7' << 8) -> 0x3739
57+
* 98 -> '9' | ('8' << 8) -> 0x3839
5458
* 99 -> '9' | ('9' << 8) -> 0x3939
5559
* </pre>
5660
*/
@@ -76,15 +80,6 @@ public final class DecimalDigits {
7680
private DecimalDigits() {
7781
}
7882

79-
/**
80-
* For values from 0 to 99 return a short encoding a pair of ASCII-encoded digit characters in little-endian
81-
* @param i value to convert
82-
* @return a short encoding a pair of ASCII-encoded digit characters
83-
*/
84-
public static short digitPair(int i) {
85-
return DIGITS[i];
86-
}
87-
8883
/**
8984
* Returns the string representation size for a given int value.
9085
*
@@ -136,4 +131,305 @@ public static int stringSize(long x) {
136131
}
137132
return 19 + d;
138133
}
134+
135+
/**
136+
* Places characters representing the integer i into the
137+
* character array buf. The characters are placed into
138+
* the buffer backwards starting with the least significant
139+
* digit at the specified index (exclusive), and working
140+
* backwards from there.
141+
*
142+
* @implNote This method converts positive inputs into negative
143+
* values, to cover the Integer.MIN_VALUE case. Converting otherwise
144+
* (negative to positive) will expose -Integer.MIN_VALUE that overflows
145+
* integer.
146+
*
147+
* @param i value to convert
148+
* @param index next index, after the least significant digit
149+
* @param buf target buffer, Latin1-encoded
150+
* @return index of the most significant digit or minus sign, if present
151+
*/
152+
public static int getCharsLatin1(int i, int index, byte[] buf) {
153+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
154+
int q;
155+
int charPos = index;
156+
157+
boolean negative = i < 0;
158+
if (!negative) {
159+
i = -i;
160+
}
161+
162+
// Generate two digits per iteration
163+
while (i <= -100) {
164+
q = i / 100;
165+
charPos -= 2;
166+
putPairLatin1(buf, charPos, (q * 100) - i);
167+
i = q;
168+
}
169+
170+
// We know there are at most two digits left at this point.
171+
if (i <= -10) {
172+
charPos -= 2;
173+
putPairLatin1(buf, charPos, -i);
174+
} else {
175+
putCharLatin1(buf, --charPos, '0' - i);
176+
}
177+
178+
if (negative) {
179+
putCharLatin1(buf, --charPos, '-');
180+
}
181+
return charPos;
182+
}
183+
184+
185+
/**
186+
* Places characters representing the long i into the
187+
* character array buf. The characters are placed into
188+
* the buffer backwards starting with the least significant
189+
* digit at the specified index (exclusive), and working
190+
* backwards from there.
191+
*
192+
* @implNote This method converts positive inputs into negative
193+
* values, to cover the Long.MIN_VALUE case. Converting otherwise
194+
* (negative to positive) will expose -Long.MIN_VALUE that overflows
195+
* long.
196+
*
197+
* @param i value to convert
198+
* @param index next index, after the least significant digit
199+
* @param buf target buffer, Latin1-encoded
200+
* @return index of the most significant digit or minus sign, if present
201+
*/
202+
public static int getCharsLatin1(long i, int index, byte[] buf) {
203+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
204+
long q;
205+
int charPos = index;
206+
207+
boolean negative = (i < 0);
208+
if (!negative) {
209+
i = -i;
210+
}
211+
212+
// Get 2 digits/iteration using longs until quotient fits into an int
213+
while (i < Integer.MIN_VALUE) {
214+
q = i / 100;
215+
charPos -= 2;
216+
putPairLatin1(buf, charPos, (int)((q * 100) - i));
217+
i = q;
218+
}
219+
220+
// Get 2 digits/iteration using ints
221+
int q2;
222+
int i2 = (int)i;
223+
while (i2 <= -100) {
224+
q2 = i2 / 100;
225+
charPos -= 2;
226+
putPairLatin1(buf, charPos, (q2 * 100) - i2);
227+
i2 = q2;
228+
}
229+
230+
// We know there are at most two digits left at this point.
231+
if (i2 <= -10) {
232+
charPos -= 2;
233+
putPairLatin1(buf, charPos, -i2);
234+
} else {
235+
putCharLatin1(buf, --charPos, '0' - i2);
236+
}
237+
238+
if (negative) {
239+
putCharLatin1(buf, --charPos, '-');
240+
}
241+
return charPos;
242+
}
243+
244+
245+
/**
246+
* This is a variant of {@link DecimalDigits#getCharsLatin1(int, int, byte[])}, but for
247+
* UTF-16 coder.
248+
*
249+
* @param i value to convert
250+
* @param index next index, after the least significant digit
251+
* @param buf target buffer, UTF16-coded.
252+
* @return index of the most significant digit or minus sign, if present
253+
*/
254+
public static int getCharsUTF16(int i, int index, byte[] buf) {
255+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
256+
int q;
257+
int charPos = index;
258+
259+
boolean negative = (i < 0);
260+
if (!negative) {
261+
i = -i;
262+
}
263+
264+
// Get 2 digits/iteration using ints
265+
while (i <= -100) {
266+
q = i / 100;
267+
charPos -= 2;
268+
putPairUTF16(buf, charPos, (q * 100) - i);
269+
i = q;
270+
}
271+
272+
// We know there are at most two digits left at this point.
273+
if (i <= -10) {
274+
charPos -= 2;
275+
putPairUTF16(buf, charPos, -i);
276+
} else {
277+
putCharUTF16(buf, --charPos, '0' - i);
278+
}
279+
280+
if (negative) {
281+
putCharUTF16(buf, --charPos, '-');
282+
}
283+
return charPos;
284+
}
285+
286+
287+
/**
288+
* This is a variant of {@link DecimalDigits#getCharsLatin1(long, int, byte[])}, but for
289+
* UTF-16 coder.
290+
*
291+
* @param i value to convert
292+
* @param index next index, after the least significant digit
293+
* @param buf target buffer, UTF16-coded.
294+
* @return index of the most significant digit or minus sign, if present
295+
*/
296+
public static int getCharsUTF16(long i, int index, byte[] buf) {
297+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
298+
long q;
299+
int charPos = index;
300+
301+
boolean negative = (i < 0);
302+
if (!negative) {
303+
i = -i;
304+
}
305+
306+
// Get 2 digits/iteration using longs until quotient fits into an int
307+
while (i < Integer.MIN_VALUE) {
308+
q = i / 100;
309+
charPos -= 2;
310+
putPairUTF16(buf, charPos, (int)((q * 100) - i));
311+
i = q;
312+
}
313+
314+
// Get 2 digits/iteration using ints
315+
int q2;
316+
int i2 = (int)i;
317+
while (i2 <= -100) {
318+
q2 = i2 / 100;
319+
charPos -= 2;
320+
putPairUTF16(buf, charPos, (q2 * 100) - i2);
321+
i2 = q2;
322+
}
323+
324+
// We know there are at most two digits left at this point.
325+
if (i2 <= -10) {
326+
charPos -= 2;
327+
putPairUTF16(buf, charPos, -i2);
328+
} else {
329+
putCharUTF16(buf, --charPos, '0' - i2);
330+
}
331+
332+
if (negative) {
333+
putCharUTF16(buf, --charPos, '-');
334+
}
335+
return charPos;
336+
}
337+
338+
/**
339+
* This is a variant of {@link DecimalDigits#getCharsUTF16(long, int, byte[])}, but for
340+
* UTF-16 coder.
341+
*
342+
* @param i value to convert
343+
* @param index next index, after the least significant digit
344+
* @param buf target buffer, UTF16-coded.
345+
* @return index of the most significant digit or minus sign, if present
346+
*/
347+
public static int getChars(long i, int index, char[] buf) {
348+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
349+
long q;
350+
int charPos = index;
351+
352+
boolean negative = (i < 0);
353+
if (!negative) {
354+
i = -i;
355+
}
356+
357+
// Get 2 digits/iteration using longs until quotient fits into an int
358+
while (i < Integer.MIN_VALUE) {
359+
q = i / 100;
360+
charPos -= 2;
361+
putPair(buf, charPos, (int)((q * 100) - i));
362+
i = q;
363+
}
364+
365+
// Get 2 digits/iteration using ints
366+
int q2;
367+
int i2 = (int)i;
368+
while (i2 <= -100) {
369+
q2 = i2 / 100;
370+
charPos -= 2;
371+
putPair(buf, charPos, (q2 * 100) - i2);
372+
i2 = q2;
373+
}
374+
375+
// We know there are at most two digits left at this point.
376+
if (i2 <= -10) {
377+
charPos -= 2;
378+
putPair(buf, charPos, -i2);
379+
} else {
380+
buf[--charPos] = (char) ('0' - i2);
381+
}
382+
383+
if (negative) {
384+
buf[--charPos] = '-';
385+
}
386+
return charPos;
387+
}
388+
389+
/**
390+
* Insert the 2-chars integer into the buf as 2 decimal digit ASCII chars,
391+
* only least significant 16 bits of {@code v} are used.
392+
* @param buf byte buffer to copy into
393+
* @param charPos insert point
394+
* @param v to convert
395+
*/
396+
public static void putPair(char[] buf, int charPos, int v) {
397+
int packed = DIGITS[v];
398+
buf[charPos ] = (char) (packed & 0xFF);
399+
buf[charPos + 1] = (char) (packed >> 8);
400+
}
401+
402+
/**
403+
* Insert the 2-bytes integer into the buf as 2 decimal digit ASCII bytes,
404+
* only least significant 16 bits of {@code v} are used.
405+
* @param buf byte buffer to copy into
406+
* @param charPos insert point
407+
* @param v to convert
408+
*/
409+
public static void putPairLatin1(byte[] buf, int charPos, int v) {
410+
int packed = DIGITS[v];
411+
putCharLatin1(buf, charPos, packed & 0xFF);
412+
putCharLatin1(buf, charPos + 1, packed >> 8);
413+
}
414+
415+
/**
416+
* Insert the 2-chars integer into the buf as 2 decimal digit UTF16 bytes,
417+
* only least significant 16 bits of {@code v} are used.
418+
* @param buf byte buffer to copy into
419+
* @param charPos insert point
420+
* @param v to convert
421+
*/
422+
public static void putPairUTF16(byte[] buf, int charPos, int v) {
423+
int packed = DIGITS[v];
424+
putCharUTF16(buf, charPos, packed & 0xFF);
425+
putCharUTF16(buf, charPos + 1, packed >> 8);
426+
}
427+
428+
private static void putCharLatin1(byte[] buf, int charPos, int c) {
429+
UNSAFE.putByte(buf, ARRAY_BYTE_BASE_OFFSET + (long) charPos, (byte) c);
430+
}
431+
432+
private static void putCharUTF16(byte[] buf, int charPos, int c) {
433+
UNSAFE.putCharUnaligned(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c);
434+
}
139435
}

‎test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
package java.lang;
2525

26+
import jdk.internal.util.DecimalDigits;
27+
2628
/**
2729
* A helper class to get access to package-private members
2830
*/
@@ -117,11 +119,17 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
117119
}
118120

119121
public static int getChars(int i, int begin, int end, byte[] value) {
120-
return StringUTF16.getChars(i, begin, end, value);
122+
StringUTF16.checkBoundsBeginEnd(begin, end, value);
123+
int pos = DecimalDigits.getCharsUTF16(i, end, value);
124+
assert begin == pos;
125+
return pos;
121126
}
122127

123128
public static int getChars(long l, int begin, int end, byte[] value) {
124-
return StringUTF16.getChars(l, begin, end, value);
129+
StringUTF16.checkBoundsBeginEnd(begin, end, value);
130+
int pos = DecimalDigits.getCharsUTF16(l, end, value);
131+
assert begin == pos;
132+
return pos;
125133
}
126134

127135
public static boolean contentEquals(byte[] v1, byte[] v2, int len) {

‎test/micro/org/openjdk/bench/java/lang/StringBuilders.java

+48
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class StringBuilders {
5454
private StringBuilder sbLatin2;
5555
private StringBuilder sbUtf16;
5656
private StringBuilder sbUtf17;
57+
private int[] intArray;
58+
private long[] longArray;
5759

5860
@Setup
5961
public void setup() {
@@ -69,6 +71,13 @@ public void setup() {
6971
sbLatin2 = new StringBuilder("Latin1 string");
7072
sbUtf16 = new StringBuilder("UTF-\uFF11\uFF16 string");
7173
sbUtf17 = new StringBuilder("UTF-\uFF11\uFF16 string");
74+
int size = 16;
75+
intArray = new int[size];
76+
longArray = new long[size];
77+
for (int i = 0; i < longArray.length; i++) {
78+
intArray[i] = ((100 * i + i) << 24) + 4543 + i * 4;
79+
longArray[i] = ((100L * i + i) << 32) + 4543 + i * 4L;
80+
}
7281
}
7382

7483
@Benchmark
@@ -224,6 +233,45 @@ public String toStringCharWithInt8() {
224233
return result.toString();
225234
}
226235

236+
@Benchmark
237+
public int appendWithIntLatin1() {
238+
StringBuilder buf = sbLatin1;
239+
buf.setLength(0);
240+
for (int i : intArray) {
241+
buf.append(i);
242+
}
243+
return buf.length();
244+
}
245+
246+
@Benchmark
247+
public int appendWithIntUtf16() {
248+
StringBuilder buf = sbUtf16;
249+
buf.setLength(0);
250+
for (int i : intArray) {
251+
buf.append(i);
252+
}
253+
return buf.length();
254+
}
255+
256+
@Benchmark
257+
public int appendWithLongLatin1() {
258+
StringBuilder buf = sbLatin1;
259+
buf.setLength(0);
260+
for (long l : longArray) {
261+
buf.append(l);
262+
}
263+
return buf.length();
264+
}
265+
266+
@Benchmark
267+
public int appendWithLongUtf16() {
268+
StringBuilder buf = sbUtf16;
269+
buf.setLength(0);
270+
for (long l : longArray) {
271+
buf.append(l);
272+
}
273+
return buf.length();
274+
}
227275

228276
@Benchmark
229277
public int appendWithBool8Latin1() {

0 commit comments

Comments
 (0)
Please sign in to comment.