diff --git a/README.md b/README.md
index fab8d53..ee42bc9 100644
--- a/README.md
+++ b/README.md
@@ -12,36 +12,18 @@ Create a ULID:
String ulid = UlidCreator.getUlid();
```
-Create a fast ULID:
-
-```java
-String ulid = UlidCreator.getFastUlid();
-```
-
Create a ULID as GUID object:
```java
UUID ulid = UlidCreator.getGuid();
```
-Create a fast ULID as GUID object:
-
-```java
-UUID ulid = UlidCreator.getFastGuid();
-```
-
Create a ULID as byte sequence:
```java
byte[] ulid = UlidCreator.getBytes();
```
-Create a fast ULID as byte sequence:
-
-```java
-byte[] ulid = UlidCreator.getFastBytes();
-```
-
### Maven dependency
Add these lines to your `pom.xml`.
@@ -51,7 +33,7 @@ Add these lines to your `pom.xml`.
com.github.f4b6a3
ulid-creator
- 1.0.1
+ 1.0.2
```
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator) and [mvnrepository.com](https://mvnrepository.com/artifact/com.github.f4b6a3/ulid-creator).
@@ -139,17 +121,19 @@ These are some examples of using the `GuidCreator` to create ULIDs:
```java
-// with the default random generator (java.security.SecureRandom)
-String ulid = UlidCreator.getGuidCreator().createUlid();
-
-// with java random generator (java.util.Random)
+// with fixed timestamp strategy (for test cases)
String ulid = UlidCreator.getGuidCreator()
- .withRandomGenerator(new Random())
- .createUlid();
+ .withTimestampStrategy(new FixedTimestampStretegy())
+ .createUlid();
+
+// with your custom timestamp strategy
+String ulid = UlidCreator.getGuidCreator()
+ .withTimestampStrategy(new MyCustomTimestampStrategy())
+ .createUlid();
-// with fast random generator (Xorshift128Plus)
+// with your custom random number generator
String ulid = UlidCreator.getGuidCreator()
- .withFastRandomGenerator()
+ .withRandomGenerator(new MyCustomRandom())
.createUlid();
// with fast random generator (Xorshift128Plus with salt)
@@ -158,5 +142,11 @@ Random random = new Xorshift128PlusRandom(salt);
String ulid = UlidCreator.getGuidCreator()
.withRandomGenerator(random)
.createUlid();
+
+// with fast random generator (the same as above)
+String ulid = UlidCreator.getGuidCreator()
+ .withFastRandomGenerator()
+ .createUlid();
+
```
diff --git a/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java b/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java
index 00d0e6c..208b4b8 100644
--- a/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java
+++ b/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java
@@ -46,8 +46,23 @@ public class UlidUtil {
* @return a ULID
*/
public static String fromUuidToUlid(UUID uuid) {
- byte[] bytes = fromUuidToBytes(uuid);
- return fromBytesToUlid(bytes);
+
+ final long msb = uuid.getMostSignificantBits();
+ final long lsb = uuid.getLeastSignificantBits();
+
+ // Extract timestamp component
+ final long timeNumber = (msb >>> 16);
+ String timestampComponent = leftPad(Base32Util.toBase32Crockford(timeNumber));
+
+ // Extract randomness component
+ byte[] randBytes = new byte[10];
+ randBytes[0] = (byte) (msb >>> 8);
+ randBytes[1] = (byte) (msb);
+ byte[] lsbBytes = ByteUtil.toBytes(lsb);
+ System.arraycopy(lsbBytes, 0, randBytes, 2, 8);
+ String randomnessComponent = Base32Util.toBase32Crockford(randBytes);
+
+ return timestampComponent + randomnessComponent;
}
/**
@@ -59,9 +74,24 @@ public class UlidUtil {
* a ULID
* @return a UUID if valid
*/
- public static UUID fromUlidToUuid(String ulid) {
- byte[] bytes = fromUlidToBytes(ulid);
- return fromBytesToUuid(bytes);
+ public static UUID fromUlidToUuid(final String ulid) {
+
+ UlidUtil.validate(ulid);
+
+ // Extract timestamp component
+ final String timestampComponent = ulid.substring(0, 10);
+ final long timeNumber = Base32Util.fromBase32CrockfordAsLong(timestampComponent);
+
+ // Extract randomness component
+ final String randomnessComponent = ulid.substring(10, 26);
+ byte[] randBytes = Base32Util.fromBase32Crockford(randomnessComponent);
+ byte[] lsbBytes = new byte[8];
+ System.arraycopy(randBytes, 2, lsbBytes, 0, 8);
+
+ final long msb = (timeNumber << 16) | ((randBytes[0] << 8) & 0x0000ff00L) | ((randBytes[1]) & 0x000000ffL);
+ final long lsb = ByteUtil.toNumber(lsbBytes);
+
+ return new UUID(msb, lsb);
}
/**
@@ -71,11 +101,11 @@ public class UlidUtil {
* a UUID
* @return an array of bytes
*/
- public static byte[] fromUuidToBytes(UUID uuid) {
- long msb = uuid.getMostSignificantBits();
- long lsb = uuid.getLeastSignificantBits();
- byte[] msbBytes = ByteUtil.toBytes(msb);
- byte[] lsbBytes = ByteUtil.toBytes(lsb);
+ public static byte[] fromUuidToBytes(final UUID uuid) {
+ final long msb = uuid.getMostSignificantBits();
+ final long lsb = uuid.getLeastSignificantBits();
+ final byte[] msbBytes = ByteUtil.toBytes(msb);
+ final byte[] lsbBytes = ByteUtil.toBytes(lsb);
return ByteUtil.concat(msbBytes, lsbBytes);
}
@@ -87,10 +117,12 @@ public class UlidUtil {
* @return a UUID
*/
public static UUID fromBytesToUuid(byte[] bytes) {
- byte[] msbBytes = ByteUtil.copy(bytes, 0, 8);
- byte[] lsbBytes = ByteUtil.copy(bytes, 8, 16);
- long msb = ByteUtil.toNumber(msbBytes);
- long lsb = ByteUtil.toNumber(lsbBytes);
+ byte[] msbBytes = new byte[8];
+ System.arraycopy(bytes, 0, msbBytes, 0, 8);
+ byte[] lsbBytes = new byte[8];
+ System.arraycopy(bytes, 8, lsbBytes, 0, 8);
+ final long msb = ByteUtil.toNumber(msbBytes);
+ final long lsb = ByteUtil.toNumber(lsbBytes);
return new UUID(msb, lsb);
}
@@ -105,12 +137,12 @@ public class UlidUtil {
byte[] timeBytes = new byte[6];
System.arraycopy(bytes, 0, timeBytes, 0, 6);
- long timeNumber = ByteUtil.toNumber(timeBytes);
- String timestampComponent = leftPad(Base32Util.toBase32Crockford(timeNumber));
+ final long timeNumber = ByteUtil.toNumber(timeBytes);
+ final String timestampComponent = leftPad(Base32Util.toBase32Crockford(timeNumber));
byte[] randBytes = new byte[10];
System.arraycopy(bytes, 6, randBytes, 0, 10);
- String randomnessComponent = Base32Util.toBase32Crockford(randBytes);
+ final String randomnessComponent = Base32Util.toBase32Crockford(randBytes);
return timestampComponent + randomnessComponent;
}
@@ -122,16 +154,16 @@ public class UlidUtil {
* a ULID string
* @return an array of bytes
*/
- public static byte[] fromUlidToBytes(String ulid) {
+ public static byte[] fromUlidToBytes(final String ulid) {
UlidUtil.validate(ulid);
byte[] bytes = new byte[16];
- String timestampComponent = ulid.substring(0, 10);
- long timeNumber = Base32Util.fromBase32CrockfordAsLong(timestampComponent);
+ final String timestampComponent = ulid.substring(0, 10);
+ final long timeNumber = Base32Util.fromBase32CrockfordAsLong(timestampComponent);
byte[] timeBytes = ByteUtil.toBytes(timeNumber);
System.arraycopy(timeBytes, 2, bytes, 0, 6);
- String randomnessComponent = ulid.substring(10, 26);
+ final String randomnessComponent = ulid.substring(10, 26);
byte[] randBytes = Base32Util.fromBase32Crockford(randomnessComponent);
System.arraycopy(randBytes, 0, bytes, 6, 10);
diff --git a/src/test/java/com/github/f4b6a3/Benchmarks.java b/src/test/java/com/github/f4b6a3/Benchmarks.java
new file mode 100644
index 0000000..2bc9647
--- /dev/null
+++ b/src/test/java/com/github/f4b6a3/Benchmarks.java
@@ -0,0 +1,89 @@
+package com.github.f4b6a3;
+
+// Add theese dependencies to pom.xml:
+//
+//
+// org.openjdk.jmh
+// jmh-core
+// 1.23
+//
+//
+// org.openjdk.jmh
+// jmh-generator-annprocess
+// 1.23
+//
+
+/*
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import com.github.f4b6a3.ulid.UlidCreator;
+
+@Threads(1)
+@State(Scope.Thread)
+@Warmup(iterations = 5)
+@Measurement(iterations = 5)
+public class Benchmarks {
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.MILLISECONDS)
+ public String getUlidThroughput() {
+ return UlidCreator.getUlid();
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.AverageTime)
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ public String getUlidAverage() {
+ return UlidCreator.getUlid();
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.MILLISECONDS)
+ public UUID getGuidThroughput() {
+ return UlidCreator.getGuid();
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.AverageTime)
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ public UUID getGuidAverage() {
+ return UlidCreator.getGuid();
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.MILLISECONDS)
+ public UUID getRandomUUIDThroughput() {
+ return UUID.randomUUID();
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.AverageTime)
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ public UUID getRandomUUIDAverage() {
+ return UUID.randomUUID();
+ }
+
+ public static void main(String[] args) throws RunnerException {
+ Options opt = new OptionsBuilder().include(MyBenchmark.class.getSimpleName()).forks(1).build();
+ new Runner(opt).run();
+ }
+}
+*/
diff --git a/src/test/java/com/github/f4b6a3/demo/DemoTest.java b/src/test/java/com/github/f4b6a3/demo/DemoTest.java
index 2a5b274..3cccdf1 100644
--- a/src/test/java/com/github/f4b6a3/demo/DemoTest.java
+++ b/src/test/java/com/github/f4b6a3/demo/DemoTest.java
@@ -7,7 +7,7 @@ public class DemoTest {
private static final String HORIZONTAL_LINE = "----------------------------------------";
public static void printList() {
- int max = 1_000;
+ int max = 100;
System.out.println(HORIZONTAL_LINE);
System.out.println("### ULID");
diff --git a/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java b/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java
index e50f0f3..d94e21b 100644
--- a/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java
+++ b/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java
@@ -182,8 +182,8 @@ public class UlidUtilTest {
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
- UUID uuid = UlidCreator.getFastGuid();
- String ulid = UlidUtil.fromUuidToUlid(uuid);
+ UUID uuid1 = UlidCreator.getGuid();
+ String ulid = UlidUtil.fromUuidToUlid(uuid1);
assertTrue("ULID is null", ulid != null);
assertTrue("ULID is empty", !ulid.isEmpty());
@@ -191,12 +191,57 @@ public class UlidUtilTest {
assertTrue("ULID is not valid", UlidUtil.isValid(ulid, /* strict */
true));
- UUID result = UlidUtil.fromUlidToUuid(ulid);
- assertEquals("Result ULID is different from original ULID", uuid, result);
+ UUID uuid2 = UlidUtil.fromUlidToUuid(ulid);
+ assertEquals("Result ULID is different from original ULID", uuid1, uuid2);
}
}
+ @Test
+ public void testToAndFromBytes() {
+ for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
+ String ulid1 = UlidCreator.getUlid();
+ byte[] bytes = UlidUtil.fromUlidToBytes(ulid1);
+ String ulid2 = UlidUtil.fromBytesToUlid(bytes);
+
+ // Check ULID 1
+ assertTrue(ulid1 != null);
+ assertTrue(!ulid1.isEmpty());
+ assertTrue(ulid1.length() == ULID_LENGTH);
+ assertTrue(UlidUtil.isValid(ulid1, /* strict */ true));
+
+ // Check ULID 2
+ assertTrue(ulid2 != null);
+ assertTrue(!ulid2.isEmpty());
+ assertTrue(ulid2.length() == ULID_LENGTH);
+ assertTrue(UlidUtil.isValid(ulid2, /* strict */ true));
+
+ assertEquals(ulid1, ulid2);
+ }
+ }
+
+ @Test
+ public void testFromUuidToBytes() {
+ for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
+ UUID uuid1 = UlidCreator.getGuid();
+ byte[] bytes = UlidUtil.fromUuidToBytes(uuid1);
+ long msb = ByteUtil.toNumber(ByteUtil.copy(bytes, 0, 8));
+ long lsb = ByteUtil.toNumber(ByteUtil.copy(bytes, 8, 16));
+ UUID uuid2 = new UUID(msb, lsb);
+ assertEquals(uuid1, uuid2);
+ }
+ }
+
+ @Test
+ public void testFromBytesToUuid() {
+ for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
+ UUID uuid1 = UlidCreator.getGuid();
+ byte[] bytes = UlidUtil.fromUuidToBytes(uuid1);
+ UUID uuid2 = UlidUtil.fromBytesToUuid(bytes);
+ assertEquals(uuid1, uuid2);
+ }
+ }
+
private String leftPad(String unpadded) {
return "0000000000".substring(unpadded.length()) + unpadded;
}