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