From 79fe74eb8f74d21c16cb2850a55cf8a37d9b3f65 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 7 Jan 2025 13:56:26 +0800 Subject: [PATCH] optimize toHexString --- .../share/classes/java/lang/Integer.java | 50 ++++++++++++++++++- .../share/classes/java/lang/Long.java | 33 +++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index a6bf739220f..3f8efd35430 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -286,7 +286,55 @@ public final class Integer extends Number * @since 1.0.2 */ public static String toHexString(int i) { - return toUnsignedString0(i, 4); + int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); + int len = Math.max(((mag + 3) / 4), 1); + byte coder = COMPACT_STRINGS ? LATIN1 : UTF16; + byte[] chars = new byte[len << coder]; + long x = hex8(i); + do { + --len; + byte b = (byte) x; + if (COMPACT_STRINGS) { + StringLatin1.putChar(chars, len, b); + } else { + StringUTF16.putChar(chars, len, b); + } + x >>>= 8; + } while (len > 0); + return new String(chars, coder); + } + + /** + * Extract the least significant 8 bytes from the input integer i, convert each byte into its corresponding 2-digit + * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret + * this string as a hexadecimal number to form and return a long value. + */ + static long hex8(long i) { + long x = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); + /* + Use long to simulate vector operations and generate 8 hexadecimal characters at a time. + ------------ + 0 = 0b0000_0000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '0' + 1 = 0b0000_0001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '1' + 2 = 0b0000_0010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '2' + 3 = 0b0000_0011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '3' + 4 = 0b0000_0100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '4' + 5 = 0b0000_0101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '5' + 6 = 0b0000_0110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '6' + 7 = 0b0000_0111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '7' + 8 = 0b0000_1000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '8' + 9 = 0b0000_1001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '9' + 10 = 0b0000_1010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'a' + 11 = 0b0000_1011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'b' + 12 = 0b0000_1100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'c' + 13 = 0b0000_1101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'd' + 14 = 0b0000_1110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'e' + 15 = 0b0000_1111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'f' + */ + long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; + return ((m << 1) + (m >> 1) - (m >> 4)) + + 0x3030_3030_3030_3030L + + (x & 0x0F0F_0F0F_0F0F_0F0FL); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 822199bb09b..deea79ef5d7 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -34,6 +34,7 @@ import java.util.Objects; import java.util.Optional; import jdk.internal.misc.CDS; +import jdk.internal.misc.Unsafe; import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -43,6 +44,7 @@ import static java.lang.Character.digit; import static java.lang.String.COMPACT_STRINGS; import static java.lang.String.LATIN1; import static java.lang.String.UTF16; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; /** * The {@code Long} class is the {@linkplain @@ -311,7 +313,36 @@ public final class Long extends Number * @since 1.0.2 */ public static String toHexString(long i) { - return toUnsignedString0(i, 4); + int mag = Long.SIZE - Long.numberOfLeadingZeros(i); + int len = Math.max(((mag + 3) / 4), 1); + byte coder = COMPACT_STRINGS ? LATIN1 : UTF16; + byte[] chars = new byte[len << coder]; + byte b; + long x = Integer.hex8(i); + if (len > 8) { + if (COMPACT_STRINGS) { + len -= 8; + Unsafe.getUnsafe().putLong(chars, ARRAY_BYTE_BASE_OFFSET + len, Long.reverseBytes(x)); + } else { + for (int j = 0; j < 8; j++) { + b = (byte) x; + StringUTF16.putChar(chars, --len, b); + x >>>= 8; + } + } + x = Integer.hex8(i >>> 32); + } + do { + --len; + b = (byte) x; + if (COMPACT_STRINGS) { + chars[len] = b; + } else { + StringUTF16.putChar(chars, len, b); + } + x >>>= 8; + } while (len > 0); + return new String(chars, coder); } /**