Skip to content

Commit b7c133a

Browse files
committed
back port from openjdk, optimization for integer/long toString
1 parent ea25b10 commit b7c133a

File tree

6 files changed

+497
-75
lines changed

6 files changed

+497
-75
lines changed

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

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Objects;
3030
import jdk.internal.HotSpotIntrinsicCandidate;
3131
import jdk.internal.misc.VM;
32+
import jdk.internal.util.DecimalDigits;
3233

3334
import static java.lang.String.COMPACT_STRINGS;
3435
import static java.lang.String.LATIN1;
@@ -396,33 +397,6 @@ private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int o
396397
} while (charPos > offset);
397398
}
398399

399-
static final byte[] DigitTens = {
400-
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
401-
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
402-
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
403-
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
404-
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
405-
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
406-
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
407-
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
408-
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
409-
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
410-
} ;
411-
412-
static final byte[] DigitOnes = {
413-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
414-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
415-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
416-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
417-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
418-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
419-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
420-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
421-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
422-
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
423-
} ;
424-
425-
426400
/**
427401
* Returns a {@code String} object representing the
428402
* specified integer. The argument is converted to signed decimal
@@ -483,7 +457,8 @@ public static String toUnsignedString(int i) {
483457
* @return index of the most significant digit or minus sign, if present
484458
*/
485459
static int getChars(int i, int index, byte[] buf) {
486-
int q, r;
460+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
461+
int q;
487462
int charPos = index;
488463

489464
boolean negative = i < 0;
@@ -494,20 +469,17 @@ static int getChars(int i, int index, byte[] buf) {
494469
// Generate two digits per iteration
495470
while (i <= -100) {
496471
q = i / 100;
497-
r = (q * 100) - i;
472+
charPos -= 2;
473+
DecimalDigits.putPair(buf, charPos, (q * 100) - i);
498474
i = q;
499-
buf[--charPos] = DigitOnes[r];
500-
buf[--charPos] = DigitTens[r];
501475
}
502476

503477
// We know there are at most two digits left at this point.
504-
q = i / 10;
505-
r = (q * 10) - i;
506-
buf[--charPos] = (byte)('0' + r);
507-
508-
// Whatever left is the remaining digit.
509-
if (q < 0) {
510-
buf[--charPos] = (byte)('0' - q);
478+
if (i < -9) {
479+
charPos -= 2;
480+
DecimalDigits.putPair(buf, charPos, -i);
481+
} else {
482+
buf[--charPos] = (byte)('0' - i);
511483
}
512484

513485
if (negative) {

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

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.math.*;
3030
import java.util.Objects;
3131
import jdk.internal.HotSpotIntrinsicCandidate;
32+
import jdk.internal.util.DecimalDigits;
3233

3334
import static java.lang.String.COMPACT_STRINGS;
3435
import static java.lang.String.LATIN1;
@@ -516,8 +517,8 @@ public static String toUnsignedString(long i) {
516517
* @return index of the most significant digit or minus sign, if present
517518
*/
518519
static int getChars(long i, int index, byte[] buf) {
520+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
519521
long q;
520-
int r;
521522
int charPos = index;
522523

523524
boolean negative = (i < 0);
@@ -528,31 +529,27 @@ static int getChars(long i, int index, byte[] buf) {
528529
// Get 2 digits/iteration using longs until quotient fits into an int
529530
while (i <= Integer.MIN_VALUE) {
530531
q = i / 100;
531-
r = (int)((q * 100) - i);
532+
charPos -= 2;
533+
DecimalDigits.putPair(buf, charPos, (int)((q * 100) - i));
532534
i = q;
533-
buf[--charPos] = Integer.DigitOnes[r];
534-
buf[--charPos] = Integer.DigitTens[r];
535535
}
536536

537537
// Get 2 digits/iteration using ints
538538
int q2;
539539
int i2 = (int)i;
540540
while (i2 <= -100) {
541541
q2 = i2 / 100;
542-
r = (q2 * 100) - i2;
542+
charPos -= 2;
543+
DecimalDigits.putPair(buf, charPos, (q2 * 100) - i2);
543544
i2 = q2;
544-
buf[--charPos] = Integer.DigitOnes[r];
545-
buf[--charPos] = Integer.DigitTens[r];
546545
}
547546

548547
// We know there are at most two digits left at this point.
549-
q2 = i2 / 10;
550-
r = (q2 * 10) - i2;
551-
buf[--charPos] = (byte)('0' + r);
552-
553-
// Whatever left is the remaining digit.
554-
if (q2 < 0) {
555-
buf[--charPos] = (byte)('0' - q2);
548+
if (i2 < -9) {
549+
charPos -= 2;
550+
DecimalDigits.putPair(buf, charPos, -i2);
551+
} else {
552+
buf[--charPos] = (byte)('0' - i2);
556553
}
557554

558555
if (negative) {

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

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.stream.Stream;
3434
import java.util.stream.StreamSupport;
3535
import jdk.internal.HotSpotIntrinsicCandidate;
36+
import jdk.internal.util.DecimalDigits;
3637
import jdk.internal.vm.annotation.ForceInline;
3738
import jdk.internal.vm.annotation.DontInline;
3839

@@ -1377,6 +1378,7 @@ public static int lastIndexOfLatin1(byte[] src, int srcCount,
13771378
* @return index of the most significant digit or minus sign, if present
13781379
*/
13791380
static int getChars(int i, int index, byte[] buf) {
1381+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
13801382
int q, r;
13811383
int charPos = index;
13821384

@@ -1390,18 +1392,16 @@ static int getChars(int i, int index, byte[] buf) {
13901392
q = i / 100;
13911393
r = (q * 100) - i;
13921394
i = q;
1393-
putChar(buf, --charPos, Integer.DigitOnes[r]);
1394-
putChar(buf, --charPos, Integer.DigitTens[r]);
1395+
charPos -= 2;
1396+
putPair(buf, charPos, r);
13951397
}
13961398

13971399
// We know there are at most two digits left at this point.
1398-
q = i / 10;
1399-
r = (q * 10) - i;
1400-
putChar(buf, --charPos, '0' + r);
1401-
1402-
// Whatever left is the remaining digit.
1403-
if (q < 0) {
1404-
putChar(buf, --charPos, '0' - q);
1400+
if (i < -9) {
1401+
charPos -= 2;
1402+
putPair(buf, charPos, -i);
1403+
} else {
1404+
putChar(buf, --charPos, '0' - i);
14051405
}
14061406

14071407
if (negative) {
@@ -1420,8 +1420,8 @@ static int getChars(int i, int index, byte[] buf) {
14201420
* @return index of the most significant digit or minus sign, if present
14211421
*/
14221422
static int getChars(long i, int index, byte[] buf) {
1423+
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
14231424
long q;
1424-
int r;
14251425
int charPos = index;
14261426

14271427
boolean negative = (i < 0);
@@ -1432,38 +1432,40 @@ static int getChars(long i, int index, byte[] buf) {
14321432
// Get 2 digits/iteration using longs until quotient fits into an int
14331433
while (i <= Integer.MIN_VALUE) {
14341434
q = i / 100;
1435-
r = (int)((q * 100) - i);
1435+
charPos -= 2;
1436+
putPair(buf, charPos, (int)((q * 100) - i));
14361437
i = q;
1437-
putChar(buf, --charPos, Integer.DigitOnes[r]);
1438-
putChar(buf, --charPos, Integer.DigitTens[r]);
14391438
}
14401439

14411440
// Get 2 digits/iteration using ints
14421441
int q2;
14431442
int i2 = (int)i;
14441443
while (i2 <= -100) {
14451444
q2 = i2 / 100;
1446-
r = (q2 * 100) - i2;
1445+
charPos -= 2;
1446+
putPair(buf, charPos, (q2 * 100) - i2);
14471447
i2 = q2;
1448-
putChar(buf, --charPos, Integer.DigitOnes[r]);
1449-
putChar(buf, --charPos, Integer.DigitTens[r]);
14501448
}
14511449

14521450
// We know there are at most two digits left at this point.
1453-
q2 = i2 / 10;
1454-
r = (q2 * 10) - i2;
1455-
putChar(buf, --charPos, '0' + r);
1456-
1457-
// Whatever left is the remaining digit.
1458-
if (q2 < 0) {
1459-
putChar(buf, --charPos, '0' - q2);
1451+
if (i2 < -9) {
1452+
charPos -= 2;
1453+
putPair(buf, charPos, -i2);
1454+
} else {
1455+
putChar(buf, --charPos, '0' - i2);
14601456
}
14611457

14621458
if (negative) {
14631459
putChar(buf, --charPos, '-');
14641460
}
14651461
return charPos;
14661462
}
1463+
1464+
static void putPair(byte[] buf, int charPos, int v) {
1465+
int packed = (int) DecimalDigits.digitPair(v);
1466+
putChar(buf, charPos, packed & 0xFF);
1467+
putChar(buf, charPos + 1, packed >> 8);
1468+
}
14671469
// End of trusted methods.
14681470

14691471
public static void checkIndex(int off, byte[] val) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package jdk.internal.util;
27+
28+
import jdk.internal.misc.Unsafe;
29+
import jdk.internal.vm.annotation.Stable;
30+
31+
/**
32+
* Digits class for decimal digits.
33+
*
34+
*/
35+
public final class DecimalDigits {
36+
private static Unsafe UNSAFE = Unsafe.getUnsafe();
37+
38+
/**
39+
* Each element of the array represents the packaging of two ascii characters based on little endian:<p>
40+
* <pre>
41+
* 00 -> '0' | ('0' << 8) -> 0x3030
42+
* 01 -> '1' | ('0' << 8) -> 0x3130
43+
* 02 -> '2' | ('0' << 8) -> 0x3230
44+
*
45+
* ...
46+
*
47+
* 10 -> '0' | ('1' << 8) -> 0x3031
48+
* 11 -> '1' | ('1' << 8) -> 0x3131
49+
* 12 -> '2' | ('1' << 8) -> 0x3231
50+
*
51+
* ...
52+
*
53+
* 97 -> '7' | ('9' << 8) -> 0x3739
54+
* 98 -> '8' | ('9' << 8) -> 0x3839
55+
* 99 -> '9' | ('9' << 8) -> 0x3939
56+
* </pre>
57+
*/
58+
@Stable
59+
private static final short[] DIGITS;
60+
61+
static {
62+
short[] digits = new short[10 * 10];
63+
64+
for (int i = 0; i < 10; i++) {
65+
short hi = (short) (i + '0');
66+
for (int j = 0; j < 10; j++) {
67+
short lo = (short) ((j + '0') << 8);
68+
digits[i * 10 + j] = (short) (hi | lo);
69+
}
70+
}
71+
DIGITS = digits;
72+
}
73+
74+
/**
75+
* For values from 0 to 99 return a short encoding a pair of ASCII-encoded digit characters in little-endian
76+
* @param i value to convert
77+
* @return a short encoding a pair of ASCII-encoded digit characters
78+
*/
79+
public static short digitPair(int i) {
80+
return DIGITS[i];
81+
}
82+
83+
public static void putPair(byte[] buf, int charPos, int value) {
84+
UNSAFE.putShortUnaligned(
85+
buf,
86+
Unsafe.ARRAY_BYTE_BASE_OFFSET + charPos,
87+
DIGITS[value],
88+
false);
89+
}
90+
}

0 commit comments

Comments
 (0)