Add test cases, and remove unused code
List of changes: Change UlidUtil // remove unused code Change UlidUtilTest // add tests and remove unused code Change UlidStructTest // add tests Change UlidValidator // add methods for char[] args Optimaze UlidSpecCreator // small optimization Optimaze UlidStruct // small optimization Update README.md
This commit is contained in:
		
							parent
							
								
									e4a3558595
								
							
						
					
					
						commit
						e52b2cdefd
					
				
							
								
								
									
										41
									
								
								README.md
								
								
								
								
							
							
						
						
									
										41
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -33,7 +33,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.2</version>
 | 
					  <version>2.3.3</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).
 | 
				
			||||||
| 
						 | 
					@ -158,37 +158,20 @@ Benchmark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This section shows benchmarks comparing `UlidCreator` to `java.util.UUID`.
 | 
					This section shows benchmarks comparing `UlidCreator` to `java.util.UUID`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* **ulid-creator v2.1.0:**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					================================================================================
 | 
				
			||||||
THROUGHPUT                         Mode  Cnt      Score     Error   Units
 | 
					THROUGHPUT (operations/millis)           Mode  Cnt      Score     Error   Units
 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					================================================================================
 | 
				
			||||||
Throughput.JDK_RandomBased         thrpt    5   2196,215 ±  13,668  ops/ms
 | 
					Throughput.JDK_RandomBased              thrpt    5   2050,995 ±  21,636  ops/ms
 | 
				
			||||||
Throughput.UlidCreator_Ulid        thrpt    5  19224,340 ± 106,231  ops/ms
 | 
					--------------------------------------------------------------------------------
 | 
				
			||||||
Throughput.UlidCreator_UlidString  thrpt    5   5006,424 ±  26,946  ops/ms
 | 
					Throughput.UlidCreator_Ulid             thrpt    5  18524,721 ± 563,781  ops/ms
 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					Throughput.UlidCreator_UlidString       thrpt    5  12223,501 ±  89,836  ops/ms
 | 
				
			||||||
Total time: 00:04:01
 | 
					================================================================================
 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					Total time: 00:04:00
 | 
				
			||||||
 | 
					================================================================================
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* **ulid-creator v2.2.0:**
 | 
					System: JVM 8, Ubuntu 20.04, CPU i5-3330, 8G RAM.
 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					 | 
				
			||||||
THROUGHPUT                         Mode  Cnt      Score     Error   Units
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					 | 
				
			||||||
Throughput.JDK_RandomBased         thrpt    5   2191,690 ±   8,947  ops/ms
 | 
					 | 
				
			||||||
Throughput.UlidCreator_Ulid        thrpt    5  19236,123 ± 156,123  ops/ms
 | 
					 | 
				
			||||||
Throughput.UlidCreator_UlidString  thrpt    5  12893,016 ± 179,618  ops/ms <- 2.5x faster
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					 | 
				
			||||||
Total time: 00:04:01
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The ULID string generation is 2.5x faster in version 2.2.0 than before.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
System: CPU i5-3330, 8G RAM, Ubuntu 20.04.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
See: [uuid-creator-benchmark](https://github.com/fabiolimace/uuid-creator-benchmark)
 | 
					See: [uuid-creator-benchmark](https://github.com/fabiolimace/uuid-creator-benchmark)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,6 @@ import com.github.f4b6a3.ulid.strategy.random.DefaultRandomStrategy;
 | 
				
			||||||
import com.github.f4b6a3.ulid.strategy.random.OtherRandomStrategy;
 | 
					import com.github.f4b6a3.ulid.strategy.random.OtherRandomStrategy;
 | 
				
			||||||
import com.github.f4b6a3.ulid.strategy.TimestampStrategy;
 | 
					import com.github.f4b6a3.ulid.strategy.TimestampStrategy;
 | 
				
			||||||
import com.github.f4b6a3.ulid.strategy.timestamp.DefaultTimestampStrategy;
 | 
					import com.github.f4b6a3.ulid.strategy.timestamp.DefaultTimestampStrategy;
 | 
				
			||||||
import com.github.f4b6a3.ulid.util.UlidUtil;
 | 
					 | 
				
			||||||
import com.github.f4b6a3.ulid.util.internal.UlidStruct;
 | 
					import com.github.f4b6a3.ulid.util.internal.UlidStruct;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -193,8 +192,18 @@ public class UlidSpecCreator {
 | 
				
			||||||
		// Get random values
 | 
							// Get random values
 | 
				
			||||||
		final byte[] bytes = new byte[10];
 | 
							final byte[] bytes = new byte[10];
 | 
				
			||||||
		this.randomStrategy.nextBytes(bytes);
 | 
							this.randomStrategy.nextBytes(bytes);
 | 
				
			||||||
		this.random1 = UlidUtil.toNumber(bytes, 0, 5);
 | 
							
 | 
				
			||||||
		this.random2 = UlidUtil.toNumber(bytes, 5, 10);
 | 
							this.random1 = (long) (bytes[0x0] & 0xff) << 32;
 | 
				
			||||||
 | 
							this.random1 |= (long) (bytes[0x1] & 0xff) << 24;
 | 
				
			||||||
 | 
							this.random1 |= (long) (bytes[0x2] & 0xff) << 16;
 | 
				
			||||||
 | 
							this.random1 |= (long) (bytes[0x3] & 0xff) << 8;
 | 
				
			||||||
 | 
							this.random1 |= (long) (bytes[0x4] & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.random2 = (long) (bytes[0x5] & 0xff) << 32;
 | 
				
			||||||
 | 
							this.random2 |= (long) (bytes[0x6] & 0xff) << 24;
 | 
				
			||||||
 | 
							this.random2 |= (long) (bytes[0x7] & 0xff) << 16;
 | 
				
			||||||
 | 
							this.random2 |= (long) (bytes[0x8] & 0xff) << 8;
 | 
				
			||||||
 | 
							this.random2 |= (long) (bytes[0x9] & 0xff);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		// Save the random values
 | 
							// Save the random values
 | 
				
			||||||
		this.randomMax1 = this.random1 | INCREMENT_MAX;
 | 
							this.randomMax1 = this.random1 | INCREMENT_MAX;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,8 @@ package com.github.f4b6a3.ulid.util;
 | 
				
			||||||
import java.time.Instant;
 | 
					import java.time.Instant;
 | 
				
			||||||
import java.util.UUID;
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.github.f4b6a3.ulid.util.internal.UlidStruct;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public final class UlidUtil {
 | 
					public final class UlidUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected static final int BASE_32 = 32;
 | 
						protected static final int BASE_32 = 32;
 | 
				
			||||||
| 
						 | 
					@ -61,12 +63,12 @@ public final class UlidUtil {
 | 
				
			||||||
		return Instant.ofEpochMilli(milliseconds);
 | 
							return Instant.ofEpochMilli(milliseconds);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private static long extractTimestamp(UUID ulid) {
 | 
						protected static long extractTimestamp(UUID ulid) {
 | 
				
			||||||
		return (ulid.getMostSignificantBits() >>> 16);
 | 
							return (ulid.getMostSignificantBits() >>> 16);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static long extractTimestamp(String ulid) {
 | 
						protected static long extractTimestamp(String ulid) {
 | 
				
			||||||
		return fromBase32Crockford(extractTimestampComponent(ulid).toCharArray());
 | 
							return UlidStruct.of(ulid).time;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static String extractTimestampComponent(String ulid) {
 | 
						public static String extractTimestampComponent(String ulid) {
 | 
				
			||||||
| 
						 | 
					@ -78,194 +80,4 @@ public final class UlidUtil {
 | 
				
			||||||
		UlidValidator.validate(ulid);
 | 
							UlidValidator.validate(ulid);
 | 
				
			||||||
		return ulid.substring(10, ULID_LENGTH);
 | 
							return ulid.substring(10, ULID_LENGTH);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get a number from a given array of bytes.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @param bytes a byte array
 | 
					 | 
				
			||||||
	 * @return a long
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public static long toNumber(final byte[] bytes) {
 | 
					 | 
				
			||||||
		return toNumber(bytes, 0, bytes.length);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public static long toNumber(final byte[] bytes, final int start, final int end) {
 | 
					 | 
				
			||||||
		long result = 0;
 | 
					 | 
				
			||||||
		for (int i = start; i < end; i++) {
 | 
					 | 
				
			||||||
			result = (result << 8) | (bytes[i] & 0xff);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return result;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get an array of bytes from a given number.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param number a long value
 | 
					 | 
				
			||||||
	 * @return a byte array
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	protected static byte[] toBytes(final long number) {
 | 
					 | 
				
			||||||
		return new byte[] { (byte) (number >>> 56), (byte) (number >>> 48), (byte) (number >>> 40),
 | 
					 | 
				
			||||||
				(byte) (number >>> 32), (byte) (number >>> 24), (byte) (number >>> 16), (byte) (number >>> 8),
 | 
					 | 
				
			||||||
				(byte) (number) };
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static char[] removeHyphens(final char[] input) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		int count = 0;
 | 
					 | 
				
			||||||
		char[] buffer = new char[input.length];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int i = 0; i < input.length; i++) {
 | 
					 | 
				
			||||||
			if ((input[i] != '-')) {
 | 
					 | 
				
			||||||
				buffer[count++] = input[i];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char[] output = new char[count];
 | 
					 | 
				
			||||||
		System.arraycopy(buffer, 0, output, 0, count);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return output;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public static char[] toBase32Crockford(long number) {
 | 
					 | 
				
			||||||
		return encode(number, UlidUtil.ALPHABET_CROCKFORD);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public static long fromBase32Crockford(char[] chars) {
 | 
					 | 
				
			||||||
		return decode(chars, UlidUtil.ALPHABET_CROCKFORD);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static boolean isCrockfordBase32(final char[] chars) {
 | 
					 | 
				
			||||||
		char[] input = toUpperCase(chars);
 | 
					 | 
				
			||||||
		for (int i = 0; i < input.length; i++) {
 | 
					 | 
				
			||||||
			if (!isCrockfordBase32(input[i])) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static boolean isCrockfordBase32(char c) {
 | 
					 | 
				
			||||||
		for (int j = 0; j < ALPHABET_CROCKFORD.length; j++) {
 | 
					 | 
				
			||||||
			if (c == ALPHABET_CROCKFORD[j]) {
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static char[] zerofill(char[] chars, int length) {
 | 
					 | 
				
			||||||
		return lpad(chars, length, '0');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static char[] lpad(char[] chars, int length, char fill) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		int delta = 0;
 | 
					 | 
				
			||||||
		int limit = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (length > chars.length) {
 | 
					 | 
				
			||||||
			delta = length - chars.length;
 | 
					 | 
				
			||||||
			limit = length;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			delta = 0;
 | 
					 | 
				
			||||||
			limit = chars.length;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char[] output = new char[chars.length + delta];
 | 
					 | 
				
			||||||
		for (int i = 0; i < limit; i++) {
 | 
					 | 
				
			||||||
			if (i < delta) {
 | 
					 | 
				
			||||||
				output[i] = fill;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				output[i] = chars[i - delta];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return output;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static String transliterate(String string, char[] alphabet1, char[] alphabet2) {
 | 
					 | 
				
			||||||
		return new String(transliterate(string.toCharArray(), alphabet1, alphabet2));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static char[] transliterate(char[] chars, char[] alphabet1, char[] alphabet2) {
 | 
					 | 
				
			||||||
		char[] output = chars.clone();
 | 
					 | 
				
			||||||
		for (int i = 0; i < output.length; i++) {
 | 
					 | 
				
			||||||
			for (int j = 0; j < alphabet1.length; j++) {
 | 
					 | 
				
			||||||
				if (output[i] == alphabet1[j]) {
 | 
					 | 
				
			||||||
					output[i] = alphabet2[j];
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return output;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected static char[] toUpperCase(final char[] chars) {
 | 
					 | 
				
			||||||
		char[] output = new char[chars.length];
 | 
					 | 
				
			||||||
		for (int i = 0; i < output.length; i++) {
 | 
					 | 
				
			||||||
			if (chars[i] >= 0x61 && chars[i] <= 0x7a) {
 | 
					 | 
				
			||||||
				output[i] = (char) ((int) chars[i] & 0xffffffdf);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				output[i] = chars[i];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return output;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Encode a long number to base 32 char array.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @param number   a long number
 | 
					 | 
				
			||||||
	 * @param alphabet an alphabet
 | 
					 | 
				
			||||||
	 * @return a base32 encoded char array
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	protected static char[] encode(long number, char[] alphabet) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		final int CHARS_MAX = 13; // 13 * 5 = 65
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (number < 0) {
 | 
					 | 
				
			||||||
			throw new IllegalArgumentException(String.format("Number '%d' is not a positive integer.", number));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		long n = number;
 | 
					 | 
				
			||||||
		char[] buffer = new char[CHARS_MAX];
 | 
					 | 
				
			||||||
		char[] output;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		int count = CHARS_MAX;
 | 
					 | 
				
			||||||
		while (n > 0) {
 | 
					 | 
				
			||||||
			buffer[--count] = alphabet[(int) (n % BASE_32)];
 | 
					 | 
				
			||||||
			n = n / BASE_32;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		output = new char[buffer.length - count];
 | 
					 | 
				
			||||||
		System.arraycopy(buffer, count, output, 0, output.length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return output;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Decode a base 32 char array to a long number.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @param chars    a base 32 encoded char array
 | 
					 | 
				
			||||||
	 * @param alphabet an alphabet
 | 
					 | 
				
			||||||
	 * @return a long number
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	protected static long decode(char[] chars, char[] alphabet) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		long n = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int i = 0; i < chars.length; i++) {
 | 
					 | 
				
			||||||
			int d = chr(chars[i], alphabet);
 | 
					 | 
				
			||||||
			n = BASE_32 * n + d;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return n;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static int chr(char c, char[] alphabet) {
 | 
					 | 
				
			||||||
		for (int i = 0; i < alphabet.length; i++) {
 | 
					 | 
				
			||||||
			if (alphabet[i] == c) {
 | 
					 | 
				
			||||||
				return (byte) i;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return (byte) '0';
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,30 @@ public final class UlidValidator {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Checks if the ULID string is a valid.
 | 
						 * Checks if the char array is a valid ULID.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * A valid ULID string is a sequence of 26 characters from Crockford's base 32
 | 
				
			||||||
 | 
						 * alphabet.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * It also checks if the timestamp is between 0 and 2^48-1.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * <pre>
 | 
				
			||||||
 | 
						 * Examples of valid ULID strings:
 | 
				
			||||||
 | 
						 * - 0123456789ABCDEFGHJKMNPKRS (26 alphanumeric, case insensitive, except U)
 | 
				
			||||||
 | 
						 * - 0123456789ABCDEFGHIJKLMNOP (26 alphanumeric, case insensitive, including OIL, except U)
 | 
				
			||||||
 | 
						 * - 0123456789-ABCDEFGHJK-MNPKRS (26 alphanumeric, case insensitive, except U, with hyphens)
 | 
				
			||||||
 | 
						 * - 0123456789-ABCDEFGHIJ-KLMNOP (26 alphanumeric, case insensitive, including OIL, except U, with hyphens)
 | 
				
			||||||
 | 
						 * </pre>
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param ulid a ULID char array
 | 
				
			||||||
 | 
						 * @return boolean true if valid
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static boolean isValid(char[] ulid) {
 | 
				
			||||||
 | 
							return (ulid != null && ulid.length != 0 && isValidString(ulid));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Checks if the ULID string is valid.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * See {@link UlidValidator#isValid(String)}.
 | 
						 * See {@link UlidValidator#isValid(String)}.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
| 
						 | 
					@ -68,7 +91,21 @@ public final class UlidValidator {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public static void validate(String ulid) {
 | 
						public static void validate(String ulid) {
 | 
				
			||||||
		if (ulid == null || ulid.length() == 0 || !isValidString(ulid.toCharArray())) {
 | 
							if (ulid == null || ulid.length() == 0 || !isValidString(ulid.toCharArray())) {
 | 
				
			||||||
			throw new InvalidUlidException(String.format("Invalid ULID: %s.", ulid));
 | 
								throw new InvalidUlidException("Invalid ULID: \"" + ulid + "\"");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Checks if the ULID char array is valid.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * See {@link UlidValidator#isValid(String)}.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param ulid a ULID char array
 | 
				
			||||||
 | 
						 * @throws InvalidUlidException if invalid
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static void validate(char[] ulid) {
 | 
				
			||||||
 | 
							if (ulid == null || ulid.length == 0 || !isValidString(ulid)) {
 | 
				
			||||||
 | 
								throw new InvalidUlidException("Invalid ULID: \"" + (ulid == null ? null : new String(ulid)) + "\"");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,23 +130,23 @@ public final class UlidStruct {
 | 
				
			||||||
		this.random2 = random2 & HALF_RANDOM_COMPONENT;
 | 
							this.random2 = random2 & HALF_RANDOM_COMPONENT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private UlidStruct(UUID uuid) {
 | 
						private UlidStruct(UUID ulid) {
 | 
				
			||||||
		final long msb = uuid.getMostSignificantBits();
 | 
							final long msb = ulid.getMostSignificantBits();
 | 
				
			||||||
		final long lsb = uuid.getLeastSignificantBits();
 | 
							final long lsb = ulid.getLeastSignificantBits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.time = (msb >>> 16);
 | 
							this.time = (msb >>> 16);
 | 
				
			||||||
		this.random1 = ((msb & 0x000000000000ffffL) << 24) | (lsb >>> 40);
 | 
							this.random1 = ((msb & 0x000000000000ffffL) << 24) | (lsb >>> 40);
 | 
				
			||||||
		this.random2 = (lsb & 0x000000ffffffffffL);
 | 
							this.random2 = (lsb & 0x000000ffffffffffL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private UlidStruct(String string) {
 | 
						private UlidStruct(String ulid) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		UlidValidator.validate(string);
 | 
							final char[] chars = ulid == null ? new char[0] : ulid.toCharArray();
 | 
				
			||||||
 | 
							UlidValidator.validate(chars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long tm = 0;
 | 
							long tm = 0;
 | 
				
			||||||
		long r1 = 0;
 | 
							long r1 = 0;
 | 
				
			||||||
		long r2 = 0;
 | 
							long r2 = 0;
 | 
				
			||||||
		final char[] chars = string.toCharArray();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tm |= BASE32_VALUES[chars[0x00]] << 45;
 | 
							tm |= BASE32_VALUES[chars[0x00]] << 45;
 | 
				
			||||||
		tm |= BASE32_VALUES[chars[0x01]] << 40;
 | 
							tm |= BASE32_VALUES[chars[0x01]] << 40;
 | 
				
			||||||
| 
						 | 
					@ -232,8 +232,8 @@ public final class UlidStruct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String toString4() {
 | 
						public String toString4() {
 | 
				
			||||||
		// apply RFC-4122 version 4 and variant 2
 | 
							// apply RFC-4122 version 4 and variant 2
 | 
				
			||||||
		final long newrandom1 = ((this.random1 & 0x0fff3fffffL) | 0x4000000000L) | 0x0000800000L;
 | 
							final long random1v4 = ((this.random1 & 0x0fff3fffffL) | 0x4000000000L) | 0x0000800000L;
 | 
				
			||||||
		return UlidStruct.of(this.time, newrandom1, this.random2).toString();
 | 
							return UlidStruct.of(this.time, random1v4, this.random2).toString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public UUID toUuid() {
 | 
						public UUID toUuid() {
 | 
				
			||||||
| 
						 | 
					@ -246,8 +246,8 @@ public final class UlidStruct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public UUID toUuid4() {
 | 
						public UUID toUuid4() {
 | 
				
			||||||
		// apply RFC-4122 version 4 and variant 2
 | 
							// apply RFC-4122 version 4 and variant 2
 | 
				
			||||||
		final long newrandom1 = ((this.random1 & 0x0fff3fffffL) | 0x4000000000L) | 0x0000800000L;
 | 
							final long random1v4 = ((this.random1 & 0x0fff3fffffL) | 0x4000000000L) | 0x0000800000L;
 | 
				
			||||||
		return UlidStruct.of(this.time, newrandom1, this.random2).toUuid();
 | 
							return UlidStruct.of(this.time, random1v4, this.random2).toUuid();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,64 +2,78 @@ package com.github.f4b6a3.ulid.util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.Assert.*;
 | 
					import static org.junit.Assert.*;
 | 
				
			||||||
import java.time.Instant;
 | 
					import java.time.Instant;
 | 
				
			||||||
import java.util.UUID;
 | 
					import java.util.Random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.f4b6a3.ulid.exception.InvalidUlidException;
 | 
					import com.github.f4b6a3.ulid.exception.InvalidUlidException;
 | 
				
			||||||
 | 
					import com.github.f4b6a3.ulid.util.internal.UlidStructTest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.github.f4b6a3.ulid.util.UlidUtil.*;
 | 
					import static com.github.f4b6a3.ulid.util.UlidUtil.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class UlidUtilTest {
 | 
					public class UlidUtilTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final String EXAMPLE_TIMESTAMP = "0123456789";
 | 
					 | 
				
			||||||
	private static final String EXAMPLE_RANDOMNESS = "ABCDEFGHJKMNPQRS";
 | 
					 | 
				
			||||||
	private static final String EXAMPLE_ULID = "0123456789ABCDEFGHJKMNPQRS";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Date: 10889-08-02T05:31:50.655Z: 281474976710655 (2^48-1)
 | 
						// Date: 10889-08-02T05:31:50.655Z: 281474976710655 (2^48-1)
 | 
				
			||||||
	private static final long TIMESTAMP_MAX = 0xffffffffffffL;
 | 
						private static final long TIMESTAMP_MAX = 0xffffffffffffL;
 | 
				
			||||||
 | 
						private static final long HALF_RANDOM_MAX = 0xffffffffffL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final String[] EXAMPLE_DATES = { "1970-01-01T00:00:00.000Z", "1985-10-26T01:16:00.123Z",
 | 
						private static final int DEFAULT_LOOP_MAX = 100_000;
 | 
				
			||||||
			"2001-09-09T01:46:40.456Z", "2020-01-15T14:30:33.789Z", "2038-01-19T03:14:07.321Z" };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final int[] NUMBERS = { 102685630, 725393777, 573697669, 614668535, 790665079, 728958755, 966150230,
 | 
						private static final Random RANDOM = new Random();
 | 
				
			||||||
			410015018, 605266173, 946077566, 214051168, 775737014, 723003700, 391609366, 147844737, 514081413,
 | 
					 | 
				
			||||||
			488279622, 550860813, 611087782, 223492126, 706308515, 158990768, 549042286, 26926303, 775714134, 602886016,
 | 
					 | 
				
			||||||
			27282100, 675097356, 641101167, 515280699, 454184468, 371424784, 633917378, 887459583, 792903202, 168552040,
 | 
					 | 
				
			||||||
			824806922, 696445335, 653338746, 357696553, 353677217, 972662902, 400738139, 537701151, 202077579,
 | 
					 | 
				
			||||||
			110209145, 356152341, 168702810, 684185451, 419840003, 480132486, 308833881, 997154252, 918202260,
 | 
					 | 
				
			||||||
			103304091, 328467776, 648729690, 733655121, 645189051, 342500864, 560919543, 509761384, 626871960,
 | 
					 | 
				
			||||||
			429248550, 319025067, 507317265, 348303729, 256009160, 660250872, 85224414, 414490625, 355994979, 318005886,
 | 
					 | 
				
			||||||
			326093128, 492813589, 569014099, 503350412, 168303553, 801566586, 800368918, 742601973, 395588591,
 | 
					 | 
				
			||||||
			257341245, 722366808, 501878988, 200718306, 184948029, 149469829, 992401543, 240364551, 976817281,
 | 
					 | 
				
			||||||
			161998068, 515579566, 275182272, 376045488, 899163436, 941443452, 974372015, 934795357, 958806784 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static final String[] NUMBERS_BASE_32_CROCKFORD = { "31XPXY", "NKS8BH", "H33VM5", "JA667Q", "QJ15VQ",
 | 
					 | 
				
			||||||
			"NQ61S3", "WSCJ2P", "C70N9A", "J1787X", "W67ZVY", "6C4AB0", "Q3SKNP", "NHGA9M", "BNEZ0P", "4CZVM1",
 | 
					 | 
				
			||||||
			"FA8GM5", "EHN3J6", "GDAY0D", "J6RXD6", "6N4E0Y", "N1JTD3", "4QM0DG", "GBKE3E", "SNQ6Z", "Q3RXAP", "HYYKW0",
 | 
					 | 
				
			||||||
			"T0JNM", "M3TARC", "K3CVBF", "FBD3SV", "DH4KGM", "B26ZGG", "JWHKY2", "TEB3QZ", "QM5FH2", "50QSK8", "RJK3GA",
 | 
					 | 
				
			||||||
			"MR5TCQ", "KF2A3T", "AN4119", "AH9BX1", "WZKA3P", "BY5HTV", "G0SARZ", "60PXCB", "393A3S", "AKMX0N",
 | 
					 | 
				
			||||||
			"50WCTT", "MCFNVB", "CGCG03", "E9WFC6", "96GVJS", "XPYQEC", "VBN9WM", "32GJWV", "9S81A0", "KANN2T",
 | 
					 | 
				
			||||||
			"NVNC2H", "K79KDV", "A6M9G0", "GPXWZQ", "F64NV8", "JNTKMR", "CSBM16", "9G7VXB", "F3T30H", "AC5CBH",
 | 
					 | 
				
			||||||
			"7M4RY8", "KNN87R", "2H8TYY", "CB9801", "AKG3B3", "9F8RKY", "9PZJA8", "ENZF8N", "GYMXTK", "F0114C",
 | 
					 | 
				
			||||||
			"50G6Y1", "QWDVVT", "QV9A8P", "P46D7N", "BS8CZF", "7NDDSX", "NGWWAR", "EYM46C", "5ZDDZ2", "5GC59X",
 | 
					 | 
				
			||||||
			"4EHEM5", "XJDP47", "757B07", "X3J341", "4TFS7M", "FBP7NE", "86DWP0", "B6KZXG", "TSG99C", "W1TJBW",
 | 
					 | 
				
			||||||
			"X17F5F", "VVFP2X", "WJCER0" };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testExtractTimestamp1() {
 | 
						public void testExtractTimestamp1() {
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String ulid = "0000000000" + EXAMPLE_RANDOMNESS;
 | 
								long time = RANDOM.nextLong() & TIMESTAMP_MAX;
 | 
				
			||||||
		long milliseconds = extractUnixMilliseconds(ulid);
 | 
								long random1 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
		assertEquals(0, milliseconds);
 | 
								long random2 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ulid = "7ZZZZZZZZZ" + EXAMPLE_RANDOMNESS;
 | 
								String timeComponent = UlidStructTest.toTimeComponent(time);
 | 
				
			||||||
		milliseconds = extractUnixMilliseconds(ulid);
 | 
								String randomComponent = UlidStructTest.toRandomComponent(random1, random2);
 | 
				
			||||||
		assertEquals(TIMESTAMP_MAX, milliseconds);
 | 
					
 | 
				
			||||||
 | 
								String ulid = timeComponent + randomComponent;
 | 
				
			||||||
 | 
								long result = extractTimestamp(ulid);
 | 
				
			||||||
 | 
								assertEquals(time, result);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testExtractTimestamp2() {
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								String ulid;
 | 
				
			||||||
 | 
								String timeComponent;
 | 
				
			||||||
 | 
								String randomComponent;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								long time;
 | 
				
			||||||
 | 
								long random1;
 | 
				
			||||||
 | 
								long random2;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								time = RANDOM.nextLong() & TIMESTAMP_MAX;
 | 
				
			||||||
 | 
								random1 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								random2 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								timeComponent = UlidStructTest.toTimeComponent(time);
 | 
				
			||||||
 | 
								randomComponent = UlidStructTest.toRandomComponent(random1, random2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								timeComponent = "7ZZZZZZZZZ";
 | 
				
			||||||
 | 
								ulid = timeComponent + randomComponent;
 | 
				
			||||||
 | 
								time = extractTimestamp(ulid);
 | 
				
			||||||
 | 
								assertEquals(TIMESTAMP_MAX, time);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								timeComponent = "0000000000";
 | 
				
			||||||
 | 
								ulid = timeComponent + randomComponent;
 | 
				
			||||||
 | 
								time = extractTimestamp(ulid);
 | 
				
			||||||
 | 
								assertEquals(0, time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				// Test the first extra bit added by the base32 encoding
 | 
									// Test the first extra bit added by the base32 encoding
 | 
				
			||||||
			ulid = "G0000000000000000000000000";
 | 
									char[] chars = timeComponent.toCharArray();
 | 
				
			||||||
			extractUnixMilliseconds(ulid);
 | 
									chars[0] = 'G'; // GZZZZZZZZZ
 | 
				
			||||||
 | 
									timeComponent = new String(chars);
 | 
				
			||||||
 | 
									ulid = timeComponent + randomComponent;
 | 
				
			||||||
 | 
									extractTimestamp(ulid);
 | 
				
			||||||
				fail("Should throw an InvalidUlidException");
 | 
									fail("Should throw an InvalidUlidException");
 | 
				
			||||||
			} catch (InvalidUlidException e) {
 | 
								} catch (InvalidUlidException e) {
 | 
				
			||||||
				// success
 | 
									// success
 | 
				
			||||||
| 
						 | 
					@ -67,228 +81,76 @@ public class UlidUtilTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				// Test the second extra bit added by the base32 encoding
 | 
									// Test the second extra bit added by the base32 encoding
 | 
				
			||||||
			ulid = "80000000000000000000000000";
 | 
									char[] chars = timeComponent.toCharArray();
 | 
				
			||||||
			extractUnixMilliseconds(ulid);
 | 
									chars[0] = '8'; // 8ZZZZZZZZZ
 | 
				
			||||||
 | 
									timeComponent = new String(chars);
 | 
				
			||||||
 | 
									ulid = timeComponent + randomComponent;
 | 
				
			||||||
 | 
									extractTimestamp(ulid);
 | 
				
			||||||
				fail("Should throw an InvalidUlidException");
 | 
									fail("Should throw an InvalidUlidException");
 | 
				
			||||||
			} catch (InvalidUlidException e) {
 | 
								} catch (InvalidUlidException e) {
 | 
				
			||||||
				// success
 | 
									// success
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testExtractTimestamp2() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		String string = "0000000000" + EXAMPLE_RANDOMNESS;
 | 
					 | 
				
			||||||
		UUID ulid = UlidConverter.fromString(string);
 | 
					 | 
				
			||||||
		long milliseconds = extractUnixMilliseconds(ulid);
 | 
					 | 
				
			||||||
		assertEquals(0, milliseconds);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "7ZZZZZZZZZ" + EXAMPLE_RANDOMNESS;
 | 
					 | 
				
			||||||
		ulid = UlidConverter.fromString(string);
 | 
					 | 
				
			||||||
		milliseconds = extractUnixMilliseconds(ulid);
 | 
					 | 
				
			||||||
		assertEquals(TIMESTAMP_MAX, milliseconds);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testExtractTimestampList() {
 | 
						public void testExtractUnixMilliseconds() {
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
		String randomnessComponent = EXAMPLE_RANDOMNESS;
 | 
								long time = RANDOM.nextLong() & TIMESTAMP_MAX;
 | 
				
			||||||
 | 
								long random1 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
		for (String i : EXAMPLE_DATES) {
 | 
								long random2 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
			long milliseconds = Instant.parse(i).toEpochMilli();
 | 
								String ulid = UlidStructTest.toString(time, random1, random2);
 | 
				
			||||||
 | 
					 | 
				
			||||||
			String timestampComponent = new String(UlidUtil.zerofill(toBase32Crockford(milliseconds), 10));
 | 
					 | 
				
			||||||
			String ulid = timestampComponent + randomnessComponent;
 | 
					 | 
				
			||||||
			long result = extractUnixMilliseconds(ulid);
 | 
								long result = extractUnixMilliseconds(ulid);
 | 
				
			||||||
 | 
								assertEquals(time, result);
 | 
				
			||||||
			assertEquals(milliseconds, result);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testExtractInstant() {
 | 
						public void testExtractInstant() {
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
		String randomnessComponent = EXAMPLE_RANDOMNESS;
 | 
								long time = RANDOM.nextLong() & TIMESTAMP_MAX;
 | 
				
			||||||
 | 
								Instant instant = Instant.ofEpochMilli(time);
 | 
				
			||||||
		for (String i : EXAMPLE_DATES) {
 | 
								long random1 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								long random2 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
			Instant instant = Instant.parse(i);
 | 
								String ulid = UlidStructTest.toString(time, random1, random2);
 | 
				
			||||||
			long milliseconds = Instant.parse(i).toEpochMilli();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			byte[] bytes = new byte[6];
 | 
					 | 
				
			||||||
			System.arraycopy(toBytes(milliseconds), 2, bytes, 0, 6);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			String timestampComponent = new String(UlidUtil.zerofill(toBase32Crockford(milliseconds), 10));
 | 
					 | 
				
			||||||
			String ulid = timestampComponent + randomnessComponent;
 | 
					 | 
				
			||||||
			Instant result = extractInstant(ulid);
 | 
								Instant result = extractInstant(ulid);
 | 
				
			||||||
 | 
					 | 
				
			||||||
			assertEquals(instant, result);
 | 
								assertEquals(instant, result);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testExtractTimestampComponent() {
 | 
						public void testExtractTimestampComponent() {
 | 
				
			||||||
		String ulid = EXAMPLE_ULID;
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
		String expected = EXAMPLE_TIMESTAMP;
 | 
								long time = RANDOM.nextLong() & TIMESTAMP_MAX;
 | 
				
			||||||
 | 
								long random1 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								long random2 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								String ulid = UlidStructTest.toString(time, random1, random2);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								char[] chars = ulid.toCharArray();
 | 
				
			||||||
 | 
								char[] timeComponent = new char[10];
 | 
				
			||||||
 | 
								System.arraycopy(chars, 0, timeComponent, 0, 10);
 | 
				
			||||||
 | 
								String expected = new String(timeComponent); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			String result = extractTimestampComponent(ulid);
 | 
								String result = extractTimestampComponent(ulid);
 | 
				
			||||||
			assertEquals(expected, result);
 | 
								assertEquals(expected, result);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testExtractRandomnessComponent() {
 | 
						public void testExtractRandomnessComponent() {
 | 
				
			||||||
		String ulid = EXAMPLE_ULID;
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
		String expected = EXAMPLE_RANDOMNESS;
 | 
								long time = RANDOM.nextLong() & TIMESTAMP_MAX;
 | 
				
			||||||
 | 
								long random1 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								long random2 = RANDOM.nextLong() & HALF_RANDOM_MAX;
 | 
				
			||||||
 | 
								String ulid = UlidStructTest.toString(time, random1, random2);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								char[] chars = ulid.toCharArray();
 | 
				
			||||||
 | 
								char[] randomComponent = new char[16];
 | 
				
			||||||
 | 
								System.arraycopy(chars, 10, randomComponent, 0, 16);
 | 
				
			||||||
 | 
								String expected = new String(randomComponent); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			String result = extractRandomnessComponent(ulid);
 | 
								String result = extractRandomnessComponent(ulid);
 | 
				
			||||||
			assertEquals(expected, result);
 | 
								assertEquals(expected, result);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testToUpperCase() {
 | 
					 | 
				
			||||||
		String string = "Aq7zmxKxPc61QKiGRu8Y3PdYMer64lrRxfb9A5JAJuDeEhXSrbsxsaUoHrFzmEJUYBKJPgV+1rAd";
 | 
					 | 
				
			||||||
		char[] chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		char[] chars2 = UlidUtil.toUpperCase(chars1);
 | 
					 | 
				
			||||||
		assertEquals(new String(string).toUpperCase(), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "kL9zTzZfzlwKYCEmWKPxFYxINf6JZCSmSqykyG5ONWZcFkJG2WGc7gq71YCEzt2hYcsTvfQqEmn0";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.toUpperCase(chars1);
 | 
					 | 
				
			||||||
		assertEquals(new String(string).toUpperCase(), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "XXEOUV3jJb3f+wpRPDVke9NgWwEgdkzChnKnpZZWS/mCSqTi757GmmqYdzuDGOa5ftqHI3/zqKrS";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.toUpperCase(chars1);
 | 
					 | 
				
			||||||
		assertEquals(new String(string).toUpperCase(), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "t9LVRQZbCxTQgaxlajNE/VYpLpKiHtKt7jHrtxSDIJ2hrHaJI2UPF1zA7I35m9cKz01lHYD1IXlM";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.toUpperCase(chars1);
 | 
					 | 
				
			||||||
		assertEquals(new String(string).toUpperCase(), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "jyS52J42LLT6GY+Zywo1R4tQv4bTfAqpFB6aiKEuA3yDxFkuXzuKe8PaGlUTaXD5WgRFMnO9nRLU";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.toUpperCase(chars1);
 | 
					 | 
				
			||||||
		assertEquals(new String(string).toUpperCase(), new String(chars2));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testZerofill() {
 | 
					 | 
				
			||||||
		assertEquals("001", new String(UlidUtil.zerofill("1".toCharArray(), 3)));
 | 
					 | 
				
			||||||
		assertEquals("000123", new String(UlidUtil.zerofill("123".toCharArray(), 6)));
 | 
					 | 
				
			||||||
		assertEquals("0000000000", new String(UlidUtil.zerofill("".toCharArray(), 10)));
 | 
					 | 
				
			||||||
		assertEquals("9876543210", new String(UlidUtil.zerofill("9876543210".toCharArray(), 10)));
 | 
					 | 
				
			||||||
		assertEquals("0000000000123456", new String(UlidUtil.zerofill("123456".toCharArray(), 16)));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testLpad() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		String string = "";
 | 
					 | 
				
			||||||
		char[] chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		char[] chars2 = UlidUtil.lpad(chars1, 8, 'x');
 | 
					 | 
				
			||||||
		assertEquals("xxxxxxxx", new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.lpad(chars1, 12, 'W');
 | 
					 | 
				
			||||||
		assertEquals("WWWWWWWWWWWW", new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "TCgpYATMlK9BmSzX";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.lpad(chars1, 13, '0');
 | 
					 | 
				
			||||||
		assertEquals(string, new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "2kgy3m9U646L6TJ5";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.lpad(chars1, 16, '0');
 | 
					 | 
				
			||||||
		assertEquals(string, new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "2kgy3m9U646L6TJ5";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.lpad(chars1, 17, '0');
 | 
					 | 
				
			||||||
		assertEquals("0" + string, new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "LH6hfYcGJu06xSNF";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.lpad(chars1, 25, '0');
 | 
					 | 
				
			||||||
		assertEquals("000000000" + string, new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "t9LVRQZbCxTQgaxlajNE/VYpLpKiHtKt7jHrtxSDIJ2hrHaJI2UPF1zA7I35m9cKz01lHYD1IXlM";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.lpad(chars1, 80, '0');
 | 
					 | 
				
			||||||
		assertEquals("0000" + string, new String(chars2));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testRemoveHyphens() {
 | 
					 | 
				
			||||||
		String string = "-ZGQ8yCsza-RFxlYyA-FaXa4wd-k4Owa-/ITDvqWOl4-Do3/NwW--Lawx6GcO-LmSRDsd3af3Zt-VMNnvLgIw9-";
 | 
					 | 
				
			||||||
		char[] chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		char[] chars2 = UlidUtil.removeHyphens(chars1);
 | 
					 | 
				
			||||||
		assertEquals(string.replace("-", ""), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "qi3q-EMvc1-Kk7XzYMj----SUnwf-lp0K7-Ucj-W-cDplP-2dG-3x+5y-r9JBc-ZT-0e--cRHoMbU/lBzZsJ6rcJ5zT/J";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.removeHyphens(chars1);
 | 
					 | 
				
			||||||
		assertEquals(string.replace("-", ""), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "RXMD0DJV---Zf3Jqcv39uGjzBuiLkLNL-IvPnTyfMteEet-I7u-Z8oyE+BIUBf/OPi30iICP1TnQpMve4j";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.removeHyphens(chars1);
 | 
					 | 
				
			||||||
		assertEquals(string.replace("-", ""), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "---dFH-b-ylQPA60-kuRxZ9-6q5MLd1-qLTKdma-rF2yEABt-t6mJg0U-ibIYcVnt-Guqdn-z-G-43Ob-W/-Gxah1+53a-";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.removeHyphens(chars1);
 | 
					 | 
				
			||||||
		assertEquals(string.replace("-", ""), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		string = "-------------------------------";
 | 
					 | 
				
			||||||
		chars1 = string.toCharArray();
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.removeHyphens(chars1);
 | 
					 | 
				
			||||||
		assertEquals(string.replace("-", ""), new String(chars2));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testTransliterate() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char[] alphabetCrockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
 | 
					 | 
				
			||||||
		char[] alphabetDefault = "0123456789abcdefghijklmnopqrstuv".toCharArray();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char[] chars2 = UlidUtil.transliterate(alphabetCrockford, alphabetCrockford, alphabetDefault);
 | 
					 | 
				
			||||||
		assertEquals(new String(alphabetDefault), new String(chars2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		chars2 = UlidUtil.transliterate(alphabetDefault, alphabetDefault, alphabetCrockford);
 | 
					 | 
				
			||||||
		assertEquals(new String(alphabetCrockford), new String(chars2));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testIsBase32Crockford() {
 | 
					 | 
				
			||||||
		assertTrue(UlidUtil.isCrockfordBase32("16JD".toCharArray()));
 | 
					 | 
				
			||||||
		assertTrue(UlidUtil.isCrockfordBase32("01BX5ZZKBKACTAV9WEVGEMMVRY".toCharArray()));
 | 
					 | 
				
			||||||
		assertTrue(UlidUtil.isCrockfordBase32(UlidUtil.ALPHABET_CROCKFORD));
 | 
					 | 
				
			||||||
		assertFalse(UlidUtil.isCrockfordBase32("U6JD".toCharArray()));
 | 
					 | 
				
			||||||
		assertFalse(UlidUtil.isCrockfordBase32("*1BX5ZZKBKACTAV9WEVGEMMVRY".toCharArray()));
 | 
					 | 
				
			||||||
		assertFalse(UlidUtil.isCrockfordBase32("u".toCharArray()));
 | 
					 | 
				
			||||||
		assertFalse(UlidUtil.isCrockfordBase32("U".toCharArray()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testToBase32Crockford() {
 | 
					 | 
				
			||||||
		assertEquals("7ZZZZZZZZZ", new String(UlidUtil.toBase32Crockford(281474976710655L)));
 | 
					 | 
				
			||||||
		// Encode from long to base 32
 | 
					 | 
				
			||||||
		for (int i = 0; i < NUMBERS.length; i++) {
 | 
					 | 
				
			||||||
			String result = new String(UlidUtil.toBase32Crockford(NUMBERS[i]));
 | 
					 | 
				
			||||||
			assertEquals(NUMBERS_BASE_32_CROCKFORD[i].length(), result.length());
 | 
					 | 
				
			||||||
			assertEquals(NUMBERS_BASE_32_CROCKFORD[i], result);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Test
 | 
					 | 
				
			||||||
	public void testFromBase32Crockford() {
 | 
					 | 
				
			||||||
		assertEquals(281474976710655L, UlidUtil.fromBase32Crockford("7ZZZZZZZZZ".toCharArray()));
 | 
					 | 
				
			||||||
		// Decode from base 32 to long
 | 
					 | 
				
			||||||
		long number = 0;
 | 
					 | 
				
			||||||
		for (int i = 0; i < NUMBERS.length; i++) {
 | 
					 | 
				
			||||||
			number = UlidUtil.fromBase32Crockford((NUMBERS_BASE_32_CROCKFORD[i]).toCharArray());
 | 
					 | 
				
			||||||
			assertEquals(NUMBERS[i], number);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,51 @@ public class UlidStructTest {
 | 
				
			||||||
	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
 | 
				
			||||||
 | 
						public void testOfAndToString() {
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
								UUID uuid0 = UUID.randomUUID();
 | 
				
			||||||
 | 
								String string0 = toString(uuid0);
 | 
				
			||||||
 | 
								String string1 = UlidStruct.of(string0).toString();
 | 
				
			||||||
 | 
								assertEquals(string0, string1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// Test RFC-4122 UUID version 4
 | 
				
			||||||
 | 
							final long versionMask = 0xffffffffffff0fffL;
 | 
				
			||||||
 | 
							final long variantMask = 0x3fffffffffffffffL;
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
								UUID uuid0 = UUID.randomUUID();
 | 
				
			||||||
 | 
								String string0 = toString(uuid0);
 | 
				
			||||||
 | 
								String string1 = UlidStruct.of(string0).toString4(); // UUID v4 in base32
 | 
				
			||||||
 | 
								UUID uuid1 = toUuid(fromString(string1));
 | 
				
			||||||
 | 
								assertEquals(uuid0.getMostSignificantBits() & versionMask, uuid1.getMostSignificantBits() & versionMask);
 | 
				
			||||||
 | 
								assertEquals(uuid0.getLeastSignificantBits() & variantMask, uuid1.getLeastSignificantBits() & variantMask);
 | 
				
			||||||
 | 
								assertEquals(4, uuid1.version());
 | 
				
			||||||
 | 
								assertEquals(2, uuid1.variant());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void testOfAndToUuid() {
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
								UUID uuid0 = UUID.randomUUID();
 | 
				
			||||||
 | 
								UUID uuid1 = UlidStruct.of(uuid0).toUuid();
 | 
				
			||||||
 | 
								assertEquals(uuid0, uuid1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Test RFC-4122 UUID version 4
 | 
				
			||||||
 | 
							final long versionMask = 0xffffffffffff0fffL;
 | 
				
			||||||
 | 
							final long variantMask = 0x3fffffffffffffffL;
 | 
				
			||||||
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
 | 
								UUID uuid0 = UUID.randomUUID();
 | 
				
			||||||
 | 
								UUID uuid1 = UlidStruct.of(uuid0).toUuid4(); // UUID v4
 | 
				
			||||||
 | 
								assertEquals(uuid0.getMostSignificantBits() & versionMask, uuid1.getMostSignificantBits() & versionMask);
 | 
				
			||||||
 | 
								assertEquals(uuid0.getLeastSignificantBits() & variantMask, uuid1.getLeastSignificantBits() & variantMask);
 | 
				
			||||||
 | 
								assertEquals(4, uuid1.version());
 | 
				
			||||||
 | 
								assertEquals(2, uuid1.variant());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void testConstructorLongs() {
 | 
						public void testConstructorLongs() {
 | 
				
			||||||
		for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
							for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
 | 
				
			||||||
| 
						 | 
					@ -90,7 +135,7 @@ public class UlidStructTest {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public UlidStruct fromString(String string) {
 | 
						public static UlidStruct fromString(String string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long time = 0;
 | 
							long time = 0;
 | 
				
			||||||
		long random1 = 0;
 | 
							long random1 = 0;
 | 
				
			||||||
| 
						 | 
					@ -111,7 +156,7 @@ public class UlidStructTest {
 | 
				
			||||||
		return UlidStruct.of(time, random1, random2);
 | 
							return UlidStruct.of(time, random1, random2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public UUID toUuid(UlidStruct struct) {
 | 
						public static UUID toUuid(UlidStruct struct) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		long time = struct.time & 0xffffffffffffL;
 | 
							long time = struct.time & 0xffffffffffffL;
 | 
				
			||||||
		long random1 = struct.random1 & 0xffffffffffL;
 | 
							long random1 = struct.random1 & 0xffffffffffL;
 | 
				
			||||||
| 
						 | 
					@ -123,24 +168,60 @@ public class UlidStructTest {
 | 
				
			||||||
		return new UUID(msb, lsb);
 | 
							return new UUID(msb, lsb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String toString(UlidStruct struct) {
 | 
						public static UUID toUuid(final long time, final long random1, final long random2) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							long tm = time & 0xffffffffffffL;
 | 
				
			||||||
 | 
							long r1 = random1 & 0xffffffffffL;
 | 
				
			||||||
 | 
							long r2 = random2 & 0xffffffffffL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							final long msb = (tm << 16) | (r1 >>> 24);
 | 
				
			||||||
 | 
							final long lsb = (r1 << 40) | r2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return new UUID(msb, lsb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static String toString(UlidStruct struct) {
 | 
				
			||||||
 | 
							return toString(struct.time, struct.random1, struct.random2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static String toString(UUID uuid) {
 | 
				
			||||||
 | 
							final long msb = uuid.getMostSignificantBits();
 | 
				
			||||||
 | 
							final long lsb = uuid.getLeastSignificantBits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							final long time = (msb >>> 16);
 | 
				
			||||||
 | 
							final long random1 = ((msb & 0xffffL) << 24) | (lsb >>> 40);
 | 
				
			||||||
 | 
							final long random2 = (lsb & 0xffffffffffL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return toString(time, random1, random2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static String toString(final long time, final long random1, final long random2) {
 | 
				
			||||||
 | 
							String timeComponent = toTimeComponent(time);
 | 
				
			||||||
 | 
							String randomComponent = toRandomComponent(random1, random2);
 | 
				
			||||||
 | 
							return timeComponent + randomComponent;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public static String toTimeComponent(final long time) {
 | 
				
			||||||
		final String tzero = "0000000000";
 | 
							final String tzero = "0000000000";
 | 
				
			||||||
		final String rzero = "00000000";
 | 
							String tm = Long.toUnsignedString(time, 32);
 | 
				
			||||||
 | 
							tm = tzero.substring(0, tzero.length() - tm.length()) + tm;
 | 
				
			||||||
 | 
							return transliterate(tm, ALPHABET_JAVA, ALPHABET_CROCKFORD);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
		String time = Long.toUnsignedString(struct.time, 32);
 | 
						public static String toRandomComponent(final long random1, final long random2) {
 | 
				
			||||||
		String random1 = Long.toUnsignedString(struct.random1, 32);
 | 
					 | 
				
			||||||
		String random2 = Long.toUnsignedString(struct.random2, 32);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		time = tzero.substring(0, tzero.length() - time.length()) + time;
 | 
							final String zeros = "00000000";
 | 
				
			||||||
		random1 = rzero.substring(0, rzero.length() - random1.length()) + random1;
 | 
					 | 
				
			||||||
		random2 = rzero.substring(0, rzero.length() - random2.length()) + random2;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		time = transliterate(time, ALPHABET_JAVA, ALPHABET_CROCKFORD);
 | 
							String r1 = Long.toUnsignedString(random1, 32);
 | 
				
			||||||
		random1 = transliterate(random1, ALPHABET_JAVA, ALPHABET_CROCKFORD);
 | 
							String r2 = Long.toUnsignedString(random2, 32);
 | 
				
			||||||
		random2 = transliterate(random2, ALPHABET_JAVA, ALPHABET_CROCKFORD);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return time + random1 + random2;
 | 
							r1 = zeros.substring(0, zeros.length() - r1.length()) + r1;
 | 
				
			||||||
 | 
							r2 = zeros.substring(0, zeros.length() - r2.length()) + r2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							r1 = transliterate(r1, ALPHABET_JAVA, ALPHABET_CROCKFORD);
 | 
				
			||||||
 | 
							r2 = transliterate(r2, ALPHABET_JAVA, ALPHABET_CROCKFORD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return r1 + r2;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static String transliterate(String string, char[] alphabet1, char[] alphabet2) {
 | 
						private static String transliterate(String string, char[] alphabet1, char[] alphabet2) {
 | 
				
			||||||
| 
						 | 
					@ -155,5 +236,4 @@ public class UlidStructTest {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return new String(output);
 | 
							return new String(output);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue