Skip to content

Instantly share code, notes, and snippets.

@trikko
Last active May 7, 2025 19:28
Show Gist options
  • Save trikko/31d12ecafc76c7eba02edd81e5d1e8e8 to your computer and use it in GitHub Desktop.
Save trikko/31d12ecafc76c7eba02edd81e5d1e8e8 to your computer and use it in GitHub Desktop.
UUID (v3, v4, v5, v7) in dlang
import std.stdio : writeln;
void main() {
writeln("UUIDv4 (random): ", UUIDv4!string());
writeln("UUIDv7 (timestamp + counter + random): ", UUIDv7!string());
writeln("UUIDv7 (timestamp + counter + random): ", UUIDv7!string());
writeln("UUIDv3 (w/ null namespace): ", UUIDv3!string("pizza"));
writeln("UUIDv5 (w/ null namespace): ", UUIDv5!string("pizza"));
writeln("UUIDv5 (w/ custom namespace): ", UUIDv5!string("pizza", [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]));
writeln("UUIDv5 (w/ OID namespace): ", UUIDv5!string("pizza", UUIDNamespace.OID));
}
/// Predefined namespaces for UUID v3 and v5
enum UUIDNamespace : ubyte[16] {
DNS = [
0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
],
URL = [
0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
],
OID = [
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
],
X500 = [
0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
]
}
/// Generate a UUID v4 (random bytes) as string
string UUIDv4(T = string)() if(is(T==string)) { return formatUUID(UUIDv4!ubyte); }
/// Generate a UUID v4 (random bytes) as ubyte[16]
ubyte[16] UUIDv4(T)() if (is(T == ubyte))
{
ubyte[16] value;
randomBytes(value);
value[6] = (value[6] & 0x0f) | 0x40;
value[8] = (value[8] & 0x3f) | 0x80;
return value;
}
/// Generate a UUIDv7 (timestamp based + counter + random bytes) as string
string UUIDv7(T = string)() if(is(T==string)) { return formatUUID(UUIDv7!ubyte); }
/// Generate a UUIDv7 (timestamp based + counter + random bytes) as ubyte[16]
ubyte[16] UUIDv7(T)() if (is(T == ubyte))
{
import core.atomic : atomicFetchAdd;
import std.datetime : DateTime, SysTime, Clock, UTC;
shared static uint counter = 0;
shared static immutable unixEpoch = SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC());
ubyte[16] value;
randomBytes(value);
// Current timestamp in ms
auto now = Clock.currTime(UTC());
auto timestamp = (now - unixEpoch).total!"msecs";
// Timestamp
value[0] = cast(ubyte)((timestamp >> 40) & 0xff);
value[1] = cast(ubyte)((timestamp >> 32) & 0xff);
value[2] = cast(ubyte)((timestamp >> 24) & 0xff);
value[3] = cast(ubyte)((timestamp >> 16) & 0xff);
value[4] = cast(ubyte)((timestamp >> 8) & 0xff);
value[5] = cast(ubyte)(timestamp & 0xff);
// Counter
auto localCounter = atomicFetchAdd(counter, 1);
value[6] = (value[6] & 0xF0) | cast(ubyte)((localCounter >> 8) & 0x0F); // first 4 bits [52-55]
value[7] = cast(ubyte)(localCounter & 0xFF); // last 8 bits [56-63]
// Version & Variant
value[6] = (value[6] & 0x0f) | 0x70;
value[8] = (value[8] & 0x3f) | 0x80;
return value;
}
/// Generate a UUID v3 (namespace + name based with MD5) as string
string UUIDv3(T = string)(string name, ubyte[16] namespace = ubyte[16].init) if(is(T==string)) {
import std.string : representation;
return UUIDv3!T(cast(ubyte[])name.representation, namespace);
}
/// Generate a UUID v3 (namespace + name based with MD5) as string
string UUIDv3(T = string)(ubyte[] name, ubyte[16] namespace = ubyte[16].init) if(is(T==string)) {
return formatUUID(UUIDv3!ubyte(name, namespace));
}
/// Generate a UUID v3 (namespace + name based with MD5) as ubyte[16]
ubyte[16] UUIDv3(T)(ubyte[] name, ubyte[16] namespace = ubyte[16].init) if (is(T == ubyte))
{
import std.digest.md : MD5;
ubyte[16] value;
// Create MD5 hash of namespace and name
auto md5 = new MD5();
md5.start();
md5.put(namespace);
md5.put(name);
ubyte[] hash = md5.finish().dup;
// Copy first 16 bytes of hash to value
value[0..16] = hash[0..16];
// Set version and variant
value[6] = (value[6] & 0x0f) | 0x30; // Version 3
value[8] = (value[8] & 0x3f) | 0x80; // Variant 1
return value;
}
/// Generate a UUID v5 (namespace + name based) as string
string UUIDv5(T = string)(string name, ubyte[16] namespace = ubyte[16].init) if(is(T==string)) {
import std.string : representation;
return UUIDv5!string(cast(ubyte[])name.representation, namespace);
}
/// Generate a UUID v5 (namespace + name based) as string
string UUIDv5(T = string)(ubyte[] name, ubyte[16] namespace = ubyte[16].init) if(is(T==string)) {
return formatUUID(UUIDv5!ubyte(name, namespace));
}
/// Generate a UUID v5 (namespace + name based) as ubyte[16]
ubyte[16] UUIDv5(T)(ubyte[] name, ubyte[16] namespace = ubyte[16].init) if (is(T == ubyte))
{
import std.digest.sha : SHA1;
ubyte[16] value;
// Create SHA1 hash of namespace and name
auto sha1 = new SHA1();
sha1.start();
sha1.put(namespace);
sha1.put(name);
ubyte[] hash = sha1.finish().dup;
// Copy first 16 bytes of hash to value
value[0..16] = hash[0..16];
// Set version and variant
value[6] = (value[6] & 0x0f) | 0x50; // Version 5
value[8] = (value[8] & 0x3f) | 0x80; // Variant 1
return value;
}
private:
string formatUUID(ubyte[16] uuid)
{
import std.string : toLower;
import std.format : format;
import std.digest : toHexString;
char[32] tmp = uuid.toHexString.toLower;
return format("%s-%s-%s-%s-%s", tmp[0..8], tmp[8..12], tmp[12..16], tmp[16..20], tmp[20..$]);
}
void randomBytes(ubyte[] buffer)
{
// Random bytes
version(Windows)
{
import core.sys.windows.windows;
import core.sys.windows.wincrypt;
HCRYPTPROV hProvider;
CryptAcquireContext(&hProvider, null, null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptGenRandom(hProvider, cast(uint)buffer.length, buffer.ptr);
CryptReleaseContext(hProvider, 0);
}
else
{
import std.file : read;
buffer[0..$] = cast(ubyte[])read("/dev/urandom", buffer.length);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment