From 0c487d3ec671072f45dcca8101e6a7b2442575df Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 23 Jan 2026 09:23:55 +0800 Subject: [PATCH] from liach --- .../share/classes/java/lang/Integer.java | 2 +- .../share/classes/java/lang/Long.java | 6 +- .../share/classes/java/util/UUID.java | 8 +- .../classes/jdk/internal/util/HexDigits.java | 76 +++---------------- 4 files changed, 17 insertions(+), 75 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index b4119e5e4d4..19d47f61540 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -286,7 +286,7 @@ public final class Integer extends Number public static String toHexString(int i) { int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); int len = Math.max(((mag + 3) >> 2), 1); - long x = HexDigits.hex8(i); + long x = HexDigits.hex8Be(i); if (COMPACT_STRINGS) { byte[] chars = new byte[len]; do { diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 7578e150dc0..c4540d0b6e1 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -310,13 +310,13 @@ public final class Long extends Number public static String toHexString(long i) { int mag = Long.SIZE - Long.numberOfLeadingZeros(i); int len = Math.max(((mag + 3) >> 2), 1); - long x = HexDigits.hex8(i); + long x = HexDigits.hex8Be((int)i); if (COMPACT_STRINGS) { byte[] chars = new byte[len]; if (len > 8) { len -= 8; Unsafe.getUnsafe().putLongUnaligned(chars, Unsafe.ARRAY_BYTE_BASE_OFFSET + len, x, true); - x = HexDigits.hex8(i >>> 32); + x = HexDigits.hex8Be((int)(i >>> 32)); } do { chars[--len] = (byte) x; @@ -332,7 +332,7 @@ public final class Long extends Number StringUTF16.putChar(chars, --len, b); x >>>= 8; } - x = HexDigits.hex8(i >>> 32); + x = HexDigits.hex8Be((int)(i >>> 32)); } do { StringUTF16.putChar(chars, --len, (byte) x); diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java index 9c3bc528f91..ecf1c607d0b 100644 --- a/src/java.base/share/classes/java/util/UUID.java +++ b/src/java.base/share/classes/java/util/UUID.java @@ -521,15 +521,15 @@ public final class UUID implements java.io.Serializable, Comparable { // Although the UUID byte ordering is defined to be big-endian, ByteArrayLittleEndian is used here to optimize // for the most common architectures. - ByteArrayLittleEndian.setLong(buf, 0, Long.reverseBytes(HexDigits.hex8(mostSigBits >>> 32))); - long x0 = Long.reverseBytes(HexDigits.hex8(mostSigBits)); + ByteArrayLittleEndian.setLong(buf, 0, Long.reverseBytes(HexDigits.hex8Be((int)(mostSigBits >>> 32)))); + long x0 = Long.reverseBytes(HexDigits.hex8Be((int)mostSigBits)); ByteArrayLittleEndian.setInt(buf, 9, (int) x0); ByteArrayLittleEndian.setInt(buf, 14, (int) (x0 >>> 32)); - long x1 = Long.reverseBytes(HexDigits.hex8(leastSigBits >>> 32)); + long x1 = Long.reverseBytes(HexDigits.hex8Be((int)(leastSigBits >>> 32))); ByteArrayLittleEndian.setInt(buf, 19, (int) (x1)); ByteArrayLittleEndian.setInt(buf, 24, (int) (x1 >>> 32)); - ByteArrayLittleEndian.setLong(buf, 28, Long.reverseBytes(HexDigits.hex8(leastSigBits))); + ByteArrayLittleEndian.setLong(buf, 28, Long.reverseBytes(HexDigits.hex8Be((int)leastSigBits))); return jla.uncheckedNewStringWithLatin1Bytes(buf); } diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index 4278712df7f..bbeaea9b220 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -110,77 +110,19 @@ public final class HexDigits { : v; } - /** - * Efficiently converts 8 hexadecimal digits to their ASCII representation using SIMD-style vector operations. - * This method processes multiple digits in parallel by treating a long value as eight 8-bit lanes, - * achieving significantly better performance compared to traditional loop-based conversion. - * - *

The conversion algorithm works as follows: - *

-     * 1. Input expansion: Each 4-bit hex digit is expanded to 8 bits
-     * 2. Vector processing:
-     *    - Add 6 to each digit: triggers carry flag for a-f digits
-     *    - Mask with 0x10 pattern to isolate carry flags
-     *    - Calculate ASCII adjustment: (carry << 1) + (carry >> 1) - (carry >> 4)
-     *    - Add ASCII '0' base (0x30) and original value
-     * 3. Byte order adjustment for final output
-     * 
- * - *

Performance characteristics: - *

- * - *

ASCII conversion mapping: - *

- * - * @param input A long containing 8 hex digits (each digit must be 0-15) - * @return A long containing 8 ASCII bytes representing the hex digits - * - * @implNote The implementation leverages CPU vector processing capabilities through - * long integer operations. The algorithm is based on the observation that - * ASCII hex digits have a specific pattern that can be computed efficiently - * using carry flag manipulation. - * - * @example - *
-     * Input:  0xABCDEF01
-     * Output: 3130666564636261 ('1','0','f','e','d','c','b','a' in ASCII)
-     * 
- * - */ - public static long hex8(long i) { + /// Prints an unsigned 4-byte number into 8 hexadecimal digits in an ASCII character buffer. + /// The buffer is represented as a big-endian 8 byte integer. + /// + /// Input: 0xA__B__C__D__E__F__0__1 + /// Output: 0x61_62_63_64_65_66_30_31 + public static long hex8Be(int i) { // Expand each 4-bit group into 8 bits, spreading them out in the long value: 0xAABBCCDD -> 0xA0A0B0B0C0C0D0D - i = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); + long x = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); - /* - * This method efficiently converts 8 hexadecimal digits simultaneously using vector operations - * The algorithm works as follows: - * - * For input values 0-15: - * - For digits 0-9: converts to ASCII '0'-'9' (0x30-0x39) - * - For digits 10-15: converts to ASCII 'a'-'f' (0x61-0x66) - * - * The conversion process: - * 1. Add 6 to each 4-bit group: i + 0x0606_0606_0606_0606L - * 2. Mask to get the adjustment flags: & 0x1010_1010_1010_1010L - * 3. Calculate the offset: (m << 1) + (m >> 1) - (m >> 4) - * - For 0-9: offset = 0 - * - For a-f: offset = 39 (to bridge the gap between '9' and 'a' in ASCII) - * 4. Add ASCII '0' base (0x30) and the original value - * 5. Reverse byte order for correct positioning - */ - long m = (i + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; + long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; - // Calculate final ASCII values and reverse bytes for proper ordering return ((m << 1) + (m >> 1) - (m >> 4)) + 0x3030_3030_3030_3030L // Add ASCII '0' base to all digits - + i; // Add original values + + x; // Add original values } }