RFR: 8366224: Introduce DecimalDigits.appendPair for efficient two-digit formatting and refactor DateTimeHelper [v22]

Shaojin Wen swen at openjdk.org
Fri Nov 14 08:03:20 UTC 2025


On Fri, 14 Nov 2025 04:46:26 GMT, Shaojin Wen <swen at openjdk.org> wrote:

>> This PR introduces a new efficient API for appending two-digit integers to StringBuilders and refactors DateTimeHelper to leverage this new functionality.
>> 
>> Changes include:
>> 
>> 1. New `appendPair` method for efficient two-digit integer formatting (00-99):
>>    - Added `AbstractStringBuilder.appendLatin1(char c1, char c2)` with core implementation
>>    - Added `JavaLangAccess.appendPair(StringBuilder, char c1, char c2)` for internal access
>>    - Added `DecimalDigits.appendPair(StringBuilder, int)` public static utility method
>>    - Enhanced Javadoc documentation for all new methods
>> 
>> 2. Refactored `DateTimeHelper` to use the new `DecimalDigits.appendPair`:
>>    - Updated `DateTimeHelper.formatTo` methods for `LocalDate` and `LocalTime`
>>    - Replaced manual formatting logic with the new efficient two-digit appending
>>    - Improved code clarity and consistency in date/time formatting
>> 
>> These changes improve code clarity and performance when formatting two-digit numbers, particularly in date/time formatting scenarios.
>
> Shaojin Wen has updated the pull request incrementally with one additional commit since the last revision:
> 
>   remove JLA

I wanted to modify the DecimalDigits.appendQuad method as follows, but this caused `MergeStore` to not work.

    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 packed = DIGITS[(v / 100) & 0x7f] | (DIGITS[(v % 100) & 0x7f] << 16);
        // 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),
                                    (byte) (packed >> 16), (byte) (packed  >> 24)}));
    }


The output is as follows:

[TraceMergeStores] MergePrimitiveStores::run:  868  StoreB  === 887 813 861 145  [[ 872 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  848  StoreB  === 888 813 840 81  [[ 853 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  559  StoreB  === 548 543 351 352  [[ 562 ]]  @java/lang/AbstractStringBuilder (java/lang/CharSequence,java/lang/Appendable)+16 *, name=coder, idx=13;  Memory: @java/lang/StringBuilder (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/Appendable):NotNull:exact+16 *, name=coder, idx=13; !jvms: AbstractStringBuilder::append @ bci:78 (line 651) StringBuilder::append @ bci:2 (line 179) DecimalDigits::appendQuad @ bci:68 (line 496)
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  739  StoreI  === 879 813 354 456  [[ 17 ]]  @java/lang/AbstractStringBuilder (java/lang/CharSequence,java/lang/Appendable)+12 *, name=count, idx=14;  Memory: @java/lang/StringBuilder (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/Appendable):NotNull:exact+12 *, name=count, idx=14; !jvms: AbstractStringBuilder::append @ bci:95 (line 654) StringBuilder::append @ bci:2 (line 179) DecimalDigits::appendQuad @ bci:68 (line 496)
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  872  StoreB  === 887 868 856 912  [[ 876 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  853  StoreB  === 888 848 851 906  [[ 858 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  858  StoreB  === 888 853 856 912  [[ 863 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  863  StoreB  === 888 858 861 145  [[ 880 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  876  StoreB  === 887 872 851 906  [[ 878 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None
[TraceMergeStores] MergePrimitiveStores::run:  878  StoreB  === 887 876 840 81  [[ 880 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=7;
[TraceMergeStores] expect no use: None
[TraceMergeStores] expect def: None

-------------

PR Comment: https://git.openjdk.org/jdk/pull/26911#issuecomment-3531435995


More information about the core-libs-dev mailing list