Development of version 3.0.0 #7
continuing... List of changes: Improve Ulid Create UlidFactory Create DefaultUlidFactory Create MonotonicUlidFactory Improve UlidTest Create UlidFactoryTest Create DefaultUlidFactoryTest Create MonotonicUlidFactoryTest Update UniquenessTest Update README.md Update javadoc Test coverage: 99.3%
This commit is contained in:
		
							parent
							
								
									3aa29be465
								
							
						
					
					
						commit
						f1eaebd3bd
					
				
							
								
								
									
										256
									
								
								README.md
								
								
								
								
							
							
						
						
									
										256
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -1,13 +1,15 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ULID Creator
 | 
					# ULID Creator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A Java library for generating ULIDs.
 | 
					A Java library for generating [ULIDs](https://github.com/ulid/spec).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Generated in lexicographical order;
 | 
					* Generated in lexicographical order;
 | 
				
			||||||
* Can be stored as a UUID/GUID;
 | 
					* Can be stored as a UUID/GUID;
 | 
				
			||||||
* Can be stored as a string of 26 chars;
 | 
					* Can be stored as a string of 26 chars;
 | 
				
			||||||
 | 
					* Can be stored as an array of 16 bytes;
 | 
				
			||||||
* String format is encoded to [Crockford's base32](https://www.crockford.com/base32.html);
 | 
					* String format is encoded to [Crockford's base32](https://www.crockford.com/base32.html);
 | 
				
			||||||
* String format is URL safe, case insensitive and accepts hyphens.
 | 
					* String format is URL safe and case insensitive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
How to Use
 | 
					How to Use
 | 
				
			||||||
------------------------------------------------------
 | 
					------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					@ -15,13 +17,13 @@ How to Use
 | 
				
			||||||
Create a ULID:
 | 
					Create a ULID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
UUID ulid = UlidCreator.getUlid(); // 01706d6c-6aad-c795-370c-98d0be881bba
 | 
					Ulid ulid = UlidCreator.getUlid();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Create a ULID string:
 | 
					Create a Monotonic ULID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
String ulid = UlidCreator.getUlidString(); // 01E1PPRTMSQ34W7JR5YSND6B8Z
 | 
					Ulid ulid = UlidCreator.getMonotonicUlid();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Maven dependency
 | 
					### Maven dependency
 | 
				
			||||||
| 
						 | 
					@ -33,7 +35,7 @@ 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>2.3.3</version>
 | 
					  <version>3.0.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).
 | 
				
			||||||
| 
						 | 
					@ -43,116 +45,162 @@ Implementation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### ULID
 | 
					### ULID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The GUIDs in this library are based on the [ULID specification](https://github.com/ulid/spec). The first 48 bits represent the count of milliseconds since Unix Epoch, 1 January 1970. The remaining 60 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, 1 January 1970. The remaining 60 bits are generated by a secure random number generator.
 | 
				
			||||||
 | 
					 | 
				
			||||||
Every time the timestamp changes the random part is reset to a new random value. If the current timestamp is equal to the previous one, the random bits are incremented by 1.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The default random number generator is `java.security.SecureRandom`.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
// GUID based on ULID spec
 | 
					// Generate a ULID
 | 
				
			||||||
UUID ulid = UlidCreator.getUlid();
 | 
					Ulid ulid = UlidCreator.getUlid();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
// GUID based on ULID spec
 | 
					// Generate a ULID with a specific time
 | 
				
			||||||
// Compatible with RFC-4122 UUID v4
 | 
					Ulid ulid = UlidCreator.getUlid(1234567890);
 | 
				
			||||||
UUID ulid = UlidCreator.getUlid4();
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Sequence of GUIDs based on ULID spec:
 | 
					Sequence of ULIDs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```text
 | 
					```text
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58ea
 | 
					01EX8Y21KBH49ZZCA7KSKH6X1C
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58eb
 | 
					01EX8Y21KBJTFK0JV5J20QPQNR
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58ec
 | 
					01EX8Y21KBG2CS1V6WQCTVM7K6
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58ed
 | 
					01EX8Y21KB8HPZNBP3PTW7HVEY
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58ee
 | 
					01EX8Y21KB3HZV38VAPTPAG1TY
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58ef
 | 
					01EX8Y21KB9FTEJHPAGAKYG9Z8
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58f0
 | 
					01EX8Y21KBQGKGH2SVPQAYEFFC
 | 
				
			||||||
01706d6c-6aac-80bd-7ff5-f660c2dd58f1
 | 
					01EX8Y21KBY17J9WR9KQR8SE7H
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bb8 < millisecond changed
 | 
					01EX8Y21KCVHYSJGVK4HBXDMR9 < millisecond changed
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bb9
 | 
					01EX8Y21KC668W3PEDEAGDHMVG
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bba
 | 
					01EX8Y21KC53D2S5ADQ2EST327
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bbb
 | 
					01EX8Y21KCPQ3TENMTY1S7HV56
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bbc
 | 
					01EX8Y21KC3755QF9STQEV05EB
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bbd
 | 
					01EX8Y21KC5ZSHK908GMDK69WE
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bbe
 | 
					01EX8Y21KCSGJS8S1FVS06B3SX
 | 
				
			||||||
01706d6c-6aad-c795-370c-98d0be881bbf
 | 
					01EX8Y21KC6ZBWQ0JBV337R1CN
 | 
				
			||||||
            ^ look                 ^ look
 | 
					         ^ look
 | 
				
			||||||
                                   
 | 
					 | 
				
			||||||
|------------|---------------------|
 | 
					 | 
				
			||||||
  millisecs        randomness
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### ULID string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The ULID string is a sequence of 26 chars. See the [ULID specification](https://github.com/ulid/spec) for more information.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See the section on GUIDs to know how the 128 bits are generated in this library.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```java
 | 
					 | 
				
			||||||
// String based on ULID spec
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B8T
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B8V
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B8W
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B8X
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B8Y
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B8Z
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B90
 | 
					 | 
				
			||||||
01E1PPRTMSQ34W7JR5YSND6B91
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEE < millisecond changed
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEF
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEG
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEH
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEJ
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEK
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEM
 | 
					 | 
				
			||||||
01E1PPRTMTYMX8G17TWSJJZMEN
 | 
					 | 
				
			||||||
         ^ look          ^ look
 | 
					 | 
				
			||||||
                                   
 | 
					 | 
				
			||||||
|---------|--------------|
 | 
					|---------|--------------|
 | 
				
			||||||
 millisecs   randomness
 | 
					    time      random
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### How use the `UlidSpecCreator` directly
 | 
					### Monotonic ULID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
These are some examples of using the `UlidSpecCreator` to create ULID strings:
 | 
					The Monotonic ULID is a 128 bit long identifier. The first 48 bits represent the count of milliseconds since Unix Epoch, 1 January 1970. The remaining 60 bits are generated by a secure random number generator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The random component is incremented by 1 whenever the current millisecond is equal to the previous one. But when the current millisecond is different, the random component changes to another random value.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
// with your custom timestamp strategy
 | 
					// Generate a Monotonic ULID
 | 
				
			||||||
TimestampStrategy customStrategy = new CustomTimestampStrategy();
 | 
					Ulid ulid = UlidCreator.getMonotonicUlid();
 | 
				
			||||||
UlidSpecCreator creator = UlidCreator.getUlidSpecCreator()
 | 
					 | 
				
			||||||
	.withTimestampStrategy(customStrategy);
 | 
					 | 
				
			||||||
String ulid = creator.createString();
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
// with your custom random strategy that wraps any random generator
 | 
					// Generate a Monotonic ULID with a specific time
 | 
				
			||||||
RandomStrategy customStrategy = new CustomRandomStrategy();
 | 
					Ulid ulid = UlidCreator.getMonotonicUlid(1234567890);
 | 
				
			||||||
UlidSpecCreator creator = UlidCreator.getUlidSpecCreator()
 | 
					 | 
				
			||||||
	.withRandomStrategy(customStrategy);
 | 
					 | 
				
			||||||
String ulid = creator.createString();
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sequence of Monotonic ULIDs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```text
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMJW
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMJX
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMJY
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMJZ
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMK0
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMK1
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMK2
 | 
				
			||||||
 | 
					01EX8Y7M8MDVX3M3EQG69EEMK3
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2J < millisecond changed
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2K
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2M
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2N
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2P
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2Q
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2R
 | 
				
			||||||
 | 
					01EX8Y7M8N1G30CYF2PJR23J2S
 | 
				
			||||||
 | 
					         ^ look          ^ look
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					|---------|--------------|
 | 
				
			||||||
 | 
					    time      random
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Other usage examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Create a ULID from a canonical string (26 chars):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					Ulid ulid = Ulid.from("0123456789ABCDEFGHJKMNPQRS");
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Convert a ULID into a canonical string in upper case:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					String string = ulid.toUpperCase(); // 0123456789ABCDEFGHJKMNPQRS
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Convert a ULID into a canonical string in lower case:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					String string = ulid.toLowerCase(); // 0123456789abcdefghjkmnpqrs
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Convert a ULID into a UUID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					UUID uuid = ulid.toUuid(); // 0110c853-1d09-52d8-d73e-1194e95b5f19
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Convert a ULID into a [RFC-4122](https://tools.ietf.org/html/rfc4122) UUID v4:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					UUID uuid = ulid.toRfc4122().toUuid(); // 0110c853-1d09-42d8-973e-1194e95b5f19
 | 
				
			||||||
 | 
					                                       //               ^ UUID v4
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Convert a ULID into a byte array:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					byte[] bytes = ulid.toBytes(); // 16 bytes (128 bits)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get the creation instant of a ULID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					Instant instant = ulid.getInstant(); // 2007-02-16T02:13:14.633Z
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get the time component of a ULID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					long time = ulid.getTime(); // 1171591994633
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get the random component of a ULID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					byte[] random = ulid.getRandom(); // 10 bytes (80 bits)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use a `UlidFctory` instance with `java.util.Random` to generate ULIDs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
// with `java.util.Random` number generator
 | 
					 | 
				
			||||||
Random random = new Random();
 | 
					Random random = new Random();
 | 
				
			||||||
UlidSpecCreator creator = UlidCreator.getUlidSpecCreator()
 | 
					UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(random::nextBytes);
 | 
				
			||||||
    .withRandomGenerator(random);
 | 
					
 | 
				
			||||||
String ulid = creator.createString();
 | 
					Ulid ulid = facory.create();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use a `UlidFctory` instance with any random generator you like(*) to generate ULIDs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					import com.github.niceguy.random.AwesomeRandom; // a hypothetical RNG
 | 
				
			||||||
 | 
					AwesomeRandom awesomeRandom = new AwesomeRandom();
 | 
				
			||||||
 | 
					UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(awesomeRandom::nextBytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ulid ulid = facory.create();
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(*) since it provides a void method like `nextBytes(byte[])`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Benchmark
 | 
					Benchmark
 | 
				
			||||||
------------------------------------------------------
 | 
					------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,14 +208,18 @@ This section shows benchmarks comparing `UlidCreator` to `java.util.UUID`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
================================================================================
 | 
					================================================================================
 | 
				
			||||||
THROUGHPUT (operations/millis)           Mode  Cnt      Score     Error   Units
 | 
					THROUGHPUT (operations/msec)           Mode  Cnt      Score     Error   Units
 | 
				
			||||||
================================================================================
 | 
					================================================================================
 | 
				
			||||||
Throughput.JDK_RandomBased              thrpt    5   2050,995 ±  21,636  ops/ms
 | 
					Throughput.Uuid01_toString              thrpt    5   2876,799 ±  39,938  ops/ms
 | 
				
			||||||
 | 
					Throughput.Uuid02_fromString            thrpt    5   1936,569 ±  38,822  ops/ms
 | 
				
			||||||
 | 
					Throughput.Uuid03_RandomBased           thrpt    5   2011,774 ±  21,198  ops/ms
 | 
				
			||||||
--------------------------------------------------------------------------------
 | 
					--------------------------------------------------------------------------------
 | 
				
			||||||
Throughput.UlidCreator_Ulid             thrpt    5  18524,721 ± 563,781  ops/ms
 | 
					Throughput.UlidCreator01_toString       thrpt    5  29487,382 ± 627,808  ops/ms
 | 
				
			||||||
Throughput.UlidCreator_UlidString       thrpt    5  12223,501 ±  89,836  ops/ms
 | 
					Throughput.UlidCreator02_fromString     thrpt    5  21194,263 ± 706,398  ops/ms
 | 
				
			||||||
 | 
					Throughput.UlidCreator03_Ulid           thrpt    5   2745,123 ±  41,326  ops/ms
 | 
				
			||||||
 | 
					Throughput.UlidCreator04_MonotonicUlid  thrpt    5  19542,344 ± 423,271  ops/ms
 | 
				
			||||||
================================================================================
 | 
					================================================================================
 | 
				
			||||||
Total time: 00:04:00
 | 
					Total time: 00:09:22
 | 
				
			||||||
================================================================================
 | 
					================================================================================
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,8 +227,8 @@ System: JVM 8, Ubuntu 20.04, CPU i5-3330, 8G RAM.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
See: [uuid-creator-benchmark](https://github.com/fabiolimace/uuid-creator-benchmark)
 | 
					See: [uuid-creator-benchmark](https://github.com/fabiolimace/uuid-creator-benchmark)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Links for generators
 | 
					Other generators
 | 
				
			||||||
-------------------------------------------
 | 
					-------------------------------------------
 | 
				
			||||||
* [UUID Creator](https://github.com/f4b6a3/uuid-creator)
 | 
					* [UUID Creator](https://github.com/f4b6a3/uuid-creator): for generating UUIDs
 | 
				
			||||||
* [ULID Creator](https://github.com/f4b6a3/ulid-creator)
 | 
					* [TSID Creator](https://github.com/f4b6a3/tsid-creator): for generating Time Sortable IDs
 | 
				
			||||||
* [TSID Creator](https://github.com/f4b6a3/tsid-creator)
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								pom.xml
								
								
								
								
							
							
						
						
									
										2
									
								
								pom.xml
								
								
								
								
							| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<groupId>com.github.f4b6a3</groupId>
 | 
						<groupId>com.github.f4b6a3</groupId>
 | 
				
			||||||
	<artifactId>ulid-creator</artifactId>
 | 
						<artifactId>ulid-creator</artifactId>
 | 
				
			||||||
	<version>2.3.4-SNAPSHOT</version>
 | 
						<version>3.0.1-SNAPSHOT</version>
 | 
				
			||||||
	<packaging>jar</packaging>
 | 
						<packaging>jar</packaging>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<name>ulid-creator</name>
 | 
						<name>ulid-creator</name>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,13 +30,23 @@ import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This class represents a ULID.
 | 
					 * This class represents a ULID.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The ULID has two components:
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * - Time component: a part of 48 bits that represent the amount of milliseconds
 | 
				
			||||||
 | 
					 * since Unix Epoch, 1970-01-01.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * - Random component: a byte array of 80 bits that has a random value generated
 | 
				
			||||||
 | 
					 * a secure random generator.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Instances of this class are immutable.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
					public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final long msb;
 | 
						private static final long serialVersionUID = 2625269413446854731L;
 | 
				
			||||||
	private final long lsb;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected static final long TIME_MAX = 281474976710655L; // 2^48 - 1
 | 
						private final long msb; // most significant bits
 | 
				
			||||||
 | 
						private final long lsb; // least significant bits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static final int ULID_LENGTH = 26;
 | 
						public static final int ULID_LENGTH = 26;
 | 
				
			||||||
	public static final int TIME_LENGTH = 10;
 | 
						public static final int TIME_LENGTH = 10;
 | 
				
			||||||
| 
						 | 
					@ -46,20 +56,17 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
	public static final int TIME_BYTES_LENGTH = 6;
 | 
						public static final int TIME_BYTES_LENGTH = 6;
 | 
				
			||||||
	public static final int RANDOM_BYTES_LENGTH = 10;
 | 
						public static final int RANDOM_BYTES_LENGTH = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 0xffffffffffffffffL + 1 = 0x0000000000000000L
 | 
						private static final char[] ALPHABET_UPPERCASE = //
 | 
				
			||||||
	private static final long INCREMENT_OVERFLOW = 0x0000000000000000L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static final char[] ALPHABET_UPPERCASE = //
 | 
					 | 
				
			||||||
			{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
 | 
								{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
 | 
				
			||||||
					'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', //
 | 
										'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', //
 | 
				
			||||||
					'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' };
 | 
										'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected static final char[] ALPHABET_LOWERCASE = //
 | 
						private static final char[] ALPHABET_LOWERCASE = //
 | 
				
			||||||
			{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
 | 
								{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
 | 
				
			||||||
					'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', //
 | 
										'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', //
 | 
				
			||||||
					'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z' };
 | 
										'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected static final long[] ALPHABET_VALUES = new long[128];
 | 
						private static final long[] ALPHABET_VALUES = new long[128];
 | 
				
			||||||
	static {
 | 
						static {
 | 
				
			||||||
		for (int i = 0; i < ALPHABET_VALUES.length; i++) {
 | 
							for (int i = 0; i < ALPHABET_VALUES.length; i++) {
 | 
				
			||||||
			ALPHABET_VALUES[i] = -1;
 | 
								ALPHABET_VALUES[i] = -1;
 | 
				
			||||||
| 
						 | 
					@ -129,139 +136,190 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
		ALPHABET_VALUES['O'] = 0x00;
 | 
							ALPHABET_VALUES['O'] = 0x00;
 | 
				
			||||||
		ALPHABET_VALUES['I'] = 0x01;
 | 
							ALPHABET_VALUES['I'] = 0x01;
 | 
				
			||||||
		ALPHABET_VALUES['L'] = 0x01;
 | 
							ALPHABET_VALUES['L'] = 0x01;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final long serialVersionUID = 2625269413446854731L;
 | 
						// 0xffffffffffffffffL + 1 = 0x0000000000000000L
 | 
				
			||||||
 | 
						private static final long INCREMENT_OVERFLOW = 0x0000000000000000L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Create a new ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * Useful to make copies of ULIDs.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param ulid a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Ulid(Ulid ulid) {
 | 
				
			||||||
 | 
							this.msb = ulid.getMostSignificantBits();
 | 
				
			||||||
 | 
							this.lsb = ulid.getLeastSignificantBits();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Create a new ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param mostSignificantBits  the first 8 bytes as a long value
 | 
				
			||||||
 | 
						 * @param leastSignificantBits the last 8 bytes as a long value
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public Ulid(long mostSignificantBits, long leastSignificantBits) {
 | 
						public Ulid(long mostSignificantBits, long leastSignificantBits) {
 | 
				
			||||||
		this.msb = mostSignificantBits;
 | 
							this.msb = mostSignificantBits;
 | 
				
			||||||
		this.lsb = leastSignificantBits;
 | 
							this.lsb = leastSignificantBits;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static Ulid of(Ulid ulid) {
 | 
						/**
 | 
				
			||||||
		return new Ulid(ulid.getMostSignificantBits(), ulid.getLeastSignificantBits());
 | 
						 * Create a new ULID.
 | 
				
			||||||
	}
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param time   the time component in milliseconds since 1970-01-01
 | 
				
			||||||
 | 
						 * @param random the random component in byte array
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Ulid(long time, byte[] random) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static Ulid of(UUID uuid) {
 | 
							// The time component has 48 bits.
 | 
				
			||||||
		return new Ulid(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
 | 
							if ((time & 0xffff000000000000L) != 0) {
 | 
				
			||||||
	}
 | 
								// ULID specification:
 | 
				
			||||||
 | 
								// "Any attempt to decode or encode a ULID larger than this (time > 2^48-1)
 | 
				
			||||||
	// TODO: test
 | 
								// should be rejected by all implementations, to prevent overflow bugs."
 | 
				
			||||||
	public static Ulid of(byte[] bytes) {
 | 
								throw new IllegalArgumentException("Invalid time value"); // time overflow!
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (bytes == null || bytes.length != ULID_BYTES_LENGTH) {
 | 
							// The random component has 80 bits (10 bytes).
 | 
				
			||||||
			throw new IllegalArgumentException("Invalid ULID bytes");
 | 
							if (random == null || random.length != RANDOM_BYTES_LENGTH) {
 | 
				
			||||||
 | 
								throw new IllegalArgumentException("Invalid random bytes"); // null or wrong length!
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long long0 = 0;
 | 
							long long0 = 0;
 | 
				
			||||||
		long long1 = 0;
 | 
							long long1 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long0 |= (bytes[0x0] & 0xffL) << 56;
 | 
							long0 |= time << 16;
 | 
				
			||||||
		long0 |= (bytes[0x1] & 0xffL) << 48;
 | 
							long0 |= (long) (random[0x0] & 0xff) << 8;
 | 
				
			||||||
		long0 |= (bytes[0x2] & 0xffL) << 40;
 | 
							long0 |= (long) (random[0x1] & 0xff);
 | 
				
			||||||
		long0 |= (bytes[0x3] & 0xffL) << 32;
 | 
					 | 
				
			||||||
		long0 |= (bytes[0x4] & 0xffL) << 24;
 | 
					 | 
				
			||||||
		long0 |= (bytes[0x5] & 0xffL) << 16;
 | 
					 | 
				
			||||||
		long0 |= (bytes[0x6] & 0xffL) << 8;
 | 
					 | 
				
			||||||
		long0 |= (bytes[0x7] & 0xffL);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long1 |= (bytes[0x8] & 0xffL) << 56;
 | 
							long1 |= (long) (random[0x2] & 0xff) << 56;
 | 
				
			||||||
		long1 |= (bytes[0x9] & 0xffL) << 48;
 | 
							long1 |= (long) (random[0x3] & 0xff) << 48;
 | 
				
			||||||
		long1 |= (bytes[0xa] & 0xffL) << 40;
 | 
							long1 |= (long) (random[0x4] & 0xff) << 40;
 | 
				
			||||||
		long1 |= (bytes[0xb] & 0xffL) << 32;
 | 
							long1 |= (long) (random[0x5] & 0xff) << 32;
 | 
				
			||||||
		long1 |= (bytes[0xc] & 0xffL) << 24;
 | 
							long1 |= (long) (random[0x6] & 0xff) << 24;
 | 
				
			||||||
		long1 |= (bytes[0xd] & 0xffL) << 16;
 | 
							long1 |= (long) (random[0x7] & 0xff) << 16;
 | 
				
			||||||
		long1 |= (bytes[0xe] & 0xffL) << 8;
 | 
							long1 |= (long) (random[0x8] & 0xff) << 8;
 | 
				
			||||||
		long1 |= (bytes[0xf] & 0xffL);
 | 
							long1 |= (long) (random[0x9] & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return new Ulid(long0, long1);
 | 
							this.msb = long0;
 | 
				
			||||||
 | 
							this.lsb = long1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: optimize
 | 
						/**
 | 
				
			||||||
	public static Ulid of(String string) {
 | 
						 * Converts a UUID into a ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
		final char[] chars = toCharArray(string);
 | 
						 * @param uuid a UUID
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
		long tm = 0;
 | 
						 */
 | 
				
			||||||
		long r1 = 0;
 | 
						public static Ulid from(UUID uuid) {
 | 
				
			||||||
		long r2 = 0;
 | 
							return new Ulid(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
 | 
				
			||||||
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x00]] << 45;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x01]] << 40;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x02]] << 35;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x03]] << 30;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x04]] << 25;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x05]] << 20;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x06]] << 15;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x07]] << 10;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x08]] << 5;
 | 
					 | 
				
			||||||
		tm |= ALPHABET_VALUES[chars[0x09]];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x0a]] << 35;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x0b]] << 30;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x0c]] << 25;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x0d]] << 20;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x0e]] << 15;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x0f]] << 10;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x10]] << 5;
 | 
					 | 
				
			||||||
		r1 |= ALPHABET_VALUES[chars[0x11]];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x12]] << 35;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x13]] << 30;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x14]] << 25;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x15]] << 20;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x16]] << 15;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x17]] << 10;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x18]] << 5;
 | 
					 | 
				
			||||||
		r2 |= ALPHABET_VALUES[chars[0x19]];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		final long msb = (tm << 16) | (r1 >>> 24);
 | 
					 | 
				
			||||||
		final long lsb = (r1 << 40) | (r2 & 0xffffffffffL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return new Ulid(msb, lsb);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static Ulid of(long time, byte[] random) {
 | 
						/**
 | 
				
			||||||
 | 
						 * Converts a byte array into a ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param bytes a byte array
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static Ulid from(byte[] bytes) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ((time & 0xffff000000000000L) != 0) {
 | 
							if (bytes == null || bytes.length != ULID_BYTES_LENGTH) {
 | 
				
			||||||
			throw new IllegalArgumentException("Invalid time value");
 | 
								throw new IllegalArgumentException("Invalid ULID bytes"); // null or wrong length!
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (random == null || random.length != RANDOM_BYTES_LENGTH) {
 | 
					 | 
				
			||||||
			throw new IllegalArgumentException("Invalid random bytes");
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long msb = 0;
 | 
							long msb = 0;
 | 
				
			||||||
		long lsb = 0;
 | 
							long lsb = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		msb |= time << 16;
 | 
							msb |= (bytes[0x0] & 0xffL) << 56;
 | 
				
			||||||
		msb |= (long) (random[0x0] & 0xff) << 8;
 | 
							msb |= (bytes[0x1] & 0xffL) << 48;
 | 
				
			||||||
		msb |= (long) (random[0x1] & 0xff);
 | 
							msb |= (bytes[0x2] & 0xffL) << 40;
 | 
				
			||||||
 | 
							msb |= (bytes[0x3] & 0xffL) << 32;
 | 
				
			||||||
 | 
							msb |= (bytes[0x4] & 0xffL) << 24;
 | 
				
			||||||
 | 
							msb |= (bytes[0x5] & 0xffL) << 16;
 | 
				
			||||||
 | 
							msb |= (bytes[0x6] & 0xffL) << 8;
 | 
				
			||||||
 | 
							msb |= (bytes[0x7] & 0xffL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		lsb |= (long) (random[0x2] & 0xff) << 56;
 | 
							lsb |= (bytes[0x8] & 0xffL) << 56;
 | 
				
			||||||
		lsb |= (long) (random[0x3] & 0xff) << 48;
 | 
							lsb |= (bytes[0x9] & 0xffL) << 48;
 | 
				
			||||||
		lsb |= (long) (random[0x4] & 0xff) << 40;
 | 
							lsb |= (bytes[0xa] & 0xffL) << 40;
 | 
				
			||||||
		lsb |= (long) (random[0x5] & 0xff) << 32;
 | 
							lsb |= (bytes[0xb] & 0xffL) << 32;
 | 
				
			||||||
		lsb |= (long) (random[0x6] & 0xff) << 24;
 | 
							lsb |= (bytes[0xc] & 0xffL) << 24;
 | 
				
			||||||
		lsb |= (long) (random[0x7] & 0xff) << 16;
 | 
							lsb |= (bytes[0xd] & 0xffL) << 16;
 | 
				
			||||||
		lsb |= (long) (random[0x8] & 0xff) << 8;
 | 
							lsb |= (bytes[0xe] & 0xffL) << 8;
 | 
				
			||||||
		lsb |= (long) (random[0x9] & 0xff);
 | 
							lsb |= (bytes[0xf] & 0xffL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return new Ulid(msb, lsb);
 | 
							return new Ulid(msb, lsb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Converts a canonical string into a ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The input string must be 26 characters long and must contain only characters
 | 
				
			||||||
 | 
						 * from Crockford's base 32 alphabet.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The first character of the input string must be between 0 and 7.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param string a canonical string
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static Ulid from(String string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							final char[] chars = toCharArray(string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							long time = 0;
 | 
				
			||||||
 | 
							long random0 = 0;
 | 
				
			||||||
 | 
							long random1 = 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]];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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 long msb = (time << 16) | (random0 >>> 24);
 | 
				
			||||||
 | 
							final long lsb = (random0 << 40) | (random1 & 0xffffffffffL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return new Ulid(msb, lsb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Convert the ULID into a UUID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * If you need a RFC-4122 UUID v4 do this: {@code Ulid.toRfc4122().toUuid()}.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a UUID.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public UUID toUuid() {
 | 
						public UUID toUuid() {
 | 
				
			||||||
		return new UUID(this.msb, this.lsb);
 | 
							return new UUID(this.msb, this.lsb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: test
 | 
						/**
 | 
				
			||||||
	public UUID toUuid4() {
 | 
						 * Convert the ULID into a byte array.
 | 
				
			||||||
		final long msb4 = (this.msb & 0xffffffffffff0fffL) | 0x0000000000004000L; // apply version 4
 | 
						 * 
 | 
				
			||||||
		final long lsb4 = (this.lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant RFC-4122
 | 
						 * @return an byte array.
 | 
				
			||||||
		return new UUID(msb4, lsb4);
 | 
						 */
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: test
 | 
					 | 
				
			||||||
	public byte[] toBytes() {
 | 
						public byte[] toBytes() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		final byte[] bytes = new byte[ULID_BYTES_LENGTH];
 | 
							final byte[] bytes = new byte[ULID_BYTES_LENGTH];
 | 
				
			||||||
| 
						 | 
					@ -287,64 +345,183 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
		return bytes;
 | 
							return bytes;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: test
 | 
						/**
 | 
				
			||||||
	public byte[] toBytes4() {
 | 
						 * Converts the ULID into a canonical string in upper case.
 | 
				
			||||||
		return Ulid.of(this.toUuid4()).toBytes();
 | 
						 * 
 | 
				
			||||||
	}
 | 
						 * The output string is 26 characters long and contains only characters from
 | 
				
			||||||
 | 
						 * Crockford's base 32 alphabet.
 | 
				
			||||||
	@Override
 | 
						 * 
 | 
				
			||||||
	public String toString() {
 | 
						 * See: https://www.crockford.com/base32.html
 | 
				
			||||||
		return this.toUpperCase();
 | 
						 * 
 | 
				
			||||||
	}
 | 
						 * @return a string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	// TODO: test
 | 
					 | 
				
			||||||
	public String toUpperCase() {
 | 
						public String toUpperCase() {
 | 
				
			||||||
		return toString(ALPHABET_UPPERCASE);
 | 
							return toString(ALPHABET_UPPERCASE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: test
 | 
						/**
 | 
				
			||||||
	public String toUpperCase4() {
 | 
						 * Converts the ULID into a canonical string in lower case.
 | 
				
			||||||
		return Ulid.of(this.toUuid4()).toUpperCase();
 | 
						 * 
 | 
				
			||||||
	}
 | 
						 * The output string is 26 characters long and contains only characters from
 | 
				
			||||||
 | 
						 * Crockford's base 32 alphabet.
 | 
				
			||||||
	// TODO: test
 | 
						 * 
 | 
				
			||||||
 | 
						 * It is at least twice as fast as {@code Ulid.toString().toLowerCase()}.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * See: https://www.crockford.com/base32.html
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public String toLowerCase() {
 | 
						public String toLowerCase() {
 | 
				
			||||||
		return toString(ALPHABET_LOWERCASE);
 | 
							return toString(ALPHABET_LOWERCASE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: test
 | 
						/**
 | 
				
			||||||
	public String toLowerCase4() {
 | 
						 * Converts the ULID into into another ULID that is compatible with UUID v4.
 | 
				
			||||||
		return Ulid.of(this.toUuid4()).toLowerCase();
 | 
						 * 
 | 
				
			||||||
	}
 | 
						 * The bytes of the returned ULID are compliant with the RFC-4122 version 4.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
	public long getTime() {
 | 
						 * If you need a RFC-4122 UUID v4 do this: {@code Ulid.toRfc4122().toUuid()}.
 | 
				
			||||||
		return this.msb >>> 16;
 | 
						 * 
 | 
				
			||||||
 | 
						 * Read: https://tools.ietf.org/html/rfc4122
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * ### RFC-4122 - 4.4. Algorithms for Creating a UUID from Truly Random or
 | 
				
			||||||
 | 
						 * Pseudo-Random Numbers
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The version 4 UUID is meant for generating UUIDs from truly-random or
 | 
				
			||||||
 | 
						 * pseudo-random numbers.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The algorithm is as follows:
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * - Set the two most significant bits (bits 6 and 7) of the
 | 
				
			||||||
 | 
						 * clock_seq_hi_and_reserved to zero and one, respectively.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * - Set the four most significant bits (bits 12 through 15) of the
 | 
				
			||||||
 | 
						 * time_hi_and_version field to the 4-bit version number from Section 4.1.3.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * - Set all the other bits to randomly (or pseudo-randomly) chosen values.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Ulid toRfc4122() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// set the 4 most significant bits of the 7th byte to 0, 1, 0 and 0
 | 
				
			||||||
 | 
							final long msb4 = (this.msb & 0xffffffffffff0fffL) | 0x0000000000004000L; // RFC-4122 version 4
 | 
				
			||||||
 | 
							// set the 2 most significant bits of the 9th byte to 1 and 0
 | 
				
			||||||
 | 
							final long lsb4 = (this.lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // RFC-4122 variant 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return new Ulid(msb4, lsb4);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns the instant of creation.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The instant of creation is extracted from the time component.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return {@link Instant}
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public Instant getInstant() {
 | 
						public Instant getInstant() {
 | 
				
			||||||
		return Instant.ofEpochMilli(this.getTime());
 | 
							return Instant.ofEpochMilli(this.getTime());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 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).
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a number of milliseconds.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public long getTime() {
 | 
				
			||||||
 | 
							return this.msb >>> 16;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns the random component as a byte array.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The random component is an array of 10 bytes (80 bits).
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a byte array
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public byte[] getRandom() {
 | 
				
			||||||
 | 
							final byte[] bytes = new byte[RANDOM_BYTES_LENGTH];
 | 
				
			||||||
 | 
							System.arraycopy(this.toBytes(), TIME_BYTES_LENGTH, bytes, 0, RANDOM_BYTES_LENGTH);
 | 
				
			||||||
 | 
							return bytes;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns the most significant bits as a number.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a number.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public long getMostSignificantBits() {
 | 
						public long getMostSignificantBits() {
 | 
				
			||||||
		return this.msb;
 | 
							return this.msb;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns the least significant bits as a number.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a number.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public long getLeastSignificantBits() {
 | 
						public long getLeastSignificantBits() {
 | 
				
			||||||
		return this.lsb;
 | 
							return this.lsb;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: test
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a new ULID by incrementing the random component of the current ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * Since the random component contains 80 bits:
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * (1) This method can generate up to 1208925819614629174706176 (2^80) ULIDs per
 | 
				
			||||||
 | 
						 * millisecond;
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * (2) This method can generate monotonic increasing ULIDs 99.999999999999992%
 | 
				
			||||||
 | 
						 * ((2^80 - 10^9) / (2^80)) of the time, considering an unrealistic rate of
 | 
				
			||||||
 | 
						 * 1,000,000,000 ULIDs per millisecond.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * Due to (1) and (2), it does not throw the error message recommended by the
 | 
				
			||||||
 | 
						 * specification. When an overflow occurs in the last 80 bits, the random
 | 
				
			||||||
 | 
						 * component simply wraps around.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public Ulid increment() {
 | 
						public Ulid increment() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long msb1 = this.msb;
 | 
							long newMsb = this.msb;
 | 
				
			||||||
		long lsb1 = this.lsb + 1; // Increment the LSB
 | 
							long newLsb = this.lsb + 1; // increment the LEAST significant bits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (lsb1 == INCREMENT_OVERFLOW) {
 | 
							if (newLsb == INCREMENT_OVERFLOW) {
 | 
				
			||||||
			// Increment the random bits of the MSB
 | 
								// carrying the extra bit by incrementing the MOST significant bits
 | 
				
			||||||
			msb1 = (msb1 & 0xffffffffffff0000L) | ((msb1 + 1) & 0x000000000000ffffL);
 | 
								newMsb = (newMsb & 0xffffffffffff0000L) | ((newMsb + 1) & 0x000000000000ffffL);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return new Ulid(msb1, lsb1);
 | 
							return new Ulid(newMsb, newLsb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Checks if the input string is valid.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The input string must be 26 characters long and must contain only characters
 | 
				
			||||||
 | 
						 * from Crockford's base 32 alphabet.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The first character of the input string must be between 0 and 7.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param string a string
 | 
				
			||||||
 | 
						 * @return true if valid
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static boolean isValid(String string) {
 | 
				
			||||||
 | 
							return string != null && isValidCharArray(string.toCharArray());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Converts the ULID into a canonical string in upper case.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * It is the same as {@code Ulid.toUpperCase()}.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String toString() {
 | 
				
			||||||
 | 
							return this.toUpperCase();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
| 
						 | 
					@ -385,14 +562,13 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: optimize
 | 
					 | 
				
			||||||
	protected String toString(char[] alphabet) {
 | 
						protected String toString(char[] alphabet) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		final char[] chars = new char[ULID_LENGTH];
 | 
							final char[] chars = new char[ULID_LENGTH];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long time = this.msb >>> 16;
 | 
							long time = this.msb >>> 16;
 | 
				
			||||||
		long random1 = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40);
 | 
							long random0 = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40);
 | 
				
			||||||
		long random2 = (this.lsb & 0xffffffffffL);
 | 
							long random1 = (this.lsb & 0xffffffffffL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		chars[0x00] = alphabet[(int) (time >>> 45 & 0b11111)];
 | 
							chars[0x00] = alphabet[(int) (time >>> 45 & 0b11111)];
 | 
				
			||||||
		chars[0x01] = alphabet[(int) (time >>> 40 & 0b11111)];
 | 
							chars[0x01] = alphabet[(int) (time >>> 40 & 0b11111)];
 | 
				
			||||||
| 
						 | 
					@ -405,29 +581,33 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
		chars[0x08] = alphabet[(int) (time >>> 5 & 0b11111)];
 | 
							chars[0x08] = alphabet[(int) (time >>> 5 & 0b11111)];
 | 
				
			||||||
		chars[0x09] = alphabet[(int) (time & 0b11111)];
 | 
							chars[0x09] = alphabet[(int) (time & 0b11111)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		chars[0x0a] = alphabet[(int) (random1 >>> 35 & 0b11111)];
 | 
							chars[0x0a] = alphabet[(int) (random0 >>> 35 & 0b11111)];
 | 
				
			||||||
		chars[0x0b] = alphabet[(int) (random1 >>> 30 & 0b11111)];
 | 
							chars[0x0b] = alphabet[(int) (random0 >>> 30 & 0b11111)];
 | 
				
			||||||
		chars[0x0c] = alphabet[(int) (random1 >>> 25 & 0b11111)];
 | 
							chars[0x0c] = alphabet[(int) (random0 >>> 25 & 0b11111)];
 | 
				
			||||||
		chars[0x0d] = alphabet[(int) (random1 >>> 20 & 0b11111)];
 | 
							chars[0x0d] = alphabet[(int) (random0 >>> 20 & 0b11111)];
 | 
				
			||||||
		chars[0x0e] = alphabet[(int) (random1 >>> 15 & 0b11111)];
 | 
							chars[0x0e] = alphabet[(int) (random0 >>> 15 & 0b11111)];
 | 
				
			||||||
		chars[0x0f] = alphabet[(int) (random1 >>> 10 & 0b11111)];
 | 
							chars[0x0f] = alphabet[(int) (random0 >>> 10 & 0b11111)];
 | 
				
			||||||
		chars[0x10] = alphabet[(int) (random1 >>> 5 & 0b11111)];
 | 
							chars[0x10] = alphabet[(int) (random0 >>> 5 & 0b11111)];
 | 
				
			||||||
		chars[0x11] = alphabet[(int) (random1 & 0b11111)];
 | 
							chars[0x11] = alphabet[(int) (random0 & 0b11111)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		chars[0x12] = alphabet[(int) (random2 >>> 35 & 0b11111)];
 | 
							chars[0x12] = alphabet[(int) (random1 >>> 35 & 0b11111)];
 | 
				
			||||||
		chars[0x13] = alphabet[(int) (random2 >>> 30 & 0b11111)];
 | 
							chars[0x13] = alphabet[(int) (random1 >>> 30 & 0b11111)];
 | 
				
			||||||
		chars[0x14] = alphabet[(int) (random2 >>> 25 & 0b11111)];
 | 
							chars[0x14] = alphabet[(int) (random1 >>> 25 & 0b11111)];
 | 
				
			||||||
		chars[0x15] = alphabet[(int) (random2 >>> 20 & 0b11111)];
 | 
							chars[0x15] = alphabet[(int) (random1 >>> 20 & 0b11111)];
 | 
				
			||||||
		chars[0x16] = alphabet[(int) (random2 >>> 15 & 0b11111)];
 | 
							chars[0x16] = alphabet[(int) (random1 >>> 15 & 0b11111)];
 | 
				
			||||||
		chars[0x17] = alphabet[(int) (random2 >>> 10 & 0b11111)];
 | 
							chars[0x17] = alphabet[(int) (random1 >>> 10 & 0b11111)];
 | 
				
			||||||
		chars[0x18] = alphabet[(int) (random2 >>> 5 & 0b11111)];
 | 
							chars[0x18] = alphabet[(int) (random1 >>> 5 & 0b11111)];
 | 
				
			||||||
		chars[0x19] = alphabet[(int) (random2 & 0b11111)];
 | 
							chars[0x19] = alphabet[(int) (random1 & 0b11111)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return new String(chars);
 | 
							return new String(chars);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static boolean isValidString(String string) {
 | 
						protected static char[] toCharArray(String string) {
 | 
				
			||||||
		return isValidArray(string == null ? null : string.toCharArray());
 | 
							char[] chars = string == null ? null : string.toCharArray();
 | 
				
			||||||
 | 
							if (!isValidCharArray(chars)) {
 | 
				
			||||||
 | 
								throw new IllegalArgumentException(String.format("Invalid ULID: \"%s\"", string));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return chars;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -439,15 +619,22 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
	 * @param chars a char array
 | 
						 * @param chars a char array
 | 
				
			||||||
	 * @return boolean true if valid
 | 
						 * @return boolean true if valid
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected static boolean isValidArray(final char[] chars) {
 | 
						protected static boolean isValidCharArray(final char[] chars) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (chars == null || chars.length != ULID_LENGTH) {
 | 
							if (chars == null || chars.length != ULID_LENGTH) {
 | 
				
			||||||
			return false; // null or wrong size!
 | 
								return false; // null or wrong size!
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// the two extra bits added by base-32 encoding must be zero
 | 
							// The time component has 48 bits.
 | 
				
			||||||
 | 
							// The base32 encoded time component has 50 bits.
 | 
				
			||||||
 | 
							// The time component cannot be greater than than 2^48-1.
 | 
				
			||||||
 | 
							// So the 2 first bits of the base32 decoded time component must be ZERO.
 | 
				
			||||||
 | 
							// As a consequence, the 1st char of the input string must be between 0 and 7.
 | 
				
			||||||
		if ((ALPHABET_VALUES[chars[0]] & 0b11000) != 0) {
 | 
							if ((ALPHABET_VALUES[chars[0]] & 0b11000) != 0) {
 | 
				
			||||||
			return false; // overflow!
 | 
								// ULID specification:
 | 
				
			||||||
 | 
								// "Any attempt to decode or encode a ULID larger than this (time > 2^48-1)
 | 
				
			||||||
 | 
								// should be rejected by all implementations, to prevent overflow bugs."
 | 
				
			||||||
 | 
								return false; // time overflow!
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (int i = 0; i < chars.length; i++) {
 | 
							for (int i = 0; i < chars.length; i++) {
 | 
				
			||||||
| 
						 | 
					@ -458,12 +645,4 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return true; // It seems to be OK.
 | 
							return true; // It seems to be OK.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static char[] toCharArray(String string) {
 | 
					 | 
				
			||||||
		char[] chars = string == null ? new char[0] : string.toCharArray();
 | 
					 | 
				
			||||||
		if (!isValidArray(chars)) {
 | 
					 | 
				
			||||||
			throw new IllegalArgumentException(String.format("Invalid ULID: \"%s\"", string));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return chars;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,43 +24,97 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.github.f4b6a3.ulid;
 | 
					package com.github.f4b6a3.ulid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.f4b6a3.ulid.creator.MonotonicUlidSpecCreator;
 | 
					import com.github.f4b6a3.ulid.creator.MonotonicUlidFactory;
 | 
				
			||||||
import com.github.f4b6a3.ulid.creator.UlidSpecCreator;
 | 
					import com.github.f4b6a3.ulid.creator.UlidFactory;
 | 
				
			||||||
 | 
					import com.github.f4b6a3.ulid.creator.DefaultUlidFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Facade to the ULID factories.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The ULID has two components:
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * - Time component: a part of 48 bits that represent the amount of milliseconds
 | 
				
			||||||
 | 
					 * since Unix Epoch, 1970-01-01.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * - Random component: a byte array of 80 bits that has a random value generated
 | 
				
			||||||
 | 
					 * a secure random generator.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The maximum ULIDs that can be generated per millisecond is 2^80.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public final class UlidCreator {
 | 
					public final class UlidCreator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private UlidCreator() {
 | 
						private UlidCreator() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The random component is always reset to a new random value.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public static Ulid getUlid() {
 | 
						public static Ulid getUlid() {
 | 
				
			||||||
		return DefaultCreatorHolder.INSTANCE.create();
 | 
							return DefaultFactoryHolder.INSTANCE.create();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a ULID with a specific time.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param time a specific time
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public static Ulid getUlid(final long time) {
 | 
						public static Ulid getUlid(final long time) {
 | 
				
			||||||
		return DefaultCreatorHolder.INSTANCE.create(time);
 | 
							return DefaultFactoryHolder.INSTANCE.create(time);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a Monotonic ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * The random component is reset to a new value every time the millisecond
 | 
				
			||||||
 | 
						 * changes.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * If more than one ULID is generated within the same millisecond, the random
 | 
				
			||||||
 | 
						 * component is incremented by one.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public static Ulid getMonotonicUlid() {
 | 
						public static Ulid getMonotonicUlid() {
 | 
				
			||||||
		return MonotonicCreatorHolder.INSTANCE.create();
 | 
							return MonotonicFactoryHolder.INSTANCE.create();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a Monotonic ULID with a specific time.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param time a specific time
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public static Ulid getMonotonicUlid(final long time) {
 | 
						public static Ulid getMonotonicUlid(final long time) {
 | 
				
			||||||
		return MonotonicCreatorHolder.INSTANCE.create(time);
 | 
							return MonotonicFactoryHolder.INSTANCE.create(time);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static UlidSpecCreator getUlidSpecCreator() {
 | 
						/**
 | 
				
			||||||
		return new UlidSpecCreator();
 | 
						 * Returns an instance of the Default ULID factory.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID factory
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static UlidFactory getDefaultFactory() {
 | 
				
			||||||
 | 
							return new DefaultUlidFactory();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static UlidSpecCreator getMonotonicUlidSpecCreator() {
 | 
						/**
 | 
				
			||||||
		return new MonotonicUlidSpecCreator();
 | 
						 * Returns an instance of the Monotonic ULID factory.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID factory
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static UlidFactory getMonotonicFactory() {
 | 
				
			||||||
 | 
							return new MonotonicUlidFactory();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static class DefaultCreatorHolder {
 | 
						private static class DefaultFactoryHolder {
 | 
				
			||||||
		static final UlidSpecCreator INSTANCE = getUlidSpecCreator();
 | 
							static final UlidFactory INSTANCE = getDefaultFactory();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static class MonotonicCreatorHolder {
 | 
						private static class MonotonicFactoryHolder {
 | 
				
			||||||
		static final UlidSpecCreator INSTANCE = getMonotonicUlidSpecCreator();
 | 
							static final UlidFactory INSTANCE = getMonotonicFactory();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,23 +26,25 @@ package com.github.f4b6a3.ulid.creator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.f4b6a3.ulid.Ulid;
 | 
					import com.github.f4b6a3.ulid.Ulid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public final class MonotonicUlidSpecCreator extends UlidSpecCreator {
 | 
					/**
 | 
				
			||||||
 | 
					 * Factory that generates default ULIDs.
 | 
				
			||||||
	private long lastTime;
 | 
					 * 
 | 
				
			||||||
	private Ulid lastUlid;
 | 
					 * The random component is always reset to a new random value.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The maximum ULIDs that can be generated per millisecond is 2^80.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DefaultUlidFactory extends UlidFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param time a specific time
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public synchronized Ulid create(final long time) {
 | 
						public Ulid create(final long time) {
 | 
				
			||||||
		
 | 
							final byte[] random = new byte[Ulid.RANDOM_BYTES_LENGTH];
 | 
				
			||||||
		if (time == this.lastTime) {
 | 
							this.randomGenerator.nextBytes(random);
 | 
				
			||||||
			this.lastUlid = lastUlid.increment();
 | 
							return new Ulid(time, random);
 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			final byte[] random = new byte[10];
 | 
					 | 
				
			||||||
			this.randomStrategy.nextBytes(random);
 | 
					 | 
				
			||||||
			this.lastUlid = Ulid.of(time, random);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.lastTime = time;
 | 
					 | 
				
			||||||
		return this.lastUlid;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * MIT License
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Copyright (c) 2020 Fabio Lima
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					 * copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					 * SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package com.github.f4b6a3.ulid.creator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.github.f4b6a3.ulid.Ulid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Factory that generates Monotonic ULIDs.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The random component is reset to a new value every time the millisecond changes.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * If more than one ULID is generated within the same millisecond, the random
 | 
				
			||||||
 | 
					 * component is incremented by one.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The maximum ULIDs that can be generated per millisecond is 2^80.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class MonotonicUlidFactory extends UlidFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private long lastTime = -1;
 | 
				
			||||||
 | 
						private Ulid lastUlid = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param time a specific time
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public synchronized Ulid create(final long time) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (time == this.lastTime) {
 | 
				
			||||||
 | 
								this.lastUlid = lastUlid.increment();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								final byte[] random = new byte[Ulid.RANDOM_BYTES_LENGTH];
 | 
				
			||||||
 | 
								this.randomGenerator.nextBytes(random);
 | 
				
			||||||
 | 
								this.lastUlid = new Ulid(time, random);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.lastTime = time;
 | 
				
			||||||
 | 
							return new Ulid(this.lastUlid);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -27,41 +27,55 @@ package com.github.f4b6a3.ulid.creator;
 | 
				
			||||||
import java.util.Random;
 | 
					import java.util.Random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.f4b6a3.ulid.Ulid;
 | 
					import com.github.f4b6a3.ulid.Ulid;
 | 
				
			||||||
import com.github.f4b6a3.ulid.strategy.DefaultRandomStrategy;
 | 
					import com.github.f4b6a3.ulid.random.DefaultRandomGenerator;
 | 
				
			||||||
import com.github.f4b6a3.ulid.strategy.RandomStrategy;
 | 
					import com.github.f4b6a3.ulid.random.RandomGenerator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class UlidSpecCreator {
 | 
					/**
 | 
				
			||||||
 | 
					 * An abstract factory for generating ULIDs.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * The only method that must be implemented is {@link UlidFactory#create(long)}.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class UlidFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected RandomStrategy randomStrategy;
 | 
						protected RandomGenerator randomGenerator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public UlidSpecCreator() {
 | 
						public UlidFactory() {
 | 
				
			||||||
		this.randomStrategy = new DefaultRandomStrategy();
 | 
							this.randomGenerator = new DefaultRandomGenerator();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a UUID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public Ulid create() {
 | 
						public Ulid create() {
 | 
				
			||||||
		return create(System.currentTimeMillis());
 | 
							return create(System.currentTimeMillis());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Ulid create(final long time) {
 | 
						/**
 | 
				
			||||||
		final byte[] random = new byte[10];
 | 
						 * Returns a UUID with a specific time.
 | 
				
			||||||
		this.randomStrategy.nextBytes(random);
 | 
						 * 
 | 
				
			||||||
		return Ulid.of(time, random);
 | 
						 * This method must be implemented by all subclasses.
 | 
				
			||||||
	}
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param time a specific time
 | 
				
			||||||
 | 
						 * @return a ULID
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public abstract Ulid create(final long time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Replaces the default random strategy with another.
 | 
						 * Replaces the default random generator with another.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * The default random strategy uses {@link java.security.SecureRandom}.
 | 
						 * The default random generator uses {@link java.security.SecureRandom}.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * See {@link Random}.
 | 
						 * See {@link Random}.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param random a random generator
 | 
						 * @param <T>             the type parameter
 | 
				
			||||||
	 * @param <T>    the type parameter
 | 
						 * @param randomGenerator a random generator
 | 
				
			||||||
	 * @return {@link AbstractRandomBasedUuidCreator}
 | 
						 * @return {@link UlidFactory}
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	@SuppressWarnings("unchecked")
 | 
						@SuppressWarnings("unchecked")
 | 
				
			||||||
	public synchronized <T extends UlidSpecCreator> T withRandomStrategy(RandomStrategy randomStrategy) {
 | 
						public synchronized <T extends UlidFactory> T withRandomGenerator(RandomGenerator randomGenerator) {
 | 
				
			||||||
		this.randomStrategy = randomStrategy;
 | 
							this.randomGenerator = randomGenerator;
 | 
				
			||||||
		return (T) this;
 | 
							return (T) this;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@
 | 
				
			||||||
 * SOFTWARE.
 | 
					 * SOFTWARE.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.github.f4b6a3.ulid.strategy;
 | 
					package com.github.f4b6a3.ulid.random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.security.SecureRandom;
 | 
					import java.security.SecureRandom;
 | 
				
			||||||
import java.util.Random;
 | 
					import java.util.Random;
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ import java.util.Random;
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * It uses an instance of {@link java.security.SecureRandom}.
 | 
					 * It uses an instance of {@link java.security.SecureRandom}.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public final class DefaultRandomStrategy implements RandomStrategy {
 | 
					public final class DefaultRandomGenerator implements RandomGenerator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final Random SECURE_RANDOM = new SecureRandom();
 | 
						private static final Random SECURE_RANDOM = new SecureRandom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,9 +22,9 @@
 | 
				
			||||||
 * SOFTWARE.
 | 
					 * SOFTWARE.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.github.f4b6a3.ulid.strategy;
 | 
					package com.github.f4b6a3.ulid.random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@FunctionalInterface
 | 
					@FunctionalInterface
 | 
				
			||||||
public interface RandomStrategy {
 | 
					public interface RandomGenerator {
 | 
				
			||||||
	void nextBytes(byte[] bytes);
 | 
						void nextBytes(byte[] bytes);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,13 +3,13 @@ package com.github.f4b6a3.ulid;
 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
import org.junit.runners.Suite;
 | 
					import org.junit.runners.Suite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.f4b6a3.ulid.creator.MonotonicUlidSpecCreatorTest;
 | 
					import com.github.f4b6a3.ulid.creator.MonotonicUlidFactoryTest;
 | 
				
			||||||
import com.github.f4b6a3.ulid.creator.UlidSpecCreatorTest;
 | 
					import com.github.f4b6a3.ulid.creator.DefaultUlidFactoryTest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@RunWith(Suite.class)
 | 
					@RunWith(Suite.class)
 | 
				
			||||||
@Suite.SuiteClasses({
 | 
					@Suite.SuiteClasses({
 | 
				
			||||||
	MonotonicUlidSpecCreatorTest.class,
 | 
						MonotonicUlidFactoryTest.class,
 | 
				
			||||||
	UlidSpecCreatorTest.class,
 | 
						DefaultUlidFactoryTest.class,
 | 
				
			||||||
	UlidTest.class,
 | 
						UlidTest.class,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,10 @@ package com.github.f4b6a3.ulid;
 | 
				
			||||||
import static org.junit.Assert.assertEquals;
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
import static org.junit.Assert.assertFalse;
 | 
					import static org.junit.Assert.assertFalse;
 | 
				
			||||||
import static org.junit.Assert.assertTrue;
 | 
					import static org.junit.Assert.assertTrue;
 | 
				
			||||||
 | 
					import static org.junit.Assert.fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					import java.time.Instant;
 | 
				
			||||||
import java.util.Random;
 | 
					import java.util.Random;
 | 
				
			||||||
import java.util.UUID;
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,98 +16,428 @@ import com.github.f4b6a3.ulid.Ulid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class UlidTest {
 | 
					public class UlidTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final int DEFAULT_LOOP_MAX = 10_000;
 | 
						private static final int DEFAULT_LOOP_MAX = 1_000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected static final long TIME_MASK = 0x0000ffffffffffffL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected static final char[] ALPHABET_CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
 | 
						protected static final char[] ALPHABET_CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
 | 
				
			||||||
	protected static final char[] ALPHABET_JAVA = "0123456789abcdefghijklmnopqrstuv".toCharArray(); // Long.parseUnsignedLong()
 | 
						protected static final char[] ALPHABET_JAVA = "0123456789abcdefghijklmnopqrstuv".toCharArray(); // Long.parseUnsignedLong()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						private static final long VERSION_MASK = 0x000000000000f000L;
 | 
				
			||||||
	public void testOfAndToString() {
 | 
						private static final long VARIANT_MASK = 0xc000000000000000L;
 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
					 | 
				
			||||||
			UUID uuid0 = UUID.randomUUID();
 | 
					 | 
				
			||||||
			String string0 = toString(uuid0);
 | 
					 | 
				
			||||||
			String string1 = Ulid.of(string0).toString();
 | 
					 | 
				
			||||||
			assertEquals(string0, string1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testOfAndToUuid() {
 | 
					 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
					 | 
				
			||||||
			UUID uuid0 = UUID.randomUUID();
 | 
					 | 
				
			||||||
			UUID uuid1 = Ulid.of(uuid0).toUuid();
 | 
					 | 
				
			||||||
			assertEquals(uuid0.toString(), uuid1.toString());
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testConstructorLongs() {
 | 
						public void testConstructorLongs() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
			Random random = new Random();
 | 
					 | 
				
			||||||
			final long msb = random.nextLong();
 | 
								final long msb = random.nextLong();
 | 
				
			||||||
			final long lsb = random.nextLong();
 | 
								final long lsb = random.nextLong();
 | 
				
			||||||
			Ulid ulid0 = new Ulid(msb, lsb); // <-- under test
 | 
								Ulid ulid0 = new Ulid(msb, lsb); // <-- test Ulid(long, long)
 | 
				
			||||||
 | 
								assertEquals(msb, ulid0.getMostSignificantBits());
 | 
				
			||||||
			assertEquals(msb, ulid0.toUuid().getMostSignificantBits());
 | 
								assertEquals(lsb, ulid0.getLeastSignificantBits());
 | 
				
			||||||
			assertEquals(lsb, ulid0.toUuid().getLeastSignificantBits());
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testConstructorString() {
 | 
						public void testConstructorTimeAndRandom() {
 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
							Random random = new Random();
 | 
				
			||||||
			Random random = new Random();
 | 
					 | 
				
			||||||
			final long random1 = random.nextLong();
 | 
					 | 
				
			||||||
			final long random2 = random.nextLong();
 | 
					 | 
				
			||||||
			Ulid ulid0 = new Ulid(random1, random2);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			String string1 = toString(ulid0);
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
			Ulid struct1 = Ulid.of(string1); // <-- under test
 | 
								final long msb = random.nextLong();
 | 
				
			||||||
			assertEquals(ulid0, struct1);
 | 
								final long lsb = random.nextLong();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// get the time
 | 
				
			||||||
 | 
								long time = msb >>> 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// get the random bytes
 | 
				
			||||||
 | 
								ByteBuffer buffer = ByteBuffer.allocate(Ulid.RANDOM_BYTES_LENGTH);
 | 
				
			||||||
 | 
								buffer.put((byte) ((msb >>> 8) & 0xff));
 | 
				
			||||||
 | 
								buffer.put((byte) (msb & 0xff));
 | 
				
			||||||
 | 
								buffer.putLong(lsb);
 | 
				
			||||||
 | 
								byte[] bytes = buffer.array();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Ulid ulid0 = new Ulid(time, bytes); // <-- test Ulid(long, byte[])
 | 
				
			||||||
 | 
								assertEquals(msb, ulid0.getMostSignificantBits());
 | 
				
			||||||
 | 
								assertEquals(lsb, ulid0.getLeastSignificantBits());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								long time = 0x0000ffffffffffffL + 1; // greater than 2^48-1
 | 
				
			||||||
 | 
								byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH];
 | 
				
			||||||
 | 
								new Ulid(time, bytes);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								long time = 0x1000000000000000L; // negative number
 | 
				
			||||||
 | 
								byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH];
 | 
				
			||||||
 | 
								new Ulid(time, bytes);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								long time = 0x0000000000000000L;
 | 
				
			||||||
 | 
								byte[] bytes = null; // null random component
 | 
				
			||||||
 | 
								new Ulid(time, bytes);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								long time = 0x0000000000000000L;
 | 
				
			||||||
 | 
								byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH + 1]; // random component with invalid size
 | 
				
			||||||
 | 
								new Ulid(time, bytes);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testConstructorUuid() {
 | 
						public void testFromStrings() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
			Random random = new Random();
 | 
					 | 
				
			||||||
			final long msb = random.nextLong();
 | 
								final long msb = random.nextLong();
 | 
				
			||||||
			final long lsb = random.nextLong();
 | 
								final long lsb = random.nextLong();
 | 
				
			||||||
			final UUID uuid0 = new UUID(msb, lsb);
 | 
								Ulid ulid0 = new Ulid(msb, lsb);
 | 
				
			||||||
			Ulid struct0 = Ulid.of(uuid0); // <-- under test
 | 
								String string0 = toString(ulid0);
 | 
				
			||||||
 | 
								Ulid ulid1 = Ulid.from(string0); // <- test Ulid.from(String)
 | 
				
			||||||
			UUID uuid1 = toUuid(struct0);
 | 
								assertEquals(ulid0, ulid1);
 | 
				
			||||||
			assertEquals(uuid0, uuid1);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testToString() {
 | 
						public void testToString() {
 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
			Random random = new Random();
 | 
								UUID uuid0 = UUID.randomUUID();
 | 
				
			||||||
			final long random1 = random.nextLong();
 | 
								String string0 = toString(uuid0);
 | 
				
			||||||
			final long random2 = random.nextLong();
 | 
								String string1 = Ulid.from(uuid0).toString(); // <- test Ulid.toString()
 | 
				
			||||||
			Ulid ulid0 = new Ulid(random1, random2);
 | 
								assertEquals(string0, string1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testToUpperCase() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								final long msb = random.nextLong();
 | 
				
			||||||
 | 
								final long lsb = random.nextLong();
 | 
				
			||||||
 | 
								Ulid ulid0 = new Ulid(msb, lsb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			String string1 = toString(ulid0);
 | 
								String string1 = toString(ulid0);
 | 
				
			||||||
			String string2 = ulid0.toString(); // <-- under test
 | 
								String string2 = ulid0.toUpperCase(); // <- test Ulid.toUpperCase()
 | 
				
			||||||
			assertEquals(string1, string2);
 | 
								assertEquals(string1, string2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// RFC-4122 UUID v4
 | 
				
			||||||
 | 
								UUID uuid0 = new UUID(msb, lsb);
 | 
				
			||||||
 | 
								String string3 = ulid0.toRfc4122().toUpperCase(); // <- test Ulid.toRfc4122().toUpperCase()
 | 
				
			||||||
 | 
								Ulid ulid3 = fromString(string3);
 | 
				
			||||||
 | 
								UUID uuid3 = new UUID(ulid3.getMostSignificantBits(), ulid3.getLeastSignificantBits());
 | 
				
			||||||
 | 
								assertEquals(4, uuid3.version()); // check version
 | 
				
			||||||
 | 
								assertEquals(2, uuid3.variant()); // check variant
 | 
				
			||||||
 | 
								assertEquals(uuid0.getMostSignificantBits() & ~VERSION_MASK,
 | 
				
			||||||
 | 
										uuid3.getMostSignificantBits() & ~VERSION_MASK); // check the rest of MSB
 | 
				
			||||||
 | 
								assertEquals(uuid0.getLeastSignificantBits() & ~VARIANT_MASK,
 | 
				
			||||||
 | 
										uuid3.getLeastSignificantBits() & ~VARIANT_MASK); // check the rest of LSB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testToLowerCase() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								final long msb = random.nextLong();
 | 
				
			||||||
 | 
								final long lsb = random.nextLong();
 | 
				
			||||||
 | 
								Ulid ulid0 = new Ulid(msb, lsb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								String string1 = toString(ulid0).toLowerCase();
 | 
				
			||||||
 | 
								String string2 = ulid0.toLowerCase(); // <- test Ulid.toLowerCase()
 | 
				
			||||||
 | 
								assertEquals(string1, string2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// RFC-4122 UUID v4
 | 
				
			||||||
 | 
								UUID uuid0 = new UUID(msb, lsb);
 | 
				
			||||||
 | 
								String string3 = ulid0.toRfc4122().toLowerCase(); // <- test Ulid.toRfc4122().toLowerCase()
 | 
				
			||||||
 | 
								Ulid ulid3 = fromString(string3);
 | 
				
			||||||
 | 
								UUID uuid3 = new UUID(ulid3.getMostSignificantBits(), ulid3.getLeastSignificantBits());
 | 
				
			||||||
 | 
								assertEquals(4, uuid3.version()); // check version
 | 
				
			||||||
 | 
								assertEquals(2, uuid3.variant()); // check variant
 | 
				
			||||||
 | 
								assertEquals(uuid0.getMostSignificantBits() & ~VERSION_MASK,
 | 
				
			||||||
 | 
										uuid3.getMostSignificantBits() & ~VERSION_MASK); // check the rest of MSB
 | 
				
			||||||
 | 
								assertEquals(uuid0.getLeastSignificantBits() & ~VARIANT_MASK,
 | 
				
			||||||
 | 
										uuid3.getLeastSignificantBits() & ~VARIANT_MASK); // check the rest of LSB
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testFromUUID() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
								final long msb = random.nextLong();
 | 
				
			||||||
 | 
								final long lsb = random.nextLong();
 | 
				
			||||||
 | 
								UUID uuid0 = new UUID(msb, lsb);
 | 
				
			||||||
 | 
								Ulid ulid0 = Ulid.from(uuid0); // <- test Ulid.from(UUID)
 | 
				
			||||||
 | 
								assertEquals(uuid0.getMostSignificantBits(), ulid0.getMostSignificantBits());
 | 
				
			||||||
 | 
								assertEquals(uuid0.getLeastSignificantBits(), ulid0.getLeastSignificantBits());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testToUuid() {
 | 
						public void testToUuid() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
			Random random = new Random();
 | 
					
 | 
				
			||||||
			final long random1 = random.nextLong();
 | 
								final long random1 = random.nextLong();
 | 
				
			||||||
			final long random2 = random.nextLong();
 | 
								final long random2 = random.nextLong();
 | 
				
			||||||
			Ulid ulid0 = new Ulid(random1, random2);
 | 
								Ulid ulid0 = new Ulid(random1, random2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			UUID uuid1 = toUuid(ulid0);
 | 
								UUID uuid1 = toUuid(ulid0);
 | 
				
			||||||
			UUID uuid2 = ulid0.toUuid(); // <-- under test
 | 
								UUID uuid2 = ulid0.toUuid(); // <-- test Ulid.toUuid()
 | 
				
			||||||
			assertEquals(uuid1, uuid2);
 | 
								assertEquals(uuid1, uuid2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// RFC-4122 UUID v4
 | 
				
			||||||
 | 
								UUID uuid3 = ulid0.toRfc4122().toUuid(); // <-- test Ulid.toRfc4122().toUuid()
 | 
				
			||||||
 | 
								assertEquals(4, uuid3.version()); // check version
 | 
				
			||||||
 | 
								assertEquals(2, uuid3.variant()); // check variant
 | 
				
			||||||
 | 
								assertEquals(uuid1.getMostSignificantBits() & ~VERSION_MASK,
 | 
				
			||||||
 | 
										uuid3.getMostSignificantBits() & ~VERSION_MASK); // check the rest of MSB
 | 
				
			||||||
 | 
								assertEquals(uuid1.getLeastSignificantBits() & ~VARIANT_MASK,
 | 
				
			||||||
 | 
										uuid3.getLeastSignificantBits() & ~VARIANT_MASK); // check the rest of LSB
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testFromBytes() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								byte[] bytes0 = new byte[Ulid.ULID_BYTES_LENGTH];
 | 
				
			||||||
 | 
								random.nextBytes(bytes0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Ulid ulid0 = Ulid.from(bytes0); // <- test Ulid.from(UUID)
 | 
				
			||||||
 | 
								ByteBuffer buffer = ByteBuffer.allocate(Ulid.ULID_BYTES_LENGTH);
 | 
				
			||||||
 | 
								buffer.putLong(ulid0.getMostSignificantBits());
 | 
				
			||||||
 | 
								buffer.putLong(ulid0.getLeastSignificantBits());
 | 
				
			||||||
 | 
								byte[] bytes1 = buffer.array();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (int j = 0; j < bytes0.length; j++) {
 | 
				
			||||||
 | 
									assertEquals(bytes0[j], bytes1[j]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								byte[] bytes = null;
 | 
				
			||||||
 | 
								Ulid.from(bytes);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								byte[] bytes = new byte[Ulid.ULID_BYTES_LENGTH + 1];
 | 
				
			||||||
 | 
								Ulid.from(bytes);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testToBytes() {
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								byte[] bytes1 = new byte[16];
 | 
				
			||||||
 | 
								random.nextBytes(bytes1);
 | 
				
			||||||
 | 
								Ulid ulid0 = Ulid.from(bytes1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								byte[] bytes2 = ulid0.toBytes(); // <-- test Ulid.toBytes()
 | 
				
			||||||
 | 
								for (int j = 0; j < bytes1.length; j++) {
 | 
				
			||||||
 | 
									assertEquals(bytes1[j], bytes2[j]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// RFC-4122 UUID v4
 | 
				
			||||||
 | 
								byte[] bytes3 = ulid0.toRfc4122().toBytes(); // <-- test Ulid.toBytes4()
 | 
				
			||||||
 | 
								assertEquals(0x40, bytes3[6] & 0b11110000); // check version
 | 
				
			||||||
 | 
								assertEquals(bytes1[6] & 0b00001111, bytes3[6] & 0b00001111); // check the other bits of 7th byte
 | 
				
			||||||
 | 
								assertEquals(0x80, bytes3[8] & 0b11000000); // check variant
 | 
				
			||||||
 | 
								assertEquals(bytes1[8] & 0b00111111, bytes3[8] & 0b00111111); // check the other bits of 9th byte
 | 
				
			||||||
 | 
								for (int j = 0; j < bytes1.length; j++) {
 | 
				
			||||||
 | 
									if (j == 6 || j == 8)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									assertEquals(bytes1[j], bytes3[j]); // check the other bytes
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testGetTimeAndGetRandom() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							long time = 0;
 | 
				
			||||||
 | 
							byte[] bytes = new byte[10];
 | 
				
			||||||
 | 
							Random random = new Random();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int i = 0; i < 100; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								time = random.nextLong() & TIME_MASK;
 | 
				
			||||||
 | 
								random.nextBytes(bytes);
 | 
				
			||||||
 | 
								Ulid ulid = new Ulid(time, bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								assertEquals(time, ulid.getTime()); // test Ulid.getTime()
 | 
				
			||||||
 | 
								assertEquals(Instant.ofEpochMilli(time), ulid.getInstant()); // test Ulid.getInstant()
 | 
				
			||||||
 | 
								for (int j = 0; j < bytes.length; j++) {
 | 
				
			||||||
 | 
									assertEquals(bytes[j], ulid.getRandom()[j]); // test Ulid.getRandom()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testIncrement() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							long msb;
 | 
				
			||||||
 | 
							long lsb;
 | 
				
			||||||
 | 
							Ulid ulid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							final int loopMax = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							msb = 0x0123456789abcdefL;
 | 
				
			||||||
 | 
							lsb = 0x0123456789abcdefL;
 | 
				
			||||||
 | 
							ulid = new Ulid(msb, lsb);
 | 
				
			||||||
 | 
							for (int i = 0; i < loopMax; i++) {
 | 
				
			||||||
 | 
								ulid = ulid.increment();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							assertEquals(msb, ulid.getMostSignificantBits());
 | 
				
			||||||
 | 
							assertEquals(msb + loopMax, ulid.getLeastSignificantBits());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							msb = 0x0123456789abcdefL;
 | 
				
			||||||
 | 
							lsb = 0xffffffffffffffffL - (loopMax / 2);
 | 
				
			||||||
 | 
							ulid = new Ulid(msb, lsb);
 | 
				
			||||||
 | 
							for (int i = 0; i < loopMax; i++) {
 | 
				
			||||||
 | 
								ulid = ulid.increment();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							assertEquals(msb + 1, ulid.getMostSignificantBits());
 | 
				
			||||||
 | 
							assertEquals((loopMax / 2) - 1, ulid.getLeastSignificantBits());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testIsValidString() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							String ulid = null; // Null
 | 
				
			||||||
 | 
							assertFalse("Null ULID should be invalid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = ""; // length: 0
 | 
				
			||||||
 | 
							assertFalse("ULID with empty string should be invalid .", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKMNPQRS"; // All upper case
 | 
				
			||||||
 | 
							assertTrue("ULID in upper case should valid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789abcdefghjklmnpqr"; // All lower case
 | 
				
			||||||
 | 
							assertTrue("ULID in lower case should be valid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789AbCdEfGhJkMnPqRs"; // Mixed case
 | 
				
			||||||
 | 
							assertTrue("Ulid in upper and lower case should valid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKLMNPQ"; // length: 25
 | 
				
			||||||
 | 
							assertFalse("ULID length lower than 26 should be invalid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKMNPQZZZ"; // length: 27
 | 
				
			||||||
 | 
							assertFalse("ULID length greater than 26 should be invalid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "u123456789ABCDEFGHJKMNPQRS"; // Letter u
 | 
				
			||||||
 | 
							assertFalse("ULID with 'u' or 'U' should be invalid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKMNPQR#"; // Special char
 | 
				
			||||||
 | 
							assertFalse("ULID with special chars should be invalid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "8ZZZZZZZZZABCDEFGHJKMNPQRS"; // time > (2^48)-1
 | 
				
			||||||
 | 
							assertFalse("ULID with timestamp greater than (2^48)-1 should be invalid.", Ulid.isValid(ulid));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testToCharArray() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							String ulid = null; // Null
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Null ULID should be invalid.");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = ""; // length: 0
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKMNPQRS"; // All upper case
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								fail("Should not throw an exception");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789abcdefghjklmnpqr"; // All lower case
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								fail("Should not throw an exception");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789AbCdEfGhJkMnPqRs"; // Mixed case
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								fail("Should not throw an exception");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKLMNPQ"; // length: 25
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKMNPQZZZ"; // length: 27
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "u123456789ABCDEFGHJKMNPQRS"; // Letter u
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "0123456789ABCDEFGHJKMNPQR@"; // Special char
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ulid = "8ZZZZZZZZZABCDEFGHJKMNPQRS"; // time > (2^48)-1
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Ulid.toCharArray(ulid);
 | 
				
			||||||
 | 
								fail("Should throw an exception");
 | 
				
			||||||
 | 
							} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
								// success
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,9 +447,9 @@ public class UlidTest {
 | 
				
			||||||
		long random1 = 0;
 | 
							long random1 = 0;
 | 
				
			||||||
		long random2 = 0;
 | 
							long random2 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String tm = string.substring(0, 10);
 | 
							String tm = string.substring(0, 10).toUpperCase();
 | 
				
			||||||
		String r1 = string.substring(10, 18);
 | 
							String r1 = string.substring(10, 18).toUpperCase();
 | 
				
			||||||
		String r2 = string.substring(18, 26);
 | 
							String r2 = string.substring(18, 26).toUpperCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tm = transliterate(tm, ALPHABET_CROCKFORD, ALPHABET_JAVA);
 | 
							tm = transliterate(tm, ALPHABET_CROCKFORD, ALPHABET_JAVA);
 | 
				
			||||||
		r1 = transliterate(r1, ALPHABET_CROCKFORD, ALPHABET_JAVA);
 | 
							r1 = transliterate(r1, ALPHABET_CROCKFORD, ALPHABET_JAVA);
 | 
				
			||||||
| 
						 | 
					@ -193,40 +526,6 @@ public class UlidTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return r1 + r2;
 | 
							return r1 + r2;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void isValidString() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		String ulid = null; // Null
 | 
					 | 
				
			||||||
		assertFalse("Null ULID should be invalid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = ""; // length: 0
 | 
					 | 
				
			||||||
		assertFalse("ULID with empty string should be invalid .", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "0123456789ABCDEFGHJKMNPQRS"; // All upper case
 | 
					 | 
				
			||||||
		assertTrue("ULID in upper case should valid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "0123456789abcdefghjklmnpqr"; // All lower case
 | 
					 | 
				
			||||||
		assertTrue("ULID in lower case should be valid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "0123456789AbCdEfGhJkMnPqRs"; // Mixed case
 | 
					 | 
				
			||||||
		assertTrue("Ulid in upper and lower case should valid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "0123456789ABCDEFGHJKLMNPQ"; // length: 25
 | 
					 | 
				
			||||||
		assertFalse("ULID length lower than 26 should be invalid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "0123456789ABCDEFGHJKMNPQZZZ"; // length: 27
 | 
					 | 
				
			||||||
		assertFalse("ULID length greater than 26 should be invalid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "u123456789ABCDEFGHJKMNPQRS"; // Letter u
 | 
					 | 
				
			||||||
		assertFalse("ULID with 'u' or 'U' should be invalid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "#123456789ABCDEFGHJKMNPQRS"; // Special char
 | 
					 | 
				
			||||||
		assertFalse("ULID with special chars should be invalid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ulid = "8ZZZZZZZZZABCDEFGHJKMNPQRS"; // timestamp > (2^48)-1
 | 
					 | 
				
			||||||
		assertFalse("ULID with timestamp greater than (2^48)-1 should be invalid.", Ulid.isValidString(ulid));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static String transliterate(String string, char[] alphabet1, char[] alphabet2) {
 | 
						private static String transliterate(String string, char[] alphabet1, char[] alphabet2) {
 | 
				
			||||||
		char[] output = string.toCharArray();
 | 
							char[] output = string.toCharArray();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ package com.github.f4b6a3.ulid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import com.github.f4b6a3.ulid.UlidCreator;
 | 
					import com.github.f4b6a3.ulid.UlidCreator;
 | 
				
			||||||
import com.github.f4b6a3.ulid.creator.UlidSpecCreator;
 | 
					import com.github.f4b6a3.ulid.creator.UlidFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
| 
						 | 
					@ -22,8 +22,8 @@ public class UniquenessTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private boolean verbose; // Show progress or not
 | 
						private boolean verbose; // Show progress or not
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ULID Spec creator
 | 
						// ULID Spec factory
 | 
				
			||||||
	private UlidSpecCreator creator;
 | 
						private UlidFactory factory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private long time = System.currentTimeMillis(); // fixed timestamp
 | 
						private long time = System.currentTimeMillis(); // fixed timestamp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,12 +32,12 @@ public class UniquenessTest {
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param threadCount
 | 
						 * @param threadCount
 | 
				
			||||||
	 * @param requestCount
 | 
						 * @param requestCount
 | 
				
			||||||
	 * @param creator
 | 
						 * @param factory
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public UniquenessTest(int threadCount, int requestCount, UlidSpecCreator creator, boolean progress) {
 | 
						public UniquenessTest(int threadCount, int requestCount, UlidFactory factory, boolean progress) {
 | 
				
			||||||
		this.threadCount = threadCount;
 | 
							this.threadCount = threadCount;
 | 
				
			||||||
		this.requestCount = requestCount;
 | 
							this.requestCount = requestCount;
 | 
				
			||||||
		this.creator = creator;
 | 
							this.factory = factory;
 | 
				
			||||||
		this.verbose = progress;
 | 
							this.verbose = progress;
 | 
				
			||||||
		this.initCache();
 | 
							this.initCache();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -90,13 +90,13 @@ public class UniquenessTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for (int i = 0; i < max; i++) {
 | 
								for (int i = 0; i < max; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Request a UUID
 | 
									// Request a ULID
 | 
				
			||||||
				Ulid ulid = creator.create(time);
 | 
									Ulid ulid = factory.create(time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (verbose) {
 | 
									if (verbose) {
 | 
				
			||||||
					// Calculate and show progress
 | 
										if (i % (max / 100) == 0) {
 | 
				
			||||||
					progress = (int) ((i * 1.0 / max) * 100);
 | 
											// Calculate and show progress
 | 
				
			||||||
					if (progress % 10 == 0) {
 | 
											progress = (int) ((i * 1.0 / max) * 100);
 | 
				
			||||||
						System.out.println(String.format("[Thread %06d] %s %s %s%%", id, ulid, i, (int) progress));
 | 
											System.out.println(String.format("[Thread %06d] %s %s %s%%", id, ulid, i, (int) progress));
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -117,9 +117,9 @@ public class UniquenessTest {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static void execute(boolean verbose, int threadCount, int requestCount) {
 | 
						public static void execute(boolean verbose, int threadCount, int requestCount) {
 | 
				
			||||||
		UlidSpecCreator creator = UlidCreator.getMonotonicUlidSpecCreator();
 | 
							UlidFactory factory = UlidCreator.getMonotonicFactory();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		UniquenessTest test = new UniquenessTest(threadCount, requestCount, creator, verbose);
 | 
							UniquenessTest test = new UniquenessTest(threadCount, requestCount, factory, verbose);
 | 
				
			||||||
		test.start();
 | 
							test.start();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@ import org.openjdk.jmh.runner.RunnerException;
 | 
				
			||||||
import org.openjdk.jmh.runner.options.Options;
 | 
					import org.openjdk.jmh.runner.options.Options;
 | 
				
			||||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
 | 
					import org.openjdk.jmh.runner.options.OptionsBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.github.f4b6a3.ulid.Ulid;
 | 
				
			||||||
import com.github.f4b6a3.ulid.UlidCreator;
 | 
					import com.github.f4b6a3.ulid.UlidCreator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Threads(1)
 | 
					@Threads(1)
 | 
				
			||||||
| 
						 | 
					@ -42,47 +43,47 @@ public class Benchmarks {
 | 
				
			||||||
	@Benchmark
 | 
						@Benchmark
 | 
				
			||||||
	@BenchmarkMode(Mode.Throughput)
 | 
						@BenchmarkMode(Mode.Throughput)
 | 
				
			||||||
	@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
						@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
				
			||||||
	public String getUlidStringThroughput() {
 | 
						public UUID getUuid() {
 | 
				
			||||||
		return UlidCreator.getUlidString();
 | 
							return UUID.randomUUID();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Benchmark
 | 
					 | 
				
			||||||
	@BenchmarkMode(Mode.AverageTime)
 | 
					 | 
				
			||||||
	@OutputTimeUnit(TimeUnit.NANOSECONDS)
 | 
					 | 
				
			||||||
	public String getUlidStringAverage() {
 | 
					 | 
				
			||||||
		return UlidCreator.getUlidString();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Benchmark
 | 
						@Benchmark
 | 
				
			||||||
	@BenchmarkMode(Mode.Throughput)
 | 
						@BenchmarkMode(Mode.Throughput)
 | 
				
			||||||
	@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
						@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
				
			||||||
	public UUID getUlidThroughput() {
 | 
						public String getUuidString() {
 | 
				
			||||||
		return UlidCreator.getUlid();
 | 
							return UUID.randomUUID().toString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Benchmark
 | 
						@Benchmark
 | 
				
			||||||
	@BenchmarkMode(Mode.AverageTime)
 | 
						@BenchmarkMode(Mode.Throughput)
 | 
				
			||||||
	@OutputTimeUnit(TimeUnit.NANOSECONDS)
 | 
						@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
				
			||||||
	public UUID getUlidAverage() {
 | 
						public Ulid getUlid() {
 | 
				
			||||||
		return UlidCreator.getUlid();
 | 
							return UlidCreator.getUlid();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Benchmark
 | 
						@Benchmark
 | 
				
			||||||
	@BenchmarkMode(Mode.Throughput)
 | 
						@BenchmarkMode(Mode.Throughput)
 | 
				
			||||||
	@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
						@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
				
			||||||
	public UUID getRandomUUIDThroughput() {
 | 
						public String getUlidString() {
 | 
				
			||||||
		return UUID.randomUUID();
 | 
							return UlidCreator.getUlid().toString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Benchmark
 | 
						@Benchmark
 | 
				
			||||||
	@BenchmarkMode(Mode.AverageTime)
 | 
						@BenchmarkMode(Mode.Throughput)
 | 
				
			||||||
	@OutputTimeUnit(TimeUnit.NANOSECONDS)
 | 
						@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
				
			||||||
	public UUID getRandomUUIDAverage() {
 | 
						public Ulid getMonotonicUlid() {
 | 
				
			||||||
		return UUID.randomUUID();
 | 
							return UlidCreator.getMonotonicUlid();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Benchmark
 | 
				
			||||||
 | 
						@BenchmarkMode(Mode.Throughput)
 | 
				
			||||||
 | 
						@OutputTimeUnit(TimeUnit.MILLISECONDS)
 | 
				
			||||||
 | 
						public String getMonotonicUlidString() {
 | 
				
			||||||
 | 
							return UlidCreator.getMonotonicUlid().toString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static void main(String[] args) throws RunnerException {
 | 
						public static void main(String[] args) throws RunnerException {
 | 
				
			||||||
		Options opt = new OptionsBuilder().include(MyBenchmark.class.getSimpleName()).forks(1).build();
 | 
							Options opt = new OptionsBuilder().include(Benchmarks.class.getSimpleName()).forks(1).build();
 | 
				
			||||||
		new Runner(opt).run();
 | 
							new Runner(opt).run();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,8 +8,9 @@ import com.github.f4b6a3.ulid.UlidCreator;
 | 
				
			||||||
import static org.junit.Assert.*;
 | 
					import static org.junit.Assert.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class UlidSpecCreatorTest extends AbstractUlidSpecCreatorTest {
 | 
					public class DefaultUlidFactoryTest extends UlidFactoryTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testGetUlid() {
 | 
						public void testGetUlid() {
 | 
				
			||||||
| 
						 | 
					@ -65,7 +66,9 @@ public class UlidSpecCreatorTest extends AbstractUlidSpecCreatorTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Instantiate and start many threads
 | 
							// Instantiate and start many threads
 | 
				
			||||||
		for (int i = 0; i < THREAD_TOTAL; i++) {
 | 
							for (int i = 0; i < THREAD_TOTAL; i++) {
 | 
				
			||||||
			threads[i] = new TestThread(UlidCreator.getUlidSpecCreator(), DEFAULT_LOOP_MAX);
 | 
								Random random = new Random();
 | 
				
			||||||
 | 
								UlidFactory factory = UlidCreator.getDefaultFactory().withRandomGenerator(random::nextBytes);
 | 
				
			||||||
 | 
								threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX);
 | 
				
			||||||
			threads[i].start();
 | 
								threads[i].start();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,8 +9,9 @@ import static org.junit.Assert.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MonotonicUlidSpecCreatorTest extends AbstractUlidSpecCreatorTest {
 | 
					public class MonotonicUlidFactoryTest extends UlidFactoryTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testGetUlid() {
 | 
						public void testGetUlid() {
 | 
				
			||||||
| 
						 | 
					@ -76,7 +77,9 @@ public class MonotonicUlidSpecCreatorTest extends AbstractUlidSpecCreatorTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Instantiate and start many threads
 | 
							// Instantiate and start many threads
 | 
				
			||||||
		for (int i = 0; i < THREAD_TOTAL; i++) {
 | 
							for (int i = 0; i < THREAD_TOTAL; i++) {
 | 
				
			||||||
			threads[i] = new TestThread(UlidCreator.getMonotonicUlidSpecCreator(), DEFAULT_LOOP_MAX);
 | 
								Random random = new Random();
 | 
				
			||||||
 | 
								UlidFactory factory = UlidCreator.getMonotonicFactory().withRandomGenerator(random::nextBytes);
 | 
				
			||||||
 | 
								threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX);
 | 
				
			||||||
			threads[i].start();
 | 
								threads[i].start();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import java.util.Random;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.UUID;
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public abstract class AbstractUlidSpecCreatorTest {
 | 
					public abstract class UlidFactoryTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected static final int DEFAULT_LOOP_MAX = 10_000;
 | 
						protected static final int DEFAULT_LOOP_MAX = 10_000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,10 +28,10 @@ public abstract class AbstractUlidSpecCreatorTest {
 | 
				
			||||||
	protected static class TestThread extends Thread {
 | 
						protected static class TestThread extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static Set<UUID> hashSet = new HashSet<>();
 | 
							public static Set<UUID> hashSet = new HashSet<>();
 | 
				
			||||||
		private UlidSpecCreator creator;
 | 
							private UlidFactory creator;
 | 
				
			||||||
		private int loopLimit;
 | 
							private int loopLimit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public TestThread(UlidSpecCreator creator, int loopLimit) {
 | 
							public TestThread(UlidFactory creator, int loopLimit) {
 | 
				
			||||||
			this.creator = creator;
 | 
								this.creator = creator;
 | 
				
			||||||
			this.loopLimit = loopLimit;
 | 
								this.loopLimit = loopLimit;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue