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:
Fabio Lima 2021-02-13 20:14:31 -03:00
parent b5b262be42
commit 1dffbf1ebb
3 changed files with 134 additions and 9 deletions

View File

@ -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[])`.

View File

@ -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;
} }

View File

@ -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()
}
} }
} }