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 * $(D 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">$(D $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 foreach(entry;ids)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 toCharUUID136 @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 * $(D 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 in ubyte[16] uuidData) 253 { 254 data = uuidData; 255 } 256 /// ditto 257 @safe pure nothrow @nogc this(in 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!(Unqual!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; 408 409 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(in 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 in 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(in 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 in 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(in 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 in 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 foreach (i, c; result) 884 { 885 static if (__traits(compiles, put(sink, c))) 886 put(sink, c); 887 else 888 sink[i] = cast(typeof(sink[i]))c; 889 } 890 } 891 892 /** 893 * Return the UUID as a string in the canonical form. 894 */ 895 @trusted pure nothrow string toString() const 896 { 897 import std.exception : assumeUnique; 898 auto result = new char[36]; 899 toString(result); 900 return result.assumeUnique; 901 } 902 903 /// 904 @safe pure unittest 905 { 906 immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; 907 auto id = UUID(str); 908 assert(id.toString() == str); 909 } 910 911 @safe pure nothrow @nogc unittest 912 { 913 import std.meta : AliasSeq; 914 foreach (Char; AliasSeq!(char, wchar, dchar)) 915 { 916 alias String = immutable(Char)[]; 917 //CTFE 918 enum String s = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; 919 enum id = UUID(s); 920 static if (is(Char == char)) 921 { 922 enum p = id.toString(); 923 static assert(s == p); 924 } 925 //nogc 926 Char[36] str; 927 id.toString(str[]); 928 assert(str == s); 929 } 930 } 931 932 @system pure nothrow @nogc unittest 933 { 934 // @system due to cast 935 import std.encoding : Char = AsciiChar; 936 enum utfstr = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; 937 alias String = immutable(Char)[]; 938 enum String s = cast(String) utfstr; 939 enum id = UUID(utfstr); 940 //nogc 941 Char[36] str; 942 id.toString(str[]); 943 assert(str == s); 944 } 945 946 @safe unittest 947 { 948 auto u1 = UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 949 35, 183, 76, 181, 45, 179, 189, 251, 70]); 950 assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); 951 u1 = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); 952 assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); 953 954 char[] buf; 955 void sink(const(char)[] data) 956 { 957 buf ~= data; 958 } 959 u1.toString(&sink); 960 assert(buf == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); 961 } 962 } 963 964 965 /** 966 * This function generates a name based (Version 3) UUID from a namespace UUID and a name. 967 * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used. 968 * 969 * Note: 970 * The default namespaces ($(LREF dnsNamespace), ...) defined by 971 * this module should be used when appropriate. 972 * 973 * RFC 4122 recommends to use Version 5 UUIDs (SHA-1) instead of Version 3 974 * UUIDs (MD5) for new applications. 975 * 976 * CTFE: 977 * CTFE is not supported. 978 * 979 * Note: 980 * RFC 4122 isn't very clear on how UUIDs should be generated from names. 981 * It is possible that different implementations return different UUIDs 982 * for the same input, so be warned. The implementation for UTF-8 strings 983 * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation. 984 * $(D std.uuid) guarantees that the same input to this function will generate 985 * the same output at any time, on any system (this especially means endianness 986 * doesn't matter). 987 * 988 * Note: 989 * This function does not provide overloads for wstring and dstring, as 990 * there's no clear answer on how that should be implemented. It could be 991 * argued, that string, wstring and dstring input should have the same output, 992 * but that wouldn't be compatible with Boost, which generates different output 993 * for strings and wstrings. It's always possible to pass wstrings and dstrings 994 * by using the ubyte[] function overload (but be aware of endianness issues!). 995 */ 996 @safe pure nothrow @nogc UUID md5UUID(const(char[]) name, const UUID namespace = UUID.init) 997 { 998 return md5UUID(cast(const(ubyte[]))name, namespace); 999 } 1000 1001 /// ditto 1002 @safe pure nothrow @nogc UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init) 1003 { 1004 import std.digest.md : MD5; 1005 1006 MD5 hash; 1007 hash.start(); 1008 1009 /* 1010 * NOTE: RFC 4122 says namespace should be converted to big-endian. 1011 * We always keep the UUID data in big-endian representation, so 1012 * that's fine 1013 */ 1014 hash.put(namespace.data[]); 1015 hash.put(data[]); 1016 1017 UUID u; 1018 u.data = hash.finish(); 1019 1020 //set variant 1021 //must be 0b10xxxxxx 1022 u.data[8] &= 0b10111111; 1023 u.data[8] |= 0b10000000; 1024 1025 //set version 1026 //must be 0b0011xxxx 1027 u.data[6] &= 0b00111111; 1028 u.data[6] |= 0b00110000; 1029 1030 return u; 1031 } 1032 1033 /// 1034 @safe unittest 1035 { 1036 //Use default UUID.init namespace 1037 auto simpleID = md5UUID("test.uuid.any.string"); 1038 1039 //use a name-based id as namespace 1040 auto namespace = md5UUID("my.app"); 1041 auto id = md5UUID("some-description", namespace); 1042 } 1043 1044 @safe pure unittest 1045 { 1046 auto simpleID = md5UUID("test.uuid.any.string"); 1047 assert(simpleID.data == cast(ubyte[16])[126, 206, 86, 72, 29, 233, 62, 213, 178, 139, 198, 136, 1048 188, 135, 153, 123]); 1049 auto namespace = md5UUID("my.app"); 1050 auto id = md5UUID("some-description", namespace); 1051 assert(id.data == cast(ubyte[16])[166, 138, 167, 79, 48, 219, 55, 166, 170, 103, 39, 73, 216, 1052 150, 144, 164]); 1053 1054 auto constTest = md5UUID(cast(const(char)[])"test"); 1055 constTest = md5UUID(cast(const(char[]))"test"); 1056 1057 char[] mutable = "test".dup; 1058 id = md5UUID(mutable, namespace); 1059 1060 const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222]; 1061 id = md5UUID(data); 1062 assert(id.data == cast(ubyte[16])[16, 50, 29, 247, 243, 185, 61, 178, 157, 100, 253, 236, 73, 1063 76, 51, 47]); 1064 1065 assert(id.variant == UUID.Variant.rfc4122); 1066 assert(id.uuidVersion == UUID.Version.nameBasedMD5); 1067 1068 auto correct = UUID("3d813cbb-47fb-32ba-91df-831e1593ac29"); 1069 1070 auto u = md5UUID("www.widgets.com", dnsNamespace); 1071 //enum ctfeId = md5UUID("www.widgets.com", dnsNamespace); 1072 //assert(ctfeId == u); 1073 assert(u == correct); 1074 assert(u.variant == UUID.Variant.rfc4122); 1075 assert(u.uuidVersion == UUID.Version.nameBasedMD5); 1076 } 1077 1078 /** 1079 * This function generates a name based (Version 5) UUID from a namespace 1080 * UUID and a name. 1081 * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used. 1082 * 1083 * Note: 1084 * The default namespaces ($(LREF dnsNamespace), ...) defined by 1085 * this module should be used when appropriate. 1086 * 1087 * CTFE: 1088 * CTFE is not supported. 1089 * 1090 * Note: 1091 * RFC 4122 isn't very clear on how UUIDs should be generated from names. 1092 * It is possible that different implementations return different UUIDs 1093 * for the same input, so be warned. The implementation for UTF-8 strings 1094 * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation. 1095 * $(D std.uuid) guarantees that the same input to this function will generate 1096 * the same output at any time, on any system (this especially means endianness 1097 * doesn't matter). 1098 * 1099 * Note: 1100 * This function does not provide overloads for wstring and dstring, as 1101 * there's no clear answer on how that should be implemented. It could be 1102 * argued, that string, wstring and dstring input should have the same output, 1103 * but that wouldn't be compatible with Boost, which generates different output 1104 * for strings and wstrings. It's always possible to pass wstrings and dstrings 1105 * by using the ubyte[] function overload (but be aware of endianness issues!). 1106 */ 1107 @safe pure nothrow @nogc UUID sha1UUID(in char[] name, const UUID namespace = UUID.init) 1108 { 1109 return sha1UUID(cast(const(ubyte[]))name, namespace); 1110 } 1111 1112 /// ditto 1113 @safe pure nothrow @nogc UUID sha1UUID(in ubyte[] data, const UUID namespace = UUID.init) 1114 { 1115 import std.digest.sha : SHA1; 1116 1117 SHA1 sha; 1118 sha.start(); 1119 1120 /* 1121 * NOTE: RFC 4122 says namespace should be converted to big-endian. 1122 * We always keep the UUID data in big-endian representation, so 1123 * that's fine 1124 */ 1125 sha.put(namespace.data[]); 1126 sha.put(data[]); 1127 1128 auto hash = sha.finish(); 1129 auto u = UUID(); 1130 u.data[] = hash[0 .. 16]; 1131 1132 //set variant 1133 //must be 0b10xxxxxx 1134 u.data[8] &= 0b10111111; 1135 u.data[8] |= 0b10000000; 1136 1137 //set version 1138 //must be 0b0101xxxx 1139 u.data[6] &= 0b01011111; 1140 u.data[6] |= 0b01010000; 1141 1142 return u; 1143 } 1144 1145 /// 1146 @safe unittest 1147 { 1148 //Use default UUID.init namespace 1149 auto simpleID = sha1UUID("test.uuid.any.string"); 1150 1151 //use a name-based id as namespace 1152 auto namespace = sha1UUID("my.app"); 1153 auto id = sha1UUID("some-description", namespace); 1154 } 1155 1156 @safe pure unittest 1157 { 1158 auto simpleID = sha1UUID("test.uuid.any.string"); 1159 assert(simpleID.data == cast(ubyte[16])[16, 209, 239, 61, 99, 12, 94, 70, 159, 79, 255, 250, 1160 131, 79, 14, 147]); 1161 auto namespace = sha1UUID("my.app"); 1162 auto id = sha1UUID("some-description", namespace); 1163 assert(id.data == cast(ubyte[16])[225, 94, 195, 219, 126, 75, 83, 71, 157, 52, 247, 43, 238, 248, 1164 148, 46]); 1165 1166 auto constTest = sha1UUID(cast(const(char)[])"test"); 1167 constTest = sha1UUID(cast(const(char[]))"test"); 1168 1169 char[] mutable = "test".dup; 1170 id = sha1UUID(mutable, namespace); 1171 1172 const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222]; 1173 id = sha1UUID(data); 1174 assert(id.data == cast(ubyte[16])[60, 65, 92, 240, 96, 46, 95, 238, 149, 100, 12, 64, 199, 194, 1175 243, 12]); 1176 1177 auto correct = UUID("21f7f8de-8051-5b89-8680-0195ef798b6a"); 1178 1179 auto u = sha1UUID("www.widgets.com", dnsNamespace); 1180 assert(u == correct); 1181 assert(u.variant == UUID.Variant.rfc4122); 1182 assert(u.uuidVersion == UUID.Version.nameBasedSHA1); 1183 } 1184 1185 /** 1186 * This function generates a random number based UUID from a random 1187 * number generator. 1188 * 1189 * This function is not supported at compile time. 1190 * 1191 * Params: 1192 * randomGen = uniform RNG 1193 * See_Also: $(REF isUniformRNG, std,random) 1194 */ 1195 @safe UUID randomUUID() 1196 { 1197 import std.random : rndGen; 1198 return randomUUID(rndGen); 1199 } 1200 1201 /// ditto 1202 UUID randomUUID(RNG)(ref RNG randomGen) 1203 if (isInputRange!RNG && isIntegral!(ElementType!RNG)) 1204 { 1205 import std.random : isUniformRNG; 1206 static assert(isUniformRNG!RNG, "randomGen must be a uniform RNG"); 1207 1208 alias E = ElementEncodingType!RNG; 1209 enum size_t elemSize = E.sizeof; 1210 static assert(elemSize <= 16); 1211 static assert(16 % elemSize == 0); 1212 1213 UUID u; 1214 foreach (ref E e ; u.asArrayOf!E()) 1215 { 1216 e = randomGen.front; 1217 randomGen.popFront(); 1218 } 1219 1220 //set variant 1221 //must be 0b10xxxxxx 1222 u.data[8] &= 0b10111111; 1223 u.data[8] |= 0b10000000; 1224 1225 //set version 1226 //must be 0b0100xxxx 1227 u.data[6] &= 0b01001111; 1228 u.data[6] |= 0b01000000; 1229 1230 return u; 1231 } 1232 1233 /// 1234 @safe unittest 1235 { 1236 import std.random : Xorshift192, unpredictableSeed; 1237 1238 //simple call 1239 auto uuid = randomUUID(); 1240 1241 //provide a custom RNG. Must be seeded manually. 1242 Xorshift192 gen; 1243 1244 gen.seed(unpredictableSeed); 1245 auto uuid3 = randomUUID(gen); 1246 } 1247 1248 /* 1249 * Original boost.uuid used Mt19937, we don't want 1250 * to use anything worse than that. If Random is changed 1251 * to something else, this assert and the randomUUID function 1252 * have to be updated. 1253 */ 1254 @safe unittest 1255 { 1256 import std.random : rndGen, Mt19937; 1257 static assert(is(typeof(rndGen) == Mt19937)); 1258 } 1259 1260 @safe unittest 1261 { 1262 import std.random : Xorshift192, unpredictableSeed; 1263 //simple call 1264 auto uuid = randomUUID(); 1265 1266 //provide a custom RNG. Must be seeded manually. 1267 Xorshift192 gen; 1268 gen.seed(unpredictableSeed); 1269 auto uuid3 = randomUUID(gen); 1270 1271 auto u1 = randomUUID(); 1272 auto u2 = randomUUID(); 1273 assert(u1 != u2); 1274 assert(u1.variant == UUID.Variant.rfc4122); 1275 assert(u1.uuidVersion == UUID.Version.randomNumberBased); 1276 } 1277 1278 /** 1279 * This is a less strict parser compared to the parser used in the 1280 * UUID constructor. It enforces the following rules: 1281 * 1282 * $(UL 1283 * $(LI hex numbers are always two hexdigits([0-9a-fA-F])) 1284 * $(LI there must be exactly 16 such pairs in the input, not less, not more) 1285 * $(LI there can be exactly one dash between two hex-pairs, but not more) 1286 * $(LI there can be multiple characters enclosing the 16 hex pairs, 1287 * as long as these characters do not contain [0-9a-fA-F]) 1288 * ) 1289 * 1290 * Note: 1291 * Like most parsers, it consumes its argument. This means: 1292 * ------------------------- 1293 * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"; 1294 * parseUUID(s); 1295 * assert(s == ""); 1296 * ------------------------- 1297 * 1298 * Throws: 1299 * $(LREF UUIDParsingException) if the input is invalid 1300 * 1301 * CTFE: 1302 * This function is supported in CTFE code. Note that error messages 1303 * caused by a malformed UUID parsed at compile time can be cryptic, 1304 * but errors are detected and reported at compile time. 1305 */ 1306 UUID parseUUID(T)(T uuidString) 1307 if (isSomeString!T) 1308 { 1309 return parseUUID(uuidString); 1310 } 1311 1312 ///ditto 1313 UUID parseUUID(Range)(ref Range uuidRange) 1314 if (isInputRange!Range 1315 && is(Unqual!(ElementType!Range) == dchar)) 1316 { 1317 import std.ascii : isHexDigit; 1318 import std.conv : ConvException, parse; 1319 1320 static if (isForwardRange!Range) 1321 auto errorCopy = uuidRange.save; 1322 1323 void parserError()(size_t pos, UUIDParsingException.Reason reason, string message, Throwable next = null, 1324 string file = __FILE__, size_t line = __LINE__) 1325 { 1326 static if (isForwardRange!Range) 1327 { 1328 import std.conv : to; 1329 static if (isInfinite!Range) 1330 { 1331 throw new UUIDParsingException(to!string(take(errorCopy, pos)), pos, reason, message, 1332 next, file, line); 1333 } 1334 else 1335 { 1336 throw new UUIDParsingException(to!string(errorCopy), pos, reason, message, next, file, 1337 line); 1338 } 1339 } 1340 else 1341 { 1342 throw new UUIDParsingException("", pos, reason, message, next, file, line); 1343 } 1344 } 1345 1346 static if (hasLength!Range) 1347 { 1348 import std.conv : to; 1349 if (uuidRange.length < 32) 1350 { 1351 throw new UUIDParsingException(to!string(uuidRange), 0, UUIDParsingException.Reason.tooLittle, 1352 "Insufficient Input"); 1353 } 1354 } 1355 1356 UUID result; 1357 size_t consumed; 1358 size_t element = 0; 1359 1360 //skip garbage 1361 size_t skip()() 1362 { 1363 size_t skipped; 1364 while (!uuidRange.empty && !isHexDigit(uuidRange.front)) 1365 { 1366 skipped++; 1367 uuidRange.popFront(); 1368 } 1369 return skipped; 1370 } 1371 1372 consumed += skip(); 1373 1374 if (uuidRange.empty) 1375 parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); 1376 1377 bool dashAllowed = false; 1378 1379 parseLoop: while (!uuidRange.empty) 1380 { 1381 immutable character = uuidRange.front; 1382 1383 if (character == '-') 1384 { 1385 if (!dashAllowed) 1386 parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected '-'"); 1387 else 1388 dashAllowed = false; 1389 1390 consumed++; 1391 } 1392 else if (!isHexDigit(character)) 1393 { 1394 parserError(consumed, UUIDParsingException.Reason.invalidChar, 1395 "Unexpected character (wanted a hexDigit)"); 1396 } 1397 else 1398 { 1399 try 1400 { 1401 consumed += 2; 1402 static if (isSomeString!Range) 1403 { 1404 if (uuidRange.length < 2) 1405 { 1406 parserError(consumed, UUIDParsingException.Reason.tooLittle, 1407 "Insufficient Input"); 1408 } 1409 auto part = uuidRange[0 .. 2]; 1410 result.data[element++] = parse!ubyte(part, 16); 1411 uuidRange.popFront(); 1412 } 1413 else 1414 { 1415 dchar[2] copyBuf; 1416 copyBuf[0] = character; 1417 uuidRange.popFront(); 1418 if (uuidRange.empty) 1419 { 1420 parserError(consumed, UUIDParsingException.Reason.tooLittle, 1421 "Insufficient Input"); 1422 } 1423 copyBuf[1] = uuidRange.front; 1424 auto part = copyBuf[]; 1425 result.data[element++] = parse!ubyte(part, 16); 1426 } 1427 1428 if (element == 16) 1429 { 1430 uuidRange.popFront(); 1431 break parseLoop; 1432 } 1433 1434 dashAllowed = true; 1435 } 1436 catch (ConvException e) 1437 { 1438 parserError(consumed, UUIDParsingException.Reason.invalidChar, 1439 "Couldn't parse ubyte", e); 1440 } 1441 } 1442 uuidRange.popFront(); 1443 } 1444 assert(element <= 16); 1445 1446 if (element < 16) 1447 parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); 1448 1449 consumed += skip(); 1450 if (!uuidRange.empty) 1451 parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character"); 1452 1453 return result; 1454 } 1455 1456 /// 1457 @safe unittest 1458 { 1459 auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"); 1460 //no dashes 1461 id = parseUUID("8ab3060e2cba4f23b74cb52db3bdfb46"); 1462 //dashes at different positions 1463 id = parseUUID("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46"); 1464 //leading / trailing characters 1465 id = parseUUID("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}"); 1466 //unicode 1467 id = parseUUID("ü8ab3060e2cba4f23b74cb52db3bdfb46ü"); 1468 //multiple trailing/leading characters 1469 id = parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||"); 1470 1471 //Can also be used in CTFE, for example as UUID literals: 1472 enum ctfeID = parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); 1473 //here parsing is done at compile time, no runtime overhead! 1474 } 1475 1476 @safe pure unittest 1477 { 1478 import std.conv : to; 1479 import std.exception; 1480 import std.meta; 1481 1482 struct TestRange(bool forward) 1483 { 1484 dstring input; 1485 1486 @property dchar front() 1487 { 1488 return input.front; 1489 } 1490 1491 void popFront() 1492 { 1493 input.popFront(); 1494 } 1495 1496 @property bool empty() 1497 { 1498 return input.empty; 1499 } 1500 1501 static if (forward) 1502 { 1503 @property TestRange!true save() 1504 { 1505 return this; 1506 } 1507 } 1508 } 1509 alias TestInputRange = TestRange!false; 1510 alias TestForwardRange = TestRange!true; 1511 1512 assert(isInputRange!TestInputRange); 1513 assert(is(ElementType!TestInputRange == dchar)); 1514 assert(isInputRange!TestForwardRange); 1515 assert(isForwardRange!TestForwardRange); 1516 assert(is(ElementType!TestForwardRange == dchar)); 1517 1518 //Helper function for unittests - Need to pass ranges by ref 1519 UUID parseHelper(T)(string input) 1520 { 1521 static if (is(T == TestInputRange) || is(T == TestForwardRange)) 1522 { 1523 T range = T(to!dstring(input)); 1524 return parseUUID(range); 1525 } 1526 else 1527 return parseUUID(to!T(input)); 1528 } 1529 1530 foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], 1531 wchar[], const(wchar)[], immutable(wchar)[], 1532 dchar[], const(dchar)[], immutable(dchar)[], 1533 immutable(char[]), immutable(wchar[]), immutable(dchar[]), 1534 TestForwardRange, TestInputRange)) 1535 { 1536 //Verify examples. 1537 auto id = parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"); 1538 //no dashes 1539 id = parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46"); 1540 //dashes at different positions 1541 id = parseHelper!S("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46"); 1542 //leading / trailing characters 1543 id = parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}"); 1544 //unicode 1545 id = parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü"); 1546 //multiple trailing/leading characters 1547 id = parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||"); 1548 enum ctfeId = parseHelper!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); 1549 assert(parseHelper!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46") == ctfeId); 1550 1551 //Test valid, working cases 1552 assert(parseHelper!S("00000000-0000-0000-0000-000000000000").empty); 1553 assert(parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46").data 1554 == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); 1555 1556 assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data 1557 == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); 1558 1559 //wstring / dstring 1560 assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data 1561 == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); 1562 assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data 1563 == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]); 1564 1565 //Test too short UUIDS 1566 auto except = collectException!UUIDParsingException( 1567 parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886")); 1568 assert(except && except.reason == UUIDParsingException.Reason.tooLittle); 1569 1570 //Test too long UUIDS 1571 except = collectException!UUIDParsingException( 1572 parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa")); 1573 assert(except && except.reason == UUIDParsingException.Reason.invalidChar); 1574 1575 //Test too long UUIDS 2 1576 except = collectException!UUIDParsingException( 1577 parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a-aa")); 1578 assert(except && except.reason == UUIDParsingException.Reason.invalidChar); 1579 1580 //Test dashes 1581 assert(parseHelper!S("8ab3060e2cba-4f23-b74c-b52db3bdfb46") 1582 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1583 assert(parseHelper!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46") 1584 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1585 assert(parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46") 1586 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1587 1588 except = collectException!UUIDParsingException( 1589 parseHelper!S("8-ab3060e2cba-4f23-b74c-b52db3bdfb46")); 1590 assert(except && except.reason == UUIDParsingException.Reason.invalidChar); 1591 1592 //Test leading/trailing characters 1593 assert(parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}") 1594 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1595 assert(parseHelper!S("{8ab3060e2cba4f23b74cb52db3bdfb46}") 1596 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1597 1598 //Boost test 1599 auto u_increasing = UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 1600 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); 1601 assert(parseHelper!S("0123456789abcdef0123456789ABCDEF") == UUID(cast(ubyte[16])[0x01, 1602 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])); 1603 1604 //unicode 1605 assert(parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü") 1606 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1607 1608 //multiple trailing/leading characters 1609 assert(parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||") 1610 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46")); 1611 } 1612 } 1613 1614 /** 1615 * Default namespace from RFC 4122 1616 * 1617 * Name string is a fully-qualified domain name 1618 */ 1619 enum dnsNamespace = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); 1620 1621 /** 1622 * Default namespace from RFC 4122 1623 * 1624 * Name string is a URL 1625 */ 1626 enum urlNamespace = UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); 1627 1628 /** 1629 * Default namespace from RFC 4122 1630 * 1631 * Name string is an ISO OID 1632 */ 1633 enum oidNamespace = UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); 1634 1635 /** 1636 * Default namespace from RFC 4122 1637 * 1638 * Name string is an X.500 DN (in DER or a text output format) 1639 */ 1640 enum x500Namespace = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"); 1641 1642 /** 1643 * Regex string to extract UUIDs from text. 1644 */ 1645 enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~ 1646 "-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"; 1647 1648 /// 1649 @safe unittest 1650 { 1651 import std.algorithm; 1652 import std.regex; 1653 1654 string test = "Lorem ipsum dolor sit amet, consetetur "~ 1655 "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~ 1656 "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~ 1657 "magna aliquyam erat, sed diam voluptua. "~ 1658 "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~ 1659 "justo duo dolores et ea rebum."; 1660 1661 auto r = regex(uuidRegex, "g"); 1662 UUID[] found; 1663 foreach (c; match(test, r)) 1664 { 1665 found ~= UUID(c.hit); 1666 } 1667 assert(found == [ 1668 UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"), 1669 UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"), 1670 ]); 1671 } 1672 1673 /** 1674 * This exception is thrown if an error occurs when parsing a UUID 1675 * from a string. 1676 */ 1677 public class UUIDParsingException : Exception 1678 { 1679 /** 1680 * The reason why parsing the UUID string failed (if known) 1681 */ 1682 enum Reason 1683 { 1684 unknown, /// 1685 tooLittle, ///The passed in input was correct, but more input was expected. 1686 tooMuch, ///The input data is too long (There's no guarantee the first part of the data is valid) 1687 invalidChar, ///Encountered an invalid character 1688 1689 } 1690 ///ditto 1691 Reason reason; 1692 ///The original input string which should have been parsed. 1693 string input; 1694 ///The position in the input string where the error occurred. 1695 size_t position; 1696 1697 private this(string input, size_t pos, Reason why = Reason.unknown, string msg = "", 1698 Throwable next = null, string file = __FILE__, size_t line = __LINE__) pure @trusted 1699 { 1700 import std.array : replace; 1701 import std.format : format; 1702 this.input = input; 1703 this.position = pos; 1704 this.reason = why; 1705 string message = format("An error occured in the UUID parser: %s\n" ~ 1706 " * Input:\t'%s'\n * Position:\t%s", msg, replace(replace(input, 1707 "\r", "\\r"), "\n", "\\n"), pos); 1708 super(message, file, line, next); 1709 } 1710 } 1711 1712 /// 1713 @safe unittest 1714 { 1715 import std.exception : collectException; 1716 1717 const inputUUID = "this-is-an-invalid-uuid"; 1718 auto ex = collectException!UUIDParsingException(UUID(inputUUID)); 1719 assert(ex !is null); // check that exception was thrown 1720 assert(ex.input == inputUUID); 1721 assert(ex.position == 0); 1722 assert(ex.reason == UUIDParsingException.Reason.tooLittle); 1723 } 1724 1725 @safe unittest 1726 { 1727 auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch); 1728 assert(ex.input == "foo"); 1729 assert(ex.position == 10); 1730 assert(ex.reason == UUIDParsingException.Reason.tooMuch); 1731 } 1732