From 1d746c8688d9d7070939d604556e0dbad7e4f545 Mon Sep 17 00:00:00 2001 From: Jose Gutierrez Date: Tue, 18 Oct 2022 12:16:45 +0200 Subject: [PATCH 1/2] Fix v3 & v5 hashing --- modules/memeid/src/main/java/memeid/Mask.java | 2 +- modules/memeid/src/main/java/memeid/Offset.java | 2 +- modules/memeid/src/test/scala/memeid/V3Spec.scala | 8 ++++++++ modules/memeid/src/test/scala/memeid/V5Spec.scala | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/memeid/src/main/java/memeid/Mask.java b/modules/memeid/src/main/java/memeid/Mask.java index 57c83769..2b79e298 100644 --- a/modules/memeid/src/main/java/memeid/Mask.java +++ b/modules/memeid/src/main/java/memeid/Mask.java @@ -30,7 +30,7 @@ public final class Mask { public final static long UB32 = 0xFFFFFFFFL; - public final static long HASHED = 0x30000000000000L; + public final static long HASHED = 0xC000000000000000L; public final static long V4_LSB = 0xC000000000000000L; diff --git a/modules/memeid/src/main/java/memeid/Offset.java b/modules/memeid/src/main/java/memeid/Offset.java index 0050647e..a0260c71 100644 --- a/modules/memeid/src/main/java/memeid/Offset.java +++ b/modules/memeid/src/main/java/memeid/Offset.java @@ -28,7 +28,7 @@ public final class Offset { public final static long CLOCK_SEQ_LOW = 0L; public final static long CLOCK_SEQ_HIGH = 8L; - public final static long HASHED = 52L; + public final static long HASHED = 62L; public final static long V4_LSB = 62L; diff --git a/modules/memeid/src/test/scala/memeid/V3Spec.scala b/modules/memeid/src/test/scala/memeid/V3Spec.scala index a5d82241..ad61b081 100644 --- a/modules/memeid/src/test/scala/memeid/V3Spec.scala +++ b/modules/memeid/src/test/scala/memeid/V3Spec.scala @@ -54,6 +54,14 @@ class V3Spec extends Specification { uuid1 must not be equalTo(uuid2) } + "create valid v3 UUIDs" in { + val dnsNs = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + val name = "a-thing" + val uuidV3 = UUID.V3.from(dnsNs, name) + + uuidV3 must be equalTo (UUID.fromString("a814fa2d-efaa-32b1-8d0c-3dd50ccaa107")) + } + } } diff --git a/modules/memeid/src/test/scala/memeid/V5Spec.scala b/modules/memeid/src/test/scala/memeid/V5Spec.scala index f9bd9df1..191535e2 100644 --- a/modules/memeid/src/test/scala/memeid/V5Spec.scala +++ b/modules/memeid/src/test/scala/memeid/V5Spec.scala @@ -54,6 +54,14 @@ class V5Spec extends Specification { uuid1 must not be equalTo(uuid2) } + "create valid v5 UUIDs" in { + val dnsNs = UUID.fromString("34c90a22-998c-4676-af7f-64533819985a") + val name = "a-thing" + val uuidV5 = UUID.V5.from(dnsNs, name) + + uuidV5 must be equalTo (UUID.fromString("053d93ac-fcaf-5517-b3cc-18850517ece3")) + } + } } From 164452d7372e8aa4345dde07a22e430808be2837 Mon Sep 17 00:00:00 2001 From: Jose Gutierrez Date: Tue, 18 Oct 2022 12:17:52 +0200 Subject: [PATCH 2/2] Run scalafmt --- modules/memeid/src/main/java/memeid/Bits.java | 106 +- modules/memeid/src/main/java/memeid/Mask.java | 21 +- .../memeid/src/main/java/memeid/Offset.java | 20 +- modules/memeid/src/main/java/memeid/UUID.java | 1419 +++++++++-------- 4 files changed, 796 insertions(+), 770 deletions(-) diff --git a/modules/memeid/src/main/java/memeid/Bits.java b/modules/memeid/src/main/java/memeid/Bits.java index 95e0e1b2..41a72bf4 100644 --- a/modules/memeid/src/main/java/memeid/Bits.java +++ b/modules/memeid/src/main/java/memeid/Bits.java @@ -26,75 +26,75 @@ */ public final class Bits { - /* Convert to a byte array from a Long. */ - public static byte[] toBytes(final long x) { - return new byte[]{ - (byte) (x >> 56), (byte) (x >> 48), (byte) (x >> 40), (byte) (x >> 32), - (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x - }; - } + /* Convert to a byte array from a Long. */ + public static byte[] toBytes(final long x) { + return new byte[] { (byte) (x >> 56), (byte) (x >> 48), (byte) (x >> 40), (byte) (x >> 32), (byte) (x >> 24), + (byte) (x >> 16), (byte) (x >> 8), (byte) x }; + } - /* Convert to a Long from a byte seq. */ - public static long fromBytes(final byte[] bytes) { - assert bytes.length == 8 : "data must be 8 bytes in length"; + /* Convert to a Long from a byte seq. */ + public static long fromBytes(final byte[] bytes) { + assert bytes.length == 8 : "data must be 8 bytes in length"; - long l = 0; + long l = 0; - for (byte aByte : bytes) { - l <<= 8; - l |= aByte & 0xFF; - } + for (byte aByte : bytes) { + l <<= 8; + l |= aByte & 0xFF; + } - return l; - } + return l; + } - /* Read the byte(s) from `num` specified by the `bitmask`. */ - public static long readByte(final long bitmask, final long num) { - final long off = maskOffset(bitmask); + /* Read the byte(s) from `num` specified by the `bitmask`. */ + public static long readByte(final long bitmask, final long num) { + final long off = maskOffset(bitmask); - return (bitmask >>> off) & (num >>> off); - } + return (bitmask >>> off) & (num >>> off); + } - public static long readByte(final long bitmask, final long offset, final long num) { - return (bitmask >>> offset) & (num >>> offset); - } + public static long readByte(final long bitmask, final long offset, final long num) { + return (bitmask >>> offset) & (num >>> offset); + } - /* Write the byte(s) from `value` in `num` using the given `bitmask`. */ - public static long writeByte(final long bitmask, final long num, final long value) { - return (num & ~bitmask) | (bitmask & value << maskOffset(bitmask)); - } + /* Write the byte(s) from `value` in `num` using the given `bitmask`. */ + public static long writeByte(final long bitmask, final long num, final long value) { + return (num & ~bitmask) | (bitmask & value << maskOffset(bitmask)); + } - /* Write the byte(s) from `value` in `num` using the given `bitmask`. */ - public static long writeByte(final long bitmask, final long offset, final long num, final long value) { - return (num & ~bitmask) | (bitmask & value << offset); - } + /* Write the byte(s) from `value` in `num` using the given `bitmask`. */ + public static long writeByte(final long bitmask, final long offset, final long num, final long value) { + return (num & ~bitmask) | (bitmask & value << offset); + } - /* Given a bitmask, retrieve its `offset`. */ - private static long maskOffset(long mask) { - if (mask == 0 || mask == -1) return 0; - else if (mask < 0) { - final long next = -(mask + 1); + /* Given a bitmask, retrieve its `offset`. */ + private static long maskOffset(long mask) { + if (mask == 0 || mask == -1) + return 0; + else if (mask < 0) { + final long next = -(mask + 1); - long offset = offset(next); + long offset = offset(next); - long res = 0; + long res = 0; - while ((1L & (next >> offset >> res)) != 0) { - res++; - } + while ((1L & (next >> offset >> res)) != 0) { + res++; + } - return 64 - (64 - res); - } else return offset(mask); - } + return 64 - (64 - res); + } else + return offset(mask); + } - private static long offset(long x) { - long res = 0; + private static long offset(long x) { + long res = 0; - while ((1L & (x >> res)) <= 0) { - res++; - } + while ((1L & (x >> res)) <= 0) { + res++; + } - return res; - } + return res; + } } diff --git a/modules/memeid/src/main/java/memeid/Mask.java b/modules/memeid/src/main/java/memeid/Mask.java index 2b79e298..058dd199 100644 --- a/modules/memeid/src/main/java/memeid/Mask.java +++ b/modules/memeid/src/main/java/memeid/Mask.java @@ -18,21 +18,22 @@ public final class Mask { - public static final long MASKS_56 = 0xFF00000000000000L; - public static final long MASKS_48 = 0xFF000000000000L; + public static final long MASKS_56 = 0xFF00000000000000L; + public static final long MASKS_48 = 0xFF000000000000L; + public static final long MASKS_64 = 0x8000000000000000L; - public final static long TIME_LOW = 0xFFFFFFFFL; - public final static long TIME_MID = 0xFFFF00000000L; - public final static long TIME_HIGH = 0xFFF000000000000L; + public final static long TIME_LOW = 0xFFFFFFFFL; + public final static long TIME_MID = 0xFFFF00000000L; + public final static long TIME_HIGH = 0xFFF000000000000L; - public final static long CLOCK_SEQ_LOW = 0xFFL; - public final static long CLOCK_SEQ_HIGH = 0x3F00L; + public final static long CLOCK_SEQ_LOW = 0xFFL; + public final static long CLOCK_SEQ_HIGH = 0x3F00L; - public final static long UB32 = 0xFFFFFFFFL; + public final static long UB32 = 0xFFFFFFFFL; public final static long HASHED = 0xC000000000000000L; - public final static long V4_LSB = 0xC000000000000000L; + public final static long V4_LSB = 0xC000000000000000L; - public final static long VERSION = 0xF000L; + public final static long VERSION = 0xF000L; } diff --git a/modules/memeid/src/main/java/memeid/Offset.java b/modules/memeid/src/main/java/memeid/Offset.java index a0260c71..3b9e2ca4 100644 --- a/modules/memeid/src/main/java/memeid/Offset.java +++ b/modules/memeid/src/main/java/memeid/Offset.java @@ -18,20 +18,20 @@ public final class Offset { - public final static long OFFSET_56 = 56L; - public final static long OFFSET_48 = 48L; + public final static long OFFSET_56 = 56L; + public final static long OFFSET_48 = 48L; - public final static long TIME_LOW = 0L; - public final static long TIME_MID = 32L; - public final static long TIME_HIGH = 48L; + public final static long TIME_LOW = 0L; + public final static long TIME_MID = 32L; + public final static long TIME_HIGH = 48L; - public final static long CLOCK_SEQ_LOW = 0L; - public final static long CLOCK_SEQ_HIGH = 8L; + public final static long CLOCK_SEQ_LOW = 0L; + public final static long CLOCK_SEQ_HIGH = 8L; public final static long HASHED = 62L; - public final static long V4_LSB = 62L; + public final static long V4_LSB = 62L; + + public final static long VERSION = 12L; - public final static long VERSION = 12L; - } diff --git a/modules/memeid/src/main/java/memeid/UUID.java b/modules/memeid/src/main/java/memeid/UUID.java index 5b757fb8..47084e13 100644 --- a/modules/memeid/src/main/java/memeid/UUID.java +++ b/modules/memeid/src/main/java/memeid/UUID.java @@ -27,706 +27,731 @@ import static memeid.Bits.*; /** - * A class that represents an immutable universally unique identifier (UUID). - * A UUID represents a 128-bit value. + * A class that represents an immutable universally unique identifier (UUID). A + * UUID represents a 128-bit value. * * @see RFC-4122 */ public class UUID implements Comparable { - public static final byte [] nibbles = new byte[256]; - - static { - java.util.Arrays.fill(nibbles, (byte) -1); - nibbles['0'] = 0; - nibbles['1'] = 1; - nibbles['2'] = 2; - nibbles['3'] = 3; - nibbles['4'] = 4; - nibbles['5'] = 5; - nibbles['6'] = 6; - nibbles['7'] = 7; - nibbles['8'] = 8; - nibbles['9'] = 9; - nibbles['A'] = 10; - nibbles['B'] = 11; - nibbles['C'] = 12; - nibbles['D'] = 13; - nibbles['E'] = 14; - nibbles['F'] = 15; - nibbles['a'] = 10; - nibbles['b'] = 11; - nibbles['c'] = 12; - nibbles['d'] = 13; - nibbles['e'] = 14; - nibbles['f'] = 15; - } - - /** - * The nil UUID is special form of UUID that is specified to have all 128 bits set to zero. - * - * @see RFC-4122 - */ - public static final UUID NIL = new UUID(new java.util.UUID(0, 0)); - - /** - * Creates a valid {@link UUID} from two {@code long} values representing - * the most/least significant bits. - * - * @param msb Most significant bit in {@code long} representation - * @param lsb Least significant bit in {@code long} representation - * @return a new {@link UUID} constructed from msb and lsb - */ - public static UUID from(long msb, long lsb) { - return fromUUID(new java.util.UUID(msb, lsb)); - } - - /** - * Creates a valid {@link UUID} from a {@link java.util.UUID}. - * - * @param juuid the {@link java.util.UUID} - * @return a valid {@link UUID} created from a {@link java.util.UUID} - */ - public static UUID fromUUID(java.util.UUID juuid) { - switch(juuid.version()) { - case 5: return new V5(juuid); - case 4: return new V4(juuid); - case 3: return new V3(juuid); - case 2: return new V2(juuid); - case 1: return new V1(juuid); - case 0: if (juuid == NIL.juuid) return NIL; - default: return new UnknownVersion(juuid); - } - } - - /** - * Creates a {@code UUID} from the string standard representation as - * described in the {@link #toString} method. - * - * @param name A string that specifies a {@code UUID} - * @return A {@code UUID} with the specified value - * @throws IllegalArgumentException If name does not conform to the string representation as - * described in {@link #toString} - */ - public static UUID fromString(String name) { - byte[] ns = nibbles; - if (name.length() == 36 && - name.charAt(8) == '-' && - name.charAt(13) == '-' && - name.charAt(18) == '-' && - name.charAt(23) == '-') { - int msb1 = parse4Nibbles(name, ns, 0); - int msb2 = parse4Nibbles(name, ns, 4); - int msb3 = parse4Nibbles(name, ns, 9); - int msb4 = parse4Nibbles(name, ns, 14); - if ((msb1 | msb2 | msb3 | msb4) >= 0) { - int lsb1 = parse4Nibbles(name, ns, 19); - int lsb2 = parse4Nibbles(name, ns, 24); - int lsb3 = parse4Nibbles(name, ns, 28); - int lsb4 = parse4Nibbles(name, ns, 32); - if ((lsb1 | lsb2 | lsb3 | lsb4) >= 0) return from( - (long) msb1 << 48 | (long) msb2 << 32 | (long) msb3 << 16 | msb4, - (long) lsb1 << 48 | (long) lsb2 << 32 | (long) lsb3 << 16 | lsb4); - } - } - throw new IllegalArgumentException("Invalid UUID string: " + name); - } - - private static int parse4Nibbles(String name, byte[] ns, int pos) { - char ch1 = name.charAt(pos); - char ch2 = name.charAt(pos + 1); - char ch3 = name.charAt(pos + 2); - char ch4 = name.charAt(pos + 3); - return (ch1 | ch2 | ch3 | ch4) > 0xFF ? -1 : ns[ch1] << 12 | ns[ch2] << 8 | ns[ch3] << 4 | ns[ch4]; - } - - /** - * Returns the most significant 64 bits of this UUID's 128 bit value. - * - * @return The most significant 64 bits of this UUID's 128 bit value - */ - public long getMostSignificantBits() { - return this.juuid.getMostSignificantBits(); - } - - /** - * Returns the most significant 64 bits of this UUID's 128 bit value. - * - * @return The most significant 64 bits of this UUID's 128 bit value - */ - public long getLeastSignificantBits() { - return this.juuid.getLeastSignificantBits(); - } - - /** - * The variant field determines the layout of the {@link UUID}. - *

- * The variant field consists of a variable number of - * the most significant bits of octet 8 of the {@link UUID}. - *

- * The variant number has the following meaning: - * - *

    - *
  • '''0''': Reserved for NCS backward compatibility
  • - *
  • '''2''': RFC-4122
  • - *
  • '''6''': Reserved, Microsoft Corporation backward compatibility
  • - *
  • '''7''': Reserved for future definition
  • - *
- *

- * Interoperability, in any form, with variants other than the one - * defined here is not guaranteed, and is not likely to be an issue in - * practice. - * - * @return The variant of this {@link UUID} - * @see RFC-4122 - */ - public int variant() { - return this.juuid.variant(); - } - - /** - * The version number associated with this {@link UUID}. The version - * number describes how this {@link UUID} was generated. - *

- * The version number has the following meaning: - * - *

    - *
  • '''1''': Time-based UUID
  • - *
  • '''2''': DCE security UUID
  • - *
  • '''3''': Name-based UUID
  • - *
  • '''4''': Randomly generated UUID
  • - *
  • '''5''': The name-based version that uses SHA-1 hashing
  • - *
- * - * @return The version number of this {@link UUID} - * @see RFC-4122 - */ - public int version() { - return this.juuid.version(); - } - - @Override - public boolean equals(Object obj) { - if ((obj == null) || !(UUID.class.isAssignableFrom(obj.getClass()))) - return false; - - return compareTo((UUID) obj) == 0; - } - - @Override - public int compareTo(UUID o) { - // When versions differ, we sort UUIDs by version - int v = this.version(); - int maybeDifferentVersions = v - o.version(); - if (maybeDifferentVersions != 0) { - return maybeDifferentVersions; - } - // For V1 UUIDs, we sort them by their fields - if (v == 1) { - // first, compare the the 60-bit timestamp - int timeComparison = Long.compareUnsigned(this.juuid.timestamp(), o.juuid.timestamp()); - - // the rest of the fields (clock sequence & node ID) are in the LSB so we just compare that - if (timeComparison == 0) { - return Long.compareUnsigned(getLeastSignificantBits(), o.getLeastSignificantBits()); - } else return timeComparison; - } else { - // For any other UUID we order lexicographically - int comparison = Long.compareUnsigned(getMostSignificantBits(), o.getMostSignificantBits()); - - if (comparison == 0) { - return Long.compareUnsigned(getLeastSignificantBits(), o.getLeastSignificantBits()); - } else return comparison; - } - } - - @Override - public int hashCode() { - return this.juuid.hashCode(); - } - - /** - * Returns a {@code String} object representing this {@code UUID}. - * - *

The UUID string representation is as described by this BNF: - *

-     * {@code
-     * UUID                   =  "-"  "-"
-     *                           "-"
-     *                           "-"
-     *                          
-     * time_low               = 4*
-     * time_mid               = 2*
-     * time_high_and_version  = 2*
-     * variant_and_sequence   = 2*
-     * node                   = 6*
-     * hexOctet               = 
-     * hexDigit               =
-     *       "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
-     *       | "a" | "b" | "c" | "d" | "e" | "f"
-     *       | "A" | "B" | "C" | "D" | "E" | "F"
-     * }
- * - * @return A string representation of this {@code UUID} - */ - @Override - public String toString() { - return this.juuid.toString(); - } - - /** - * Returns this {@link UUID} as a {@link java.util.UUID}. - * - * @return this {@link UUID} as a {@link java.util.UUID} - */ - public java.util.UUID asJava() { - return this.juuid; - } - - /** - * Returns this {@link UUID} as a {@link V1} if versions match; - * otherwise, returns {@link Optional#empty}. - * - * @return this {@link UUID} as a {@link V1} if versions match; - * otherwise, returns {@link Optional#empty}. - */ - public Optional asV1() { - if (isV1()) return Optional.of((V1) this); - else return Optional.empty(); - } - - /** - * Returns this {@link UUID} as a {@link V2} if versions match; - * otherwise, returns {@link Optional#empty}. - * - * @return this {@link UUID} as a {@link V2} if versions match; - * otherwise, returns {@link Optional#empty}. - */ - public Optional asV2() { - if (isV2()) return Optional.of((V2) this); - else return Optional.empty(); - } - - /** - * Returns this {@link UUID} as a {@link V3} if versions match; - * otherwise, returns {@link Optional#empty}. - * - * @return this {@link UUID} as a {@link V3} if versions match; - * otherwise, returns {@link Optional#empty}. - */ - public Optional asV3() { - if (isV3()) return Optional.of((V3) this); - else return Optional.empty(); - } - - /** - * Returns this {@link UUID} as a {@link V4} if versions match; - * otherwise, returns {@link Optional#empty}. - * - * @return this {@link UUID} as a {@link V4} if versions match; - * otherwise, returns {@link Optional#empty}. - */ - public Optional asV4() { - if (isV4()) return Optional.of((V4) this); - else return Optional.empty(); - } - - /** - * Returns this {@link UUID} as a {@link V5} if versions match; - * otherwise, returns {@link Optional#empty}. - * - * @return this {@link UUID} as a {@link V5} if versions match; - * otherwise, returns {@link Optional#empty}. - */ - public Optional asV5() { - if (isV5()) return Optional.of((V5) this); - else return Optional.empty(); - } - - /** - * Returns {@code true} if this UUID is a - * NIL UUID; otherwise, - * returns {@code false}. - * - * @return {@code true} if this {@link UUID} is a {@code NIL UUID}; {@code false} otherwise - */ - public boolean isNil() { - return this == NIL; - } - - /** - * Returns {@code true} if this UUID is a {@link V1}; otherwise, - * returns {@code false}. - * - * @return {@code true} if this {@link UUID} is a {@link V1}; {@code false} otherwise - */ - public boolean isV1() { - return this instanceof V1; - } - - /** - * Returns {@code true} if this UUID is a {@link V2}; otherwise, - * returns {@code false}. - * - * @return {@code true} if this {@link UUID} is a {@link V2}; {@code false} otherwise - */ - public boolean isV2() { - return this instanceof V2; - } - - /** - * Returns {@code true} if this UUID is a {@link V3}; otherwise, - * returns {@code false}. - * - * @return {@code true} if this {@link UUID} is a {@link V3}; {@code false} otherwise - */ - public boolean isV3() { - return this instanceof V3; - } - - /** - * Returns {@code true} if this UUID is a {@link V4}; otherwise, - * returns {@code false}. - * - * @return {@code true} if this {@link UUID} is a {@link V4}; {@code false} otherwise - */ - public boolean isV4() { - return this instanceof V4; - } - - /** - * Returns {@code true} if this UUID is a {@link V5}; otherwise, - * returns {@code false}. - * - * @return {@code true} if this {@link UUID} is a {@link V5}; {@code false} otherwise - */ - public boolean isV5() { - return this instanceof V5; - } - - - /** - * Version 1 UUIDs are those generated using a timestamp and the MAC address of the - * computer on which it was generated. - * - * @see RFC-4122 - */ - public final static class V1 extends UUID { - - /** - * Get the time_low component of the timestamp field - * - * @return time_low component of the timestamp field - * @see RFC-4122 - */ - public final long timeLow() { - return Bits.readByte(Mask.TIME_LOW, Offset.TIME_LOW, this.asJava().timestamp()); - } - - /** - * Get the time_mid component of the timestamp field - * - * @return time_mid component of the timestamp field - * @see RFC-4122 - */ - public final long timeMid() { - return Bits.readByte(Mask.TIME_MID, Offset.TIME_MID, this.asJava().timestamp()); - } - - /** - * Get the time_high component of the timestamp field - * - * @return time_high component of the timestamp field - * @see RFC-4122 - */ - public final long timeHigh() { - return Bits.readByte(Mask.TIME_HIGH, Offset.TIME_HIGH, this.asJava().timestamp()); - } - - /** - * Get the clock_seq_low component of the clock sequence field - * - * @return clock_seq_low component of the clock sequence field - * @see RFC-4122 - */ - public final long clockSeqLow() { - return Bits.readByte(Mask.CLOCK_SEQ_LOW, Offset.CLOCK_SEQ_LOW, this.asJava().clockSequence()); - } - - /** - * Get the clock_seq_high component of the clock sequence field - * - * @return clock_seq_high component of the clock sequence field - * @see RFC-4122 - */ - public final long clockSeqHigh() { - return Bits.readByte(Mask.CLOCK_SEQ_HIGH, Offset.CLOCK_SEQ_HIGH, this.asJava().clockSequence()); - } - - private V1(java.util.UUID uuid) { - super(uuid); - } - - /** - * Constructs a time-based {@link V1} UUID using the default {@link Node} - * and {@link Timestamp#monotonic()} as the monotonic timestamp supplier. - * - * @return a {@link V1} UUID - */ - public static UUID next() { - return next(Node.getInstance()); - } - - /** - * Constructs a time-based {@link V1} UUID using the provided {@link Node} - * and {@link Timestamp#monotonic()} as the monotonic timestamp supplier. - * - * @param node Node for the V1 UUID generation - * @return a {@link V1} UUID - */ - public static UUID next(Node node) { - return next(node, Timestamp::monotonic); - } - - /** - * Constructs a time-based {@link V1} UUID using the provided {@link Node} and - * monotonic timestamp supplier. - * - * @param node node for the V1 UUID generation - * @param monotonicSupplier monotonic timestamp which assures the V1 UUID time is unique - * @return a {@link V1} UUID - */ - public static UUID next(Node node, LongSupplier monotonicSupplier) { - final long timestamp = monotonicSupplier.getAsLong(); - final long low = readByte(Mask.TIME_LOW, Offset.TIME_LOW, timestamp); - final long mid = readByte(Mask.TIME_MID, Offset.TIME_MID, timestamp); - final long high = readByte(Mask.TIME_HIGH, Offset.TIME_HIGH, timestamp); - final long msb = writeByte(Mask.VERSION, Offset.VERSION, high, 1) | (low << 32) | (mid << 16); - - final long clkHigh = writeByte( - Mask.CLOCK_SEQ_HIGH, Offset.CLOCK_SEQ_HIGH, - readByte(Mask.CLOCK_SEQ_HIGH, Offset.CLOCK_SEQ_HIGH, node.id), 0x2); - - final long clkLow = readByte(Mask.CLOCK_SEQ_LOW, Offset.CLOCK_SEQ_LOW, node.clockSequence); - - final long lsb = writeByte( - Mask.MASKS_56, - Offset.OFFSET_56, - writeByte(Mask.MASKS_48, Offset.OFFSET_48, node.id, clkLow), - clkHigh - ); - - return new UUID.V1(new java.util.UUID(msb, lsb)); - } - - } - - - /** - * DCE Security version, with embedded POSIX UIDs. - * - * @see RFC-4122 - */ - public final static class V2 extends UUID { - - private V2(java.util.UUID uuid) { - super(uuid); - } - - } - - /** - * Version 3 UUIDs are those generated by hashing a namespace identifier and name using - * MD5 as the hashing algorithm. - * - * @see RFC-4122 - */ - public final static class V3 extends UUID { - - /** - * Construct a namespace name-based {@link V3} UUID. Uses MD5 as a hash algorithm - * - * @param namespace {@link UUID} used for the {@link V3} generation - * @param name name used for the {@link V3} generation in string format - * @return a {@link V3} UUID - */ - public static UUID from(UUID namespace, String name) { - return from(namespace, name, String::getBytes); - } - - /** - * Construct a namespace name-based {@link V3} UUID. Uses MD5 as a hash algorithm - * - * @param namespace {@link UUID} used for the {@link V3} generation - * @param name name used for the {@link V3} generation - * @param nameToBytes function used to convert the name to a byte array - * @param the type of the name parameter - * @return a {@link V3} UUID - */ - public static UUID from(UUID namespace, A name, Function nameToBytes) { - MessageDigest md; - - try { - md = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException nsae) { - throw new InternalError("MD5 not supported", nsae); - } - - md.update(toBytes(namespace.getMostSignificantBits())); - md.update(toBytes(namespace.getLeastSignificantBits())); - md.update(nameToBytes.apply(name)); - - byte[] bytes = md.digest(); - - long rawMsb = fromBytes(Arrays.copyOfRange(bytes, 0, 8)); - long rawLsb = fromBytes(Arrays.copyOfRange(bytes, 8, 16)); - - long msb = writeByte(Mask.VERSION, Offset.VERSION, rawMsb, 3); - long lsb = writeByte(Mask.HASHED, Offset.HASHED, rawLsb, 0x2); - - return new V3(new java.util.UUID(msb, lsb)); - } - - private V3(java.util.UUID uuid) { - super(uuid); - } - - } - - - /** - * Version 4 UUIDs are those generated using random numbers. - * - * @see RFC-4122 - */ - public final static class V4 extends UUID { - - /** - * Construct a {@link V4} (random) UUID from the given `msb` and `lsb`. - * - * @param msb Most significant bit in long representation - * @param lsb Least significant bit in long representation - */ - public static UUID from(long msb, long lsb) { - return new UUID.V4(new java.util.UUID( - writeByte(Mask.VERSION, Offset.VERSION, msb, 0x4), - writeByte(Mask.V4_LSB, Offset.V4_LSB, lsb, 0x2))); - } - - /** - * Construct a {@link V4} random UUID. - * - * @return the {@link V4} random UUID - */ - public static UUID random() { - return new V4(java.util.UUID.randomUUID()); - } - - /** - * Constructs a SQUUID (random, time-based) {@link V4} UUID. - * - * @param posix the posix timestamp to be used to construct the UUID - * @return a {@link V4} SQUUID. - */ - public static UUID squuid(long posix) { - final UUID uuid = random(); - - final long msb = (posix << 32) | (uuid.getMostSignificantBits() & Mask.UB32); - - return new UUID.V4(new java.util.UUID(msb, uuid.getLeastSignificantBits())); - } - - /** - * Constructs a SQUUID (random, time-based) {@link V4} UUID using - * the result of {@link System#currentTimeMillis()} as posix timestamp. - * - * @return a {@link V4} SQUUID. - */ - public static UUID squuid() { - return squuid(MILLISECONDS.toSeconds(System.currentTimeMillis())); - } - - private V4(java.util.UUID uuid) { - super(uuid); - } - - } - - - /** - * Version 5 UUIDs are those generated by hashing a namespace identifier and name using - * SHA-1 as the hashing algorithm. - * - * @see RFC-4122 - */ - public final static class V5 extends UUID { - - /** - * Construct a namespace name-based {@link V5} UUID. Uses SHA1 as a hash algorithm - * - * @param namespace {@link UUID} used for the {@link V5} generation - * @param name name used for the {@link V5} generation in string format - * @return a {@link V5} UUID - */ - public static UUID from(UUID namespace, String name) { - return from(namespace, name, String::getBytes); - } - - /** - * Construct a namespace name-based {@link V5} UUID. Uses MD5 as a hash algorithm - * - * @param namespace {@link UUID} used for the {@link V5} generation - * @param name name used for the {@link V5} generation - * @param nameToBytes function used to convert the name to a byte array - * @param the type of the name parameter - * @return a {@link V5} UUID - */ - public static UUID from(UUID namespace, A name, Function nameToBytes) { - MessageDigest md; - - try { - md = MessageDigest.getInstance("SHA1"); - } catch (NoSuchAlgorithmException nsae) { - throw new InternalError("SHA1 not supported", nsae); - } - - md.update(toBytes(namespace.getMostSignificantBits())); - md.update(toBytes(namespace.getLeastSignificantBits())); - md.update(nameToBytes.apply(name)); - byte[] bytes = md.digest(); - - long rawMsb = fromBytes(Arrays.copyOfRange(bytes, 0, 8)); - long rawLsb = fromBytes(Arrays.copyOfRange(bytes, 8, 16)); - - long msb = writeByte(Mask.VERSION, Offset.VERSION, rawMsb, 5); - long lsb = writeByte(Mask.HASHED, Offset.HASHED, rawLsb, 0x2); - - return new V5(new java.util.UUID(msb, lsb)); - } - - private V5(java.util.UUID uuid) { - super(uuid); - } - - } - - - /** - * Not standard-version UUIDs. - * - * @see RFC-4122 - */ - public final static class UnknownVersion extends UUID { - - private UnknownVersion(java.util.UUID uuid) { - super(uuid); - } - - } - - private final java.util.UUID juuid; - - private UUID(java.util.UUID juuid) { - this.juuid = juuid; - } + public static final byte[] nibbles = new byte[256]; + + static { + java.util.Arrays.fill(nibbles, (byte) -1); + nibbles['0'] = 0; + nibbles['1'] = 1; + nibbles['2'] = 2; + nibbles['3'] = 3; + nibbles['4'] = 4; + nibbles['5'] = 5; + nibbles['6'] = 6; + nibbles['7'] = 7; + nibbles['8'] = 8; + nibbles['9'] = 9; + nibbles['A'] = 10; + nibbles['B'] = 11; + nibbles['C'] = 12; + nibbles['D'] = 13; + nibbles['E'] = 14; + nibbles['F'] = 15; + nibbles['a'] = 10; + nibbles['b'] = 11; + nibbles['c'] = 12; + nibbles['d'] = 13; + nibbles['e'] = 14; + nibbles['f'] = 15; + } + + /** + * The nil UUID is special form of UUID that is specified to have all 128 bits + * set to zero. + * + * @see RFC-4122 + */ + public static final UUID NIL = new UUID(new java.util.UUID(0, 0)); + + /** + * Creates a valid {@link UUID} from two {@code long} values representing the + * most/least significant bits. + * + * @param msb Most significant bit in {@code long} representation + * @param lsb Least significant bit in {@code long} representation + * @return a new {@link UUID} constructed from msb and lsb + */ + public static UUID from(long msb, long lsb) { + return fromUUID(new java.util.UUID(msb, lsb)); + } + + /** + * Creates a valid {@link UUID} from a {@link java.util.UUID}. + * + * @param juuid the {@link java.util.UUID} + * @return a valid {@link UUID} created from a {@link java.util.UUID} + */ + public static UUID fromUUID(java.util.UUID juuid) { + switch (juuid.version()) { + case 5: + return new V5(juuid); + case 4: + return new V4(juuid); + case 3: + return new V3(juuid); + case 2: + return new V2(juuid); + case 1: + return new V1(juuid); + case 0: + if (juuid == NIL.juuid) + return NIL; + default: + return new UnknownVersion(juuid); + } + } + + /** + * Creates a {@code UUID} from the string standard representation as described + * in the {@link #toString} method. + * + * @param name A string that specifies a {@code UUID} + * @return A {@code UUID} with the specified value + * @throws IllegalArgumentException If name does not conform to the string + * representation as described in + * {@link #toString} + */ + public static UUID fromString(String name) { + byte[] ns = nibbles; + if (name.length() == 36 && name.charAt(8) == '-' && name.charAt(13) == '-' && name.charAt(18) == '-' + && name.charAt(23) == '-') { + int msb1 = parse4Nibbles(name, ns, 0); + int msb2 = parse4Nibbles(name, ns, 4); + int msb3 = parse4Nibbles(name, ns, 9); + int msb4 = parse4Nibbles(name, ns, 14); + if ((msb1 | msb2 | msb3 | msb4) >= 0) { + int lsb1 = parse4Nibbles(name, ns, 19); + int lsb2 = parse4Nibbles(name, ns, 24); + int lsb3 = parse4Nibbles(name, ns, 28); + int lsb4 = parse4Nibbles(name, ns, 32); + if ((lsb1 | lsb2 | lsb3 | lsb4) >= 0) + return from((long) msb1 << 48 | (long) msb2 << 32 | (long) msb3 << 16 | msb4, + (long) lsb1 << 48 | (long) lsb2 << 32 | (long) lsb3 << 16 | lsb4); + } + } + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + + private static int parse4Nibbles(String name, byte[] ns, int pos) { + char ch1 = name.charAt(pos); + char ch2 = name.charAt(pos + 1); + char ch3 = name.charAt(pos + 2); + char ch4 = name.charAt(pos + 3); + return (ch1 | ch2 | ch3 | ch4) > 0xFF ? -1 : ns[ch1] << 12 | ns[ch2] << 8 | ns[ch3] << 4 | ns[ch4]; + } + + /** + * Returns the most significant 64 bits of this UUID's 128 bit value. + * + * @return The most significant 64 bits of this UUID's 128 bit value + */ + public long getMostSignificantBits() { + return this.juuid.getMostSignificantBits(); + } + + /** + * Returns the most significant 64 bits of this UUID's 128 bit value. + * + * @return The most significant 64 bits of this UUID's 128 bit value + */ + public long getLeastSignificantBits() { + return this.juuid.getLeastSignificantBits(); + } + + /** + * The variant field determines the layout of the {@link UUID}. + *

+ * The variant field consists of a variable number of the most significant bits + * of octet 8 of the {@link UUID}. + *

+ * The variant number has the following meaning: + * + *

    + *
  • '''0''': Reserved for NCS backward compatibility
  • + *
  • '''2''': + * RFC-4122
  • + *
  • '''6''': Reserved, Microsoft Corporation backward compatibility
  • + *
  • '''7''': Reserved for future definition
  • + *
+ *

+ * Interoperability, in any form, with variants other than the one defined here + * is not guaranteed, and is not likely to be an issue in practice. + * + * @return The variant of this {@link UUID} + * @see RFC-4122 + */ + public int variant() { + return this.juuid.variant(); + } + + /** + * The version number associated with this {@link UUID}. The version number + * describes how this {@link UUID} was generated. + *

+ * The version number has the following meaning: + * + *

    + *
  • '''1''': Time-based UUID
  • + *
  • '''2''': DCE security UUID
  • + *
  • '''3''': Name-based UUID
  • + *
  • '''4''': Randomly generated UUID
  • + *
  • '''5''': The name-based version that uses SHA-1 hashing
  • + *
+ * + * @return The version number of this {@link UUID} + * @see RFC-4122 + */ + public int version() { + return this.juuid.version(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(UUID.class.isAssignableFrom(obj.getClass()))) + return false; + + return compareTo((UUID) obj) == 0; + } + + @Override + public int compareTo(UUID o) { + // When versions differ, we sort UUIDs by version + int v = this.version(); + int maybeDifferentVersions = v - o.version(); + if (maybeDifferentVersions != 0) { + return maybeDifferentVersions; + } + // For V1 UUIDs, we sort them by their fields + if (v == 1) { + // first, compare the the 60-bit timestamp + int timeComparison = Long.compareUnsigned(this.juuid.timestamp(), o.juuid.timestamp()); + + // the rest of the fields (clock sequence & node ID) are in the LSB so we just + // compare that + if (timeComparison == 0) { + return Long.compareUnsigned(getLeastSignificantBits(), o.getLeastSignificantBits()); + } else + return timeComparison; + } else { + // For any other UUID we order lexicographically + int comparison = Long.compareUnsigned(getMostSignificantBits(), o.getMostSignificantBits()); + + if (comparison == 0) { + return Long.compareUnsigned(getLeastSignificantBits(), o.getLeastSignificantBits()); + } else + return comparison; + } + } + + @Override + public int hashCode() { + return this.juuid.hashCode(); + } + + /** + * Returns a {@code String} object representing this {@code UUID}. + * + *

+ * The UUID string representation is as described by this BNF:

+ * + *
+	 * {@code
+	 * UUID                   =  "-"  "-"
+	 *                           "-"
+	 *                           "-"
+	 *                          
+	 * time_low               = 4*
+	 * time_mid               = 2*
+	 * time_high_and_version  = 2*
+	 * variant_and_sequence   = 2*
+	 * node                   = 6*
+	 * hexOctet               = 
+	 * hexDigit               =
+	 *       "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+	 *       | "a" | "b" | "c" | "d" | "e" | "f"
+	 *       | "A" | "B" | "C" | "D" | "E" | "F"
+	 * }
+	 * 
+ * + *
+ * + * @return A string representation of this {@code UUID} + */ + @Override + public String toString() { + return this.juuid.toString(); + } + + /** + * Returns this {@link UUID} as a {@link java.util.UUID}. + * + * @return this {@link UUID} as a {@link java.util.UUID} + */ + public java.util.UUID asJava() { + return this.juuid; + } + + /** + * Returns this {@link UUID} as a {@link V1} if versions match; otherwise, + * returns {@link Optional#empty}. + * + * @return this {@link UUID} as a {@link V1} if versions match; otherwise, + * returns {@link Optional#empty}. + */ + public Optional asV1() { + if (isV1()) + return Optional.of((V1) this); + else + return Optional.empty(); + } + + /** + * Returns this {@link UUID} as a {@link V2} if versions match; otherwise, + * returns {@link Optional#empty}. + * + * @return this {@link UUID} as a {@link V2} if versions match; otherwise, + * returns {@link Optional#empty}. + */ + public Optional asV2() { + if (isV2()) + return Optional.of((V2) this); + else + return Optional.empty(); + } + + /** + * Returns this {@link UUID} as a {@link V3} if versions match; otherwise, + * returns {@link Optional#empty}. + * + * @return this {@link UUID} as a {@link V3} if versions match; otherwise, + * returns {@link Optional#empty}. + */ + public Optional asV3() { + if (isV3()) + return Optional.of((V3) this); + else + return Optional.empty(); + } + + /** + * Returns this {@link UUID} as a {@link V4} if versions match; otherwise, + * returns {@link Optional#empty}. + * + * @return this {@link UUID} as a {@link V4} if versions match; otherwise, + * returns {@link Optional#empty}. + */ + public Optional asV4() { + if (isV4()) + return Optional.of((V4) this); + else + return Optional.empty(); + } + + /** + * Returns this {@link UUID} as a {@link V5} if versions match; otherwise, + * returns {@link Optional#empty}. + * + * @return this {@link UUID} as a {@link V5} if versions match; otherwise, + * returns {@link Optional#empty}. + */ + public Optional asV5() { + if (isV5()) + return Optional.of((V5) this); + else + return Optional.empty(); + } + + /** + * Returns {@code true} if this UUID is a + * NIL UUID; + * otherwise, returns {@code false}. + * + * @return {@code true} if this {@link UUID} is a {@code NIL UUID}; + * {@code false} otherwise + */ + public boolean isNil() { + return this == NIL; + } + + /** + * Returns {@code true} if this UUID is a {@link V1}; otherwise, returns + * {@code false}. + * + * @return {@code true} if this {@link UUID} is a {@link V1}; {@code false} + * otherwise + */ + public boolean isV1() { + return this instanceof V1; + } + + /** + * Returns {@code true} if this UUID is a {@link V2}; otherwise, returns + * {@code false}. + * + * @return {@code true} if this {@link UUID} is a {@link V2}; {@code false} + * otherwise + */ + public boolean isV2() { + return this instanceof V2; + } + + /** + * Returns {@code true} if this UUID is a {@link V3}; otherwise, returns + * {@code false}. + * + * @return {@code true} if this {@link UUID} is a {@link V3}; {@code false} + * otherwise + */ + public boolean isV3() { + return this instanceof V3; + } + + /** + * Returns {@code true} if this UUID is a {@link V4}; otherwise, returns + * {@code false}. + * + * @return {@code true} if this {@link UUID} is a {@link V4}; {@code false} + * otherwise + */ + public boolean isV4() { + return this instanceof V4; + } + + /** + * Returns {@code true} if this UUID is a {@link V5}; otherwise, returns + * {@code false}. + * + * @return {@code true} if this {@link UUID} is a {@link V5}; {@code false} + * otherwise + */ + public boolean isV5() { + return this instanceof V5; + } + + /** + * Version 1 UUIDs are those generated using a timestamp and the MAC address of + * the computer on which it was generated. + * + * @see RFC-4122 + */ + public final static class V1 extends UUID { + + /** + * Get the time_low component of the timestamp field + * + * @return time_low component of the timestamp field + * @see RFC-4122 + */ + public final long timeLow() { + return Bits.readByte(Mask.TIME_LOW, Offset.TIME_LOW, this.asJava().timestamp()); + } + + /** + * Get the time_mid component of the timestamp field + * + * @return time_mid component of the timestamp field + * @see RFC-4122 + */ + public final long timeMid() { + return Bits.readByte(Mask.TIME_MID, Offset.TIME_MID, this.asJava().timestamp()); + } + + /** + * Get the time_high component of the timestamp field + * + * @return time_high component of the timestamp field + * @see RFC-4122 + */ + public final long timeHigh() { + return Bits.readByte(Mask.TIME_HIGH, Offset.TIME_HIGH, this.asJava().timestamp()); + } + + /** + * Get the clock_seq_low component of the clock sequence field + * + * @return clock_seq_low component of the clock sequence field + * @see RFC-4122 + */ + public final long clockSeqLow() { + return Bits.readByte(Mask.CLOCK_SEQ_LOW, Offset.CLOCK_SEQ_LOW, this.asJava().clockSequence()); + } + + /** + * Get the clock_seq_high component of the clock sequence field + * + * @return clock_seq_high component of the clock sequence field + * @see RFC-4122 + */ + public final long clockSeqHigh() { + return Bits.readByte(Mask.CLOCK_SEQ_HIGH, Offset.CLOCK_SEQ_HIGH, this.asJava().clockSequence()); + } + + private V1(java.util.UUID uuid) { + super(uuid); + } + + /** + * Constructs a time-based {@link V1} UUID using the default {@link Node} and + * {@link Timestamp#monotonic()} as the monotonic timestamp supplier. + * + * @return a {@link V1} UUID + */ + public static UUID next() { + return next(Node.getInstance()); + } + + /** + * Constructs a time-based {@link V1} UUID using the provided {@link Node} and + * {@link Timestamp#monotonic()} as the monotonic timestamp supplier. + * + * @param node Node for the V1 UUID generation + * @return a {@link V1} UUID + */ + public static UUID next(Node node) { + return next(node, Timestamp::monotonic); + } + + /** + * Constructs a time-based {@link V1} UUID using the provided {@link Node} and + * monotonic timestamp supplier. + * + * @param node node for the V1 UUID generation + * @param monotonicSupplier monotonic timestamp which assures the V1 UUID time + * is unique + * @return a {@link V1} UUID + */ + public static UUID next(Node node, LongSupplier monotonicSupplier) { + final long timestamp = monotonicSupplier.getAsLong(); + final long low = readByte(Mask.TIME_LOW, Offset.TIME_LOW, timestamp); + final long mid = readByte(Mask.TIME_MID, Offset.TIME_MID, timestamp); + final long high = readByte(Mask.TIME_HIGH, Offset.TIME_HIGH, timestamp); + final long msb = writeByte(Mask.VERSION, Offset.VERSION, high, 1) | (low << 32) | (mid << 16); + + final long clkHigh = writeByte(Mask.CLOCK_SEQ_HIGH, Offset.CLOCK_SEQ_HIGH, + readByte(Mask.CLOCK_SEQ_HIGH, Offset.CLOCK_SEQ_HIGH, node.id), 0x2); + + final long clkLow = readByte(Mask.CLOCK_SEQ_LOW, Offset.CLOCK_SEQ_LOW, node.clockSequence); + + final long lsb = writeByte(Mask.MASKS_56, Offset.OFFSET_56, + writeByte(Mask.MASKS_48, Offset.OFFSET_48, node.id, clkLow), clkHigh); + + return new UUID.V1(new java.util.UUID(msb, lsb)); + } + + } + + /** + * DCE Security version, with embedded POSIX UIDs. + * + * @see RFC-4122 + */ + public final static class V2 extends UUID { + + private V2(java.util.UUID uuid) { + super(uuid); + } + + } + + /** + * Version 3 UUIDs are those generated by hashing a namespace identifier and + * name using MD5 as the hashing algorithm. + * + * @see RFC-4122 + */ + public final static class V3 extends UUID { + + /** + * Construct a namespace name-based {@link V3} UUID. Uses MD5 as a hash + * algorithm + * + * @param namespace {@link UUID} used for the {@link V3} generation + * @param name name used for the {@link V3} generation in string format + * @return a {@link V3} UUID + */ + public static UUID from(UUID namespace, String name) { + return from(namespace, name, String::getBytes); + } + + /** + * Construct a namespace name-based {@link V3} UUID. Uses MD5 as a hash + * algorithm + * + * @param namespace {@link UUID} used for the {@link V3} generation + * @param name name used for the {@link V3} generation + * @param nameToBytes function used to convert the name to a byte array + * @param the type of the name parameter + * @return a {@link V3} UUID + */ + public static UUID from(UUID namespace, A name, Function nameToBytes) { + MessageDigest md; + + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError("MD5 not supported", nsae); + } + + md.update(toBytes(namespace.getMostSignificantBits())); + md.update(toBytes(namespace.getLeastSignificantBits())); + md.update(nameToBytes.apply(name)); + + byte[] bytes = md.digest(); + + long rawMsb = fromBytes(Arrays.copyOfRange(bytes, 0, 8)); + long rawLsb = fromBytes(Arrays.copyOfRange(bytes, 8, 16)); + + long msb = writeByte(Mask.VERSION, Offset.VERSION, rawMsb, 3); + long lsb = writeByte(Mask.HASHED, Offset.HASHED, rawLsb, 0x2); + + return new V3(new java.util.UUID(msb, lsb)); + } + + private V3(java.util.UUID uuid) { + super(uuid); + } + + } + + /** + * Version 4 UUIDs are those generated using random numbers. + * + * @see RFC-4122 + */ + public final static class V4 extends UUID { + + /** + * Construct a {@link V4} (random) UUID from the given `msb` and `lsb`. + * + * @param msb Most significant bit in long representation + * @param lsb Least significant bit in long representation + */ + public static UUID from(long msb, long lsb) { + return new UUID.V4(new java.util.UUID(writeByte(Mask.VERSION, Offset.VERSION, msb, 0x4), + writeByte(Mask.V4_LSB, Offset.V4_LSB, lsb, 0x2))); + } + + /** + * Construct a {@link V4} random UUID. + * + * @return the {@link V4} random UUID + */ + public static UUID random() { + return new V4(java.util.UUID.randomUUID()); + } + + /** + * Constructs a SQUUID (random, time-based) {@link V4} UUID. + * + * @param posix the posix timestamp to be used to construct the UUID + * @return a {@link V4} SQUUID. + */ + public static UUID squuid(long posix) { + final UUID uuid = random(); + + final long msb = (posix << 32) | (uuid.getMostSignificantBits() & Mask.UB32); + + return new UUID.V4(new java.util.UUID(msb, uuid.getLeastSignificantBits())); + } + + /** + * Constructs a SQUUID (random, time-based) {@link V4} UUID using the result of + * {@link System#currentTimeMillis()} as posix timestamp. + * + * @return a {@link V4} SQUUID. + */ + public static UUID squuid() { + return squuid(MILLISECONDS.toSeconds(System.currentTimeMillis())); + } + + private V4(java.util.UUID uuid) { + super(uuid); + } + + } + + /** + * Version 5 UUIDs are those generated by hashing a namespace identifier and + * name using SHA-1 as the hashing algorithm. + * + * @see RFC-4122 + */ + public final static class V5 extends UUID { + + /** + * Construct a namespace name-based {@link V5} UUID. Uses SHA1 as a hash + * algorithm + * + * @param namespace {@link UUID} used for the {@link V5} generation + * @param name name used for the {@link V5} generation in string format + * @return a {@link V5} UUID + */ + public static UUID from(UUID namespace, String name) { + return from(namespace, name, String::getBytes); + } + + /** + * Construct a namespace name-based {@link V5} UUID. Uses MD5 as a hash + * algorithm + * + * @param namespace {@link UUID} used for the {@link V5} generation + * @param name name used for the {@link V5} generation + * @param nameToBytes function used to convert the name to a byte array + * @param the type of the name parameter + * @return a {@link V5} UUID + */ + public static UUID from(UUID namespace, A name, Function nameToBytes) { + MessageDigest md; + + try { + md = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError("SHA1 not supported", nsae); + } + + md.update(toBytes(namespace.getMostSignificantBits())); + md.update(toBytes(namespace.getLeastSignificantBits())); + md.update(nameToBytes.apply(name)); + byte[] bytes = md.digest(); + + long rawMsb = fromBytes(Arrays.copyOfRange(bytes, 0, 8)); + long rawLsb = fromBytes(Arrays.copyOfRange(bytes, 8, 16)); + + long msb = writeByte(Mask.VERSION, Offset.VERSION, rawMsb, 5); + long lsb = writeByte(Mask.HASHED, Offset.HASHED, rawLsb, 0x2); + + return new V5(new java.util.UUID(msb, lsb)); + } + + private V5(java.util.UUID uuid) { + super(uuid); + } + + } + + /** + * Not standard-version UUIDs. + * + * @see RFC-4122 + */ + public final static class UnknownVersion extends UUID { + + private UnknownVersion(java.util.UUID uuid) { + super(uuid); + } + + } + + private final java.util.UUID juuid; + + private UUID(java.util.UUID juuid) { + this.juuid = juuid; + } }