From fc916ed7201edb6571d1d55c3935616fb7e27307 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 15 Jan 2022 10:06:02 +0100 Subject: [PATCH] Introduce name based UUID and correct time based UUID for high clock sequence --- .../src/org/argeo/util/UuidUtils.java | 522 ++++++++++++------ 1 file changed, 358 insertions(+), 164 deletions(-) diff --git a/org.argeo.util/src/org/argeo/util/UuidUtils.java b/org.argeo.util/src/org/argeo/util/UuidUtils.java index 7584abc1b..3fe5b3afe 100644 --- a/org.argeo.util/src/org/argeo/util/UuidUtils.java +++ b/org.argeo.util/src/org/argeo/util/UuidUtils.java @@ -1,38 +1,79 @@ package org.argeo.util; +import static java.lang.System.Logger.Level.DEBUG; + +import java.lang.System.Logger; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.time.Duration; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.BitSet; +import java.util.Objects; import java.util.Random; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; /** - * Utilities to simplify and extends usage of {@link UUID}. Only the RFC 4122 - * variant (also known as Leach–Salz variant) is supported. + * Static utilities to simplify and extend usage of {@link UUID}. Only the RFC + * 4122 variant (also known as Leach–Salz variant) is supported. + * + * @see https://datatracker.ietf.org/doc/html/rfc4122 */ public class UuidUtils { /** Nil UUID (00000000-0000-0000-0000-000000000000). */ public final static UUID NIL_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + + /** Start of the Gregorian time, used by time-based UUID. */ public final static LocalDateTime GREGORIAN_START = LocalDateTime.of(1582, 10, 15, 0, 0, 0); + /** + * Standard DNS namespace ID for type 3 or 5 UUID (as defined in Appendix C of + * RFC4122). + */ + public final static UUID NS_DNS = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); + /** + * Standard URL namespace ID for type 3 or 5 UUID (as defined in Appendix C of + * RFC4122). + */ + public final static UUID NS_URL = UUID.fromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); + /** + * Standard OID namespace ID (typically an LDAP type) for type 3 or 5 UUID (as + * defined in Appendix C of RFC4122). + */ + public final static UUID NS_OID = UUID.fromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); + /** + * Standard X500 namespace ID (typically an LDAP DN) for type 3 or 5 UUID (as + * defined in Appendix C of RFC4122). + */ + public final static UUID NS_X500 = UUID.fromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8"); + + /* + * INTERNAL STATIC UTILITIES + */ + private final static Logger logger = System.getLogger(UuidUtils.class.getName()); + private final static long MOST_SIG_VERSION1 = (1l << 12); private final static long LEAST_SIG_RFC4122_VARIANT = (1l << 63); + private final static int MAX_CLOCKSEQUENCE = 16384; - private final static SecureRandom RANDOM; - private final static AtomicInteger CLOCK_SEQUENCE; + private final static SecureRandom SECURE_RANDOM; + private final static Random UNSECURE_RANDOM; private final static byte[] HARDWARE_ADDRESS; + + private final static AtomicInteger CLOCK_SEQUENCE; + /** A start timestamp to which {@link System#nanoTime()}/100 can be added. */ private final static long START_TIMESTAMP; + static { - RANDOM = new SecureRandom(); - CLOCK_SEQUENCE = new AtomicInteger(RANDOM.nextInt(16384)); + SECURE_RANDOM = new SecureRandom(); + UNSECURE_RANDOM = new Random(); + CLOCK_SEQUENCE = new AtomicInteger(SECURE_RANDOM.nextInt(MAX_CLOCKSEQUENCE)); HARDWARE_ADDRESS = getHardwareAddress(); long nowVm = System.nanoTime() / 100; @@ -40,6 +81,10 @@ public class UuidUtils { START_TIMESTAMP = (duration.getSeconds() * 10000000 + duration.getNano() / 100) - nowVm; } + /* + * TIME-BASED (version 1) + */ + private static byte[] getHardwareAddress() { InetAddress localHost; try { @@ -56,16 +101,30 @@ public class UuidUtils { } + private synchronized static long nextClockSequence() { + int i = CLOCK_SEQUENCE.incrementAndGet(); + while (i < 0 || i >= MAX_CLOCKSEQUENCE) { + CLOCK_SEQUENCE.set(SECURE_RANDOM.nextInt(MAX_CLOCKSEQUENCE)); + i = CLOCK_SEQUENCE.incrementAndGet(); + } + return (long) i; + } + public static UUID timeUUIDwithRandomNode() { long timestamp = START_TIMESTAMP + System.nanoTime() / 100; - return timeUUID(timestamp, RANDOM); + return timeUUID(timestamp, SECURE_RANDOM); + } + + public static UUID timeUUIDwithUnsecureRandomNode() { + long timestamp = START_TIMESTAMP + System.nanoTime() / 100; + return timeUUID(timestamp, UNSECURE_RANDOM); } public static UUID timeUUID(long timestamp, Random random) { byte[] node = new byte[6]; random.nextBytes(node); node[0] = (byte) (node[0] | 1); - long clockSequence = CLOCK_SEQUENCE.incrementAndGet(); + long clockSequence = nextClockSequence(); return timeUUID(timestamp, clockSequence, node); } @@ -76,8 +135,8 @@ public class UuidUtils { public static UUID timeUUID(long timestamp) { if (HARDWARE_ADDRESS == null) - return timeUUID(timestamp, RANDOM); - long clockSequence = CLOCK_SEQUENCE.incrementAndGet(); + return timeUUID(timestamp, SECURE_RANDOM); + long clockSequence = nextClockSequence(); return timeUUID(timestamp, clockSequence, HARDWARE_ADDRESS); } @@ -88,7 +147,7 @@ public class UuidUtils { } catch (SocketException e) { throw new IllegalStateException("Cannot get hardware address", e); } - long clockSequence = CLOCK_SEQUENCE.incrementAndGet(); + long clockSequence = nextClockSequence(); return timeUUID(timestamp, clockSequence, node); } @@ -100,7 +159,9 @@ public class UuidUtils { } public static UUID timeUUID(long timestamp, long clockSequence, byte[] node) { - assert node.length >= 6; + Objects.requireNonNull(node, "Node array cannot be null"); + if (node.length < 6) + throw new IllegalArgumentException("Node array must be at least 6 bytes long"); long mostSig = MOST_SIG_VERSION1 // base for version 1 UUID | ((timestamp & 0xFFFFFFFFL) << 32) // time_low @@ -116,141 +177,231 @@ public class UuidUtils { | ((node[3] & 0xFFL) << 24) // | ((node[4] & 0xFFL) << 32) // | ((node[5] & 0xFFL) << 40); // -// for (int i = 0; i < 6; i++) { -// leastSig = leastSig | ((node[i] & 0xFFL) << (8 * i)); -// } UUID uuid = new UUID(mostSig, leastSig); // tests assert uuid.node() == BitSet.valueOf(node).toLongArray()[0]; assert uuid.timestamp() == timestamp; - assert uuid.clockSequence() == clockSequence; + assert uuid.clockSequence() == clockSequence + : "uuid.clockSequence()=" + uuid.clockSequence() + " clockSequence=" + clockSequence; assert uuid.version() == 1; assert uuid.variant() == 2; return uuid; } - @Deprecated - public static UUID timeBasedUUID() { - return timeBasedUUID(LocalDateTime.now(ZoneOffset.UTC)); +// @Deprecated +// public static UUID timeBasedUUID() { +// return timeBasedUUID(LocalDateTime.now(ZoneOffset.UTC)); +// } +// +// @Deprecated +// public static UUID timeBasedRandomUUID() { +// return timeBasedRandomUUID(LocalDateTime.now(ZoneOffset.UTC), RANDOM); +// } +// +// @Deprecated +// public static UUID timeBasedUUID(LocalDateTime time) { +// if (HARDWARE_ADDRESS == null) +// return timeBasedRandomUUID(time, RANDOM); +// return timeBasedUUID(time, BitSet.valueOf(HARDWARE_ADDRESS)); +// } +// +// @Deprecated +// public static UUID timeBasedAddressUUID(LocalDateTime time, NetworkInterface nic) throws SocketException { +// byte[] nodeBytes = nic.getHardwareAddress(); +// BitSet node = BitSet.valueOf(nodeBytes); +// return timeBasedUUID(time, node); +// } +// +// @Deprecated +// public static UUID timeBasedRandomUUID(LocalDateTime time, Random random) { +// byte[] nodeBytes = new byte[6]; +// random.nextBytes(nodeBytes); +// BitSet node = BitSet.valueOf(nodeBytes); +// // set random marker +// node.set(0, true); +// return timeBasedUUID(time, node); +// } +// +// @Deprecated +// public static UUID timeBasedUUID(LocalDateTime time, BitSet node) { +// // most significant +// Duration duration = Duration.between(GREGORIAN_START, time); +// +// // Number of 100 ns intervals in one second: 1000000000 / 100 = 10000000 +// long timeNanos = duration.getSeconds() * 10000000 + duration.getNano() / 100; +// BitSet timeBits = BitSet.valueOf(new long[] { timeNanos }); +// assert timeBits.length() <= 60; +// +// int clockSequence; +// synchronized (CLOCK_SEQUENCE) { +// clockSequence = CLOCK_SEQUENCE.incrementAndGet(); +// if (clockSequence > 16384) +// CLOCK_SEQUENCE.set(0); +// } +// BitSet clockSequenceBits = BitSet.valueOf(new long[] { clockSequence }); +// +// // Build the UUID, bit by bit +// // see https://tools.ietf.org/html/rfc4122#section-4.2.2 +// // time +// BitSet time_low = new BitSet(32); +// BitSet time_mid = new BitSet(16); +// BitSet time_hi_and_version = new BitSet(16); +// +// for (int i = 0; i < 60; i++) { +// if (i < 32) +// time_low.set(i, timeBits.get(i)); +// else if (i < 48) +// time_mid.set(i - 32, timeBits.get(i)); +// else +// time_hi_and_version.set(i - 48, timeBits.get(i)); +// } +// // version +// time_hi_and_version.set(12, true); +// time_hi_and_version.set(13, false); +// time_hi_and_version.set(14, false); +// time_hi_and_version.set(15, false); +// +// // clock sequence +// BitSet clk_seq_hi_res = new BitSet(8); +// BitSet clk_seq_low = new BitSet(8); +// for (int i = 0; i < 8; i++) { +// clk_seq_low.set(i, clockSequenceBits.get(i)); +// } +// for (int i = 8; i < 14; i++) { +// clk_seq_hi_res.set(i - 8, clockSequenceBits.get(i)); +// } +// // variant +// clk_seq_hi_res.set(6, false); +// clk_seq_hi_res.set(7, true); +// +//// String str = toHexString(time_low.toLongArray()[0]) + "-" + toHexString(time_mid.toLongArray()[0]) + "-" +//// + toHexString(time_hi_and_version.toLongArray()[0]) + "-" +//// + toHexString(clock_seq_hi_and_reserved.toLongArray()[0]) + toHexString(clock_seq_low.toLongArray()[0]) +//// + "-" + toHexString(node.toLongArray()[0]); +//// UUID uuid = UUID.fromString(str); +// +// BitSet uuidBits = new BitSet(128); +// for (int i = 0; i < 128; i++) { +// if (i < 48) +// uuidBits.set(i, node.get(i)); +// else if (i < 56) +// uuidBits.set(i, clk_seq_low.get(i - 48)); +// else if (i < 64) +// uuidBits.set(i, clk_seq_hi_res.get(i - 56)); +// else if (i < 80) +// uuidBits.set(i, time_hi_and_version.get(i - 64)); +// else if (i < 96) +// uuidBits.set(i, time_mid.get(i - 80)); +// else +// uuidBits.set(i, time_low.get(i - 96)); +// } +// +// long[] uuidLongs = uuidBits.toLongArray(); +// assert uuidLongs.length == 2; +// UUID uuid = new UUID(uuidLongs[1], uuidLongs[0]); +// +// // tests +// assert uuid.node() == node.toLongArray()[0]; +// assert uuid.timestamp() == timeNanos; +// assert uuid.clockSequence() == clockSequence; +// assert uuid.version() == 1; +// assert uuid.variant() == 2; +// return uuid; +// } + + /* + * NAME BASED (version 3 and 5) + */ + + public static UUID nameUUIDv5(UUID namespace, String name) { + Objects.requireNonNull(namespace, "Namespace cannot be null"); + Objects.requireNonNull(name, "Name cannot be null"); + return nameUUIDv5(namespace, name.getBytes(StandardCharsets.UTF_8)); } - @Deprecated - public static UUID timeBasedRandomUUID() { - return timeBasedRandomUUID(LocalDateTime.now(ZoneOffset.UTC), RANDOM); + public static UUID nameUUIDv5(UUID namespace, byte[] name) { + byte[] bytes = DigestUtils.sha1(toBytes(namespace), name); + bytes[6] &= 0x0f; + bytes[6] |= 0x50;// v5 + bytes[8] &= 0x3f; + bytes[8] |= 0x80;// variant 1 + UUID result = fromBytes(bytes, 0); + return result; } - @Deprecated - public static UUID timeBasedUUID(LocalDateTime time) { - if (HARDWARE_ADDRESS == null) - return timeBasedRandomUUID(time, RANDOM); - return timeBasedUUID(time, BitSet.valueOf(HARDWARE_ADDRESS)); + public static UUID nameUUIDv3(UUID namespace, String name) { + Objects.requireNonNull(namespace, "Namespace cannot be null"); + Objects.requireNonNull(name, "Name cannot be null"); + return nameUUIDv3(namespace, name.getBytes(StandardCharsets.UTF_8)); } - @Deprecated - public static UUID timeBasedAddressUUID(LocalDateTime time, NetworkInterface nic) throws SocketException { - byte[] nodeBytes = nic.getHardwareAddress(); - BitSet node = BitSet.valueOf(nodeBytes); - return timeBasedUUID(time, node); + public static UUID nameUUIDv3(UUID namespace, byte[] name) { + byte[] arr = new byte[name.length + 16]; + copyBytes(namespace, arr, 0); + System.arraycopy(name, 0, arr, 16, name.length); + return UUID.nameUUIDFromBytes(arr); } - @Deprecated - public static UUID timeBasedRandomUUID(LocalDateTime time, Random random) { - byte[] nodeBytes = new byte[6]; - random.nextBytes(nodeBytes); - BitSet node = BitSet.valueOf(nodeBytes); - // set random marker - node.set(0, true); - return timeBasedUUID(time, node); + /* + * RANDOM v4 + */ + public static UUID unsecureRandomUUID() { + byte[] arr = new byte[16]; + UNSECURE_RANDOM.nextBytes(arr); + arr[6] &= 0x0f; + arr[6] |= 0x40;// v4 + arr[8] &= 0x3f; + arr[8] |= 0x80;// variant 1 + return fromBytes(arr); } - @Deprecated - public static UUID timeBasedUUID(LocalDateTime time, BitSet node) { - // most significant - Duration duration = Duration.between(GREGORIAN_START, time); + /* + * UTILITIES + */ + /** + * Convert bytes to an UUID. Byte array must not be null and be exactly of + * length 16. + */ + public static UUID fromBytes(byte[] data) { + Objects.requireNonNull(data, "Byte array must not be null"); + if (data.length != 16) + throw new IllegalArgumentException("Byte array as length " + data.length); + return fromBytes(data, 0); + } - // Number of 100 ns intervals in one second: 1000000000 / 100 = 10000000 - long timeNanos = duration.getSeconds() * 10000000 + duration.getNano() / 100; - BitSet timeBits = BitSet.valueOf(new long[] { timeNanos }); - assert timeBits.length() <= 60; - - int clockSequence; - synchronized (CLOCK_SEQUENCE) { - clockSequence = CLOCK_SEQUENCE.incrementAndGet(); - if (clockSequence > 16384) - CLOCK_SEQUENCE.set(0); - } - BitSet clockSequenceBits = BitSet.valueOf(new long[] { clockSequence }); - - // Build the UUID, bit by bit - // see https://tools.ietf.org/html/rfc4122#section-4.2.2 - // time - BitSet time_low = new BitSet(32); - BitSet time_mid = new BitSet(16); - BitSet time_hi_and_version = new BitSet(16); - - for (int i = 0; i < 60; i++) { - if (i < 32) - time_low.set(i, timeBits.get(i)); - else if (i < 48) - time_mid.set(i - 32, timeBits.get(i)); - else - time_hi_and_version.set(i - 48, timeBits.get(i)); - } - // version - time_hi_and_version.set(12, true); - time_hi_and_version.set(13, false); - time_hi_and_version.set(14, false); - time_hi_and_version.set(15, false); - - // clock sequence - BitSet clk_seq_hi_res = new BitSet(8); - BitSet clk_seq_low = new BitSet(8); - for (int i = 0; i < 8; i++) { - clk_seq_low.set(i, clockSequenceBits.get(i)); - } - for (int i = 8; i < 14; i++) { - clk_seq_hi_res.set(i - 8, clockSequenceBits.get(i)); - } - // variant - clk_seq_hi_res.set(6, false); - clk_seq_hi_res.set(7, true); - -// String str = toHexString(time_low.toLongArray()[0]) + "-" + toHexString(time_mid.toLongArray()[0]) + "-" -// + toHexString(time_hi_and_version.toLongArray()[0]) + "-" -// + toHexString(clock_seq_hi_and_reserved.toLongArray()[0]) + toHexString(clock_seq_low.toLongArray()[0]) -// + "-" + toHexString(node.toLongArray()[0]); -// UUID uuid = UUID.fromString(str); - - BitSet uuidBits = new BitSet(128); - for (int i = 0; i < 128; i++) { - if (i < 48) - uuidBits.set(i, node.get(i)); - else if (i < 56) - uuidBits.set(i, clk_seq_low.get(i - 48)); - else if (i < 64) - uuidBits.set(i, clk_seq_hi_res.get(i - 56)); - else if (i < 80) - uuidBits.set(i, time_hi_and_version.get(i - 64)); - else if (i < 96) - uuidBits.set(i, time_mid.get(i - 80)); - else - uuidBits.set(i, time_low.get(i - 96)); - } + /** + * Convert bytes to an UUID, starting to read the array at this offset. + */ + public static UUID fromBytes(byte[] data, int offset) { + Objects.requireNonNull(data, "Byte array cannot be null"); + long msb = 0; + long lsb = 0; + for (int i = offset; i < 8 + offset; i++) + msb = (msb << 8) | (data[i] & 0xff); + for (int i = 8 + offset; i < 16 + offset; i++) + lsb = (lsb << 8) | (data[i] & 0xff); + return new UUID(msb, lsb); + } - long[] uuidLongs = uuidBits.toLongArray(); - assert uuidLongs.length == 2; - UUID uuid = new UUID(uuidLongs[1], uuidLongs[0]); + public static byte[] toBytes(UUID uuid) { + Objects.requireNonNull(uuid, "UUID cannot be null"); + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + return BytesUtils.toBytes(msb, lsb); + } - // tests - assert uuid.node() == node.toLongArray()[0]; - assert uuid.timestamp() == timeNanos; - assert uuid.clockSequence() == clockSequence; - assert uuid.version() == 1; - assert uuid.variant() == 2; - return uuid; + public static void copyBytes(UUID uuid, byte[] arr, int offset) { + Objects.requireNonNull(uuid, "UUID cannot be null"); + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + BytesUtils.copyBytes(msb, lsb, arr, offset); } + /** + * Converts an UUID to a binary string (list of 0 and 1), with a separator to + * make it more readable. + */ public static String toBinaryString(UUID uuid, int charsPerSegment, char separator) { String binaryString = toBinaryString(uuid); StringBuilder sb = new StringBuilder(128 + (128 / charsPerSegment)); @@ -262,6 +413,7 @@ public class UuidUtils { return sb.toString(); } + /** Converts an UUID to a binary string (list of 0 and 1). */ public static String toBinaryString(UUID uuid) { String most = zeroTo64Chars(Long.toBinaryString(uuid.getMostSignificantBits())); String least = zeroTo64Chars(Long.toBinaryString(uuid.getLeastSignificantBits())); @@ -282,6 +434,10 @@ public class UuidUtils { return str; } + /** + * Converts an UUID hex representation without '-' to the standard form (with + * '-'). + */ public static String compactToStd(String compact) { if (compact.length() != 32) throw new IllegalArgumentException( @@ -298,10 +454,18 @@ public class UuidUtils { return std; } - public static UUID compactToUuid(String compact) { + /** + * Converts an UUID hex representation without '-' to an {@link UUID}. + */ + public static UUID fromCompact(String compact) { return UUID.fromString(compactToStd(compact)); } - + + /** To a 32 characters hex string without '-'. */ + public static String toCompact(UUID uuid) { + return BytesUtils.toHexString(toBytes(uuid)); + } + public static String firstBlock(UUID uuid) { return uuid.toString().substring(0, 8); } @@ -330,49 +494,79 @@ public class UuidUtils { private UuidUtils() { } - public final static void main(String[] args) throws Exception { - UUID uuid; + final static void smokeTests() throws AssertionError { + + // warm up a bit before measuring perf and logging it + int warmUpCycles = 10; + // int warmUpCycles = 10000000; + if (logger.isLoggable(DEBUG)) + for (int i = 0; i < warmUpCycles; i++) { + UUID.randomUUID(); + unsecureRandomUUID(); + timeUUID(); + timeUUIDwithRandomNode(); + nameUUIDv5(NS_DNS, "example.org"); + nameUUIDv3(NS_DNS, "example.org"); + } + + long begin; -// uuid = compactToUuid("996b1f5122de4b2f94e49168d32f22d1"); -// System.out.println(uuid.toString() + ", isRandom=" + isRandom(uuid)); + { + begin = System.nanoTime(); + UUID uuid = UUID.randomUUID(); + long duration = System.nanoTime() - begin; + assert isRandom(uuid); + logger.log(DEBUG, () -> uuid.toString() + " in " + duration + " ns, isRandom=" + isRandom(uuid)); + } - // warm up before measuring perf - for (int i = 0; i < 10; i++) { - UUID.randomUUID(); - timeUUID(); - timeUUIDwithRandomNode(); - timeBasedRandomUUID(); - timeBasedUUID(); + { + begin = System.nanoTime(); + UUID uuid = unsecureRandomUUID(); + long duration = System.nanoTime() - begin; + assert isRandom(uuid); + logger.log(DEBUG, () -> uuid.toString() + " in " + duration + " ns, isRandom=" + isRandom(uuid)); } - long begin; - long duration; - - begin = System.nanoTime(); - uuid = UUID.randomUUID(); - duration = System.nanoTime() - begin; - System.out.println(uuid.toString() + " in " + duration + " ns, isRandom=" + isRandom(uuid)); - - begin = System.nanoTime(); - uuid = timeUUID(); - duration = System.nanoTime() - begin; - System.out.println(uuid.toString() + " in " + duration + " ns, isTimeBasedRandom=" + isTimeBasedRandom(uuid)); - - begin = System.nanoTime(); - uuid = timeUUIDwithRandomNode(); - duration = System.nanoTime() - begin; - System.out.println(uuid.toString() + " in " + duration + " ns, isTimeBasedRandom=" + isTimeBasedRandom(uuid)); - - begin = System.nanoTime(); - uuid = timeBasedUUID(); - duration = System.nanoTime() - begin; - System.out.println(uuid.toString() + " in " + duration + " ns, isTimeBasedRandom=" + isTimeBasedRandom(uuid)); - - begin = System.nanoTime(); - uuid = timeBasedRandomUUID(); - duration = System.nanoTime() - begin; - System.out.println(uuid.toString() + " in " + duration + " ns, isTimeBasedRandom=" + isTimeBasedRandom(uuid)); -// System.out.println(toBinaryString(uuid, 8, ' ')); -// System.out.println(toBinaryString(uuid, 16, '\n')); + { + begin = System.nanoTime(); + UUID uuid = timeUUID(); + long duration = System.nanoTime() - begin; + assert isTimeBased(uuid); + logger.log(DEBUG, + () -> uuid.toString() + " in " + duration + " ns, isTimeBasedRandom=" + isTimeBasedRandom(uuid)); + } + + { + begin = System.nanoTime(); + UUID uuid = timeUUIDwithRandomNode(); + long duration = System.nanoTime() - begin; + assert isTimeBasedRandom(uuid); + logger.log(DEBUG, + () -> uuid.toString() + " in " + duration + " ns, isTimeBasedRandom=" + isTimeBasedRandom(uuid)); + } + + { + begin = System.nanoTime(); + UUID uuid = nameUUIDv5(NS_DNS, "example.org"); + long duration = System.nanoTime() - begin; + assert isNameBased(uuid); + // uuidgen --sha1 --namespace @dns --name example.org + assert "aad03681-8b63-5304-89e0-8ca8f49461b5".equals(uuid.toString()); + logger.log(DEBUG, () -> uuid.toString() + " in " + duration + " ns, isNameBased=" + isNameBased(uuid)); + } + + { + begin = System.nanoTime(); + UUID uuid = nameUUIDv3(NS_DNS, "example.org"); + long duration = System.nanoTime() - begin; + assert isNameBased(uuid); + // uuidgen --md5 --namespace @dns --name example.org + assert "04738bdf-b25a-3829-a801-b21a1d25095b".equals(uuid.toString()); + logger.log(DEBUG, () -> uuid.toString() + " in " + duration + " ns, isNameBased=" + isNameBased(uuid)); + } + } + + public static void main(String[] args) { + smokeTests(); } } -- 2.30.2