Add static methods for extracting time and random components #9
Add static methods to Ulid: - Ulid.getInstant(String); - Ulid.getTime(String); - Ulid.getRandom(String); List of changes: Updated Ulid Updated test cases Updated README.md Test coverage 99.4%
This commit is contained in:
parent
b5b262be42
commit
1dffbf1ebb
24
README.md
24
README.md
|
@ -35,14 +35,11 @@ Add these lines to your `pom.xml`.
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.f4b6a3</groupId>
|
<groupId>com.github.f4b6a3</groupId>
|
||||||
<artifactId>ulid-creator</artifactId>
|
<artifactId>ulid-creator</artifactId>
|
||||||
<version>3.0.1</version>
|
<version>3.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator).
|
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator).
|
||||||
|
|
||||||
Implementation
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
### ULID
|
### ULID
|
||||||
|
|
||||||
The ULID is a 128 bit long identifier. The first 48 bits represent the count of milliseconds since Unix Epoch, 1970-01-01. The remaining 80 bits are generated by a secure random number generator.
|
The ULID is a 128 bit long identifier. The first 48 bits represent the count of milliseconds since Unix Epoch, 1970-01-01. The remaining 80 bits are generated by a secure random number generator.
|
||||||
|
@ -168,25 +165,40 @@ Get the creation instant of a ULID:
|
||||||
Instant instant = ulid.getInstant(); // 2007-02-16T02:13:14.633Z
|
Instant instant = ulid.getInstant(); // 2007-02-16T02:13:14.633Z
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// static method
|
||||||
|
Instant instant = Ulid.getInstant("0123456789ABCDEFGHJKMNPQRS"); // 2007-02-16T02:13:14.633Z
|
||||||
|
```
|
||||||
|
|
||||||
Get the time component of a ULID:
|
Get the time component of a ULID:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
long time = ulid.getTime(); // 1171591994633
|
long time = ulid.getTime(); // 1171591994633
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// static method
|
||||||
|
long time = Ulid.getTime("0123456789ABCDEFGHJKMNPQRS"); // 1171591994633
|
||||||
|
```
|
||||||
|
|
||||||
Get the random component of a ULID:
|
Get the random component of a ULID:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
byte[] random = ulid.getRandom(); // 10 bytes (80 bits)
|
byte[] random = ulid.getRandom(); // 10 bytes (80 bits)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// static method
|
||||||
|
byte[] random = Ulid.getRandom("0123456789ABCDEFGHJKMNPQRS"); // 10 bytes (80 bits)
|
||||||
|
```
|
||||||
|
|
||||||
Use a `UlidFactory` instance with `java.util.Random` to generate ULIDs:
|
Use a `UlidFactory` instance with `java.util.Random` to generate ULIDs:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(random::nextBytes);
|
UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(random::nextBytes);
|
||||||
|
|
||||||
Ulid ulid = facory.create();
|
Ulid ulid = factory.create();
|
||||||
```
|
```
|
||||||
|
|
||||||
Use a `UlidFactory` instance with any random generator you like(*) to generate ULIDs:
|
Use a `UlidFactory` instance with any random generator you like(*) to generate ULIDs:
|
||||||
|
@ -196,7 +208,7 @@ import com.github.niceguy.random.AwesomeRandom; // a hypothetical RNG
|
||||||
AwesomeRandom awesomeRandom = new AwesomeRandom();
|
AwesomeRandom awesomeRandom = new AwesomeRandom();
|
||||||
UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(awesomeRandom::nextBytes);
|
UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(awesomeRandom::nextBytes);
|
||||||
|
|
||||||
Ulid ulid = facory.create();
|
Ulid ulid = factory.create();
|
||||||
```
|
```
|
||||||
|
|
||||||
(*) since it provides a void method like `nextBytes(byte[])`.
|
(*) since it provides a void method like `nextBytes(byte[])`.
|
||||||
|
|
|
@ -423,6 +423,18 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
|
||||||
return Instant.ofEpochMilli(this.getTime());
|
return Instant.ofEpochMilli(this.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instant of creation.
|
||||||
|
*
|
||||||
|
* The instant of creation is extracted from the time component.
|
||||||
|
*
|
||||||
|
* @param string a canonical string
|
||||||
|
* @return {@link Instant}
|
||||||
|
*/
|
||||||
|
public static Instant getInstant(String string) {
|
||||||
|
return Instant.ofEpochMilli(getTime(string));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time component as a number.
|
* Returns the time component as a number.
|
||||||
*
|
*
|
||||||
|
@ -435,6 +447,35 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
|
||||||
return this.msb >>> 16;
|
return this.msb >>> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time component as a number.
|
||||||
|
*
|
||||||
|
* The time component is a number between 0 and 2^48-1. It is equivalent to the
|
||||||
|
* count of milliseconds since 1970-01-01 (Unix epoch).
|
||||||
|
*
|
||||||
|
* @param string a canonical string
|
||||||
|
* @return a number of milliseconds.
|
||||||
|
*/
|
||||||
|
public static long getTime(String string) {
|
||||||
|
|
||||||
|
final char[] chars = toCharArray(string);
|
||||||
|
|
||||||
|
long time = 0;
|
||||||
|
|
||||||
|
time |= ALPHABET_VALUES[chars[0x00]] << 45;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x01]] << 40;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x02]] << 35;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x03]] << 30;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x04]] << 25;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x05]] << 20;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x06]] << 15;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x07]] << 10;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x08]] << 5;
|
||||||
|
time |= ALPHABET_VALUES[chars[0x09]];
|
||||||
|
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the random component as a byte array.
|
* Returns the random component as a byte array.
|
||||||
*
|
*
|
||||||
|
@ -443,8 +484,71 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
|
||||||
* @return a byte array
|
* @return a byte array
|
||||||
*/
|
*/
|
||||||
public byte[] getRandom() {
|
public byte[] getRandom() {
|
||||||
|
|
||||||
final byte[] bytes = new byte[RANDOM_BYTES_LENGTH];
|
final byte[] bytes = new byte[RANDOM_BYTES_LENGTH];
|
||||||
System.arraycopy(this.toBytes(), TIME_BYTES_LENGTH, bytes, 0, RANDOM_BYTES_LENGTH);
|
|
||||||
|
bytes[0x0] = (byte) (msb >>> 8);
|
||||||
|
bytes[0x1] = (byte) (msb);
|
||||||
|
|
||||||
|
bytes[0x2] = (byte) (lsb >>> 56);
|
||||||
|
bytes[0x3] = (byte) (lsb >>> 48);
|
||||||
|
bytes[0x4] = (byte) (lsb >>> 40);
|
||||||
|
bytes[0x5] = (byte) (lsb >>> 32);
|
||||||
|
bytes[0x6] = (byte) (lsb >>> 24);
|
||||||
|
bytes[0x7] = (byte) (lsb >>> 16);
|
||||||
|
bytes[0x8] = (byte) (lsb >>> 8);
|
||||||
|
bytes[0x9] = (byte) (lsb);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the random component as a byte array.
|
||||||
|
*
|
||||||
|
* The random component is an array of 10 bytes (80 bits).
|
||||||
|
*
|
||||||
|
* @param string a canonical string
|
||||||
|
* @return a byte array
|
||||||
|
*/
|
||||||
|
public static byte[] getRandom(String string) {
|
||||||
|
|
||||||
|
final char[] chars = toCharArray(string);
|
||||||
|
|
||||||
|
long random0 = 0;
|
||||||
|
long random1 = 0;
|
||||||
|
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x0a]] << 35;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x0b]] << 30;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x0c]] << 25;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x0d]] << 20;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x0e]] << 15;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x0f]] << 10;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x10]] << 5;
|
||||||
|
random0 |= ALPHABET_VALUES[chars[0x11]];
|
||||||
|
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x12]] << 35;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x13]] << 30;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x14]] << 25;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x15]] << 20;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x16]] << 15;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x17]] << 10;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x18]] << 5;
|
||||||
|
random1 |= ALPHABET_VALUES[chars[0x19]];
|
||||||
|
|
||||||
|
final byte[] bytes = new byte[RANDOM_BYTES_LENGTH];
|
||||||
|
|
||||||
|
bytes[0x0] = (byte) (random0 >>> 32);
|
||||||
|
bytes[0x1] = (byte) (random0 >>> 24);
|
||||||
|
bytes[0x2] = (byte) (random0 >>> 16);
|
||||||
|
bytes[0x3] = (byte) (random0 >>> 8);
|
||||||
|
bytes[0x4] = (byte) (random0);
|
||||||
|
|
||||||
|
bytes[0x5] = (byte) (random1 >>> 32);
|
||||||
|
bytes[0x6] = (byte) (random1 >>> 24);
|
||||||
|
bytes[0x7] = (byte) (random1 >>> 16);
|
||||||
|
bytes[0x8] = (byte) (random1 >>> 8);
|
||||||
|
bytes[0x9] = (byte) (random1);
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,20 +281,29 @@ public class UlidTest {
|
||||||
public void testGetTimeAndGetRandom() {
|
public void testGetTimeAndGetRandom() {
|
||||||
|
|
||||||
long time = 0;
|
long time = 0;
|
||||||
byte[] bytes = new byte[10];
|
byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH];
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
|
|
||||||
time = random.nextLong() & TIME_MASK;
|
time = random.nextLong() & TIME_MASK;
|
||||||
random.nextBytes(bytes);
|
random.nextBytes(bytes);
|
||||||
Ulid ulid = new Ulid(time, bytes);
|
|
||||||
|
|
||||||
|
// Instance methods
|
||||||
|
Ulid ulid = new Ulid(time, bytes);
|
||||||
assertEquals(time, ulid.getTime()); // test Ulid.getTime()
|
assertEquals(time, ulid.getTime()); // test Ulid.getTime()
|
||||||
assertEquals(Instant.ofEpochMilli(time), ulid.getInstant()); // test Ulid.getInstant()
|
assertEquals(Instant.ofEpochMilli(time), ulid.getInstant()); // test Ulid.getInstant()
|
||||||
for (int j = 0; j < bytes.length; j++) {
|
for (int j = 0; j < bytes.length; j++) {
|
||||||
assertEquals(bytes[j], ulid.getRandom()[j]); // test Ulid.getRandom()
|
assertEquals(bytes[j], ulid.getRandom()[j]); // test Ulid.getRandom()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Static methods
|
||||||
|
String string = new Ulid(time, bytes).toString();
|
||||||
|
assertEquals(time, Ulid.getTime(string)); // test Ulid.getTime()
|
||||||
|
assertEquals(Instant.ofEpochMilli(time), Ulid.getInstant(string)); // test Ulid.getInstant()
|
||||||
|
for (int j = 0; j < bytes.length; j++) {
|
||||||
|
assertEquals(bytes[j], Ulid.getRandom(string)[j]); // test Ulid.getRandom()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue