|
31 | 31 |
|
32 | 32 | import jdk.internal.access.JavaLangAccess; |
33 | 33 | import jdk.internal.access.SharedSecrets; |
34 | | -import jdk.internal.util.HexDigits; |
| 34 | +import jdk.internal.util.ByteArrayLittleEndian; |
35 | 35 |
|
36 | 36 | /** |
37 | 37 | * A class that represents an immutable universally unique identifier (UUID). |
|
76 | 76 | * @since 1.5 |
77 | 77 | */ |
78 | 78 | public final class UUID implements java.io.Serializable, Comparable<UUID> { |
79 | | - |
80 | 79 | /** |
81 | 80 | * Explicit serialVersionUID for interoperability. |
82 | 81 | */ |
@@ -462,31 +461,108 @@ public long node() { |
462 | 461 | */ |
463 | 462 | @Override |
464 | 463 | public String toString() { |
465 | | - int i0 = (int) (mostSigBits >> 32); |
466 | | - int i1 = (int) mostSigBits; |
467 | | - int i2 = (int) (leastSigBits >> 32); |
468 | | - int i3 = (int) leastSigBits; |
469 | | - |
470 | 464 | byte[] buf = new byte[36]; |
471 | | - HexDigits.put4(buf, 0, i0 >> 16); |
472 | | - HexDigits.put4(buf, 4, i0); |
473 | 465 | buf[8] = '-'; |
474 | | - HexDigits.put4(buf, 9, i1 >> 16); |
475 | 466 | buf[13] = '-'; |
476 | | - HexDigits.put4(buf, 14, i1); |
477 | 467 | buf[18] = '-'; |
478 | | - HexDigits.put4(buf, 19, i2 >> 16); |
479 | 468 | buf[23] = '-'; |
480 | | - HexDigits.put4(buf, 24, i2); |
481 | | - HexDigits.put4(buf, 28, i3 >> 16); |
482 | | - HexDigits.put4(buf, 32, i3); |
| 469 | + |
| 470 | + // Although the UUID byte ordering is defined to be big-endian, ByteArrayLittleEndian is used here to optimize |
| 471 | + // for the most common architectures. hex8 reverses the order internally. |
| 472 | + ByteArrayLittleEndian.setLong(buf, 0, hex8(mostSigBits >>> 32)); |
| 473 | + long x0 = hex8(mostSigBits); |
| 474 | + ByteArrayLittleEndian.setInt(buf, 9, (int) x0); |
| 475 | + ByteArrayLittleEndian.setInt(buf, 14, (int) (x0 >>> 32)); |
| 476 | + |
| 477 | + long x1 = hex8(leastSigBits >>> 32); |
| 478 | + ByteArrayLittleEndian.setInt(buf, 19, (int) (x1)); |
| 479 | + ByteArrayLittleEndian.setInt(buf, 24, (int) (x1 >>> 32)); |
| 480 | + ByteArrayLittleEndian.setLong(buf, 28, hex8(leastSigBits)); |
| 481 | + |
483 | 482 | try { |
484 | 483 | return jla.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); |
485 | 484 | } catch (CharacterCodingException cce) { |
486 | 485 | throw new AssertionError(cce); |
487 | 486 | } |
488 | 487 | } |
489 | 488 |
|
| 489 | + /** |
| 490 | + * Efficiently converts 8 hexadecimal digits to their ASCII representation using SIMD-style vector operations. |
| 491 | + * This method processes multiple digits in parallel by treating a long value as eight 8-bit lanes, |
| 492 | + * achieving significantly better performance compared to traditional loop-based conversion. |
| 493 | + * |
| 494 | + * <p>The conversion algorithm works as follows: |
| 495 | + * <pre> |
| 496 | + * 1. Input expansion: Each 4-bit hex digit is expanded to 8 bits |
| 497 | + * 2. Vector processing: |
| 498 | + * - Add 6 to each digit: triggers carry flag for a-f digits |
| 499 | + * - Mask with 0x10 pattern to isolate carry flags |
| 500 | + * - Calculate ASCII adjustment: (carry << 1) + (carry >> 1) - (carry >> 4) |
| 501 | + * - Add ASCII '0' base (0x30) and original value |
| 502 | + * 3. Byte order adjustment for final output |
| 503 | + * </pre> |
| 504 | + * |
| 505 | + * <p>Performance characteristics: |
| 506 | + * <ul> |
| 507 | + * <li>Processes 8 digits in parallel using vector operations |
| 508 | + * <li>Avoids branching and loops completely |
| 509 | + * <li>Uses only integer arithmetic and bit operations |
| 510 | + * <li>Constant time execution regardless of input values |
| 511 | + * </ul> |
| 512 | + * |
| 513 | + * <p>ASCII conversion mapping: |
| 514 | + * <ul> |
| 515 | + * <li>Digits 0-9 → ASCII '0'-'9' (0x30-0x39) |
| 516 | + * <li>Digits a-f → ASCII 'a'-'f' (0x61-0x66) |
| 517 | + * </ul> |
| 518 | + * |
| 519 | + * @param input A long containing 8 hex digits (each digit must be 0-15) |
| 520 | + * @return A long containing 8 ASCII bytes representing the hex digits |
| 521 | + * |
| 522 | + * @implNote The implementation leverages CPU vector processing capabilities through |
| 523 | + * long integer operations. The algorithm is based on the observation that |
| 524 | + * ASCII hex digits have a specific pattern that can be computed efficiently |
| 525 | + * using carry flag manipulation. |
| 526 | + * |
| 527 | + * @example |
| 528 | + * <pre> |
| 529 | + * Input: 0xABCDEF01 |
| 530 | + * Output: 3130666564636261 ('1','0','f','e','d','c','b','a' in ASCII) |
| 531 | + * </pre> |
| 532 | + * |
| 533 | + * @see Long#reverseBytes(long) |
| 534 | + */ |
| 535 | + private static long hex8(long i) { |
| 536 | + // Expand each 4-bit group into 8 bits, spreading them out in the long value: 0xAABBCCDD -> 0xA0A0B0B0C0C0D0D |
| 537 | + i = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); |
| 538 | + |
| 539 | + /* |
| 540 | + * This method efficiently converts 8 hexadecimal digits simultaneously using vector operations |
| 541 | + * The algorithm works as follows: |
| 542 | + * |
| 543 | + * For input values 0-15: |
| 544 | + * - For digits 0-9: converts to ASCII '0'-'9' (0x30-0x39) |
| 545 | + * - For digits 10-15: converts to ASCII 'a'-'f' (0x61-0x66) |
| 546 | + * |
| 547 | + * The conversion process: |
| 548 | + * 1. Add 6 to each 4-bit group: i + 0x0606_0606_0606_0606L |
| 549 | + * 2. Mask to get the adjustment flags: & 0x1010_1010_1010_1010L |
| 550 | + * 3. Calculate the offset: (m << 1) + (m >> 1) - (m >> 4) |
| 551 | + * - For 0-9: offset = 0 |
| 552 | + * - For a-f: offset = 39 (to bridge the gap between '9' and 'a' in ASCII) |
| 553 | + * 4. Add ASCII '0' base (0x30) and the original value |
| 554 | + * 5. Reverse byte order for correct positioning |
| 555 | + */ |
| 556 | + long m = (i + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; |
| 557 | + |
| 558 | + // Calculate final ASCII values and reverse bytes for proper ordering |
| 559 | + return Long.reverseBytes( |
| 560 | + ((m << 1) + (m >> 1) - (m >> 4)) |
| 561 | + + 0x3030_3030_3030_3030L // Add ASCII '0' base to all digits |
| 562 | + + i // Add original values |
| 563 | + ); |
| 564 | + } |
| 565 | + |
490 | 566 | /** |
491 | 567 | * Returns a hash code for this {@code UUID}. |
492 | 568 | * |
|
0 commit comments