1 /**
2  * A $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, UUID), or
3  * $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, Universally unique identifier),
4  * is intended to uniquely identify information in a distributed environment
5  * without significant central coordination. It can be
6  * used to tag objects with very short lifetimes, or to reliably identify very
7  * persistent objects across a network.
8  *
9 $(SCRIPT inhibitQuickIndex = 1;)
10 
11 $(DIVC quickindex,
12 $(BOOKTABLE ,
13 $(TR $(TH Category) $(TH Functions)
14 )
15 $(TR $(TDNW Parsing UUIDs)
16      $(TD $(MYREF parseUUID)
17           $(MYREF UUID)
18           $(MYREF UUIDParsingException)
19           $(MYREF uuidRegex)
20           )
21      )
22 $(TR $(TDNW Generating UUIDs)
23      $(TD $(MYREF sha1UUID)
24           $(MYREF randomUUID)
25           $(MYREF md5UUID)
26           $(MYREF timestampRandomUUID)
27           )
28      )
29 $(TR $(TDNW Using UUIDs)
30      $(TD $(MYREF2 UUID.uuidVersion, uuidVersion)
31           $(MYREF2 UUID.variant, variant)
32           $(MYREF2 UUID.toString, toString)
33           $(MYREF2 UUID.data, data)
34           $(MYREF2 UUID.swap, swap)
35           $(MYREF2 UUID.opEquals, opEquals)
36           $(MYREF2 UUID.opCmp, opCmp)
37           $(MYREF2 UUID.toHash, toHash)
38           )
39      )
40 $(TR $(TDNW UUID namespaces)
41      $(TD $(MYREF dnsNamespace)
42           $(MYREF urlNamespace)
43           $(MYREF oidNamespace)
44           $(MYREF x500Namespace)
45           )
46      )
47 )
48 )
49 
50  * UUIDs have many applications. Some examples follow: Databases may use UUIDs to identify
51  * rows or records in order to ensure that they are unique across different
52  * databases, or for publication/subscription services. Network messages may be
53  * identified with a UUID to ensure that different parts of a message are put back together
54  * again. Distributed computing may use UUIDs to identify a remote procedure call.
55  * Transactions and classes involved in serialization may be identified by UUIDs.
56  * Microsoft's component object model (COM) uses UUIDs to distinguish different software
57  * component interfaces. UUIDs are inserted into documents from Microsoft Office programs.
58  * UUIDs identify audio or video streams in the Advanced Systems Format (ASF). UUIDs are
59  * also a basis for OIDs (object identifiers), and URNs (uniform resource name).
60  *
61  * An attractive feature of UUIDs when compared to alternatives is their relative small size,
62  * of 128 bits, or 16 bytes. Another is that the creation of UUIDs does not require
63  * a centralized authority.
64  *
65  * When UUIDs are generated by one of the defined mechanisms, they are either guaranteed
66  * to be unique, different from all other generated UUIDs (that is, it has never been
67  * generated before and it will never be generated again), or it is extremely likely
68  * to be unique (depending on the mechanism).
69  *
70  * For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly
71  * initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to
72  * `UUID.init`, which is a UUID with all 16 bytes set to 0.
73  * Use UUID's constructors or the UUID generator functions to get an initialized UUID.
74  *
75  * This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html,
76  * boost.uuid) from the Boost project with some minor additions and API
77  * changes for a more D-like API.
78  *
79  * Standards:
80  * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122)
81  *
82  * See_Also:
83  * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier)
84  *
85  * Copyright: Copyright Johannes Pfau 2011 - .
86  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
87  * Authors:   Johannes Pfau
88  * Source:    $(PHOBOSSRC std/uuid.d)
89  *
90  * Macros:
91  * MYREF2 = <a href="#$2">$(TT $1)</a>&nbsp;
92  * MYREF3 = <a href="#$2">`$1`</a>
93  */
94 /*          Copyright Johannes Pfau 2011 - 2012.
95  * Distributed under the Boost Software License, Version 1.0.
96  *    (See accompanying file LICENSE_1_0.txt or copy at
97  *          http://www.boost.org/LICENSE_1_0.txt)
98  */
99 module std.uuid;
100 
101 ///
102 @safe unittest
103 {
104     import std.uuid;
105 
106     UUID[] ids;
107     ids ~= randomUUID();
108     ids ~= md5UUID("test.name.123");
109     ids ~= sha1UUID("test.name.123");
110 
111     foreach (entry; ids)
112     {
113         assert(entry.variant == UUID.Variant.rfc4122);
114     }
115     assert(ids[0].uuidVersion == UUID.Version.randomNumberBased);
116     assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd");
117     assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207,
118         234, 161, 157, 12, 205]);
119     UUID id;
120     assert(id.empty);
121 }
122 
123 import core.time : dur;
124 import std.bitmanip : bigEndianToNative, nativeToBigEndian;
125 import std.datetime.systime : SysTime;
126 import std.datetime : Clock, DateTime, UTC;
127 import std.range.primitives;
128 import std.traits;
129 
130 /**
131  *
132  */
133 public struct UUID
134 {
135     import std.meta : AliasSeq, allSatisfy;
136 
137     private:
138         alias skipSeq = AliasSeq!(8, 13, 18, 23);
139         alias byteSeq = AliasSeq!(0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34);
140 
141         @safe pure nothrow @nogc Char toChar(Char)(size_t i) const
142         {
143             if (i <= 9)
144                 return cast(Char)('0' + i);
145             else
146                 return cast(Char)('a' + (i-10));
147         }
148 
149         @safe pure nothrow unittest
150         {
151             assert(UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45,
152                 179, 189, 251, 70]).toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
153         }
154 
155         // Reinterpret the UUID as an array of some other primitive.
156         @trusted ref T[16 / T.sizeof] asArrayOf(T)() return
157         if (isIntegral!T)
158         {
159             return *cast(typeof(return)*)&data;
160         }
161 
162     public:
163         /**
164          * RFC 4122 defines different internal data layouts for UUIDs. These are
165          * the UUID formats supported by this module. It's
166          * possible to read, compare and use all these Variants, but
167          * UUIDs generated by this module will always be in rfc4122 format.
168          *
169          * Note: Do not confuse this with $(REF _Variant, std,_variant).
170          */
171         enum Variant
172         {
173             ncs, /// NCS backward compatibility
174             rfc4122, /// Defined in RFC 4122 document
175             microsoft, /// Microsoft Corporation backward compatibility
176             future ///Reserved for future use
177         }
178 
179         /**
180          * RFC 4122 defines different UUID versions. The version shows
181          * how a UUID was generated, e.g. a version 4 UUID was generated
182          * from a random number, a version 3 UUID from an MD5 hash of a name.
183          *
184          * Note:
185          * All of these UUID versions can be read and processed by
186          * `std.uuid`, but only version 3, 4 and 5 UUIDs can be generated.
187          */
188         enum Version
189         {
190             ///Unknown version
191             unknown = -1,
192             ///Version 1
193             timeBased = 1,
194             ///Version 2
195             dceSecurity = 2,
196             ///Version 3 (Name based + MD5)
197             nameBasedMD5 = 3,
198             ///Version 4 (Random)
199             randomNumberBased = 4,
200             ///Version 5 (Name based + SHA-1)
201             nameBasedSHA1 = 5,
202             ///Version 7 (milliseconds since unix epoch + random)
203             timestampRandom = 7
204         }
205 
206         union
207         {
208             /**
209              * It is sometimes useful to get or set the 16 bytes of a UUID
210              * directly.
211              *
212              * Note:
213              * UUID uses a 16-ubyte representation for the UUID data.
214              * RFC 4122 defines a UUID as a special structure in big-endian
215              * format. These 16-ubytes always equal the big-endian structure
216              * defined in RFC 4122.
217              *
218              * Example:
219              * -----------------------------------------------
220              * auto rawData = uuid.data; //get data
221              * rawData[0] = 1; //modify
222              * uuid.data = rawData; //set data
223              * uuid.data[1] = 2; //modify directly
224              * -----------------------------------------------
225              */
226             ubyte[16] data;
227             private ulong[2] ulongs;
228             static if (size_t.sizeof == 4)
229                 private uint[4] uints;
230         }
231 
232         /*
233          * We could use a union here to also provide access to the
234          * fields specified in RFC 4122, but as we never have to access
235          * those (only necessary for version 1 (and maybe 2) UUIDs),
236          * that is not needed right now.
237          */
238 
239         @safe pure unittest
240         {
241             UUID tmp;
242             tmp.data = cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,
243                 13,14,15];
244             assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
245                 12,13,14,15]);
246             tmp.data[2] = 3;
247             assert(tmp.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11,
248                 12,13,14,15]);
249 
250             auto tmp2 = cast(immutable UUID) tmp;
251             assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11,
252                 12,13,14,15]);
253         }
254 
255         /**
256          * Construct a UUID struct from the 16 byte representation
257          * of a UUID.
258          */
259         @safe pure nothrow @nogc this(ref const scope ubyte[16] uuidData)
260         {
261             data = uuidData;
262         }
263         /// ditto
264         @safe pure nothrow @nogc this(const ubyte[16] uuidData)
265         {
266             data = uuidData;
267         }
268 
269         ///
270         @safe pure unittest
271         {
272             enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
273             auto uuid = UUID(data);
274             enum ctfe = UUID(data);
275             assert(uuid.data == data);
276             assert(ctfe.data == data);
277         }
278 
279         /**
280          * Construct a UUID struct from the 16 byte representation
281          * of a UUID. Variadic constructor to allow a simpler syntax, see examples.
282          * You need to pass exactly 16 ubytes.
283          */
284         @safe pure this(T...)(T uuidData)
285         if (uuidData.length == 16 && allSatisfy!(isIntegral, T))
286         {
287             import std.conv : to;
288 
289             foreach (idx, it; uuidData)
290             {
291                 this.data[idx] = to!ubyte(it);
292             }
293         }
294 
295         ///
296         @safe unittest
297         {
298             auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
299             assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
300                 12,13,14,15]);
301         }
302 
303         @safe unittest
304         {
305             UUID tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
306             assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
307                 12,13,14,15]);
308 
309             enum UUID ctfeID = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
310             assert(ctfeID == tmp);
311 
312             //Too few arguments
313             assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))));
314 
315             //Too many arguments
316             assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1))));
317         }
318 
319         /**
320          * UUID V7 constructor
321          *
322          * This implementation is not guaranteed to use a cryptographically secure PRNG.
323          * For more information please see: std.random.unpredictableSeed
324          *
325          * Params:
326          *   timestamp = the timestamp part of the UUID V7
327          *   random = UUID V7 has 74 bits of random data, which rounds to 10 ubyte's.
328          *    If no random data is given, random data is generated.
329          */
330         @safe pure this(SysTime timestamp, ubyte[10] random = generateRandomData!10)
331         {
332             ulong epoch = (timestamp - SysTime.fromUnixTime(0)).total!"msecs";
333             this(epoch, random);
334         }
335 
336         /// ditto
337         @safe pure this(ulong epoch_msecs, ubyte[10] random = generateRandomData!10)
338         {
339             ubyte[8] epoch = epoch_msecs.nativeToBigEndian;
340 
341             this.data[0 .. 6] = epoch[2 .. 8];
342             this.data[6 .. $] = random;
343 
344             // version and variant
345             this.data[6] = (this.data[6] & 0x0F) | 0x70;
346             this.data[8] = (this.data[8] & 0x3F) | 0x80;
347         }
348 
349         ///
350         @system unittest
351         {
352             import std.datetime : DateTime, SysTime;
353             SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
354             UUID u = UUID(st);
355             SysTime o = u.v7Timestamp();
356             assert(o == st, st.toString() ~ " | " ~ o.toString());
357         }
358 
359         /**
360          * <a name="UUID(string)"></a>
361          * Parse a UUID from its canonical string form. An UUID in its
362          * canonical form looks like this: 8ab3060e-2cba-4f23-b74c-b52db3bdfb46
363          *
364          * Throws:
365          * $(LREF UUIDParsingException) if the input is invalid
366          *
367          * CTFE:
368          * This function is supported in CTFE code. Note that error messages
369          * caused by a malformed UUID parsed at compile time can be cryptic,
370          * but errors are detected and reported at
371          * compile time.
372          *
373          * Note:
374          * This is a strict parser. It only accepts the pattern above.
375          * It doesn't support any leading or trailing characters. It only
376          * accepts characters used for hex numbers and the string must have
377          * hyphens exactly like above.
378          *
379          * For a less strict parser, see $(LREF parseUUID)
380          */
381         this(T)(in T[] uuid)
382         if (isSomeChar!T)
383         {
384             import std.conv : to, parse;
385             if (uuid.length < 36)
386             {
387                 throw new UUIDParsingException(to!string(uuid), 0,
388                     UUIDParsingException.Reason.tooLittle, "Insufficient Input");
389             }
390             if (uuid.length > 36)
391             {
392                 throw new UUIDParsingException(to!string(uuid), 35, UUIDParsingException.Reason.tooMuch,
393                     "Input is too long, need exactly 36 characters");
394             }
395             static immutable skipInd = [skipSeq];
396             foreach (pos; skipInd)
397                 if (uuid[pos] != '-')
398                     throw new UUIDParsingException(to!string(uuid), pos,
399                         UUIDParsingException.Reason.invalidChar, "Expected '-'");
400 
401             ubyte[16] data2; //ctfe bug
402             uint pos = void;
403 
404             foreach (i, p; byteSeq)
405             {
406                 enum uint s = 'a'-10-'0';
407                 uint h = uuid[p];
408                 uint l = uuid[p+1];
409                 pos = p;
410                 if (h < '0') goto Lerr;
411                 if (l < '0') goto Lerr;
412                 if (h > '9')
413                 {
414                     h |= 0x20; //poorman's tolower
415                     if (h < 'a') goto Lerr;
416                     if (h > 'f') goto Lerr;
417                     h -= s;
418                 }
419                 if (l > '9')
420                 {
421                     l |= 0x20; //poorman's tolower
422                     if (l < 'a') goto Lerr;
423                     if (l > 'f') goto Lerr;
424                     l -= s;
425                 }
426                 h -= '0';
427                 l -= '0';
428 
429                 data2[i] = cast(ubyte)((h << 4) ^ l);
430             }
431             this.data = data2;
432             return;
433 
434         Lerr: throw new UUIDParsingException(to!string(uuid), pos,
435                 UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte");
436         }
437 
438         ///
439         @safe pure unittest
440         {
441             auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46");
442             assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76,
443                181, 45, 179, 189, 251, 70]);
444             assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
445 
446             //Can also be used in CTFE, for example as UUID literals:
447             enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
448             //here parsing is done at compile time, no runtime overhead!
449         }
450 
451         @safe pure unittest
452         {
453             import std.conv : to;
454             import std.exception;
455             import std.meta : AliasSeq;
456 
457             static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
458                                   wchar[], const(wchar)[], immutable(wchar)[],
459                                   dchar[], const(dchar)[], immutable(dchar)[],
460                                   immutable(char[]), immutable(wchar[]), immutable(dchar[])))
461             {{
462                 //Test valid, working cases
463                 assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty);
464 
465                 auto id = UUID(to!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"));
466                 assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76,
467                     181, 45, 179, 189, 251, 70]);
468                 assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
469 
470                 enum UUID ctfe = UUID(to!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
471                 assert(ctfe == id);
472 
473                 assert(UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a")).data
474                     == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
475 
476                 //Test too short UUIDS
477                 auto except = collectException!UUIDParsingException(
478                     UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886")));
479                 assert(except && except.reason == UUIDParsingException.Reason.tooLittle);
480 
481                 //Test too long UUIDS
482                 except = collectException!UUIDParsingException(
483                     UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa")));
484                 assert(except && except.reason == UUIDParsingException.Reason.tooMuch);
485 
486                 //Test dashes
487                 except = collectException!UUIDParsingException(
488                     UUID(to!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46")));
489                 assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
490 
491                 //Test dashes 2
492                 except = collectException!UUIDParsingException(
493                     UUID(to!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")));
494                 assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
495 
496                 //Test invalid characters
497                 //make sure 36 characters in total or we'll get a 'tooMuch' reason
498                 except = collectException!UUIDParsingException(
499                     UUID(to!S("{8ab3060e-2cba-4f23-b74c-b52db3bdf6}")));
500                 assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
501 
502                 //Boost test
503                 assert(UUID(to!S("01234567-89ab-cdef-0123-456789ABCDEF"))
504                     == UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01,
505                     0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
506             }
507         }}
508 
509         /**
510          * Returns true if and only if the UUID is equal
511          * to {00000000-0000-0000-0000-000000000000}
512          */
513         @trusted pure nothrow @nogc @property bool empty() const
514         {
515             if (__ctfe)
516                 return data == (ubyte[16]).init;
517 
518             auto p = cast(const(size_t*))data.ptr;
519             static if (size_t.sizeof == 4)
520                 return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0;
521             else static if (size_t.sizeof == 8)
522                 return p[0] == 0 && p[1] == 0;
523             else
524                 static assert(false, "nonsense, it's not 32 or 64 bit");
525         }
526 
527         ///
528         @safe pure unittest
529         {
530             UUID id;
531             assert(id.empty);
532             id = UUID("00000000-0000-0000-0000-000000000001");
533             assert(!id.empty);
534         }
535 
536         @safe pure unittest
537         {
538             ubyte[16] getData(size_t i)
539             {
540                 ubyte[16] data;
541                 data[i] = 1;
542                 return data;
543             }
544 
545             for (size_t i = 0; i < 16; i++)
546             {
547                 assert(!UUID(getData(i)).empty);
548             }
549 
550             enum ctfeEmpty = UUID.init.empty;
551             assert(ctfeEmpty);
552 
553             bool ctfeTest()
554             {
555                 for (size_t i = 0; i < 16; i++)
556                 {
557                     auto ctfeEmpty2 = UUID(getData(i)).empty;
558                     assert(!ctfeEmpty2);
559                 }
560                 return true;
561             }
562             enum res = ctfeTest();
563         }
564 
565         /**
566          * If the UUID is of version 7 it has a timestamp that this function
567          * returns, otherwise an UUIDParsingException is thrown.
568          */
569         SysTime v7Timestamp() const {
570             if (this.uuidVersion != Version.timestampRandom)
571             {
572                 throw new UUIDParsingException("The UUID is not of version" ~
573                     " v7 therefore no timestamp exist", 0);
574             }
575 
576             import std.bitmanip : bigEndianToNative;
577 
578             ubyte[8] tmp = void;
579             tmp[0 .. 2] = 0;
580             tmp[2 .. 8] = data[0 .. 6];
581 
582             ulong milli = tmp.bigEndianToNative!ulong;
583 
584             return SysTime(DateTime(1970, 1, 1), UTC()) + dur!"msecs"(milli);
585         }
586 
587         /**
588          * If the UUID is of version 7 it has a timestamp that this function
589          * returns as described in RFC 9562 (Method 3), otherwise an
590          * UUIDParsingException is thrown.
591          */
592         SysTime v7Timestamp_method3() const {
593             auto ret = v7Timestamp();
594 
595             const ubyte[2] rand_a = [
596                 data[6] & 0x0f, // masks version bits
597                 data[7]
598             ];
599 
600             const float hnsecs = rand_a.bigEndianToNative!ushort / MonotonicUUIDsFactory.subMsecsPart;
601             ret += dur!"hnsecs"(cast(ulong) hnsecs);
602 
603             return ret;
604         }
605 
606         /**
607          * RFC 4122 defines different internal data layouts for UUIDs.
608          * Returns the format used by this UUID.
609          *
610          * Note: Do not confuse this with $(REF _Variant, std,_variant).
611          * The type of this property is $(MYREF3 std.uuid.UUID.Variant, _Variant).
612          *
613          * See_Also:
614          * $(MYREF3 UUID.Variant, Variant)
615          */
616         @safe pure nothrow @nogc @property Variant variant() const
617         {
618             //variant is stored in octet 7
619             //which is index 8, since indexes count backwards
620             immutable octet7 = data[8]; //octet 7 is array index 8
621 
622             if ((octet7 & 0x80) == 0x00) //0b0xxxxxxx
623                 return Variant.ncs;
624             else if ((octet7 & 0xC0) == 0x80) //0b10xxxxxx
625                 return Variant.rfc4122;
626             else if ((octet7 & 0xE0) == 0xC0) //0b110xxxxx
627                 return Variant.microsoft;
628             else
629             {
630                 //assert((octet7 & 0xE0) == 0xE0, "Unknown UUID variant!") //0b111xxxx
631                 return Variant.future;
632             }
633         }
634 
635         ///
636         @safe pure unittest
637         {
638             assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
639                == UUID.Variant.rfc4122);
640         }
641         @system pure unittest
642         {
643             // @system due to Variant
644             Variant[ubyte] tests = cast(Variant[ubyte])[0x00 : Variant.ncs,
645                                     0x10 : Variant.ncs,
646                                     0x20 : Variant.ncs,
647                                     0x30 : Variant.ncs,
648                                     0x40 : Variant.ncs,
649                                     0x50 : Variant.ncs,
650                                     0x60 : Variant.ncs,
651                                     0x70 : Variant.ncs,
652                                     0x80 : Variant.rfc4122,
653                                     0x90 : Variant.rfc4122,
654                                     0xa0 : Variant.rfc4122,
655                                     0xb0 : Variant.rfc4122,
656                                     0xc0 : Variant.microsoft,
657                                     0xd0 : Variant.microsoft,
658                                     0xe0 : Variant.future,
659                                     0xf0 : Variant.future];
660             foreach (key, value; tests)
661             {
662                 UUID u;
663                 u.data[8] = key;
664                 assert(u.variant == value);
665             }
666         }
667 
668         /**
669          * RFC 4122 defines different UUID versions. The version shows
670          * how a UUID was generated, e.g. a version 4 UUID was generated
671          * from a random number, a version 3 UUID from an MD5 hash of a name.
672          * Returns the version used by this UUID.
673          *
674          * See_Also:
675          * $(MYREF3 UUID.Version, Version)
676          */
677         @safe pure nothrow @nogc @property Version uuidVersion() const
678         {
679             //version is stored in octet 9
680             //which is index 6, since indexes count backwards
681             immutable octet9 = data[6];
682             if ((octet9 & 0xF0) == 0x10)
683                 return Version.timeBased;
684             else if ((octet9 & 0xF0) == 0x20)
685                 return Version.dceSecurity;
686             else if ((octet9 & 0xF0) == 0x30)
687                 return Version.nameBasedMD5;
688             else if ((octet9 & 0xF0) == 0x40)
689                 return Version.randomNumberBased;
690             else if ((octet9 & 0xF0) == 0x50)
691                 return Version.nameBasedSHA1;
692             else if ((octet9 & 0xF0) == 0x70)
693                 return Version.timestampRandom;
694             else
695                 return Version.unknown;
696         }
697 
698         ///
699         @safe unittest
700         {
701             assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
702                 == UUID.Version.randomNumberBased);
703         }
704         @system unittest
705         {
706             // @system due to cast
707             Version[ubyte] tests = cast(Version[ubyte]) [
708                 0x00 : UUID.Version.unknown,
709                 0x10 : UUID.Version.timeBased,
710                 0x20 : UUID.Version.dceSecurity,
711                 0x30 : UUID.Version.nameBasedMD5,
712                 0x40 : UUID.Version.randomNumberBased,
713                 0x50 : UUID.Version.nameBasedSHA1,
714                 0x60 : UUID.Version.unknown,
715                 0x70 : UUID.Version.timestampRandom,
716                 0x80 : UUID.Version.unknown,
717                 0x90 : UUID.Version.unknown,
718                 0xa0 : UUID.Version.unknown,
719                 0xb0 : UUID.Version.unknown,
720                 0xc0 : UUID.Version.unknown,
721                 0xd0 : UUID.Version.unknown,
722                 0xe0 : UUID.Version.unknown,
723                 0xf0 : UUID.Version.unknown];
724             foreach (key, value; tests)
725             {
726                 UUID u;
727                 u.data[6] = key;
728                 assert(u.uuidVersion == value);
729             }
730         }
731 
732         /**
733          * Swap the data of this UUID with the data of rhs.
734          */
735         @safe pure nothrow @nogc void swap(ref UUID rhs)
736         {
737             immutable bck = data;
738             data = rhs.data;
739             rhs.data = bck;
740         }
741 
742         ///
743         @safe unittest
744         {
745             immutable ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
746             UUID u1;
747             UUID u2 = UUID(data);
748             u1.swap(u2);
749 
750             assert(u1 == UUID(data));
751             assert(u2 == UUID.init);
752         }
753 
754         /**
755          * All of the standard numeric operators are defined for
756          * the UUID struct.
757          */
758         @safe pure nothrow @nogc bool opEquals(const UUID s) const
759         {
760             return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
761         }
762 
763         ///
764         @safe pure unittest
765         {
766             //compare UUIDs
767             assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init);
768 
769             //UUIDs in associative arrays:
770             int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1,
771                 UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
772                 UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
773 
774             assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
775 
776             //UUIDS can be sorted:
777             import std.algorithm;
778             UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
779                           UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
780                           UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
781             sort(ids);
782         }
783 
784         /**
785          * ditto
786          */
787         @safe pure nothrow @nogc bool opEquals(ref const scope UUID s) const
788         {
789             return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
790         }
791 
792         /**
793          * ditto
794          */
795         @safe pure nothrow @nogc int opCmp(const UUID s) const
796         {
797             import std.algorithm.comparison : cmp;
798             return cmp(this.data[], s.data[]);
799         }
800 
801         /**
802          * ditto
803          */
804         @safe pure nothrow @nogc int opCmp(ref const scope UUID s) const
805         {
806             import std.algorithm.comparison : cmp;
807             return cmp(this.data[], s.data[]);
808         }
809 
810         /**
811          * ditto
812          */
813        @safe pure nothrow @nogc UUID opAssign(const UUID s)
814         {
815             ulongs[0] = s.ulongs[0];
816             ulongs[1] = s.ulongs[1];
817             return this;
818         }
819 
820         /**
821          * ditto
822          */
823         @safe pure nothrow @nogc UUID opAssign(ref const scope UUID s)
824         {
825             ulongs[0] = s.ulongs[0];
826             ulongs[1] = s.ulongs[1];
827             return this;
828         }
829 
830         /**
831          * ditto
832          */
833         //MurmurHash2
834         @safe pure nothrow @nogc size_t toHash() const
835         {
836             static if (size_t.sizeof == 4)
837             {
838                 enum uint m = 0x5bd1e995;
839                 enum uint n = 16;
840                 enum uint r = 24;
841 
842                 uint h = n;
843 
844                 uint k = uints[0];
845                 k *= m;
846                 k ^= k >> r;
847                 k *= m;
848 
849                 h ^= k;
850                 h *= m;
851 
852                 k = uints[1];
853                 k *= m;
854                 k ^= k >> r;
855                 k *= m;
856 
857                 h ^= k;
858                 h *= m;
859 
860                 k = uints[2];
861                 k *= m;
862                 k ^= k >> r;
863                 k *= m;
864 
865                 h ^= k;
866                 h *= m;
867 
868                 k = uints[3];
869                 k *= m;
870                 k ^= k >> r;
871                 k *= m;
872 
873                 h ^= k;
874                 h *= m;
875             }
876             else
877             {
878                 enum ulong m = 0xc6a4a7935bd1e995UL;
879                 enum ulong n = m * 16;
880                 enum uint r = 47;
881 
882                 ulong h = n;
883 
884                 ulong k = ulongs[0];
885                 k *= m;
886                 k ^= k >> r;
887                 k *= m;
888 
889                 h ^= k;
890                 h *= m;
891 
892                 k = ulongs[1];
893                 k *= m;
894                 k ^= k >> r;
895                 k *= m;
896 
897                 h ^= k;
898                 h *= m;
899             }
900             return h;
901         }
902         @safe unittest
903         {
904             assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init);
905             int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1,
906                 UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
907                 UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
908 
909             assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
910 
911             import std.algorithm;
912             UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
913                           UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
914                           UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
915             sort(ids);
916             auto id2 = ids.dup;
917 
918             ids = [UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
919                    UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
920                    UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
921             sort(ids);
922             assert(ids == id2);
923 
924             //test comparsion
925             UUID u1;
926             UUID u2 = UUID(cast(ubyte[16])[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
927             UUID u3 = UUID(cast(ubyte[16])[255,255,255,255,255,255,255,255,255,
928                 255,255,255,255,255,255,255]);
929 
930             assert(u1 == u1);
931 
932             assert(u1 != u2);
933 
934             assert(u1 < u2);
935             assert(u2 < u3);
936 
937             assert(u1 <= u1);
938             assert(u1 <= u2);
939             assert(u2 <= u3);
940 
941             assert(u2 >= u2);
942             assert(u3 >= u2);
943 
944             assert(u3 >= u3);
945             assert(u2 >= u1);
946             assert(u3 >= u1);
947 
948             // test hash
949             assert(u1.toHash() != u2.toHash());
950             assert(u2.toHash() != u3.toHash());
951             assert(u3.toHash() != u1.toHash());
952         }
953 
954 
955         /**
956          * Write the UUID into `sink` as an ASCII string in the canonical form,
957          * which is 36 characters in the form "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
958          * Params:
959          *      sink = OutputRange or writeable array at least 36 entries long
960          */
961         void toString(Writer)(scope Writer sink) const
962         {
963             char[36] result = void;
964             foreach (pos; skipSeq)
965                 result[pos] = '-';
966             foreach (i, pos; byteSeq)
967             {
968                 const uint entry = this.data[i];
969                 const uint hi = entry >> 4;
970                 result[pos  ] = toChar!char(hi);
971                 const uint lo = (entry) & 0x0F;
972                 result[pos+1] = toChar!char(lo);
973             }
974             static if (!__traits(compiles, put(sink, result[])) || isSomeString!Writer)
975             {
976                 foreach (i, c; result)
977                     sink[i] = cast(typeof(sink[i]))c;
978             }
979             else
980             {
981                 put(sink, result[]);
982             }
983         }
984 
985         /**
986          * Return the UUID as a string in the canonical form.
987          */
988         @trusted pure nothrow string toString() const
989         {
990             import std.exception : assumeUnique;
991             auto result = new char[36];
992             toString(result);
993             return result.assumeUnique;
994         }
995 
996         ///
997         @safe pure unittest
998         {
999             immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
1000             auto id = UUID(str);
1001             assert(id.toString() == str);
1002         }
1003 
1004         @safe pure nothrow @nogc unittest
1005         {
1006             import std.meta : AliasSeq;
1007             static foreach (Char; AliasSeq!(char, wchar, dchar))
1008             {{
1009                 alias String = immutable(Char)[];
1010                 //CTFE
1011                 enum String s = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
1012                 enum id = UUID(s);
1013                 static if (is(Char == char))
1014                 {
1015                     enum p = id.toString();
1016                     static assert(s == p);
1017                 }
1018                 //nogc
1019                 Char[36] str;
1020                 id.toString(str[]);
1021                 assert(str == s);
1022             }}
1023         }
1024 
1025         @system pure nothrow @nogc unittest
1026         {
1027             // @system due to cast
1028             import std.encoding : Char = AsciiChar;
1029             enum  utfstr = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
1030             alias String = immutable(Char)[];
1031             enum String s = cast(String) utfstr;
1032             enum id = UUID(utfstr);
1033             //nogc
1034             Char[36] str;
1035             id.toString(str[]);
1036             assert(str == s);
1037         }
1038 
1039         @safe unittest
1040         {
1041             auto u1 = UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79,
1042                 35, 183, 76, 181, 45, 179, 189, 251, 70]);
1043             assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1044             u1 = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1045             assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1046 
1047             char[] buf;
1048             void sink(scope const(char)[] data)
1049             {
1050                 buf ~= data;
1051             }
1052             u1.toString(&sink);
1053             assert(buf == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1054         }
1055 }
1056 
1057 ///
1058 @safe unittest
1059 {
1060     UUID id;
1061     assert(id.empty);
1062 
1063     id = randomUUID;
1064     assert(!id.empty);
1065 
1066     id = UUID(cast(ubyte[16]) [138, 179, 6, 14, 44, 186, 79,
1067         35, 183, 76, 181, 45, 179, 189, 251, 70]);
1068     assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1069 }
1070 
1071 /**
1072  * This function generates a name based (Version 3) UUID from a namespace UUID and a name.
1073  * If no namespace UUID was passed, the empty UUID `UUID.init` is used.
1074  *
1075  * Note:
1076  * The default namespaces ($(LREF dnsNamespace), ...) defined by
1077  * this module should be used when appropriate.
1078  *
1079  * RFC 4122 recommends to use Version 5 UUIDs (SHA-1) instead of Version 3
1080  * UUIDs (MD5) for new applications.
1081  *
1082  * CTFE:
1083  * CTFE is not supported.
1084  *
1085  * Note:
1086  * RFC 4122 isn't very clear on how UUIDs should be generated from names.
1087  * It is possible that different implementations return different UUIDs
1088  * for the same input, so be warned. The implementation for UTF-8 strings
1089  * and byte arrays used by `std.uuid` is compatible with Boost's implementation.
1090  * `std.uuid` guarantees that the same input to this function will generate
1091  * the same output at any time, on any system (this especially means endianness
1092  * doesn't matter).
1093  *
1094  * Note:
1095  * This function does not provide overloads for wstring and dstring, as
1096  * there's no clear answer on how that should be implemented. It could be
1097  * argued, that string, wstring and dstring input should have the same output,
1098  * but that wouldn't be compatible with Boost, which generates different output
1099  * for strings and wstrings. It's always possible to pass wstrings and dstrings
1100  * by using the ubyte[] function overload (but be aware of endianness issues!).
1101  */
1102 @safe pure nothrow @nogc UUID md5UUID(const(char[]) name, const UUID namespace = UUID.init)
1103 {
1104     return md5UUID(cast(const(ubyte[]))name, namespace);
1105 }
1106 
1107 /// ditto
1108 @safe pure nothrow @nogc UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init)
1109 {
1110     import std.digest.md : MD5;
1111 
1112     MD5 hash;
1113     hash.start();
1114 
1115     /*
1116      * NOTE: RFC 4122 says namespace should be converted to big-endian.
1117      * We always keep the UUID data in big-endian representation, so
1118      * that's fine
1119      */
1120     hash.put(namespace.data[]);
1121     hash.put(data[]);
1122 
1123     UUID u;
1124     u.data = hash.finish();
1125 
1126     //set variant
1127     //must be 0b10xxxxxx
1128     u.data[8] &= 0b10111111;
1129     u.data[8] |= 0b10000000;
1130 
1131     //set version
1132     //must be 0b0011xxxx
1133     u.data[6] &= 0b00111111;
1134     u.data[6] |= 0b00110000;
1135 
1136     return u;
1137 }
1138 
1139 ///
1140 @safe unittest
1141 {
1142     //Use default UUID.init namespace
1143     auto simpleID = md5UUID("test.uuid.any.string");
1144 
1145     //use a name-based id as namespace
1146     auto namespace = md5UUID("my.app");
1147     auto id = md5UUID("some-description", namespace);
1148 }
1149 
1150 @safe pure unittest
1151 {
1152     auto simpleID = md5UUID("test.uuid.any.string");
1153     assert(simpleID.data == cast(ubyte[16])[126, 206, 86, 72, 29, 233, 62, 213, 178, 139, 198, 136,
1154         188, 135, 153, 123]);
1155     auto namespace = md5UUID("my.app");
1156     auto id = md5UUID("some-description", namespace);
1157     assert(id.data == cast(ubyte[16])[166, 138, 167, 79, 48, 219, 55, 166, 170, 103, 39, 73, 216,
1158         150, 144, 164]);
1159 
1160     auto constTest = md5UUID(cast(const(char)[])"test");
1161     constTest = md5UUID(cast(const(char[]))"test");
1162 
1163     char[] mutable = "test".dup;
1164     id = md5UUID(mutable, namespace);
1165 
1166     const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222];
1167     id = md5UUID(data);
1168     assert(id.data == cast(ubyte[16])[16, 50, 29, 247, 243, 185, 61, 178, 157, 100, 253, 236, 73,
1169         76, 51, 47]);
1170 
1171     assert(id.variant == UUID.Variant.rfc4122);
1172     assert(id.uuidVersion == UUID.Version.nameBasedMD5);
1173 
1174     auto correct = UUID("3d813cbb-47fb-32ba-91df-831e1593ac29");
1175 
1176     auto u = md5UUID("www.widgets.com", dnsNamespace);
1177     //enum ctfeId = md5UUID("www.widgets.com", dnsNamespace);
1178     //assert(ctfeId == u);
1179     assert(u == correct);
1180     assert(u.variant == UUID.Variant.rfc4122);
1181     assert(u.uuidVersion == UUID.Version.nameBasedMD5);
1182 }
1183 
1184  /**
1185  * This function generates a name based (Version 5) UUID from a namespace
1186  * UUID and a name.
1187  * If no namespace UUID was passed, the empty UUID `UUID.init` is used.
1188  *
1189  * Note:
1190  * The default namespaces ($(LREF dnsNamespace), ...) defined by
1191  * this module should be used when appropriate.
1192  *
1193  * CTFE:
1194  * CTFE is not supported.
1195  *
1196  * Note:
1197  * RFC 4122 isn't very clear on how UUIDs should be generated from names.
1198  * It is possible that different implementations return different UUIDs
1199  * for the same input, so be warned. The implementation for UTF-8 strings
1200  * and byte arrays used by `std.uuid` is compatible with Boost's implementation.
1201  * `std.uuid` guarantees that the same input to this function will generate
1202  * the same output at any time, on any system (this especially means endianness
1203  * doesn't matter).
1204  *
1205  * Note:
1206  * This function does not provide overloads for wstring and dstring, as
1207  * there's no clear answer on how that should be implemented. It could be
1208  * argued, that string, wstring and dstring input should have the same output,
1209  * but that wouldn't be compatible with Boost, which generates different output
1210  * for strings and wstrings. It's always possible to pass wstrings and dstrings
1211  * by using the ubyte[] function overload (but be aware of endianness issues!).
1212  */
1213 @safe pure nothrow @nogc UUID sha1UUID(scope const(char)[] name, scope const UUID namespace = UUID.init)
1214 {
1215     return sha1UUID(cast(const(ubyte[]))name, namespace);
1216 }
1217 
1218 /// ditto
1219 @safe pure nothrow @nogc UUID sha1UUID(scope const(ubyte)[] data, scope const UUID namespace = UUID.init)
1220 {
1221     import std.digest.sha : SHA1;
1222 
1223     SHA1 sha;
1224     sha.start();
1225 
1226     /*
1227      * NOTE: RFC 4122 says namespace should be converted to big-endian.
1228      * We always keep the UUID data in big-endian representation, so
1229      * that's fine
1230      */
1231     sha.put(namespace.data[]);
1232     sha.put(data[]);
1233 
1234     auto hash = sha.finish();
1235     auto u = UUID();
1236     u.data[] = hash[0 .. 16];
1237 
1238     //set variant
1239     //must be 0b10xxxxxx
1240     u.data[8] &= 0b10111111;
1241     u.data[8] |= 0b10000000;
1242 
1243     //set version
1244     //must be 0b0101xxxx
1245     u.data[6] &= 0b01011111;
1246     u.data[6] |= 0b01010000;
1247 
1248     return u;
1249 }
1250 
1251 ///
1252 @safe unittest
1253 {
1254     //Use default UUID.init namespace
1255     auto simpleID = sha1UUID("test.uuid.any.string");
1256 
1257     //use a name-based id as namespace
1258     auto namespace = sha1UUID("my.app");
1259     auto id = sha1UUID("some-description", namespace);
1260 }
1261 
1262 @safe pure unittest
1263 {
1264     auto simpleID = sha1UUID("test.uuid.any.string");
1265     assert(simpleID.data == cast(ubyte[16])[16, 209, 239, 61, 99, 12, 94, 70, 159, 79, 255, 250,
1266         131, 79, 14, 147]);
1267     auto namespace = sha1UUID("my.app");
1268     auto id = sha1UUID("some-description", namespace);
1269     assert(id.data == cast(ubyte[16])[225, 94, 195, 219, 126, 75, 83, 71, 157, 52, 247, 43, 238, 248,
1270         148, 46]);
1271 
1272     auto constTest = sha1UUID(cast(const(char)[])"test");
1273     constTest = sha1UUID(cast(const(char[]))"test");
1274 
1275     char[] mutable = "test".dup;
1276     id = sha1UUID(mutable, namespace);
1277 
1278     const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222];
1279     id = sha1UUID(data);
1280     assert(id.data == cast(ubyte[16])[60, 65, 92, 240, 96, 46, 95, 238, 149, 100, 12, 64, 199, 194,
1281         243, 12]);
1282 
1283     auto correct = UUID("21f7f8de-8051-5b89-8680-0195ef798b6a");
1284 
1285     auto u = sha1UUID("www.widgets.com", dnsNamespace);
1286     assert(u == correct);
1287     assert(u.variant == UUID.Variant.rfc4122);
1288     assert(u.uuidVersion == UUID.Version.nameBasedSHA1);
1289 }
1290 
1291 /**
1292  * This function generates a random number based UUID from a random
1293  * number generator.
1294  *
1295  * This function is not supported at compile time.
1296  *
1297  * Bugs:
1298  * $(LINK2 https://github.com/dlang/phobos/issues/9881, Issue #9881 - Randomness in UUID generation is insufficient)
1299  *
1300  * Warning:
1301  * $(B This function must not be used for cryptographic purposes.)
1302  * UUIDs generated by this function do not have sufficient randomness
1303  * for all use cases.
1304  * This especially applies to the overload that accepts a caller-provided RNG.
1305  * At the moment, Phobos does not provide a $(I cryptographically-secure
1306  * pseudo-random number generator (CSPRNG)) that could be supplied to this
1307  * function.
1308  *
1309  * While the function overload with no parameters will attempt to use the
1310  * system CSPRNG where available and implemented, there are no guarantees.
1311  * See $(REF unpredictableSeed, std, random) for details.
1312  *
1313  * Params:
1314  *      randomGen = uniform RNG
1315  * See_Also: $(REF isUniformRNG, std,random)
1316  */
1317 @nogc nothrow @safe UUID randomUUID()
1318 {
1319     import std.conv : bitCast;
1320     import std.random : unpredictableSeed;
1321 
1322     enum bufferSize = UUID.data.sizeof;
1323     ubyte[bufferSize] data;
1324 
1325     static assert(ulong.sizeof * 2 == bufferSize);
1326     const half1 = unpredictableSeed!ulong();
1327     const half2 = unpredictableSeed!ulong();
1328 
1329     data[0 .. ulong.sizeof] = (() @trusted => half1.bitCast!(ubyte[ulong.sizeof]))();
1330     data[ulong.sizeof .. $] = (() @trusted => half2.bitCast!(ubyte[ulong.sizeof]))();
1331 
1332     // set variant
1333     // must be 0b_10xxxxxx
1334     data[8] &= 0b_10_111111;
1335     data[8] |= 0b_10_000000;
1336 
1337     // set version
1338     // must be 0b_0100xxxx
1339     data[6] &= 0b_0100_1111;
1340     data[6] |= 0b_0100_0000;
1341 
1342     return UUID(data);
1343 }
1344 
1345 /// ditto
1346 UUID randomUUID(RNG)(ref RNG randomGen)
1347 if (isInputRange!RNG && isIntegral!(ElementType!RNG))
1348 {
1349     import std.random : isUniformRNG;
1350     static assert(isUniformRNG!RNG, "randomGen must be a uniform RNG");
1351 
1352     alias E = ElementEncodingType!RNG;
1353     enum size_t elemSize = E.sizeof;
1354     static assert(elemSize <= 16);
1355     static assert(16 % elemSize == 0);
1356 
1357     UUID u;
1358     foreach (ref E e ; u.asArrayOf!E())
1359     {
1360         e = randomGen.front;
1361         randomGen.popFront();
1362     }
1363 
1364     //set variant
1365     //must be 0b10xxxxxx
1366     u.data[8] &= 0b10111111;
1367     u.data[8] |= 0b10000000;
1368 
1369     //set version
1370     //must be 0b0100xxxx
1371     u.data[6] &= 0b01001111;
1372     u.data[6] |= 0b01000000;
1373 
1374     return u;
1375 }
1376 
1377 ///
1378 @safe unittest
1379 {
1380     import std.random : Xorshift192, unpredictableSeed;
1381 
1382     //simple call
1383     auto uuid = randomUUID();
1384 
1385     //provide a custom RNG. Must be seeded manually.
1386     Xorshift192 gen;
1387 
1388     gen.seed(unpredictableSeed);
1389     auto uuid3 = randomUUID(gen);
1390 }
1391 
1392 @safe unittest
1393 {
1394     import std.random : Xorshift192, unpredictableSeed;
1395     //simple call
1396     auto uuid = randomUUID();
1397 
1398     //provide a custom RNG. Must be seeded manually.
1399     Xorshift192 gen;
1400     gen.seed(unpredictableSeed);
1401     auto uuid3 = randomUUID(gen);
1402 
1403     auto u1 = randomUUID();
1404     auto u2 = randomUUID();
1405     assert(u1 != u2);
1406     assert(u1.variant == UUID.Variant.rfc4122);
1407     assert(u1.uuidVersion == UUID.Version.randomNumberBased);
1408 }
1409 
1410 ///
1411 class MonotonicUUIDsFactory
1412 {
1413     import core.sync.mutex : Mutex;
1414     import core.time : Duration;
1415     import std.datetime.stopwatch : StopWatch;
1416 
1417     private shared Mutex mtx;
1418     private StopWatch startTimePoint;
1419 
1420     ///
1421     this(in SysTime startTime = SysTime.fromUnixTime(0)) shared
1422     {
1423         this(Clock.currTime(UTC()) - startTime);
1424     }
1425 
1426     ///
1427     this(in Duration timeElapsed, bool autostartDisabledForTesting = false) shared
1428     {
1429         mtx = new shared Mutex();
1430 
1431         (cast() startTimePoint).setTimeElapsed = timeElapsed;
1432 
1433         if (!autostartDisabledForTesting)
1434             (cast() startTimePoint).start();
1435     }
1436 
1437     private auto peek() shared
1438     {
1439         mtx.lock();
1440         scope(exit) mtx.unlock();
1441 
1442         return (cast() startTimePoint).peek;
1443     }
1444 
1445     // hnsecs is 1/10_000 of millisecond
1446     // rand_a size is 12 bits (4096 values)
1447     private enum float subMsecsPart = 1.0f / 10_000 * 4096;
1448 
1449     /**
1450      * Returns a monotonic timestamp + random based UUIDv7
1451      * as described in RFC 9562 (Method 3).
1452      */
1453     UUID createUUIDv7_method3(ubyte[8] externalRandom = generateRandomData!8) shared
1454     {
1455         const curr = peek.split!("msecs", "hnsecs");
1456         const qhnsecs = cast(ushort) (curr.hnsecs * subMsecsPart);
1457 
1458         ubyte[10] rand;
1459 
1460         // Whole rand_a is 16 bit, but usable only 12 MSB.
1461         // additional 4 less significant bits consumed
1462         // by a version value
1463         rand[0 .. 2] = qhnsecs.nativeToBigEndian;
1464         rand[2 .. $] = externalRandom;
1465 
1466         return UUID(curr.msecs, rand);
1467     }
1468 }
1469 
1470 /// Generate monotone UUIDs
1471 @system unittest
1472 {
1473     auto f = new shared MonotonicUUIDsFactory;
1474 
1475     UUID[10] monotonic;
1476 
1477     foreach (ref u; monotonic)
1478         u = f.createUUIDv7_method3;
1479 }
1480 
1481 @system unittest
1482 {
1483     import std.conv : to;
1484     import std.datetime;
1485 
1486     const currTime = SysTime(DateTime(2025, 9, 12, 21, 38, 45), UTC());
1487     Duration d = currTime - SysTime.fromUnixTime(0) + dur!"msecs"(123);
1488 
1489     auto f = new shared MonotonicUUIDsFactory(d, true);
1490 
1491     const u1 = f.createUUIDv7_method3();
1492     assert(u1.uuidVersion == UUID.Version.timestampRandom);
1493 
1494     // sub-millisecond part zeroed
1495     assert((u1.data[6] & 0b0000_1111) == 0);
1496     assert(u1.data[7] == 0);
1497 
1498     const uuidv7_milli_1 = u1.v7Timestamp;
1499 
1500     {
1501         const st = u1.v7Timestamp_method3;
1502         assert(cast(DateTime) st == cast(DateTime) currTime, st.to!string);
1503 
1504         const sp = st.fracSecs.split!("msecs", "usecs", "hnsecs");
1505         assert(sp.msecs == 123, sp.to!string);
1506         assert(sp.usecs == 0, sp.to!string);
1507     }
1508 
1509     // 0.3 usecs, but Method 3 precision is only 0.25 of usec,
1510     // thus, expected value is 2
1511     d += dur!"hnsecs"(3);
1512     f = new shared MonotonicUUIDsFactory(d, true);
1513 
1514     const u2 = f.createUUIDv7_method3();
1515     const uuidv7_milli_2 = u2.v7Timestamp;
1516     assert(uuidv7_milli_1 == uuidv7_milli_2);
1517 
1518     {
1519         const st = u2.v7Timestamp_method3;
1520         assert(cast(DateTime) st == cast(DateTime) currTime, st.to!string);
1521 
1522         const sp = st.fracSecs.split!("msecs", "usecs", "hnsecs");
1523         assert(sp.msecs == 123, sp.to!string);
1524         assert(sp.usecs == 0, sp.to!string);
1525         assert(sp.hnsecs == 2, sp.to!string);
1526     }
1527 }
1528 
1529 @system unittest
1530 {
1531     import core.thread.osthread : Thread;
1532     import std.datetime;
1533 
1534     scope f = new shared MonotonicUUIDsFactory;
1535 
1536     UUID[1000] uuids;
1537 
1538     foreach (ref u; uuids)
1539     {
1540         // UUIDv7 Method 3 monotonicity is only guaranteed if UUIDs are
1541         // generated slower than 2.5 microseconds
1542         Thread.sleep(dur!("hnsecs")(25));
1543         u = f.createUUIDv7_method3;
1544     }
1545 
1546     foreach (i; 1 .. uuids.length)
1547     {
1548         assert(uuids[i-1].v7Timestamp_method3 < uuids[i].v7Timestamp_method3);
1549         assert(uuids[i-1].data[8 .. $] != uuids[i].data[8 .. $], "random parts are equal");
1550     }
1551 }
1552 
1553 /**
1554  * This function returns a timestamp + random based UUID aka. uuid v7.
1555  */
1556 UUID timestampRandomUUID()
1557 {
1558     return UUID(Clock.currTime(UTC()));
1559 }
1560 
1561 ///
1562 @system unittest
1563 {
1564     UUID u = timestampRandomUUID();
1565     assert(u.uuidVersion == UUID.Version.timestampRandom);
1566 }
1567 
1568 /**
1569  * This is a less strict parser compared to the parser used in the
1570  * UUID constructor. It enforces the following rules:
1571  *
1572  * $(UL
1573  *   $(LI hex numbers are always two hexdigits([0-9a-fA-F]))
1574  *   $(LI there must be exactly 16 such pairs in the input, not less, not more)
1575  *   $(LI there can be exactly one dash between two hex-pairs, but not more)
1576  *   $(LI there can be multiple characters enclosing the 16 hex pairs,
1577  *     as long as these characters do not contain [0-9a-fA-F])
1578  * )
1579  *
1580  * Note:
1581  * Like most parsers, it consumes its argument. This means:
1582  * -------------------------
1583  * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46";
1584  * parseUUID(s);
1585  * assert(s == "");
1586  * -------------------------
1587  *
1588  * Throws:
1589  * $(LREF UUIDParsingException) if the input is invalid
1590  *
1591  * CTFE:
1592  * This function is supported in CTFE code. Note that error messages
1593  * caused by a malformed UUID parsed at compile time can be cryptic,
1594  * but errors are detected and reported at compile time.
1595  */
1596 UUID parseUUID(T)(T uuidString)
1597 if (isSomeString!T)
1598 {
1599     return parseUUID(uuidString);
1600 }
1601 
1602 ///ditto
1603 UUID parseUUID(Range)(ref Range uuidRange)
1604 if (isInputRange!Range && isSomeChar!(ElementType!Range))
1605 {
1606     import std.ascii : isHexDigit;
1607     import std.conv : ConvException, parse;
1608 
1609     static if (isForwardRange!Range)
1610         auto errorCopy = uuidRange.save;
1611 
1612     void parserError()(size_t pos, UUIDParsingException.Reason reason, string message, Throwable next = null,
1613         string file = __FILE__, size_t line = __LINE__)
1614     {
1615         static if (isForwardRange!Range)
1616         {
1617             import std.conv : to;
1618             static if (isInfinite!Range)
1619             {
1620                 throw new UUIDParsingException(to!string(take(errorCopy, pos)), pos, reason, message,
1621                     next, file, line);
1622             }
1623             else
1624             {
1625                 throw new UUIDParsingException(to!string(errorCopy), pos, reason, message, next, file,
1626                     line);
1627             }
1628         }
1629         else
1630         {
1631             throw new UUIDParsingException("", pos, reason, message, next, file, line);
1632         }
1633     }
1634 
1635     static if (hasLength!Range)
1636     {
1637         import std.conv : to;
1638         if (uuidRange.length < 32)
1639         {
1640             throw new UUIDParsingException(to!string(uuidRange), 0, UUIDParsingException.Reason.tooLittle,
1641                 "Insufficient Input");
1642         }
1643     }
1644 
1645     UUID result;
1646     size_t consumed;
1647     size_t element = 0;
1648 
1649     //skip garbage
1650     size_t skip()()
1651     {
1652         size_t skipped;
1653         while (!uuidRange.empty && !isHexDigit(uuidRange.front))
1654         {
1655             skipped++;
1656             uuidRange.popFront();
1657         }
1658         return skipped;
1659     }
1660 
1661     consumed += skip();
1662 
1663     if (uuidRange.empty)
1664         parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input");
1665 
1666     bool dashAllowed = false;
1667 
1668     parseLoop: while (!uuidRange.empty)
1669     {
1670         immutable character = uuidRange.front;
1671 
1672         if (character == '-')
1673         {
1674             if (!dashAllowed)
1675                 parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected '-'");
1676             else
1677                 dashAllowed = false;
1678 
1679             consumed++;
1680         }
1681         else if (!isHexDigit(character))
1682         {
1683             parserError(consumed, UUIDParsingException.Reason.invalidChar,
1684                 "Unexpected character (wanted a hexDigit)");
1685         }
1686         else
1687         {
1688             try
1689             {
1690                 consumed += 2;
1691                 static if (isSomeString!Range)
1692                 {
1693                     if (uuidRange.length < 2)
1694                     {
1695                         parserError(consumed, UUIDParsingException.Reason.tooLittle,
1696                             "Insufficient Input");
1697                     }
1698                     auto part = uuidRange[0 .. 2];
1699                     result.data[element++] = parse!ubyte(part, 16);
1700                     uuidRange.popFront();
1701                 }
1702                 else
1703                 {
1704                     dchar[2] copyBuf;
1705                     copyBuf[0] = character;
1706                     uuidRange.popFront();
1707                     if (uuidRange.empty)
1708                     {
1709                         parserError(consumed, UUIDParsingException.Reason.tooLittle,
1710                             "Insufficient Input");
1711                     }
1712                     copyBuf[1] = uuidRange.front;
1713                     auto part = copyBuf[];
1714                     result.data[element++] = parse!ubyte(part, 16);
1715                 }
1716 
1717                 if (element == 16)
1718                 {
1719                     uuidRange.popFront();
1720                     break parseLoop;
1721                 }
1722 
1723                 dashAllowed = true;
1724             }
1725             catch (ConvException e)
1726             {
1727                 parserError(consumed, UUIDParsingException.Reason.invalidChar,
1728                     "Couldn't parse ubyte", e);
1729             }
1730         }
1731         uuidRange.popFront();
1732     }
1733     assert(element <= 16);
1734 
1735     if (element < 16)
1736         parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input");
1737 
1738     consumed += skip();
1739     if (!uuidRange.empty)
1740         parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character");
1741 
1742     return result;
1743 }
1744 
1745 ///
1746 @safe unittest
1747 {
1748     auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
1749     //no dashes
1750     id = parseUUID("8ab3060e2cba4f23b74cb52db3bdfb46");
1751     //dashes at different positions
1752     id = parseUUID("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
1753     //leading / trailing characters
1754     id = parseUUID("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
1755     //unicode
1756     id = parseUUID("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
1757     //multiple trailing/leading characters
1758     id = parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||");
1759 
1760     //Can also be used in CTFE, for example as UUID literals:
1761     enum ctfeID = parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1762     //here parsing is done at compile time, no runtime overhead!
1763 }
1764 
1765 @safe pure unittest
1766 {
1767     import std.conv : to;
1768     import std.exception;
1769     import std.meta;
1770 
1771     struct TestRange(bool forward)
1772     {
1773         dstring input;
1774 
1775         @property dchar front()
1776         {
1777             return input.front;
1778         }
1779 
1780         void popFront()
1781         {
1782             input.popFront();
1783         }
1784 
1785         @property bool empty()
1786         {
1787             return input.empty;
1788         }
1789 
1790         static if (forward)
1791         {
1792             @property TestRange!true save()
1793             {
1794                 return this;
1795             }
1796         }
1797     }
1798     alias TestInputRange = TestRange!false;
1799     alias TestForwardRange = TestRange!true;
1800 
1801     assert(isInputRange!TestInputRange);
1802     assert(is(ElementType!TestInputRange == dchar));
1803     assert(isInputRange!TestForwardRange);
1804     assert(isForwardRange!TestForwardRange);
1805     assert(is(ElementType!TestForwardRange == dchar));
1806 
1807     //Helper function for unittests - Need to pass ranges by ref
1808     UUID parseHelper(T)(string input)
1809     {
1810         static if (is(T == TestInputRange) || is(T == TestForwardRange))
1811         {
1812             T range = T(to!dstring(input));
1813             return parseUUID(range);
1814         }
1815         else
1816             return parseUUID(to!T(input));
1817     }
1818 
1819     static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
1820                           wchar[], const(wchar)[], immutable(wchar)[],
1821                           dchar[], const(dchar)[], immutable(dchar)[],
1822                           immutable(char[]), immutable(wchar[]), immutable(dchar[]),
1823                           TestForwardRange, TestInputRange))
1824     {{
1825         //Verify examples.
1826         auto id = parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
1827         //no dashes
1828         id = parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46");
1829         //dashes at different positions
1830         id = parseHelper!S("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
1831         //leading / trailing characters
1832         id = parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
1833         //unicode
1834         id = parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
1835         //multiple trailing/leading characters
1836         id = parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||");
1837         enum ctfeId = parseHelper!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
1838         assert(parseHelper!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46") == ctfeId);
1839 
1840         //Test valid, working cases
1841         assert(parseHelper!S("00000000-0000-0000-0000-000000000000").empty);
1842         assert(parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46").data
1843             == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]);
1844 
1845         assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
1846             == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
1847 
1848         //wstring / dstring
1849         assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
1850             == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
1851         assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
1852             == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
1853 
1854         //Test too short UUIDS
1855         auto except = collectException!UUIDParsingException(
1856             parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886"));
1857         assert(except && except.reason == UUIDParsingException.Reason.tooLittle);
1858 
1859         //Test too long UUIDS
1860         except = collectException!UUIDParsingException(
1861             parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa"));
1862         assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
1863 
1864         //Test too long UUIDS 2
1865         except = collectException!UUIDParsingException(
1866             parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a-aa"));
1867         assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
1868 
1869         //Test dashes
1870         assert(parseHelper!S("8ab3060e2cba-4f23-b74c-b52db3bdfb46")
1871             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1872         assert(parseHelper!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")
1873             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1874         assert(parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46")
1875             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1876 
1877         except = collectException!UUIDParsingException(
1878             parseHelper!S("8-ab3060e2cba-4f23-b74c-b52db3bdfb46"));
1879         assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
1880 
1881         //Test leading/trailing characters
1882         assert(parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}")
1883             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1884         assert(parseHelper!S("{8ab3060e2cba4f23b74cb52db3bdfb46}")
1885             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1886 
1887         //Boost test
1888         auto u_increasing = UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
1889             0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
1890         assert(parseHelper!S("0123456789abcdef0123456789ABCDEF") == UUID(cast(ubyte[16])[0x01,
1891             0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
1892 
1893         //unicode
1894         assert(parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü")
1895             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1896 
1897         //multiple trailing/leading characters
1898         assert(parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||")
1899             == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1900     }}
1901 
1902     // Test input range with non-dchar element type.
1903     {
1904         import std.utf : byCodeUnit;
1905         auto range = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46".byCodeUnit;
1906         assert(parseUUID(range).data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]);
1907     }
1908 }
1909 
1910 /**
1911  * Default namespace from RFC 4122
1912  *
1913  * Name string is a fully-qualified domain name
1914  */
1915 enum dnsNamespace = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
1916 
1917 /**
1918  * Default namespace from RFC 4122
1919  *
1920  * Name string is a URL
1921  */
1922 enum urlNamespace = UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
1923 
1924 /**
1925  * Default namespace from RFC 4122
1926  *
1927  * Name string is an ISO OID
1928  */
1929 enum oidNamespace = UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
1930 
1931 /**
1932  * Default namespace from RFC 4122
1933  *
1934  * Name string is an X.500 DN (in DER or a text output format)
1935  */
1936 enum x500Namespace = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8");
1937 
1938 /**
1939  * Regex string to extract UUIDs from text.
1940  */
1941 enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~
1942     "-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}";
1943 
1944 ///
1945 @safe unittest
1946 {
1947     import std.algorithm;
1948     import std.regex;
1949 
1950     string test = "Lorem ipsum dolor sit amet, consetetur "~
1951     "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~
1952     "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~
1953     "magna aliquyam erat, sed diam voluptua. "~
1954     "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~
1955     "justo duo dolores et ea rebum.";
1956 
1957     auto r = regex(uuidRegex, "g");
1958     UUID[] found;
1959     foreach (c; match(test, r))
1960     {
1961         found ~= UUID(c.hit);
1962     }
1963     assert(found == [
1964         UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"),
1965         UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"),
1966     ]);
1967 }
1968 
1969 private ubyte[Size] generateRandomData(ubyte Size)() {
1970     import std.random : Random, uniform, unpredictableSeed;
1971 
1972     auto rnd = Random(unpredictableSeed);
1973 
1974     ubyte[Size] bytes;
1975     foreach (idx; 0 .. bytes.length)
1976     {
1977         bytes[idx] = uniform!(ubyte)(rnd);
1978         rnd.popFront();
1979     }
1980     return bytes;
1981 }
1982 
1983 /**
1984  * This exception is thrown if an error occurs when parsing a UUID
1985  * from a string.
1986  */
1987 public class UUIDParsingException : Exception
1988 {
1989     /**
1990      * The reason why parsing the UUID string failed (if known)
1991      */
1992     enum Reason
1993     {
1994         unknown, ///
1995         tooLittle, ///The passed in input was correct, but more input was expected.
1996         tooMuch, ///The input data is too long (There's no guarantee the first part of the data is valid)
1997         invalidChar, ///Encountered an invalid character
1998 
1999     }
2000     ///ditto
2001     Reason reason;
2002     ///The original input string which should have been parsed.
2003     string input;
2004     ///The position in the input string where the error occurred.
2005     size_t position;
2006 
2007     private this(string input, size_t pos, Reason why = Reason.unknown, string msg = "",
2008         Throwable next = null, string file = __FILE__, size_t line = __LINE__) pure @trusted
2009     {
2010         import std.array : replace;
2011         import std.format : format;
2012         this.input = input;
2013         this.position = pos;
2014         this.reason = why;
2015         string message = format("An error occured in the UUID parser: %s\n" ~
2016           " * Input:\t'%s'\n * Position:\t%s", msg, replace(replace(input,
2017           "\r", "\\r"), "\n", "\\n"), pos);
2018         super(message, file, line, next);
2019     }
2020 }
2021 
2022 ///
2023 @safe unittest
2024 {
2025     import std.exception : collectException;
2026 
2027     const inputUUID = "this-is-an-invalid-uuid";
2028     auto ex = collectException!UUIDParsingException(UUID(inputUUID));
2029     assert(ex !is null); // check that exception was thrown
2030     assert(ex.input == inputUUID);
2031     assert(ex.position == 0);
2032     assert(ex.reason == UUIDParsingException.Reason.tooLittle);
2033 }
2034 
2035 @safe unittest
2036 {
2037     auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch);
2038     assert(ex.input == "foo");
2039     assert(ex.position == 10);
2040     assert(ex.reason == UUIDParsingException.Reason.tooMuch);
2041 }
2042 
2043 /// uuidv7
2044 @system unittest
2045 {
2046     import std.datetime : DateTime, SysTime;
2047 
2048     SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
2049     UUID u = UUID(st);
2050     assert(u.uuidVersion == UUID.Version.timestampRandom);
2051     SysTime o = u.v7Timestamp();
2052     assert(o == st, st.toString() ~ " | " ~ o.toString());
2053     string s = u.toString();
2054     UUID u2 = UUID(s);
2055     SysTime o2 = u2.v7Timestamp();
2056     assert(o2 == st, st.toString() ~ " | " ~ o2.toString());
2057 }
2058 
2059 @system unittest
2060 {
2061     import std.datetime : SysTime;
2062 
2063     UUID u = timestampRandomUUID();
2064     assert(u.uuidVersion == UUID.Version.timestampRandom);
2065 
2066     SysTime o = u.v7Timestamp();
2067     assert(o.year > 2024);
2068     assert(o.year < 3024);
2069 }
2070 
2071 /// uuid v7 generated by external tool
2072 @system unittest
2073 {
2074     import std.datetime : DateTime, SysTime;
2075     UUID u = UUID("0198c2b2-c5a8-7a0f-a1db-86aac7906c7b");
2076     auto d = DateTime(2025,8,19);
2077     assert((cast(DateTime) u.v7Timestamp()).year == d.year);
2078 }