mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8366224: Introduce DecimalDigits.appendPair for efficient two-digit formatting and refactor DateTimeHelper
Reviewed-by: liach, rriggs
This commit is contained in:
parent
65f1ad6169
commit
4ffdf7af88
@ -88,6 +88,8 @@ import java.time.temporal.UnsupportedTemporalTypeException;
|
||||
import java.time.temporal.ValueRange;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.util.DecimalDigits;
|
||||
|
||||
/**
|
||||
* A month-day in the ISO-8601 calendar system, such as {@code --12-03}.
|
||||
* <p>
|
||||
@ -764,10 +766,12 @@ public final class MonthDay
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder(10).append("--")
|
||||
.append(month < 10 ? "0" : "").append(month)
|
||||
.append(day < 10 ? "-0" : "-").append(day)
|
||||
.toString();
|
||||
StringBuilder buf = new StringBuilder(10);
|
||||
buf.append("--");
|
||||
DecimalDigits.appendPair(buf, month);
|
||||
buf.append('-');
|
||||
DecimalDigits.appendPair(buf, day);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@ -101,6 +101,8 @@ import java.time.temporal.UnsupportedTemporalTypeException;
|
||||
import java.time.temporal.ValueRange;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.util.DecimalDigits;
|
||||
|
||||
/**
|
||||
* A year-month in the ISO-8601 calendar system, such as {@code 2007-12}.
|
||||
* <p>
|
||||
@ -1213,18 +1215,17 @@ public final class YearMonth
|
||||
public String toString() {
|
||||
int absYear = Math.abs(year);
|
||||
StringBuilder buf = new StringBuilder(9);
|
||||
if (absYear < 1000) {
|
||||
if (absYear < 10000) {
|
||||
if (year < 0) {
|
||||
buf.append(year - 10000).deleteCharAt(1);
|
||||
} else {
|
||||
buf.append(year + 10000).deleteCharAt(0);
|
||||
buf.append('-');
|
||||
}
|
||||
DecimalDigits.appendQuad(buf, absYear);
|
||||
} else {
|
||||
buf.append(year);
|
||||
}
|
||||
return buf.append(month < 10 ? "-0" : "-")
|
||||
.append(month)
|
||||
.toString();
|
||||
buf.append('-');
|
||||
DecimalDigits.appendPair(buf, month);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@ -88,6 +88,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
import jdk.internal.util.DecimalDigits;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/**
|
||||
@ -465,12 +466,14 @@ public final class ZoneOffset
|
||||
StringBuilder buf = new StringBuilder();
|
||||
int absHours = absTotalSeconds / SECONDS_PER_HOUR;
|
||||
int absMinutes = (absTotalSeconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
|
||||
buf.append(totalSeconds < 0 ? "-" : "+")
|
||||
.append(absHours < 10 ? "0" : "").append(absHours)
|
||||
.append(absMinutes < 10 ? ":0" : ":").append(absMinutes);
|
||||
buf.append(totalSeconds < 0 ? '-' : '+');
|
||||
DecimalDigits.appendPair(buf, absHours);
|
||||
buf.append(':');
|
||||
DecimalDigits.appendPair(buf, absMinutes);
|
||||
int absSeconds = absTotalSeconds % SECONDS_PER_MINUTE;
|
||||
if (absSeconds != 0) {
|
||||
buf.append(absSeconds < 10 ? ":0" : ":").append(absSeconds);
|
||||
buf.append(':');
|
||||
DecimalDigits.appendPair(buf, absSeconds);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@ -74,6 +74,8 @@ import java.time.temporal.UnsupportedTemporalTypeException;
|
||||
import java.time.temporal.ValueRange;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.util.DecimalDigits;
|
||||
|
||||
/**
|
||||
* A date expressed in terms of a standard year-month-day calendar system.
|
||||
* <p>
|
||||
@ -426,18 +428,22 @@ abstract class ChronoLocalDateImpl<D extends ChronoLocalDate>
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// getLong() reduces chances of exceptions in toString()
|
||||
long yoe = getLong(YEAR_OF_ERA);
|
||||
long moy = getLong(MONTH_OF_YEAR);
|
||||
long dom = getLong(DAY_OF_MONTH);
|
||||
// Using get() instead of getLong() for performance reasons,
|
||||
// as the values of YEAR_OF_ERA, MONTH_OF_YEAR, and DAY_OF_MONTH
|
||||
// are guaranteed to be within the int range for all chronologies.
|
||||
int yoe = get(YEAR_OF_ERA);
|
||||
int moy = get(MONTH_OF_YEAR);
|
||||
int dom = get(DAY_OF_MONTH);
|
||||
StringBuilder buf = new StringBuilder(30);
|
||||
buf.append(getChronology().toString())
|
||||
.append(" ")
|
||||
.append(getEra())
|
||||
.append(" ")
|
||||
.append(yoe)
|
||||
.append(moy < 10 ? "-0" : "-").append(moy)
|
||||
.append(dom < 10 ? "-0" : "-").append(dom);
|
||||
.append('-');
|
||||
DecimalDigits.appendPair(buf, moy);
|
||||
buf.append('-');
|
||||
DecimalDigits.appendPair(buf, dom);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
@ -49,24 +49,23 @@ public final class DateTimeHelper {
|
||||
* Requires extra capacity of 10 to avoid StringBuilder reallocation.
|
||||
*/
|
||||
public static void formatTo(StringBuilder buf, LocalDate date) {
|
||||
int year = date.getYear(),
|
||||
month = date.getMonthValue(),
|
||||
day = date.getDayOfMonth();
|
||||
int absYear = Math.abs(year);
|
||||
if (absYear < 1000) {
|
||||
int year = date.getYear(),
|
||||
absYear = Math.abs(year);
|
||||
if (absYear < 10000) {
|
||||
if (year < 0) {
|
||||
buf.append('-');
|
||||
}
|
||||
buf.repeat('0', absYear < 10 ? 3 : absYear < 100 ? 2 : 1);
|
||||
buf.append(absYear);
|
||||
DecimalDigits.appendQuad(buf, absYear);
|
||||
} else {
|
||||
if (year > 9999) {
|
||||
buf.append('+');
|
||||
}
|
||||
buf.append(year);
|
||||
}
|
||||
buf.append(month < 10 ? "-0" : "-").append(month)
|
||||
.append(day < 10 ? "-0" : "-").append(day);
|
||||
buf.append('-');
|
||||
DecimalDigits.appendPair(buf, date.getMonthValue());
|
||||
buf.append('-');
|
||||
DecimalDigits.appendPair(buf, date.getDayOfMonth());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,14 +73,14 @@ public final class DateTimeHelper {
|
||||
* Requires extra capacity of 18 to avoid StringBuilder reallocation.
|
||||
*/
|
||||
public static void formatTo(StringBuilder buf, LocalTime time) {
|
||||
int hour = time.getHour(),
|
||||
minute = time.getMinute(),
|
||||
second = time.getSecond(),
|
||||
DecimalDigits.appendPair(buf, time.getHour());
|
||||
buf.append(':');
|
||||
DecimalDigits.appendPair(buf, time.getMinute());
|
||||
int second = time.getSecond(),
|
||||
nano = time.getNano();
|
||||
buf.append(hour < 10 ? "0" : "").append(hour)
|
||||
.append(minute < 10 ? ":0" : ":").append(minute);
|
||||
if ((second | nano) > 0) {
|
||||
buf.append(second < 10 ? ":0" : ":").append(second);
|
||||
buf.append(':');
|
||||
DecimalDigits.appendPair(buf, second);
|
||||
if (nano > 0) {
|
||||
buf.append('.');
|
||||
int zeros = 9 - DecimalDigits.stringSize(nano);
|
||||
|
||||
@ -25,6 +25,8 @@
|
||||
|
||||
package jdk.internal.util;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
@ -36,6 +38,7 @@ import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
|
||||
* @since 21
|
||||
*/
|
||||
public final class DecimalDigits {
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
/**
|
||||
@ -443,4 +446,56 @@ public final class DecimalDigits {
|
||||
assert charPos >= 0 && charPos < (buf.length >> 1);
|
||||
UNSAFE.putCharUnaligned(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the two-digit string representation of the {@code int}
|
||||
* argument to the given {@code StringBuilder}.
|
||||
* <p>
|
||||
* The integer {@code v} is formatted as two decimal digits.
|
||||
* Values from 0 to 9 are formatted with a leading zero (e.g., 5 becomes "05"),
|
||||
* and values from 10 to 99 are formatted as regular two-digit numbers.
|
||||
* If the value is outside the range 0-99, the behavior is unspecified.
|
||||
*
|
||||
* @param buf the {@code StringBuilder} to append to.
|
||||
* @param v the {@code int} value (should be between 0 and 99 inclusive).
|
||||
*/
|
||||
public static void appendPair(StringBuilder buf, int v) {
|
||||
// The & 0x7f operation keeps the index within the safe range [0, 127] for the DIGITS array,
|
||||
// which allows the JIT compiler to eliminate array bounds checks for performance.
|
||||
int packed = DIGITS[v & 0x7f];
|
||||
// The temporary String and byte[] objects created here are typically eliminated
|
||||
// by the JVM's escape analysis and scalar replacement optimizations during
|
||||
// runtime compilation, avoiding actual heap allocations in optimized code.
|
||||
buf.append(
|
||||
JLA.uncheckedNewStringWithLatin1Bytes(
|
||||
new byte[] {(byte) packed, (byte) (packed >> 8)}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the four-digit string representation of the {@code int}
|
||||
* argument to the given {@code StringBuilder}.
|
||||
* <p>
|
||||
* The integer {@code v} is formatted as four decimal digits.
|
||||
* Values from 0 to 9 are formatted with leading zeros (e.g., 5 becomes "0005"),
|
||||
* values from 10 to 99 add two leading zeros (e.g., 25 becomes "0025"),
|
||||
* values from 100 to 999 add one leading zero (e.g., 123 becomes "0123"),
|
||||
* and values from 1000 to 9999 have no leading zeros.
|
||||
* If the value is outside the range 0-9999, the behavior is unspecified.
|
||||
*
|
||||
* @param buf the {@code StringBuilder} to append to.
|
||||
* @param v the {@code int} value (should be between 0 and 9999 inclusive).
|
||||
*/
|
||||
public static void appendQuad(StringBuilder buf, int v) {
|
||||
// The & 0x7f operation keeps the index within the safe range [0, 127] for the DIGITS array,
|
||||
// which allows the JIT compiler to eliminate array bounds checks for performance.
|
||||
int packedHigh = DIGITS[(v / 100) & 0x7f];
|
||||
int packedLow = DIGITS[(v % 100) & 0x7f];
|
||||
// The temporary String and byte[] objects created here are typically eliminated
|
||||
// by the JVM's escape analysis and scalar replacement optimizations during
|
||||
// runtime compilation, avoiding actual heap allocations in optimized code.
|
||||
buf.append(
|
||||
JLA.uncheckedNewStringWithLatin1Bytes(
|
||||
new byte[] {(byte) packedHigh, (byte) (packedHigh >> 8),
|
||||
(byte) packedLow, (byte) (packedLow >> 8)}));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user