/** * A $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, UUID), or * $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, Universally unique identifier), * is intended to uniquely identify information in a distributed environment * without significant central coordination. It can be * used to tag objects with very short lifetimes, or to reliably identify very * persistent objects across a network. *$(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex,$(BOOKTABLE ,$(TR $(TH Category) $(TH Functions))$(TR $(TDNW Parsing UUIDs) $(TD $(MYREF parseUUID) $(MYREF UUID) $(MYREF UUIDParsingException) $(MYREF uuidRegex) ) )$(TR $(TDNW Generating UUIDs) $(TD $(MYREF sha1UUID) $(MYREF randomUUID) $(MYREF md5UUID) $(MYREF timestampRandomUUID) ) )$(TR $(TDNW Using UUIDs) $(TD $(MYREF2 UUID.uuidVersion, uuidVersion) $(MYREF2 UUID.variant, variant) $(MYREF2 UUID.toString, toString) $(MYREF2 UUID.data, data) $(MYREF2 UUID.swap, swap) $(MYREF2 UUID.opEquals, opEquals) $(MYREF2 UUID.opCmp, opCmp) $(MYREF2 UUID.toHash, toHash) ) )$(TR $(TDNW UUID namespaces) $(TD $(MYREF dnsNamespace) $(MYREF urlNamespace) $(MYREF oidNamespace) $(MYREF x500Namespace) ) ))) * UUIDs have many applications. Some examples follow: Databases may use UUIDs to identify * rows or records in order to ensure that they are unique across different * databases, or for publication/subscription services. Network messages may be * identified with a UUID to ensure that different parts of a message are put back together * again. Distributed computing may use UUIDs to identify a remote procedure call. * Transactions and classes involved in serialization may be identified by UUIDs. * Microsoft's component object model (COM) uses UUIDs to distinguish different software * component interfaces. UUIDs are inserted into documents from Microsoft Office programs. * UUIDs identify audio or video streams in the Advanced Systems Format (ASF). UUIDs are * also a basis for OIDs (object identifiers), and URNs (uniform resource name). * * An attractive feature of UUIDs when compared to alternatives is their relative small size, * of 128 bits, or 16 bytes. Another is that the creation of UUIDs does not require * a centralized authority. * * When UUIDs are generated by one of the defined mechanisms, they are either guaranteed * to be unique, different from all other generated UUIDs (that is, it has never been * generated before and it will never be generated again), or it is extremely likely * to be unique (depending on the mechanism). * * For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly * initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to * `UUID.init`, which is a UUID with all 16 bytes set to 0. * Use UUID's constructors or the UUID generator functions to get an initialized UUID. * * This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html, * boost.uuid) from the Boost project with some minor additions and API * changes for a more D-like API. * * Standards: * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122) * * See_Also: * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier) * * Copyright: Copyright Johannes Pfau 2011 - . * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Johannes Pfau * Source: $(PHOBOSSRC std/uuid.d) * * Macros: * MYREF2 = <a href="#$2">$(TT $1)</a> * MYREF3 = <a href="#$2">`$1`</a> *//* Copyright Johannes Pfau 2011 - 2012. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */module std.uuid; ///@safe unittest{ import std.uuid; UUID[] ids; ids ~= randomUUID(); ids ~= md5UUID("test.name.123"); ids ~= sha1UUID("test.name.123"); foreach (entry; ids) { assert(entry.variant == UUID.Variant.rfc4122); } assert(ids[0].uuidVersion == UUID.Version.randomNumberBased); assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd"); assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207, 234, 161, 157, 12, 205]); UUID id; assert(id.empty);} import core.time : dur;import std.bitmanip : bigEndianToNative, nativeToBigEndian;import std.datetime.systime : SysTime;import std.datetime : Clock, DateTime, UTC;import std.range.primitives;import std.traits; /** * */public struct UUID{ import std.meta : AliasSeq, allSatisfy; private: alias skipSeq = AliasSeq!(8, 13, 18, 23); alias byteSeq = AliasSeq!(0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34); @safe pure nothrow @nogc Char toChar(Char)(size_t i) const { if (i <= 9) return cast(Char)('0' + i); else return cast(Char)('a' + (i-10)); } @safe pure nothrow unittest { assert(UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]).toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); } // Reinterpret the UUID as an array of some other primitive. @trusted ref T[16 / T.sizeof] asArrayOf(T)() return if (isIntegral!T) { return *cast(typeof(return)*)&data; } public: /** * RFC 4122 defines different internal data layouts for UUIDs. These are * the UUID formats supported by this module. It's * possible to read, compare and use all these Variants, but * UUIDs generated by this module will always be in rfc4122 format. * * Note: Do not confuse this with $(REF _Variant, std,_variant). */ enum Variant { ncs, /// NCS backward compatibility rfc4122, /// Defined in RFC 4122 document microsoft, /// Microsoft Corporation backward compatibility future ///Reserved for future use } /** * RFC 4122 defines different UUID versions. The version shows * how a UUID was generated, e.g. a version 4 UUID was generated * from a random number, a version 3 UUID from an MD5 hash of a name. * * Note: * All of these UUID versions can be read and processed by * `std.uuid`, but only version 3, 4 and 5 UUIDs can be generated. */ enum Version { ///Unknown version unknown = -1, ///Version 1 timeBased = 1, ///Version 2 dceSecurity = 2, ///Version 3 (Name based + MD5) nameBasedMD5 = 3, ///Version 4 (Random) randomNumberBased = 4, ///Version 5 (Name based + SHA-1) nameBasedSHA1 = 5, ///Version 7 (milliseconds since unix epoch + random) timestampRandom = 7 } union { /** * It is sometimes useful to get or set the 16 bytes of a UUID * directly. * * Note: * UUID uses a 16-ubyte representation for the UUID data. * RFC 4122 defines a UUID as a special structure in big-endian * format. These 16-ubytes always equal the big-endian structure * defined in RFC 4122. * * Example: * ----------------------------------------------- * auto rawData = uuid.data; //get data * rawData[0] = 1; //modify * uuid.data = rawData; //set data * uuid.data[1] = 2; //modify directly * ----------------------------------------------- */ ubyte[16] data; private ulong[2] ulongs; static if (size_t.sizeof == 4) private uint[4] uints; } /* * We could use a union here to also provide access to the * fields specified in RFC 4122, but as we never have to access * those (only necessary for version 1 (and maybe 2) UUIDs), * that is not needed right now. */ @safe pure unittest { UUID tmp; tmp.data = cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12, 13,14,15]; assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); tmp.data[2] = 3; assert(tmp.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, 12,13,14,15]); auto tmp2 = cast(immutable UUID) tmp; assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, 12,13,14,15]); } /** * Construct a UUID struct from the 16 byte representation * of a UUID. */ @safe pure nothrow @nogc this(ref const scope ubyte[16] uuidData) { data = uuidData; } /// ditto @safe pure nothrow @nogc this(const ubyte[16] uuidData) { data = uuidData; } /// @safe pure unittest { enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; auto uuid = UUID(data); enum ctfe = UUID(data); assert(uuid.data == data); assert(ctfe.data == data); } /** * Construct a UUID struct from the 16 byte representation * of a UUID. Variadic constructor to allow a simpler syntax, see examples. * You need to pass exactly 16 ubytes. */ @safe pure this(T...)(T uuidData) if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) { import std.conv : to; foreach (idx, it; uuidData) { this.data[idx] = to!ubyte(it); } } /// @safe unittest { auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); } @safe unittest { UUID tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); enum UUID ctfeID = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(ctfeID == tmp); //Too few arguments assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)))); //Too many arguments assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1)))); } /** * UUID V7 constructor * * This implementation is not guaranteed to use a cryptographically secure PRNG. * For more information please see: std.random.unpredictableSeed * * Params: * timestamp = the timestamp part of the UUID V7 * random = UUID V7 has 74 bits of random data, which rounds to 10 ubyte's. * If no random data is given, random data is generated. */ @safe pure this(SysTime timestamp, ubyte[10] random = generateRandomData!10) { ulong epoch = (timestamp - SysTime.fromUnixTime(0)).total!"msecs"; this(epoch, random); } /// ditto @safe pure this(ulong epoch_msecs, ubyte[10] random = generateRandomData!10) { ubyte[8] epoch = epoch_msecs.nativeToBigEndian; this.data[0 .. 6] = epoch[2 .. 8]; this.data[6 .. $] = random; // version and variant this.data[6] = (this.data[6] & 0x0F) | 0x70; this.data[8] = (this.data[8] & 0x3F) | 0x80; } /// @system unittest { import std.datetime : DateTime, SysTime; SysTime st = DateTime(2025, 8, 19, 10, 38, 45); UUID u = UUID(st); SysTime o = u.v7Timestamp(); assert(o == st, st.toString() ~ " | " ~ o.toString()); } /** * <a name="UUID(string)"></a> * Parse a UUID from its canonical string form. An UUID in its * canonical form looks like this: 8ab3060e-2cba-4f23-b74c-b52db3bdfb46 * * Throws: * $(LREF UUIDParsingException) if the input is invalid * * CTFE: * This function is supported in CTFE code. Note that error messages * caused by a malformed UUID parsed at compile time can be cryptic, * but errors are detected and reported at * compile time. * * Note: * This is a strict parser. It only accepts the pattern above. * It doesn't support any leading or trailing characters. It only * accepts characters used for hex numbers and the string must have * hyphens exactly like above. * * For a less strict parser, see $(LREF parseUUID) */ this(T)(in T[] uuid) if (isSomeChar!T) { import std.conv : to, parse; if (uuid.length < 36) { throw new UUIDParsingException(to!string(uuid), 0, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); } if (uuid.length > 36) { throw new UUIDParsingException(to!string(uuid), 35, UUIDParsingException.Reason.tooMuch, "Input is too long, need exactly 36 characters"); } static immutable skipInd = [skipSeq]; foreach (pos; skipInd) if (uuid[pos] != '-') throw new UUIDParsingException(to!string(uuid), pos, UUIDParsingException.Reason.invalidChar, "Expected '-'"); ubyte[16] data2; //ctfe bug uint pos = void; foreach (i, p; byteSeq) { enum uint s = 'a'-10-'0'; uint h = uuid[p]; uint l = uuid[p+1]; pos = p; if (h < '0') goto Lerr; if (l < '0') goto Lerr; if (h > '9') { h |= 0x20; //poorman's tolower if (h < 'a') goto Lerr; if (h > 'f') goto Lerr; h -= s; } if (l > '9') { l |= 0x20; //poorman's tolower if (l < 'a') goto Lerr; if (l > 'f') goto Lerr; l -= s; } h -= '0'; l -= '0'; data2[i] = cast(ubyte)((h << 4) ^ l); } this.data = data2; return; Lerr: throw new UUIDParsingException(to!string(uuid), pos, UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte"); } /// @safe pure unittest { auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"); assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); //Can also be used in CTFE, for example as UUID literals: enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); //here parsing is done at compile time, no runtime overhead! } @safe pure unittest { import std.conv : to; import std.exception; import std.meta : AliasSeq; static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], wchar[], const(wchar)[], immutable(wchar)[], dchar[], const(dchar)[], immutable(dchar)[], immutable(char[]), immutable(wchar[]), immutable(dchar[]))) {{ //Test valid, working cases assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty); auto id = UUID(to!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46")); assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); enum UUID ctfe = UUID(to!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); assert(ctfe == id); assert(UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a")).data == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); //Test too short UUIDS auto except = collectException!UUIDParsingException( UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886"))); assert(except && except.reason == UUIDParsingException.Reason.tooLittle); //Test too long UUIDS except = collectException!UUIDParsingException( UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa"))); assert(except && except.reason == UUIDParsingException.Reason.tooMuch); //Test dashes except = collectException!UUIDParsingException( UUID(to!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46"))); assert(except && except.reason == UUIDParsingException.Reason.invalidChar); //Test dashes 2 except = collectException!UUIDParsingException( UUID(to!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46"))); assert(except && except.reason == UUIDParsingException.Reason.invalidChar); //Test invalid characters //make sure 36 characters in total or we'll get a 'tooMuch' reason except = collectException!UUIDParsingException( UUID(to!S("{8ab3060e-2cba-4f23-b74c-b52db3bdf6}"))); assert(except && except.reason == UUIDParsingException.Reason.invalidChar);Lines 1–500 of 2078. Load more
/** * A $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, UUID), or * $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, Universally unique identifier), * is intended to uniquely identify information in a distributed environment * without significant central coordination. It can be * used to tag objects with very short lifetimes, or to reliably identify very * persistent objects across a network. * $(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex, $(BOOKTABLE , $(TR $(TH Category) $(TH Functions) ) $(TR $(TDNW Parsing UUIDs) $(TD $(MYREF parseUUID) $(MYREF UUID) $(MYREF UUIDParsingException) $(MYREF uuidRegex) ) ) $(TR $(TDNW Generating UUIDs) $(TD $(MYREF sha1UUID) $(MYREF randomUUID) $(MYREF md5UUID) $(MYREF timestampRandomUUID) ) ) $(TR $(TDNW Using UUIDs) $(TD $(MYREF2 UUID.uuidVersion, uuidVersion) $(MYREF2 UUID.variant, variant) $(MYREF2 UUID.toString, toString) $(MYREF2 UUID.data, data) $(MYREF2 UUID.swap, swap) $(MYREF2 UUID.opEquals, opEquals) $(MYREF2 UUID.opCmp, opCmp) $(MYREF2 UUID.toHash, toHash) ) ) $(TR $(TDNW UUID namespaces) $(TD $(MYREF dnsNamespace) $(MYREF urlNamespace) $(MYREF oidNamespace) $(MYREF x500Namespace) ) ) ) ) * UUIDs have many applications. Some examples follow: Databases may use UUIDs to identify * rows or records in order to ensure that they are unique across different * databases, or for publication/subscription services. Network messages may be * identified with a UUID to ensure that different parts of a message are put back together * again. Distributed computing may use UUIDs to identify a remote procedure call. * Transactions and classes involved in serialization may be identified by UUIDs. * Microsoft's component object model (COM) uses UUIDs to distinguish different software * component interfaces. UUIDs are inserted into documents from Microsoft Office programs. * UUIDs identify audio or video streams in the Advanced Systems Format (ASF). UUIDs are * also a basis for OIDs (object identifiers), and URNs (uniform resource name). * * An attractive feature of UUIDs when compared to alternatives is their relative small size, * of 128 bits, or 16 bytes. Another is that the creation of UUIDs does not require * a centralized authority. * * When UUIDs are generated by one of the defined mechanisms, they are either guaranteed * to be unique, different from all other generated UUIDs (that is, it has never been * generated before and it will never be generated again), or it is extremely likely * to be unique (depending on the mechanism). * * For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly * initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to * `UUID.init`, which is a UUID with all 16 bytes set to 0. * Use UUID's constructors or the UUID generator functions to get an initialized UUID. * * This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html, * boost.uuid) from the Boost project with some minor additions and API * changes for a more D-like API. * * Standards: * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122) * * See_Also: * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier) * * Copyright: Copyright Johannes Pfau 2011 - . * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Johannes Pfau * Source: $(PHOBOSSRC std/uuid.d) * * Macros: * MYREF2 = <a href="#$2">$(TT $1)</a> * MYREF3 = <a href="#$2">`$1`</a> */ /* Copyright Johannes Pfau 2011 - 2012. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ module std.uuid; /// @safe unittest { import std.uuid; UUID[] ids; ids ~= randomUUID(); ids ~= md5UUID("test.name.123"); ids ~= sha1UUID("test.name.123"); foreach (entry; ids) { assert(entry.variant == UUID.Variant.rfc4122); } assert(ids[0].uuidVersion == UUID.Version.randomNumberBased); assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd"); assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207, 234, 161, 157, 12, 205]); UUID id; assert(id.empty); } import core.time : dur; import std.bitmanip : bigEndianToNative, nativeToBigEndian; import std.datetime.systime : SysTime; import std.datetime : Clock, DateTime, UTC; import std.range.primitives; import std.traits; /** * */ public struct UUID { import std.meta : AliasSeq, allSatisfy; private: alias skipSeq = AliasSeq!(8, 13, 18, 23); alias byteSeq = AliasSeq!(0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34); @safe pure nothrow @nogc Char toChar(Char)(size_t i) const { if (i <= 9) return cast(Char)('0' + i); else return cast(Char)('a' + (i-10)); } @safe pure nothrow unittest { assert(UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]).toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); } // Reinterpret the UUID as an array of some other primitive. @trusted ref T[16 / T.sizeof] asArrayOf(T)() return if (isIntegral!T) { return *cast(typeof(return)*)&data; } public: /** * RFC 4122 defines different internal data layouts for UUIDs. These are * the UUID formats supported by this module. It's * possible to read, compare and use all these Variants, but * UUIDs generated by this module will always be in rfc4122 format. * * Note: Do not confuse this with $(REF _Variant, std,_variant). */ enum Variant { ncs, /// NCS backward compatibility rfc4122, /// Defined in RFC 4122 document microsoft, /// Microsoft Corporation backward compatibility future ///Reserved for future use } /** * RFC 4122 defines different UUID versions. The version shows * how a UUID was generated, e.g. a version 4 UUID was generated * from a random number, a version 3 UUID from an MD5 hash of a name. * * Note: * All of these UUID versions can be read and processed by * `std.uuid`, but only version 3, 4 and 5 UUIDs can be generated. */ enum Version { ///Unknown version unknown = -1, ///Version 1 timeBased = 1, ///Version 2 dceSecurity = 2, ///Version 3 (Name based + MD5) nameBasedMD5 = 3, ///Version 4 (Random) randomNumberBased = 4, ///Version 5 (Name based + SHA-1) nameBasedSHA1 = 5, ///Version 7 (milliseconds since unix epoch + random) timestampRandom = 7 } union { /** * It is sometimes useful to get or set the 16 bytes of a UUID * directly. * * Note: * UUID uses a 16-ubyte representation for the UUID data. * RFC 4122 defines a UUID as a special structure in big-endian * format. These 16-ubytes always equal the big-endian structure * defined in RFC 4122. * * Example: * ----------------------------------------------- * auto rawData = uuid.data; //get data * rawData[0] = 1; //modify * uuid.data = rawData; //set data * uuid.data[1] = 2; //modify directly * ----------------------------------------------- */ ubyte[16] data; private ulong[2] ulongs; static if (size_t.sizeof == 4) private uint[4] uints; } /* * We could use a union here to also provide access to the * fields specified in RFC 4122, but as we never have to access * those (only necessary for version 1 (and maybe 2) UUIDs), * that is not needed right now. */ @safe pure unittest { UUID tmp; tmp.data = cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12, 13,14,15]; assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); tmp.data[2] = 3; assert(tmp.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, 12,13,14,15]); auto tmp2 = cast(immutable UUID) tmp; assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, 12,13,14,15]); } /** * Construct a UUID struct from the 16 byte representation * of a UUID. */ @safe pure nothrow @nogc this(ref const scope ubyte[16] uuidData) { data = uuidData; } /// ditto @safe pure nothrow @nogc this(const ubyte[16] uuidData) { data = uuidData; } /// @safe pure unittest { enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; auto uuid = UUID(data); enum ctfe = UUID(data); assert(uuid.data == data); assert(ctfe.data == data); } /** * Construct a UUID struct from the 16 byte representation * of a UUID. Variadic constructor to allow a simpler syntax, see examples. * You need to pass exactly 16 ubytes. */ @safe pure this(T...)(T uuidData) if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) { import std.conv : to; foreach (idx, it; uuidData) { this.data[idx] = to!ubyte(it); } } /// @safe unittest { auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); } @safe unittest { UUID tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); enum UUID ctfeID = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(ctfeID == tmp); //Too few arguments assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)))); //Too many arguments assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1)))); } /** * UUID V7 constructor * * This implementation is not guaranteed to use a cryptographically secure PRNG. * For more information please see: std.random.unpredictableSeed * * Params: * timestamp = the timestamp part of the UUID V7 * random = UUID V7 has 74 bits of random data, which rounds to 10 ubyte's. * If no random data is given, random data is generated. */ @safe pure this(SysTime timestamp, ubyte[10] random = generateRandomData!10) { ulong epoch = (timestamp - SysTime.fromUnixTime(0)).total!"msecs"; this(epoch, random); } /// ditto @safe pure this(ulong epoch_msecs, ubyte[10] random = generateRandomData!10) { ubyte[8] epoch = epoch_msecs.nativeToBigEndian; this.data[0 .. 6] = epoch[2 .. 8]; this.data[6 .. $] = random; // version and variant this.data[6] = (this.data[6] & 0x0F) | 0x70; this.data[8] = (this.data[8] & 0x3F) | 0x80; } /// @system unittest { import std.datetime : DateTime, SysTime; SysTime st = DateTime(2025, 8, 19, 10, 38, 45); UUID u = UUID(st); SysTime o = u.v7Timestamp(); assert(o == st, st.toString() ~ " | " ~ o.toString()); } /** * <a name="UUID(string)"></a> * Parse a UUID from its canonical string form. An UUID in its * canonical form looks like this: 8ab3060e-2cba-4f23-b74c-b52db3bdfb46 * * Throws: * $(LREF UUIDParsingException) if the input is invalid * * CTFE: * This function is supported in CTFE code. Note that error messages * caused by a malformed UUID parsed at compile time can be cryptic, * but errors are detected and reported at * compile time. * * Note: * This is a strict parser. It only accepts the pattern above. * It doesn't support any leading or trailing characters. It only * accepts characters used for hex numbers and the string must have * hyphens exactly like above. * * For a less strict parser, see $(LREF parseUUID) */ this(T)(in T[] uuid) if (isSomeChar!T) { import std.conv : to, parse; if (uuid.length < 36) { throw new UUIDParsingException(to!string(uuid), 0, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); } if (uuid.length > 36) { throw new UUIDParsingException(to!string(uuid), 35, UUIDParsingException.Reason.tooMuch, "Input is too long, need exactly 36 characters"); } static immutable skipInd = [skipSeq]; foreach (pos; skipInd) if (uuid[pos] != '-') throw new UUIDParsingException(to!string(uuid), pos, UUIDParsingException.Reason.invalidChar, "Expected '-'"); ubyte[16] data2; //ctfe bug uint pos = void; foreach (i, p; byteSeq) { enum uint s = 'a'-10-'0'; uint h = uuid[p]; uint l = uuid[p+1]; pos = p; if (h < '0') goto Lerr; if (l < '0') goto Lerr; if (h > '9') { h |= 0x20; //poorman's tolower if (h < 'a') goto Lerr; if (h > 'f') goto Lerr; h -= s; } if (l > '9') { l |= 0x20; //poorman's tolower if (l < 'a') goto Lerr; if (l > 'f') goto Lerr; l -= s; } h -= '0'; l -= '0'; data2[i] = cast(ubyte)((h << 4) ^ l); } this.data = data2; return; Lerr: throw new UUIDParsingException(to!string(uuid), pos, UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte"); } /// @safe pure unittest { auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"); assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); //Can also be used in CTFE, for example as UUID literals: enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); //here parsing is done at compile time, no runtime overhead! } @safe pure unittest { import std.conv : to; import std.exception; import std.meta : AliasSeq; static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], wchar[], const(wchar)[], immutable(wchar)[], dchar[], const(dchar)[], immutable(dchar)[], immutable(char[]), immutable(wchar[]), immutable(dchar[]))) {{ //Test valid, working cases assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty); auto id = UUID(to!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46")); assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); enum UUID ctfe = UUID(to!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); assert(ctfe == id); assert(UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a")).data == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); //Test too short UUIDS auto except = collectException!UUIDParsingException( UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886"))); assert(except && except.reason == UUIDParsingException.Reason.tooLittle); //Test too long UUIDS except = collectException!UUIDParsingException( UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa"))); assert(except && except.reason == UUIDParsingException.Reason.tooMuch); //Test dashes except = collectException!UUIDParsingException( UUID(to!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46"))); assert(except && except.reason == UUIDParsingException.Reason.invalidChar); //Test dashes 2 except = collectException!UUIDParsingException( UUID(to!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46"))); assert(except && except.reason == UUIDParsingException.Reason.invalidChar); //Test invalid characters //make sure 36 characters in total or we'll get a 'tooMuch' reason except = collectException!UUIDParsingException( UUID(to!S("{8ab3060e-2cba-4f23-b74c-b52db3bdf6}"))); assert(except && except.reason == UUIDParsingException.Reason.invalidChar);