8342650: Move getChars to DecimalDigits

Reviewed-by: liach
This commit is contained in:
Shaojin Wen 2024-11-11 05:06:56 +00:00
parent ca69a53b76
commit e1d684c645
12 changed files with 405 additions and 376 deletions

View File

@ -830,9 +830,9 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
int spaceNeeded = count + DecimalDigits.stringSize(i);
ensureCapacityInternal(spaceNeeded);
if (isLatin1()) {
StringLatin1.getChars(i, spaceNeeded, value);
DecimalDigits.getCharsLatin1(i, spaceNeeded, value);
} else {
StringUTF16.getChars(i, count, spaceNeeded, value);
DecimalDigits.getCharsUTF16(i, spaceNeeded, value);
}
this.count = spaceNeeded;
return this;
@ -855,9 +855,9 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
int spaceNeeded = count + DecimalDigits.stringSize(l);
ensureCapacityInternal(spaceNeeded);
if (isLatin1()) {
StringLatin1.getChars(l, spaceNeeded, value);
DecimalDigits.getCharsLatin1(l, spaceNeeded, value);
} else {
StringUTF16.getChars(l, count, spaceNeeded, value);
DecimalDigits.getCharsUTF16(l, spaceNeeded, value);
}
this.count = spaceNeeded;
return this;

View File

@ -432,11 +432,11 @@ public final class Integer extends Number
int size = DecimalDigits.stringSize(i);
if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
StringLatin1.getChars(i, size, buf);
DecimalDigits.getCharsLatin1(i, size, buf);
return new String(buf, LATIN1);
} else {
byte[] buf = new byte[size * 2];
StringUTF16.getChars(i, size, buf);
DecimalDigits.getCharsUTF16(i, size, buf);
return new String(buf, UTF16);
}
}

View File

@ -462,11 +462,11 @@ public final class Long extends Number
int size = DecimalDigits.stringSize(i);
if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
StringLatin1.getChars(i, size, buf);
DecimalDigits.getCharsLatin1(i, size, buf);
return new String(buf, LATIN1);
} else {
byte[] buf = new byte[size * 2];
StringUTF16.getChars(i, size, buf);
DecimalDigits.getCharsUTF16(i, size, buf);
return new String(buf, UTF16);
}
}

View File

@ -298,12 +298,12 @@ final class StringConcatHelper {
static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
int index = (int)indexCoder;
if (indexCoder < UTF16) {
index = StringLatin1.getChars(value, index, buf);
index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
return index;
} else {
index = StringUTF16.getChars(value, index, buf);
index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
return index | UTF16;
@ -324,12 +324,12 @@ final class StringConcatHelper {
static long prepend(long indexCoder, byte[] buf, long value, String prefix) {
int index = (int)indexCoder;
if (indexCoder < UTF16) {
index = StringLatin1.getChars(value, index, buf);
index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
return index;
} else {
index = StringUTF16.getChars(value, index, buf);
index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
return index | UTF16;
@ -682,11 +682,11 @@ final class StringConcatHelper {
*/
static int prepend(int index, byte coder, byte[] buf, int value, String prefix) {
if (coder == String.LATIN1) {
index = StringLatin1.getChars(value, index, buf);
index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
} else {
index = StringUTF16.getChars(value, index, buf);
index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
}
@ -706,11 +706,11 @@ final class StringConcatHelper {
*/
static int prepend(int index, byte coder, byte[] buf, long value, String prefix) {
if (coder == String.LATIN1) {
index = StringLatin1.getChars(value, index, buf);
index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
} else {
index = StringUTF16.getChars(value, index, buf);
index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
}

View File

@ -34,7 +34,6 @@ import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.DecimalDigits;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import static java.lang.String.LATIN1;
@ -86,120 +85,6 @@ final class StringLatin1 {
return ret;
}
/**
* Places characters representing the integer i into the
* character array buf. The characters are placed into
* the buffer backwards starting with the least significant
* digit at the specified index (exclusive), and working
* backwards from there.
*
* @implNote This method converts positive inputs into negative
* values, to cover the Integer.MIN_VALUE case. Converting otherwise
* (negative to positive) will expose -Integer.MIN_VALUE that overflows
* integer.
*
* @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
*/
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;
int charPos = index;
boolean negative = i < 0;
if (!negative) {
i = -i;
}
// Generate two digits per iteration
while (i <= -100) {
q = i / 100;
charPos -= 2;
writeDigitPair(buf, charPos, (q * 100) - i);
i = q;
}
// We know there are at most two digits left at this point.
if (i < -9) {
charPos -= 2;
writeDigitPair(buf, charPos, -i);
} else {
buf[--charPos] = (byte)('0' - i);
}
if (negative) {
buf[--charPos] = (byte)'-';
}
return charPos;
}
/**
* Places characters representing the long i into the
* character array buf. The characters are placed into
* the buffer backwards starting with the least significant
* digit at the specified index (exclusive), and working
* backwards from there.
*
* @implNote This method converts positive inputs into negative
* values, to cover the Long.MIN_VALUE case. Converting otherwise
* (negative to positive) will expose -Long.MIN_VALUE that overflows
* long.
*
* @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
*/
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 charPos = index;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using longs until quotient fits into an int
while (i <= Integer.MIN_VALUE) {
q = i / 100;
charPos -= 2;
writeDigitPair(buf, charPos, (int)((q * 100) - i));
i = q;
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
charPos -= 2;
writeDigitPair(buf, charPos, (q2 * 100) - i2);
i2 = q2;
}
// We know there are at most two digits left at this point.
if (i2 < -9) {
charPos -= 2;
writeDigitPair(buf, charPos, -i2);
} else {
buf[--charPos] = (byte)('0' - i2);
}
if (negative) {
buf[--charPos] = (byte)'-';
}
return charPos;
}
private static void writeDigitPair(byte[] buf, int charPos, int value) {
short pair = DecimalDigits.digitPair(value);
buf[charPos] = (byte)(pair);
buf[charPos + 1] = (byte)(pair >> 8);
}
public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

View File

@ -35,7 +35,6 @@ import java.util.stream.StreamSupport;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.DecimalDigits;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
@ -1513,20 +1512,6 @@ final class StringUTF16 {
return codePointCount(val, beginIndex, endIndex, true /* checked */);
}
public static int getChars(int i, int begin, int end, byte[] value) {
checkBoundsBeginEnd(begin, end, value);
int pos = getChars(i, end, value);
assert begin == pos;
return pos;
}
public static int getChars(long l, int begin, int end, byte[] value) {
checkBoundsBeginEnd(begin, end, value);
int pos = getChars(l, end, value);
assert begin == pos;
return pos;
}
public static boolean contentEquals(byte[] v1, byte[] v2, int len) {
checkBoundsOffCount(0, len, v2);
for (int i = 0; i < len; i++) {
@ -1662,109 +1647,6 @@ final class StringUTF16 {
static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
// Used by trusted callers. Assumes all necessary bounds checks have
// been done by the caller.
/**
* This is a variant of {@link StringLatin1#getChars(int, int, byte[])}, but for
* UTF-16 coder.
*
* @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
*/
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;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using ints
while (i <= -100) {
q = i / 100;
r = (q * 100) - i;
i = q;
charPos -= 2;
putPair(buf, charPos, r);
}
// We know there are at most two digits left at this point.
if (i < -9) {
charPos -= 2;
putPair(buf, charPos, -i);
} else {
putChar(buf, --charPos, '0' - i);
}
if (negative) {
putChar(buf, --charPos, '-');
}
return charPos;
}
/**
* This is a variant of {@link StringLatin1#getChars(long, int, byte[])}, but for
* UTF-16 coder.
*
* @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
*/
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 charPos = index;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using longs until quotient fits into an int
while (i <= Integer.MIN_VALUE) {
q = i / 100;
charPos -= 2;
putPair(buf, charPos, (int)((q * 100) - i));
i = q;
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
charPos -= 2;
putPair(buf, charPos, (q2 * 100) - i2);
i2 = q2;
}
// We know there are at most two digits left at this point.
if (i2 < -9) {
charPos -= 2;
putPair(buf, charPos, -i2);
} else {
putChar(buf, --charPos, '0' - i2);
}
if (negative) {
putChar(buf, --charPos, '-');
}
return charPos;
}
private 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) {
String.checkIndex(off, length(val));
}

View File

@ -2648,14 +2648,6 @@ public final class System {
return str.coder();
}
public int getCharsLatin1(long i, int index, byte[] buf) {
return StringLatin1.getChars(i, index, buf);
}
public int getCharsUTF16(long i, int index, byte[] buf) {
return StringUTF16.getChars(i, index, buf);
}
public String join(String prefix, String suffix, String delimiter, String[] elements, int size) {
return String.join(prefix, suffix, delimiter, elements, size);
}

View File

@ -35,9 +35,15 @@ import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.StreamCorruptedException;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.DecimalDigits;
/**
* Immutable, arbitrary-precision signed decimal numbers. A {@code
* BigDecimal} consists of an arbitrary precision integer
@ -328,6 +334,8 @@ import java.util.Objects;
* @since 1.1
*/
public class BigDecimal extends Number implements Comparable<BigDecimal> {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
/*
* Let l = log_2(10).
* Then, L < l < L + ulp(L) / 2, that is, L = roundTiesToEven(l).
@ -4164,103 +4172,6 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
return BigDecimal.valueOf(1, this.scale(), 1);
}
// Private class to build a string representation for BigDecimal object. The
// StringBuilder field acts as a buffer to hold the temporary representation
// of BigDecimal. The cmpCharArray holds all the characters for the compact
// representation of BigDecimal (except for '-' sign' if it is negative) if
// its intCompact field is not INFLATED.
static class StringBuilderHelper {
final StringBuilder sb; // Placeholder for BigDecimal string
final char[] cmpCharArray; // character array to place the intCompact
StringBuilderHelper() {
sb = new StringBuilder(32);
// All non negative longs can be made to fit into 19 character array.
cmpCharArray = new char[19];
}
// Accessors.
StringBuilder getStringBuilder() {
sb.setLength(0);
return sb;
}
char[] getCompactCharArray() {
return cmpCharArray;
}
/**
* Places characters representing the intCompact in {@code long} into
* cmpCharArray and returns the offset to the array where the
* representation starts.
*
* @param intCompact the number to put into the cmpCharArray.
* @return offset to the array where the representation starts.
* Note: intCompact must be greater or equal to zero.
*/
int putIntCompact(long intCompact) {
assert intCompact >= 0;
long q;
int r;
// since we start from the least significant digit, charPos points to
// the last character in cmpCharArray.
int charPos = cmpCharArray.length;
// Get 2 digits/iteration using longs until quotient fits into an int
while (intCompact > Integer.MAX_VALUE) {
q = intCompact / 100;
r = (int)(intCompact - q * 100);
intCompact = q;
cmpCharArray[--charPos] = DIGIT_ONES[r];
cmpCharArray[--charPos] = DIGIT_TENS[r];
}
// Get 2 digits/iteration using ints when i2 >= 100
int q2;
int i2 = (int)intCompact;
while (i2 >= 100) {
q2 = i2 / 100;
r = i2 - q2 * 100;
i2 = q2;
cmpCharArray[--charPos] = DIGIT_ONES[r];
cmpCharArray[--charPos] = DIGIT_TENS[r];
}
cmpCharArray[--charPos] = DIGIT_ONES[i2];
if (i2 >= 10)
cmpCharArray[--charPos] = DIGIT_TENS[i2];
return charPos;
}
static final char[] DIGIT_TENS = {
'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 char[] DIGIT_ONES = {
'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',
};
}
/**
* Lay out this {@code BigDecimal} into a {@code char[]} array.
* The Java 1.2 equivalent to this was called {@code getValueString}.
@ -4271,6 +4182,8 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
* {@code BigDecimal}
*/
private String layoutChars(boolean sci) {
long intCompact = this.intCompact;
int scale = this.scale;
if (scale == 0) // zero scale is trivial
return (intCompact != INFLATED) ?
Long.toString(intCompact):
@ -4280,18 +4193,24 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
// currency fast path
int lowInt = (int)intCompact % 100;
int highInt = (int)intCompact / 100;
return (Integer.toString(highInt) + '.' +
StringBuilderHelper.DIGIT_TENS[lowInt] +
StringBuilderHelper.DIGIT_ONES[lowInt]) ;
int highIntSize = DecimalDigits.stringSize(highInt);
byte[] buf = new byte[highIntSize + 3];
DecimalDigits.putPairLatin1(buf, highIntSize + 1, lowInt);
buf[highIntSize] = '.';
DecimalDigits.getCharsLatin1(highInt, highIntSize, buf);
try {
return JLA.newStringNoRepl(buf, StandardCharsets.ISO_8859_1);
} catch (CharacterCodingException cce) {
throw new AssertionError(cce);
}
}
StringBuilderHelper sbHelper = new StringBuilderHelper();
char[] coeff;
int offset; // offset is the starting index for coeff array
// Get the significand as an absolute value
if (intCompact != INFLATED) {
offset = sbHelper.putIntCompact(Math.abs(intCompact));
coeff = sbHelper.getCompactCharArray();
coeff = new char[19];
offset = DecimalDigits.getChars(Math.abs(intCompact), coeff.length, coeff);
} else {
offset = 0;
coeff = intVal.abs().toString().toCharArray();
@ -4301,7 +4220,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
// If E-notation is needed, length will be: +1 if negative, +1
// if '.' needed, +2 for "E+", + up to 10 for adjusted exponent.
// Otherwise it could have +1 if negative, plus leading "0.00000"
StringBuilder buf = sbHelper.getStringBuilder();
StringBuilder buf = new StringBuilder(32);;
if (signum() < 0) // prefix '-' if negative
buf.append('-');
int coeffLen = coeff.length - offset;

View File

@ -487,10 +487,6 @@ public interface JavaLangAccess {
*/
Object classData(Class<?> c);
int getCharsLatin1(long i, int index, byte[] buf);
int getCharsUTF16(long i, int index, byte[] buf);
/**
* Returns the {@link NativeLibraries} object associated with the provided class loader.
* This is used by {@link SymbolLookup#loaderLookup()}.

View File

@ -25,14 +25,18 @@
package jdk.internal.util;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.Stable;
import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
/**
* Digits class for decimal digits.
*
* @since 21
*/
public final class DecimalDigits {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
/**
* Each element of the array represents the packaging of two ascii characters based on little endian:<p>
@ -76,15 +80,6 @@ public final class DecimalDigits {
private DecimalDigits() {
}
/**
* 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];
}
/**
* Returns the string representation size for a given int value.
*
@ -136,4 +131,306 @@ public final class DecimalDigits {
}
return 19 + d;
}
/**
* Places characters representing the integer i into the
* character array buf. The characters are placed into
* the buffer backwards starting with the least significant
* digit at the specified index (exclusive), and working
* backwards from there.
*
* @implNote This method converts positive inputs into negative
* values, to cover the Integer.MIN_VALUE case. Converting otherwise
* (negative to positive) will expose -Integer.MIN_VALUE that overflows
* integer.
*
* @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.
int q;
int charPos = index;
boolean negative = i < 0;
if (!negative) {
i = -i;
}
// Generate two digits per iteration
while (i <= -100) {
q = i / 100;
charPos -= 2;
putPairLatin1(buf, charPos, (q * 100) - i);
i = q;
}
// We know there are at most two digits left at this point.
if (i < -9) {
charPos -= 2;
putPairLatin1(buf, charPos, -i);
} else {
putCharLatin1(buf, --charPos, '0' - i);
}
if (negative) {
putCharLatin1(buf, --charPos, '-');
}
return charPos;
}
/**
* Places characters representing the long i into the
* character array buf. The characters are placed into
* the buffer backwards starting with the least significant
* digit at the specified index (exclusive), and working
* backwards from there.
*
* @implNote This method converts positive inputs into negative
* values, to cover the Long.MIN_VALUE case. Converting otherwise
* (negative to positive) will expose -Long.MIN_VALUE that overflows
* long.
*
* @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.
long q;
int charPos = index;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using longs until quotient fits into an int
while (i <= Integer.MIN_VALUE) {
q = i / 100;
charPos -= 2;
putPairLatin1(buf, charPos, (int)((q * 100) - i));
i = q;
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
charPos -= 2;
putPairLatin1(buf, charPos, (q2 * 100) - i2);
i2 = q2;
}
// We know there are at most two digits left at this point.
if (i2 < -9) {
charPos -= 2;
putPairLatin1(buf, charPos, -i2);
} else {
putCharLatin1(buf, --charPos, '0' - i2);
}
if (negative) {
putCharLatin1(buf, --charPos, '-');
}
return charPos;
}
/**
* This is a variant of {@link DecimalDigits#getCharsLatin1(int, int, byte[])}, but for
* UTF-16 coder.
*
* @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.
int q, r;
int charPos = index;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using ints
while (i <= -100) {
q = i / 100;
r = (q * 100) - i;
i = q;
charPos -= 2;
putPairUTF16(buf, charPos, r);
}
// We know there are at most two digits left at this point.
if (i < -9) {
charPos -= 2;
putPairUTF16(buf, charPos, -i);
} else {
putCharUTF16(buf, --charPos, '0' - i);
}
if (negative) {
putCharUTF16(buf, --charPos, '-');
}
return charPos;
}
/**
* This is a variant of {@link DecimalDigits#getCharsLatin1(long, int, byte[])}, but for
* UTF-16 coder.
*
* @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.
long q;
int charPos = index;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using longs until quotient fits into an int
while (i <= Integer.MIN_VALUE) {
q = i / 100;
charPos -= 2;
putPairUTF16(buf, charPos, (int)((q * 100) - i));
i = q;
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
charPos -= 2;
putPairUTF16(buf, charPos, (q2 * 100) - i2);
i2 = q2;
}
// We know there are at most two digits left at this point.
if (i2 < -9) {
charPos -= 2;
putPairUTF16(buf, charPos, -i2);
} else {
putCharUTF16(buf, --charPos, '0' - i2);
}
if (negative) {
putCharUTF16(buf, --charPos, '-');
}
return charPos;
}
/**
* This is a variant of {@link DecimalDigits#getCharsUTF16(long, int, byte[])}, but for
* UTF-16 coder.
*
* @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 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;
boolean negative = (i < 0);
if (!negative) {
i = -i;
}
// Get 2 digits/iteration using longs until quotient fits into an int
while (i <= Integer.MIN_VALUE) {
q = i / 100;
charPos -= 2;
putPair(buf, charPos, (int)((q * 100) - i));
i = q;
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int)i;
while (i2 <= -100) {
q2 = i2 / 100;
charPos -= 2;
putPair(buf, charPos, (q2 * 100) - i2);
i2 = q2;
}
// We know there are at most two digits left at this point.
if (i2 < -9) {
charPos -= 2;
putPair(buf, charPos, -i2);
} else {
buf[--charPos] = (char) ('0' - i2);
}
if (negative) {
buf[--charPos] = '-';
}
return charPos;
}
/**
* Insert the 2-chars integer into the buf as 2 decimal digit ASCII chars,
* only least significant 16 bits of {@code v} are used.
* @param buf byte buffer to copy into
* @param charPos insert point
* @param v to convert
*/
public static void putPair(char[] buf, int charPos, int v) {
int packed = DIGITS[v];
buf[charPos ] = (char) (packed & 0xFF);
buf[charPos + 1] = (char) (packed >> 8);
}
/**
* Insert the 2-bytes integer into the buf as 2 decimal digit ASCII bytes,
* only least significant 16 bits of {@code v} are used.
* @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) {
int packed = DIGITS[v];
putCharLatin1(buf, charPos, packed & 0xFF);
putCharLatin1(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.
* @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) {
int packed = DIGITS[v];
putCharUTF16(buf, charPos, packed & 0xFF);
putCharUTF16(buf, charPos + 1, packed >> 8);
}
private static void putCharLatin1(byte[] buf, int charPos, int c) {
UNSAFE.putByte(buf, ARRAY_BYTE_BASE_OFFSET + charPos, (byte) c);
}
private static void putCharUTF16(byte[] buf, int charPos, int c) {
UNSAFE.putChar(buf, ARRAY_BYTE_BASE_OFFSET + (charPos << 1), (char) c);
}
}

View File

@ -23,6 +23,8 @@
package java.lang;
import jdk.internal.util.DecimalDigits;
/**
* A helper class to get access to package-private members
*/
@ -117,11 +119,17 @@ public class Helper {
}
public static int getChars(int i, int begin, int end, byte[] value) {
return StringUTF16.getChars(i, begin, end, value);
StringUTF16.checkBoundsBeginEnd(begin, end, value);
int pos = DecimalDigits.getCharsUTF16(i, end, value);
assert begin == pos;
return pos;
}
public static int getChars(long l, int begin, int end, byte[] value) {
return StringUTF16.getChars(l, begin, end, value);
StringUTF16.checkBoundsBeginEnd(begin, end, value);
int pos = DecimalDigits.getCharsUTF16(l, end, value);
assert begin == pos;
return pos;
}
public static boolean contentEquals(byte[] v1, byte[] v2, int len) {

View File

@ -54,6 +54,8 @@ public class StringBuilders {
private StringBuilder sbLatin2;
private StringBuilder sbUtf16;
private StringBuilder sbUtf17;
private int[] intsArray;
private long[] longArray;
@Setup
public void setup() {
@ -69,6 +71,13 @@ public class StringBuilders {
sbLatin2 = new StringBuilder("Latin1 string");
sbUtf16 = new StringBuilder("UTF-\uFF11\uFF16 string");
sbUtf17 = new StringBuilder("UTF-\uFF11\uFF16 string");
int size = 16;
intsArray = new int[size];
longArray = new long[size];
for (int i = 0; i < longArray.length; i++) {
intsArray[i] = ((100 * i + i) << 24) + 4543 + i * 4;
longArray[i] = ((100L * i + i) << 32) + 4543 + i * 4L;
}
}
@Benchmark
@ -224,6 +233,47 @@ public class StringBuilders {
return result.toString();
}
@Benchmark
public int appendWithIntLatin1() {
StringBuilder buf = sbLatin1;
buf.setLength(0);
for (long l : longArray) {
buf.append(l);
}
return buf.length();
}
@Benchmark
public int appendWithIntUtf16() {
StringBuilder buf = sbUtf16;
buf.setLength(0);
buf.setLength(0);
for (long l : longArray) {
buf.append(l);
}
return buf.length();
}
@Benchmark
public int appendWithLongLatin1() {
StringBuilder buf = sbLatin1;
buf.setLength(0);
for (long l : longArray) {
buf.append(l);
}
return buf.length();
}
@Benchmark
public int appendWithLongUtf16() {
StringBuilder buf = sbUtf16;
buf.setLength(0);
buf.setLength(0);
for (long l : longArray) {
buf.append(l);
}
return buf.length();
}
@Benchmark
public int appendWithBool8Latin1() {