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> 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 }