Skip to content

Commit

Permalink
back port from openjdk, optimization for integer/long toString
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Oct 5, 2023
1 parent ea25b10 commit 1d42026
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 75 deletions.
48 changes: 10 additions & 38 deletions src/java.base/share/classes/java/lang/Integer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.VM;
import jdk.internal.util.DecimalDigits;

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

static final byte[] DigitTens = {
'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 byte[] DigitOnes = {
'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',
} ;


/**
* Returns a {@code String} object representing the
* specified integer. The argument is converted to signed decimal
Expand Down Expand Up @@ -483,7 +457,8 @@ public static String toUnsignedString(int i) {
* @return index of the most significant digit or minus sign, if present
*/
static int getChars(int i, int index, byte[] buf) {
int q, r;
// Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
int q;
int charPos = index;

boolean negative = i < 0;
Expand All @@ -494,20 +469,17 @@ static int getChars(int i, int index, byte[] buf) {
// Generate two digits per iteration
while (i <= -100) {
q = i / 100;
r = (q * 100) - i;
charPos -= 2;
DecimalDigits.putPair(buf, charPos, (q * 100) - i);
i = q;
buf[--charPos] = DigitOnes[r];
buf[--charPos] = DigitTens[r];
}

// We know there are at most two digits left at this point.
q = i / 10;
r = (q * 10) - i;
buf[--charPos] = (byte)('0' + r);

// Whatever left is the remaining digit.
if (q < 0) {
buf[--charPos] = (byte)('0' - q);
if (i < -9) {
charPos -= 2;
DecimalDigits.putPair(buf, charPos, -i);
} else {
buf[--charPos] = (byte)('0' - i);
}

if (negative) {
Expand Down
25 changes: 11 additions & 14 deletions src/java.base/share/classes/java/lang/Long.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.math.*;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.util.DecimalDigits;

import static java.lang.String.COMPACT_STRINGS;
import static java.lang.String.LATIN1;
Expand Down Expand Up @@ -516,8 +517,8 @@ public static String toUnsignedString(long i) {
* @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 r;
int charPos = index;

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

// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
r = (q2 * 100) - i2;
charPos -= 2;
DecimalDigits.putPair(buf, charPos, (q2 * 100) - i2);
i2 = q2;
buf[--charPos] = Integer.DigitOnes[r];
buf[--charPos] = Integer.DigitTens[r];
}

// We know there are at most two digits left at this point.
q2 = i2 / 10;
r = (q2 * 10) - i2;
buf[--charPos] = (byte)('0' + r);

// Whatever left is the remaining digit.
if (q2 < 0) {
buf[--charPos] = (byte)('0' - q2);
if (i2 < -9) {
charPos -= 2;
DecimalDigits.putPair(buf, charPos, -i2);
} else {
buf[--charPos] = (byte)('0' - i2);
}

if (negative) {
Expand Down
48 changes: 25 additions & 23 deletions src/java.base/share/classes/java/lang/StringUTF16.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.util.DecimalDigits;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.DontInline;

Expand Down Expand Up @@ -1377,6 +1378,7 @@ public static int lastIndexOfLatin1(byte[] src, int srcCount,
* @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;

Expand All @@ -1390,18 +1392,16 @@ static int getChars(int i, int index, byte[] buf) {
q = i / 100;
r = (q * 100) - i;
i = q;
putChar(buf, --charPos, Integer.DigitOnes[r]);
putChar(buf, --charPos, Integer.DigitTens[r]);
charPos -= 2;
putPair(buf, charPos, r);
}

// We know there are at most two digits left at this point.
q = i / 10;
r = (q * 10) - i;
putChar(buf, --charPos, '0' + r);

// Whatever left is the remaining digit.
if (q < 0) {
putChar(buf, --charPos, '0' - q);
if (i < -9) {
charPos -= 2;
putPair(buf, charPos, -i);
} else {
putChar(buf, --charPos, '0' - i);
}

if (negative) {
Expand All @@ -1420,8 +1420,8 @@ static int getChars(int i, int index, byte[] buf) {
* @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 r;
int charPos = index;

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

// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
r = (q2 * 100) - i2;
charPos -= 2;
putPair(buf, charPos, (q2 * 100) - i2);
i2 = q2;
putChar(buf, --charPos, Integer.DigitOnes[r]);
putChar(buf, --charPos, Integer.DigitTens[r]);
}

// We know there are at most two digits left at this point.
q2 = i2 / 10;
r = (q2 * 10) - i2;
putChar(buf, --charPos, '0' + r);

// Whatever left is the remaining digit.
if (q2 < 0) {
putChar(buf, --charPos, '0' - q2);
if (i2 < -9) {
charPos -= 2;
putPair(buf, charPos, -i2);
} else {
putChar(buf, --charPos, '0' - i2);
}

if (negative) {
putChar(buf, --charPos, '-');
}
return charPos;
}

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) {
Expand Down
90 changes: 90 additions & 0 deletions src/java.base/share/classes/jdk/internal/util/DecimalDigits.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package jdk.internal.util;

import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.Stable;

/**
* Digits class for decimal digits.
*
*/
public final class DecimalDigits {
private static Unsafe UNSAFE = Unsafe.getUnsafe();

/**
* Each element of the array represents the packaging of two ascii characters based on little endian:<p>
* <pre>
* 00 -> '0' | ('0' << 8) -> 0x3030
* 01 -> '1' | ('0' << 8) -> 0x3130
* 02 -> '2' | ('0' << 8) -> 0x3230
*
* ...
*
* 10 -> '0' | ('1' << 8) -> 0x3031
* 11 -> '1' | ('1' << 8) -> 0x3131
* 12 -> '2' | ('1' << 8) -> 0x3231
*
* ...
*
* 97 -> '7' | ('9' << 8) -> 0x3739
* 98 -> '8' | ('9' << 8) -> 0x3839
* 99 -> '9' | ('9' << 8) -> 0x3939
* </pre>
*/
@Stable
private static final short[] DIGITS;

static {
short[] digits = new short[10 * 10];

for (int i = 0; i < 10; i++) {
short hi = (short) (i + '0');
for (int j = 0; j < 10; j++) {
short lo = (short) ((j + '0') << 8);
digits[i * 10 + j] = (short) (hi | lo);
}
}
DIGITS = digits;
}

/**
* 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];
}

public static void putPair(byte[] buf, int charPos, int value) {
UNSAFE.putShortUnaligned(
buf,
Unsafe.ARRAY_BYTE_BASE_OFFSET + charPos,
DIGITS[value],
false);
}
}

0 comments on commit 1d42026

Please sign in to comment.