Last active
July 14, 2023 01:03
-
-
Save fabiolimace/c45d9210b512a8fbcd9682c10325168c to your computer and use it in GitHub Desktop.
Generates COMB UUID/GUID in Java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.package.name; | |
import java.security.SecureRandom; | |
import java.util.Random; | |
import java.util.UUID; | |
/** | |
* A UUID generator that creates COMB UUIDs. | |
* | |
* The COMB UUIDs combine the creation time and random bytes. | |
* | |
* The PREFIX or SUFFIX has 6 bytes and corresponds to the milliseconds since | |
* 1970-01-01T00:00:00Z (Unix epoch). | |
* | |
* For RFC-4122 compliance, it uses the version number 4. | |
* | |
* Read: The Cost of GUIDs as Primary Keys | |
* http://www.informit.com/articles/article.aspx?p=25862 | |
* | |
* Tags: uuid guid comb comb-guid comb-uuid uuid-generator guid-generator comb-generator generator comb time random | |
* | |
* @author: Fabio Lima 2020 | |
*/ | |
public abstract class CombUuidCreator { | |
private static final int RANDOM_VERSION = 4; | |
/** | |
* Returns a prefix COMB UUID. | |
* | |
* It uses a thread local {@link SecureRandom}. | |
* | |
* @return a random-based UUID | |
*/ | |
public static UUID getPrefixComb() { | |
return getPrefixComb(SecureRandomLazyHolder.INSTANCE.get()); | |
} | |
/** | |
* Returns a prefix COMB UUID. | |
* | |
* It uses any instance of {@link Random}. | |
* | |
* @return a random-based UUID | |
*/ | |
public static UUID getPrefixComb(Random random) { | |
return getCombGuid(random, /* prefix = */true); | |
} | |
/** | |
* Returns a suffix COMB UUID. | |
* | |
* It uses a thread local {@link SecureRandom}. | |
* | |
* @return a random-based UUID | |
*/ | |
public static UUID getSuffixComb() { | |
return getSuffixComb(SecureRandomLazyHolder.INSTANCE.get()); | |
} | |
/** | |
* Returns a suffix COMB UUID. | |
* | |
* It uses any instance of {@link Random}. | |
* | |
* @return a random-based UUID | |
*/ | |
public static UUID getSuffixComb(Random random) { | |
return getCombGuid(random, /* prefix = */false); | |
} | |
/** | |
* Returns prefix or suffix COMB UUID. | |
* | |
* It uses any instance of {@link Random}. | |
* | |
* @return a random-based UUID | |
*/ | |
private static UUID getCombGuid(Random random, boolean prefix) { | |
long msb = 0; | |
long lsb = 0; | |
// (3) set bits randomly | |
final byte[] bytes = new byte[16]; | |
random.nextBytes(bytes); | |
final long rand0 = (bytes[8] << 8) | (bytes[9] & 0xff); | |
final long rand1 = toNumber(bytes, 0, 8); | |
// Insert the prefix in the MSB | |
final long timestamp = System.currentTimeMillis(); | |
if (prefix) { | |
msb = (rand0 & 0x000000000000ffffL) | ((timestamp & 0x0000ffffffffffffL) << 16); | |
lsb = rand1; | |
} else { | |
msb = rand1; | |
lsb = (rand0 << 48) | (timestamp & 0x0000ffffffffffffL); | |
} | |
// Apply version and variant bits (required for RFC-4122 compliance) | |
msb = (msb & 0xffffffffffff0fffL) | ((RANDOM_VERSION & 0x0f) << 12); // apply version bits | |
lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant bits | |
// Return the UUID | |
return new UUID(msb, lsb); | |
} | |
private static long toNumber(final byte[] bytes, final int start, final int length) { | |
long result = 0; | |
for (int i = start; i < length; i++) { | |
result = (result << 8) | (bytes[i] & 0xff); | |
} | |
return result; | |
} | |
// Holds thread local secure random | |
private static class SecureRandomLazyHolder { | |
static final ThreadLocal<Random> INSTANCE = ThreadLocal.withInitial(SecureRandom::new); | |
} | |
/** | |
* For tests! | |
*/ | |
public static void main(String[] args) { | |
Random random = new Random(); | |
System.out.println("// Prefix COMB using thread local `java.security.SecureRandom` (DEFAULT)"); | |
System.out.println("CombUuidCreator.getPrefixComb()"); | |
System.out.println(); | |
for (int i = 0; i < 5; i++) { | |
System.out.println(" " + CombUuidCreator.getPrefixComb()); | |
} | |
System.out.println("|----prefix---|----------------------|"); | |
System.out.println(); | |
System.out.println("// Prefix COMB using `java.util.Random` (FASTER)"); | |
System.out.println("CombUuidCreator.getPrefixComb(new Random())"); | |
System.out.println(); | |
for (int i = 0; i < 5; i++) { | |
System.out.println(" " + CombUuidCreator.getPrefixComb(random)); | |
} | |
System.out.println("|----prefix---|----------------------|"); | |
System.out.println(); | |
System.out.println("// Suffix COMB using thread local `java.security.SecureRandom` (DEFAULT)"); | |
System.out.println("CombUuidCreator.getSuffixComb()"); | |
System.out.println(); | |
for (int i = 0; i < 5; i++) { | |
System.out.println(" " + CombUuidCreator.getSuffixComb()); | |
} | |
System.out.println("|-----------------------|---suffix---|"); | |
System.out.println(); | |
System.out.println("// Suffix COMB using `java.util.Random` (FASTER)"); | |
System.out.println("CombUuidCreator.getSuffixComb(new Random())"); | |
System.out.println(); | |
for (int i = 0; i < 5; i++) { | |
System.out.println(" " + CombUuidCreator.getSuffixComb(random)); | |
} | |
System.out.println("|-----------------------|---suffix---|"); | |
} | |
} | |
// OUTPUT: | |
// | |
// //Prefix COMB using thread local `java.security.SecureRandom` (DEFAULT) | |
// CombUuidCreator.getPrefixComb() | |
// | |
// 01755c83-802d-41c3-9223-6f111718e48f | |
// 01755c83-802d-42d7-9151-e8f2d8009ec3 | |
// 01755c83-802e-4d9d-8bf8-ff86681e3b33 | |
// 01755c83-802e-46d7-8425-3595e84543aa | |
// 01755c83-802e-4a93-9316-a344e10a863f | |
// |----prefix---|----------------------| | |
// | |
// //Prefix COMB using `java.util.Random` (FASTER) | |
// CombUuidCreator.getPrefixComb(new Random()) | |
// | |
// 01755c83-802e-458d-859e-34e4f35925a6 | |
// 01755c83-802e-4848-9052-7f7b811d13e0 | |
// 01755c83-802e-4d77-975e-fb8cfdf73746 | |
// 01755c83-802e-4ee1-9056-70c1c62dc0b9 | |
// 01755c83-802e-4731-8fd2-ce7a89c711a2 | |
// |----prefix---|----------------------| | |
// | |
// //Suffix COMB using thread local `java.security.SecureRandom` (DEFAULT) | |
// CombUuidCreator.getSuffixComb() | |
// | |
// 6ccdb118-43e0-4e57-837b-01755c83802e | |
// 99800a56-e6cb-4732-9fde-01755c83802e | |
// 13fe7ddd-78f0-4a80-8cf9-01755c83802e | |
// e0902720-195b-4d70-b6b2-01755c83802e | |
// 1110dea5-3d2b-4e61-8664-01755c83802e | |
// |-----------------------|---suffix---| | |
// | |
// //Suffix COMB using `java.util.Random` (FASTER) | |
// CombUuidCreator.getSuffixComb(new Random()) | |
// | |
// 7b9c79ac-e6e0-449d-98c7-01755c83802f | |
// 04dfe2c4-53bd-4f71-8b76-01755c83802f | |
// 94767d25-40c7-4493-b981-01755c83802f | |
// 04e85179-7cdd-4ecd-9963-01755c83802f | |
// d1a789ab-d792-4a88-9943-01755c83802f | |
// |-----------------------|---suffix---| |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment