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