From 73d43dfc417cc6ccfc0f7578a1db83d6bcff5c4e Mon Sep 17 00:00:00 2001 From: Fabio Lima Date: Sun, 8 Nov 2020 11:15:22 -0300 Subject: [PATCH] [#5] Generate ULID compatible with RFC-4122 UUID v4 Now it can generate UUIDs and ULIDs compatible with RFC-4122 UUID v4. List of Changes: Created UlidCreator.getUlid4(); Created UlidCreator.getUlidString4(); Updated README.md Coverage 94.1% --- README.md | 14 +++++- .../com/github/f4b6a3/ulid/UlidCreator.java | 30 +++++++++++++ .../f4b6a3/ulid/creator/UlidSpecCreator.java | 44 +++++++++++++++++++ .../f4b6a3/ulid/ulid/UlidCreatorTest.java | 28 ++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e0af6d..c5ebae5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Add these lines to your `pom.xml`. com.github.f4b6a3 ulid-creator - 2.2.0 + 2.3.0 ``` See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator). @@ -54,6 +54,12 @@ The default random number generator is `java.security.SecureRandom`. UUID ulid = UlidCreator.getUlid(); ``` +```java +// GUID based on ULID spec +// Compatible with RFC-4122 UUID v4 +UUID ulid = UlidCreator.getUlid4(); +``` + Sequence of GUIDs based on ULID spec: ```text @@ -90,6 +96,12 @@ See the section on GUIDs to know how the 128 bits are generated in this library. String ulid = UlidCreator.getUlidString(); ``` +```java +// String based on ULID spec +// Compatible with RFC-4122 UUID v4 +String ulid = UlidCreator.getUlidString4(); +``` + Sequence of Strings based on ULID spec: ```text diff --git a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java index 706b53a..483d25a 100644 --- a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java +++ b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java @@ -80,6 +80,20 @@ public final class UlidCreator { return UlidSpecCreatorHolder.INSTANCE.create(); } + /** + * Returns a ULID as GUID. + * + * It is compatible with the RFC-4122 UUID v4. + * + * The random component is generated by a secure random number generator: + * {@link java.security.SecureRandom}. + * + * @return a UUID + */ + public static UUID getUlid4() { + return UlidSpecCreatorHolder.INSTANCE.create4(); + } + /** * Returns a ULID string. * @@ -94,6 +108,22 @@ public final class UlidCreator { return UlidSpecCreatorHolder.INSTANCE.createString(); } + /** + * Returns a ULID string. + * + * It is compatible with the RFC-4122 UUID v4. + * + * The returning string is encoded to Crockford's base32. + * + * The random component is generated by a secure random number generator: + * {@link java.security.SecureRandom}. + * + * @return a ULID string + */ + public static String getUlidString4() { + return UlidSpecCreatorHolder.INSTANCE.createString4(); + } + /** * Return a GUID creator for direct use. * diff --git a/src/main/java/com/github/f4b6a3/ulid/creator/UlidSpecCreator.java b/src/main/java/com/github/f4b6a3/ulid/creator/UlidSpecCreator.java index e17d64e..14456a6 100644 --- a/src/main/java/com/github/f4b6a3/ulid/creator/UlidSpecCreator.java +++ b/src/main/java/com/github/f4b6a3/ulid/creator/UlidSpecCreator.java @@ -125,6 +125,18 @@ public class UlidSpecCreator { return struct.toUuid(); } + /** + * + * Return a GUID based on the ULID specification. + * + * It is compatible with the RFC-4122 UUID v4. + * + * @return {@link UUID} a GUID value + */ + public synchronized UUID create4() { + return applyVersion4(create()); + } + /** * Returns a ULID string. * @@ -140,6 +152,24 @@ public class UlidSpecCreator { return struct.toString(); } + /** + * Returns a ULID string. + * + * It is compatible with the RFC-4122 UUID v4. + * + * The returning string is encoded to Crockford's base32. + * + * The random component is generated by a secure random number generator: + * {@link java.security.SecureRandom}. + * + * @return a ULID string + */ + public synchronized String createString4() { + UUID uuid = applyVersion4(create()); + final UlidStruct struct = new UlidStruct(uuid); + return struct.toString(); + } + /** * Return the current timestamp and resets or increments the random part. * @@ -245,4 +275,18 @@ public class UlidSpecCreator { protected long extractRandom2(UUID uuid) { return uuid.getLeastSignificantBits() & HALF_RANDOM_COMPONENT; } + + /** + * Apply the RFC-4122 version 4 to a given UUID. + * + * It makes the returning UUID compatible with RFC-4122 UUID v4. + * + * @param ulid a UUID + * @return a UUID + */ + private static UUID applyVersion4(UUID ulid) { + final long msb = (ulid.getMostSignificantBits() & 0xffffffffffff0fffL) | 0x0000000000004000L; // set version + final long lsb = (ulid.getLeastSignificantBits() & 0x3fffffffffffffffL) | 0x8000000000000000L; // set variant + return new UUID(msb, lsb); + } } diff --git a/src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java b/src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java index dec8c16..61c8573 100644 --- a/src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java @@ -3,12 +3,14 @@ package com.github.f4b6a3.ulid.ulid; import org.junit.Test; import com.github.f4b6a3.ulid.UlidCreator; +import com.github.f4b6a3.ulid.util.UlidConverter; import com.github.f4b6a3.ulid.util.UlidUtil; import com.github.f4b6a3.ulid.util.UlidValidator; import static org.junit.Assert.*; import java.util.Arrays; import java.util.HashSet; +import java.util.UUID; public class UlidCreatorTest { @@ -32,6 +34,25 @@ public class UlidCreatorTest { checkOrdering(list); checkCreationTime(list, startTime, endTime); } + + @Test + public void testGetUlid4() { + String[] list = new String[DEFAULT_LOOP_MAX]; + + long startTime = System.currentTimeMillis(); + + for (int i = 0; i < DEFAULT_LOOP_MAX; i++) { + list[i] = UlidCreator.getUlidString4(); + } + + long endTime = System.currentTimeMillis(); + + checkNullOrInvalid(list); + checkUniqueness(list); + checkOrdering(list); + checkCreationTime(list, startTime, endTime); + checkVersion4(list); + } private void checkNullOrInvalid(String[] list) { for (String ulid : list) { @@ -73,4 +94,11 @@ public class UlidCreatorTest { assertEquals("The ULID list is not ordered", list[i], other[i]); } } + + private void checkVersion4(String[] list) { + for (String ulid : list) { + UUID uuid = UlidConverter.fromString(ulid); + assertEquals(String.format("ULID is is not version 4 %s", uuid), 4, uuid.version()); + } + } }