Skip to content

Commit 4b43c25

Browse files
wenshaoy1yang0
authored andcommitted
8310929: Optimization for Integer.toString
Reviewed-by: redestad, rriggs
1 parent 111ecdb commit 4b43c25

File tree

8 files changed

+215
-172
lines changed

8 files changed

+215
-172
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -832,7 +832,7 @@ public AbstractStringBuilder append(int i) {
832832
int spaceNeeded = count + Integer.stringSize(i);
833833
ensureCapacityInternal(spaceNeeded);
834834
if (isLatin1()) {
835-
Integer.getChars(i, spaceNeeded, value);
835+
StringLatin1.getChars(i, spaceNeeded, value);
836836
} else {
837837
StringUTF16.getChars(i, count, spaceNeeded, value);
838838
}
@@ -857,7 +857,7 @@ public AbstractStringBuilder append(long l) {
857857
int spaceNeeded = count + Long.stringSize(l);
858858
ensureCapacityInternal(spaceNeeded);
859859
if (isLatin1()) {
860-
Long.getChars(l, spaceNeeded, value);
860+
StringLatin1.getChars(l, spaceNeeded, value);
861861
} else {
862862
StringUTF16.getChars(l, count, spaceNeeded, value);
863863
}

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

+1-75
Original file line numberDiff line numberDiff line change
@@ -414,33 +414,6 @@ private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int l
414414
} while (charPos > 0);
415415
}
416416

417-
static final byte[] DigitTens = {
418-
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
419-
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
420-
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
421-
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
422-
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
423-
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
424-
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
425-
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
426-
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
427-
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
428-
} ;
429-
430-
static final byte[] DigitOnes = {
431-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
432-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
433-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
434-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
435-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
436-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
437-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
438-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
439-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
440-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
441-
} ;
442-
443-
444417
/**
445418
* Returns a {@code String} object representing the
446419
* specified integer. The argument is converted to signed decimal
@@ -456,7 +429,7 @@ public static String toString(int i) {
456429
int size = stringSize(i);
457430
if (COMPACT_STRINGS) {
458431
byte[] buf = new byte[size];
459-
getChars(i, size, buf);
432+
StringLatin1.getChars(i, size, buf);
460433
return new String(buf, LATIN1);
461434
} else {
462435
byte[] buf = new byte[size * 2];
@@ -483,53 +456,6 @@ public static String toUnsignedString(int i) {
483456
return Long.toString(toUnsignedLong(i));
484457
}
485458

486-
/**
487-
* Places characters representing the integer i into the
488-
* character array buf. The characters are placed into
489-
* the buffer backwards starting with the least significant
490-
* digit at the specified index (exclusive), and working
491-
* backwards from there.
492-
*
493-
* @implNote This method converts positive inputs into negative
494-
* values, to cover the Integer.MIN_VALUE case. Converting otherwise
495-
* (negative to positive) will expose -Integer.MIN_VALUE that overflows
496-
* integer.
497-
*
498-
* @param i value to convert
499-
* @param index next index, after the least significant digit
500-
* @param buf target buffer, Latin1-encoded
501-
* @return index of the most significant digit or minus sign, if present
502-
*/
503-
static int getChars(int i, int index, byte[] buf) {
504-
int q, r;
505-
int charPos = index;
506-
507-
boolean negative = i < 0;
508-
if (!negative) {
509-
i = -i;
510-
}
511-
512-
// Generate two digits per iteration
513-
while (i <= -100) {
514-
q = i / 100;
515-
r = (q * 100) - i;
516-
i = q;
517-
buf[--charPos] = DigitOnes[r];
518-
buf[--charPos] = DigitTens[r];
519-
}
520-
521-
// We know there are at most two digits left at this point.
522-
buf[--charPos] = DigitOnes[-i];
523-
if (i < -9) {
524-
buf[--charPos] = DigitTens[-i];
525-
}
526-
527-
if (negative) {
528-
buf[--charPos] = (byte)'-';
529-
}
530-
return charPos;
531-
}
532-
533459
/**
534460
* Returns the string representation size for a given int value.
535461
*

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

+1-60
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ public static String toString(long i) {
459459
int size = stringSize(i);
460460
if (COMPACT_STRINGS) {
461461
byte[] buf = new byte[size];
462-
getChars(i, size, buf);
462+
StringLatin1.getChars(i, size, buf);
463463
return new String(buf, LATIN1);
464464
} else {
465465
byte[] buf = new byte[size * 2];
@@ -486,65 +486,6 @@ public static String toUnsignedString(long i) {
486486
return toUnsignedString(i, 10);
487487
}
488488

489-
/**
490-
* Places characters representing the long i into the
491-
* character array buf. The characters are placed into
492-
* the buffer backwards starting with the least significant
493-
* digit at the specified index (exclusive), and working
494-
* backwards from there.
495-
*
496-
* @implNote This method converts positive inputs into negative
497-
* values, to cover the Long.MIN_VALUE case. Converting otherwise
498-
* (negative to positive) will expose -Long.MIN_VALUE that overflows
499-
* long.
500-
*
501-
* @param i value to convert
502-
* @param index next index, after the least significant digit
503-
* @param buf target buffer, Latin1-encoded
504-
* @return index of the most significant digit or minus sign, if present
505-
*/
506-
static int getChars(long i, int index, byte[] buf) {
507-
long q;
508-
int r;
509-
int charPos = index;
510-
511-
boolean negative = (i < 0);
512-
if (!negative) {
513-
i = -i;
514-
}
515-
516-
// Get 2 digits/iteration using longs until quotient fits into an int
517-
while (i <= Integer.MIN_VALUE) {
518-
q = i / 100;
519-
r = (int)((q * 100) - i);
520-
i = q;
521-
buf[--charPos] = Integer.DigitOnes[r];
522-
buf[--charPos] = Integer.DigitTens[r];
523-
}
524-
525-
// Get 2 digits/iteration using ints
526-
int q2;
527-
int i2 = (int)i;
528-
while (i2 <= -100) {
529-
q2 = i2 / 100;
530-
r = (q2 * 100) - i2;
531-
i2 = q2;
532-
buf[--charPos] = Integer.DigitOnes[r];
533-
buf[--charPos] = Integer.DigitTens[r];
534-
}
535-
536-
// We know there are at most two digits left at this point.
537-
buf[--charPos] = Integer.DigitOnes[-i2];
538-
if (i2 < -9) {
539-
buf[--charPos] = Integer.DigitTens[-i2];
540-
}
541-
542-
if (negative) {
543-
buf[--charPos] = (byte)'-';
544-
}
545-
return charPos;
546-
}
547-
548489
/**
549490
* Returns the string representation size for a given long value.
550491
*

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -250,7 +250,7 @@ static long prepend(long indexCoder, byte[] buf, char value, String prefix) {
250250
*/
251251
private static long prepend(long indexCoder, byte[] buf, int value) {
252252
if (indexCoder < UTF16) {
253-
return Integer.getChars(value, (int)indexCoder, buf);
253+
return StringLatin1.getChars(value, (int)indexCoder, buf);
254254
} else {
255255
return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
256256
}
@@ -285,7 +285,7 @@ static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
285285
*/
286286
private static long prepend(long indexCoder, byte[] buf, long value) {
287287
if (indexCoder < UTF16) {
288-
return Long.getChars(value, (int)indexCoder, buf);
288+
return StringLatin1.getChars(value, (int)indexCoder, buf);
289289
} else {
290290
return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
291291
}

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

+145
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
import java.util.stream.Stream;
3434
import java.util.stream.StreamSupport;
3535
import jdk.internal.util.ArraysSupport;
36+
import jdk.internal.util.ByteArrayLittleEndian;
3637
import jdk.internal.vm.annotation.IntrinsicCandidate;
38+
import jdk.internal.vm.annotation.Stable;
3739

3840
import static java.lang.String.LATIN1;
3941
import static java.lang.String.UTF16;
@@ -42,6 +44,40 @@
4244

4345
final class StringLatin1 {
4446

47+
/**
48+
* Each element of the array represents the packaging of two ascii characters based on little endian:<p>
49+
* <pre>
50+
* 00 -> '0' | ('0' << 8) -> 0x3030
51+
* 01 -> '1' | ('0' << 8) -> 0x3130
52+
* 02 -> '2' | ('0' << 8) -> 0x3230
53+
*
54+
* ...
55+
*
56+
* 10 -> '0' | ('1' << 8) -> 0x3031
57+
* 11 -> '1' | ('1' << 8) -> 0x3131
58+
* 12 -> '2' | ('1' << 8) -> 0x3231
59+
*
60+
* ...
61+
*
62+
* 97 -> '7' | ('9' << 8) -> 0x3739
63+
* 98 -> '8' | ('9' << 8) -> 0x3839
64+
* 99 -> '9' | ('9' << 8) -> 0x3939
65+
* </pre>
66+
*/
67+
@Stable
68+
static final short[] PACKED_DIGITS = new short[] {
69+
0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930,
70+
0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931,
71+
0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932,
72+
0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933,
73+
0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934,
74+
0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935,
75+
0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936,
76+
0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937,
77+
0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938,
78+
0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939
79+
};
80+
4581
public static char charAt(byte[] value, int index) {
4682
checkIndex(index, value.length);
4783
return (char)(value[index] & 0xff);
@@ -79,6 +115,115 @@ public static byte[] inflate(byte[] value, int off, int len) {
79115
return ret;
80116
}
81117

118+
/**
119+
* Places characters representing the integer i into the
120+
* character array buf. The characters are placed into
121+
* the buffer backwards starting with the least significant
122+
* digit at the specified index (exclusive), and working
123+
* backwards from there.
124+
*
125+
* @implNote This method converts positive inputs into negative
126+
* values, to cover the Integer.MIN_VALUE case. Converting otherwise
127+
* (negative to positive) will expose -Integer.MIN_VALUE that overflows
128+
* integer.
129+
*
130+
* @param i value to convert
131+
* @param index next index, after the least significant digit
132+
* @param buf target buffer, Latin1-encoded
133+
* @return index of the most significant digit or minus sign, if present
134+
*/
135+
static int getChars(int i, int index, byte[] buf) {
136+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
137+
int q, r;
138+
int charPos = index;
139+
140+
boolean negative = i < 0;
141+
if (!negative) {
142+
i = -i;
143+
}
144+
145+
// Generate two digits per iteration
146+
while (i <= -100) {
147+
q = i / 100;
148+
r = (q * 100) - i;
149+
i = q;
150+
charPos -= 2;
151+
ByteArrayLittleEndian.setShort(buf, charPos, PACKED_DIGITS[r]);
152+
}
153+
154+
// We know there are at most two digits left at this point.
155+
if (i < -9) {
156+
charPos -= 2;
157+
ByteArrayLittleEndian.setShort(buf, charPos, PACKED_DIGITS[-i]);
158+
} else {
159+
buf[--charPos] = (byte)('0' - i);
160+
}
161+
162+
if (negative) {
163+
buf[--charPos] = (byte)'-';
164+
}
165+
return charPos;
166+
}
167+
168+
/**
169+
* Places characters representing the long i into the
170+
* character array buf. The characters are placed into
171+
* the buffer backwards starting with the least significant
172+
* digit at the specified index (exclusive), and working
173+
* backwards from there.
174+
*
175+
* @implNote This method converts positive inputs into negative
176+
* values, to cover the Long.MIN_VALUE case. Converting otherwise
177+
* (negative to positive) will expose -Long.MIN_VALUE that overflows
178+
* long.
179+
*
180+
* @param i value to convert
181+
* @param index next index, after the least significant digit
182+
* @param buf target buffer, Latin1-encoded
183+
* @return index of the most significant digit or minus sign, if present
184+
*/
185+
static int getChars(long i, int index, byte[] buf) {
186+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
187+
long q;
188+
int charPos = index;
189+
190+
boolean negative = (i < 0);
191+
if (!negative) {
192+
i = -i;
193+
}
194+
195+
// Get 2 digits/iteration using longs until quotient fits into an int
196+
while (i <= Integer.MIN_VALUE) {
197+
q = i / 100;
198+
charPos -= 2;
199+
ByteArrayLittleEndian.setShort(buf, charPos, PACKED_DIGITS[(int)((q * 100) - i)]);
200+
i = q;
201+
}
202+
203+
// Get 2 digits/iteration using ints
204+
int q2;
205+
int i2 = (int)i;
206+
while (i2 <= -100) {
207+
q2 = i2 / 100;
208+
charPos -= 2;
209+
ByteArrayLittleEndian.setShort(buf, charPos, PACKED_DIGITS[(q2 * 100) - i2]);
210+
i2 = q2;
211+
}
212+
213+
// We know there are at most two digits left at this point.
214+
if (i2 < -9) {
215+
charPos -= 2;
216+
ByteArrayLittleEndian.setShort(buf, charPos, PACKED_DIGITS[-i2]);
217+
} else {
218+
buf[--charPos] = (byte)('0' - i2);
219+
}
220+
221+
if (negative) {
222+
buf[--charPos] = (byte)'-';
223+
}
224+
return charPos;
225+
}
226+
82227
public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
83228
inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
84229
}

0 commit comments

Comments
 (0)