Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active July 14, 2023 01:03
Show Gist options
  • Save fabiolimace/c45d9210b512a8fbcd9682c10325168c to your computer and use it in GitHub Desktop.
Save fabiolimace/c45d9210b512a8fbcd9682c10325168c to your computer and use it in GitHub Desktop.
Generates COMB UUID/GUID in Java
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