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.
9 $(SCRIPT inhibitQuickIndex = 1;)
13 $(TR $(TH Category) $(TH Functions)
15 $(TR $(TDNW Parsing UUIDs)
16 $(TD $(MYREF parseUUID)
18 $(MYREF UUIDParsingException)
22 $(TR $(TDNW Generating UUIDs)
23 $(TD $(MYREF sha1UUID)
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)
39 $(TR $(TDNW UUID namespaces)
40 $(TD $(MYREF dnsNamespace)
43 $(MYREF x500Namespace)
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).
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.
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).
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.
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.
79 * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122)
82 * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier)
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)
90 * MYREF2 = <a href="#$2">$(TT $1)</a>
91 * MYREF3 = <a href="#$2">`$1`</a>
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)
107 ids
~= md5UUID("test.name.123");
108 ids
~= sha1UUID("test.name.123");
112 assert(entry
.variant
== UUID
.Variant
.rfc4122
);
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]);
122 import std
.range
.primitives
;
130 import std
.meta
: AliasSeq
, allSatisfy
;
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);
136 @safe pure nothrow @nogc Char
toChar(Char
)(size_t i
) const
139 return cast(Char
)('0' + i
);
141 return cast(Char
)('a' + (i
-10));
144 @safe pure nothrow unittest
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");
150 // Reinterpret the UUID as an array of some other primitive.
151 @trusted ref T
[16 / T
.sizeof
] asArrayOf(T
)() return
154 return *cast(typeof(return)*)&data
;
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.
164 * Note: Do not confuse this with $(REF _Variant, std,_variant).
168 ncs
, /// NCS backward compatibility
169 rfc4122
, /// Defined in RFC 4122 document
170 microsoft
, /// Microsoft Corporation backward compatibility
171 future
///Reserved for future use
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.
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.
191 ///Version 3 (Name based + MD5)
193 ///Version 4 (Random)
194 randomNumberBased
= 4,
195 ///Version 5 (Name based + SHA-1)
202 * It is sometimes useful to get or set the 16 bytes of a UUID
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.
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 * -----------------------------------------------
220 private ulong[2] ulongs
;
221 static if (size_t
.sizeof
== 4)
222 private uint[4] uints
;
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.
235 tmp
.data
= cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,
237 assert(tmp
.data
== cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
240 assert(tmp
.data
== cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11,
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,
249 * Construct a UUID struct from the 16 byte representation
252 @safe pure nothrow @nogc this(ref const scope ubyte[16] uuidData
)
257 @safe pure nothrow @nogc this(const ubyte[16] uuidData
)
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
);
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.
277 @safe pure this(T
...)(T uuidData
)
278 if (uuidData
.length
== 16 && allSatisfy
!(isIntegral
, T
))
280 import std
.conv
: to
;
282 foreach (idx
, it
; uuidData
)
284 this.data
[idx
] = to
!ubyte(it
);
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,
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,
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
);
306 assert(!__traits(compiles
, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))));
309 assert(!__traits(compiles
, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1))));
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
318 * $(LREF UUIDParsingException) if the input is invalid
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
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.
332 * For a less strict parser, see $(LREF parseUUID)
337 import std
.conv
: to
, parse
;
338 if (uuid
.length
< 36)
340 throw new UUIDParsingException(to
!string(uuid
), 0,
341 UUIDParsingException
.Reason
.tooLittle
, "Insufficient Input");
343 if (uuid
.length
> 36)
345 throw new UUIDParsingException(to
!string(uuid
), 35, UUIDParsingException
.Reason
.tooMuch
,
346 "Input is too long, need exactly 36 characters");
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 '-'");
354 ubyte[16] data2
; //ctfe bug
357 foreach (i
, p
; byteSeq
)
359 enum uint s
= 'a'-10-'0';
363 if (h
< '0') goto Lerr
;
364 if (l
< '0') goto Lerr
;
367 h |
= 0x20; //poorman's tolower
368 if (h
< 'a') goto Lerr
;
369 if (h
> 'f') goto Lerr
;
374 l |
= 0x20; //poorman's tolower
375 if (l
< 'a') goto Lerr
;
376 if (l
> 'f') goto Lerr
;
382 data2
[i
] = cast(ubyte)((h
<< 4) ^ l
);
387 Lerr
: throw new UUIDParsingException(to
!string(uuid
), pos
,
388 UUIDParsingException
.Reason
.invalidChar
, "Couldn't parse ubyte");
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");
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!
406 import std
.conv
: to
;
407 import std
.exception
;
408 import std
.meta
: AliasSeq
;
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[])))
415 //Test valid, working cases
416 assert(UUID(to
!S("00000000-0000-0000-0000-000000000000")).empty
);
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");
423 enum UUID ctfe
= UUID(to
!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
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]);
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
);
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
);
440 except
= collectException
!UUIDParsingException(
441 UUID(to
!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46")));
442 assert(except
&& except
.reason
== UUIDParsingException
.Reason
.invalidChar
);
445 except
= collectException
!UUIDParsingException(
446 UUID(to
!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")));
447 assert(except
&& except
.reason
== UUIDParsingException
.Reason
.invalidChar
);
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
);
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]));
463 * Returns true if and only if the UUID is equal
464 * to {00000000-0000-0000-0000-000000000000}
466 @trusted pure nothrow @nogc @property bool empty() const
469 return data
== (ubyte[16]).init
;
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;
477 static assert(false, "nonsense, it's not 32 or 64 bit");
485 id
= UUID("00000000-0000-0000-0000-000000000001");
491 ubyte[16] getData(size_t i
)
498 for (size_t i
= 0; i
< 16; i
++)
500 assert(!UUID(getData(i
)).empty
);
503 enum ctfeEmpty
= UUID
.init
.empty
;
508 for (size_t i
= 0; i
< 16; i
++)
510 auto ctfeEmpty2
= UUID(getData(i
)).empty
;
515 enum res
= ctfeTest();
519 * RFC 4122 defines different internal data layouts for UUIDs.
520 * Returns the format used by this UUID.
522 * Note: Do not confuse this with $(REF _Variant, std,_variant).
523 * The type of this property is $(MYREF3 std.uuid.UUID.Variant, _Variant).
526 * $(MYREF3 UUID.Variant, Variant)
528 @safe pure nothrow @nogc @property Variant
variant() const
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
534 if ((octet7
& 0x80) == 0x00) //0b0xxxxxxx
536 else if ((octet7
& 0xC0) == 0x80) //0b10xxxxxx
537 return Variant
.rfc4122
;
538 else if ((octet7
& 0xE0) == 0xC0) //0b110xxxxx
539 return Variant
.microsoft
;
542 //assert((octet7 & 0xE0) == 0xE0, "Unknown UUID variant!") //0b111xxxx
543 return Variant
.future
;
550 assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
551 == UUID
.Variant
.rfc4122
);
553 @system pure unittest
555 // @system due to Variant
556 Variant
[ubyte] tests
= cast(Variant
[ubyte])[0x00 : 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
)
576 assert(u
.variant
== value
);
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.
587 * $(MYREF3 UUID.Version, Version)
589 @safe pure nothrow @nogc @property Version
uuidVersion() const
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
;
605 return Version
.unknown
;
611 assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
612 == UUID
.Version
.randomNumberBased
);
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
)
638 assert(u
.uuidVersion
== value
);
643 * Swap the data of this UUID with the data of rhs.
645 @safe pure nothrow @nogc void swap(ref UUID rhs
)
647 immutable bck
= data
;
655 immutable ubyte[16] data
= [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
657 UUID u2
= UUID(data
);
660 assert(u1
== UUID(data
));
661 assert(u2
== UUID
.init
);
665 * All of the standard numeric operators are defined for
668 @safe pure nothrow @nogc bool opEquals(const UUID s
) const
670 return ulongs
[0] == s
.ulongs
[0] && ulongs
[1] == s
.ulongs
[1];
677 assert(UUID("00000000-0000-0000-0000-000000000000") == UUID
.init
);
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];
684 assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
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")];
697 @safe pure nothrow @nogc bool opEquals(ref const scope UUID s
) const
699 return ulongs
[0] == s
.ulongs
[0] && ulongs
[1] == s
.ulongs
[1];
705 @safe pure nothrow @nogc int opCmp(const UUID s
) const
707 import std
.algorithm
.comparison
: cmp;
708 return cmp(this.data
[], s
.data
[]);
714 @safe pure nothrow @nogc int opCmp(ref const scope UUID s
) const
716 import std
.algorithm
.comparison
: cmp;
717 return cmp(this.data
[], s
.data
[]);
723 @safe pure nothrow @nogc UUID
opAssign(const UUID s
)
725 ulongs
[0] = s
.ulongs
[0];
726 ulongs
[1] = s
.ulongs
[1];
733 @safe pure nothrow @nogc UUID
opAssign(ref const scope UUID s
)
735 ulongs
[0] = s
.ulongs
[0];
736 ulongs
[1] = s
.ulongs
[1];
744 @safe pure nothrow @nogc size_t
toHash() const
746 static if (size_t
.sizeof
== 4)
748 enum uint m
= 0x5bd1e995;
788 enum ulong m
= 0xc6a4a7935bd1e995UL
;
789 enum ulong n
= m
* 16;
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];
819 assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
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")];
828 ids
= [UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
829 UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
830 UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
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]);
859 assert(u1
.toHash() != u2
.toHash());
860 assert(u2
.toHash() != u3
.toHash());
861 assert(u3
.toHash() != u1
.toHash());
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"
869 * sink = OutputRange or writeable array at least 36 entries long
871 void toString(Writer
)(scope Writer sink
) const
873 char[36] result
= void;
874 foreach (pos
; skipSeq
)
876 foreach (i
, pos
; byteSeq
)
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
);
884 static if (!__traits(compiles
, put(sink
, result
[])) || isSomeString
!Writer
)
886 foreach (i
, c
; result
)
887 sink
[i
] = cast(typeof(sink
[i
]))c
;
896 * Return the UUID as a string in the canonical form.
898 @trusted pure nothrow string
toString() const
900 import std
.exception
: assumeUnique
;
901 auto result
= new char[36];
903 return result
.assumeUnique
;
909 immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
911 assert(id
.toString() == str);
914 @safe pure nothrow @nogc unittest
916 import std
.meta
: AliasSeq
;
917 static foreach (Char
; AliasSeq
!(char, wchar, dchar))
919 alias String
= immutable(Char
)[];
921 enum String s
= "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
923 static if (is(Char
== char))
925 enum p
= id
.toString();
926 static assert(s
== p
);
935 @system pure nothrow @nogc unittest
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
);
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");
958 void sink(scope const(char)[] data
)
963 assert(buf
== "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
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");
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.
986 * The default namespaces ($(LREF dnsNamespace), ...) defined by
987 * this module should be used when appropriate.
989 * RFC 4122 recommends to use Version 5 UUIDs (SHA-1) instead of Version 3
990 * UUIDs (MD5) for new applications.
993 * CTFE is not supported.
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
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!).
1012 @safe pure nothrow @nogc UUID
md5UUID(const(char[]) name
, const UUID namespace
= UUID
.init
)
1014 return md5UUID(cast(const(ubyte[]))name
, namespace
);
1018 @safe pure nothrow @nogc UUID
md5UUID(const(ubyte[]) data
, const UUID namespace
= UUID
.init
)
1020 import std
.digest
.md
: MD5
;
1026 * NOTE: RFC 4122 says namespace should be converted to big-endian.
1027 * We always keep the UUID data in big-endian representation, so
1030 hash
.put(namespace
.data
[]);
1034 u
.data
= hash
.finish();
1037 //must be 0b10xxxxxx
1038 u
.data
[8] &= 0b10111111;
1039 u
.data
[8] |
= 0b10000000;
1042 //must be 0b0011xxxx
1043 u
.data
[6] &= 0b00111111;
1044 u
.data
[6] |
= 0b00110000;
1052 //Use default UUID.init namespace
1053 auto simpleID
= md5UUID("test.uuid.any.string");
1055 //use a name-based id as namespace
1056 auto namespace
= md5UUID("my.app");
1057 auto id
= md5UUID("some-description", namespace
);
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,
1070 auto constTest
= md5UUID(cast(const(char)[])"test");
1071 constTest
= md5UUID(cast(const(char[]))"test");
1073 char[] mutable
= "test".dup
;
1074 id
= md5UUID(mutable
, namespace
);
1076 const(ubyte)[] data
= cast(ubyte[])[0,1,2,244,165,222];
1078 assert(id
.data
== cast(ubyte[16])[16, 50, 29, 247, 243, 185, 61, 178, 157, 100, 253, 236, 73,
1081 assert(id
.variant
== UUID
.Variant
.rfc4122
);
1082 assert(id
.uuidVersion
== UUID
.Version
.nameBasedMD5
);
1084 auto correct
= UUID("3d813cbb-47fb-32ba-91df-831e1593ac29");
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
);
1095 * This function generates a name based (Version 5) UUID from a namespace
1097 * If no namespace UUID was passed, the empty UUID `UUID.init` is used.
1100 * The default namespaces ($(LREF dnsNamespace), ...) defined by
1101 * this module should be used when appropriate.
1104 * CTFE is not supported.
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
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!).
1123 @safe pure nothrow @nogc UUID
sha1UUID(scope const(char)[] name
, scope const UUID namespace
= UUID
.init
)
1125 return sha1UUID(cast(const(ubyte[]))name
, namespace
);
1129 @safe pure nothrow @nogc UUID
sha1UUID(scope const(ubyte)[] data
, scope const UUID namespace
= UUID
.init
)
1131 import std
.digest
.sha
: SHA1
;
1137 * NOTE: RFC 4122 says namespace should be converted to big-endian.
1138 * We always keep the UUID data in big-endian representation, so
1141 sha
.put(namespace
.data
[]);
1144 auto hash
= sha
.finish();
1146 u
.data
[] = hash
[0 .. 16];
1149 //must be 0b10xxxxxx
1150 u
.data
[8] &= 0b10111111;
1151 u
.data
[8] |
= 0b10000000;
1154 //must be 0b0101xxxx
1155 u
.data
[6] &= 0b01011111;
1156 u
.data
[6] |
= 0b01010000;
1164 //Use default UUID.init namespace
1165 auto simpleID
= sha1UUID("test.uuid.any.string");
1167 //use a name-based id as namespace
1168 auto namespace
= sha1UUID("my.app");
1169 auto id
= sha1UUID("some-description", namespace
);
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,
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,
1182 auto constTest
= sha1UUID(cast(const(char)[])"test");
1183 constTest
= sha1UUID(cast(const(char[]))"test");
1185 char[] mutable
= "test".dup
;
1186 id
= sha1UUID(mutable
, namespace
);
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,
1193 auto correct
= UUID("21f7f8de-8051-5b89-8680-0195ef798b6a");
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
);
1202 * This function generates a random number based UUID from a random
1205 * This function is not supported at compile time.
1208 * randomGen = uniform RNG
1209 * See_Also: $(REF isUniformRNG, std,random)
1211 @nogc nothrow @safe UUID
randomUUID()
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
)
1218 return randomUUID(rndGen
);
1222 import std
.random
: unpredictableSeed
, Xorshift192
;
1223 static assert(Xorshift192
.sizeof
>= UUID
.sizeof
);
1224 static Xorshift192 rng
;
1225 static bool initialized
;
1228 rng
.seed(unpredictableSeed
);
1231 return randomUUID(rng
);
1236 UUID
randomUUID(RNG
)(ref RNG randomGen
)
1237 if (isInputRange
!RNG
&& isIntegral
!(ElementType
!RNG
))
1239 import std
.random
: isUniformRNG
;
1240 static assert(isUniformRNG
!RNG
, "randomGen must be a uniform RNG");
1242 alias E
= ElementEncodingType
!RNG
;
1243 enum size_t elemSize
= E
.sizeof
;
1244 static assert(elemSize
<= 16);
1245 static assert(16 % elemSize
== 0);
1248 foreach (ref E e
; u
.asArrayOf
!E())
1250 e
= randomGen
.front
;
1251 randomGen
.popFront();
1255 //must be 0b10xxxxxx
1256 u
.data
[8] &= 0b10111111;
1257 u
.data
[8] |
= 0b10000000;
1260 //must be 0b0100xxxx
1261 u
.data
[6] &= 0b01001111;
1262 u
.data
[6] |
= 0b01000000;
1270 import std
.random
: Xorshift192
, unpredictableSeed
;
1273 auto uuid
= randomUUID();
1275 //provide a custom RNG. Must be seeded manually.
1278 gen
.seed(unpredictableSeed
);
1279 auto uuid3
= randomUUID(gen
);
1284 import std
.random
: Xorshift192
, unpredictableSeed
;
1286 auto uuid
= randomUUID();
1288 //provide a custom RNG. Must be seeded manually.
1290 gen
.seed(unpredictableSeed
);
1291 auto uuid3
= randomUUID(gen
);
1293 auto u1
= randomUUID();
1294 auto u2
= randomUUID();
1296 assert(u1
.variant
== UUID
.Variant
.rfc4122
);
1297 assert(u1
.uuidVersion
== UUID
.Version
.randomNumberBased
);
1301 * This is a less strict parser compared to the parser used in the
1302 * UUID constructor. It enforces the following rules:
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])
1313 * Like most parsers, it consumes its argument. This means:
1314 * -------------------------
1315 * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46";
1318 * -------------------------
1321 * $(LREF UUIDParsingException) if the input is invalid
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.
1328 UUID
parseUUID(T
)(T uuidString
)
1331 return parseUUID(uuidString
);
1335 UUID
parseUUID(Range
)(ref Range uuidRange
)
1336 if (isInputRange
!Range
&& isSomeChar
!(ElementType
!Range
))
1338 import std
.ascii
: isHexDigit
;
1339 import std
.conv
: ConvException
, parse
;
1341 static if (isForwardRange
!Range
)
1342 auto errorCopy
= uuidRange
.save
;
1344 void parserError()(size_t pos
, UUIDParsingException
.Reason reason
, string message
, Throwable next
= null,
1345 string file
= __FILE__
, size_t line
= __LINE__
)
1347 static if (isForwardRange
!Range
)
1349 import std
.conv
: to
;
1350 static if (isInfinite
!Range
)
1352 throw new UUIDParsingException(to
!string(take(errorCopy
, pos
)), pos
, reason
, message
,
1357 throw new UUIDParsingException(to
!string(errorCopy
), pos
, reason
, message
, next
, file
,
1363 throw new UUIDParsingException("", pos
, reason
, message
, next
, file
, line
);
1367 static if (hasLength
!Range
)
1369 import std
.conv
: to
;
1370 if (uuidRange
.length
< 32)
1372 throw new UUIDParsingException(to
!string(uuidRange
), 0, UUIDParsingException
.Reason
.tooLittle
,
1373 "Insufficient Input");
1385 while (!uuidRange
.empty
&& !isHexDigit(uuidRange
.front
))
1388 uuidRange
.popFront();
1395 if (uuidRange
.empty
)
1396 parserError(consumed
, UUIDParsingException
.Reason
.tooLittle
, "Insufficient Input");
1398 bool dashAllowed
= false;
1400 parseLoop
: while (!uuidRange
.empty
)
1402 immutable character
= uuidRange
.front
;
1404 if (character
== '-')
1407 parserError(consumed
, UUIDParsingException
.Reason
.invalidChar
, "Unexpected '-'");
1409 dashAllowed
= false;
1413 else if (!isHexDigit(character
))
1415 parserError(consumed
, UUIDParsingException
.Reason
.invalidChar
,
1416 "Unexpected character (wanted a hexDigit)");
1423 static if (isSomeString
!Range
)
1425 if (uuidRange
.length
< 2)
1427 parserError(consumed
, UUIDParsingException
.Reason
.tooLittle
,
1428 "Insufficient Input");
1430 auto part
= uuidRange
[0 .. 2];
1431 result
.data
[element
++] = parse
!ubyte(part
, 16);
1432 uuidRange
.popFront();
1437 copyBuf
[0] = character
;
1438 uuidRange
.popFront();
1439 if (uuidRange
.empty
)
1441 parserError(consumed
, UUIDParsingException
.Reason
.tooLittle
,
1442 "Insufficient Input");
1444 copyBuf
[1] = uuidRange
.front
;
1445 auto part
= copyBuf
[];
1446 result
.data
[element
++] = parse
!ubyte(part
, 16);
1451 uuidRange
.popFront();
1457 catch (ConvException e
)
1459 parserError(consumed
, UUIDParsingException
.Reason
.invalidChar
,
1460 "Couldn't parse ubyte", e
);
1463 uuidRange
.popFront();
1465 assert(element
<= 16);
1468 parserError(consumed
, UUIDParsingException
.Reason
.tooLittle
, "Insufficient Input");
1471 if (!uuidRange
.empty
)
1472 parserError(consumed
, UUIDParsingException
.Reason
.invalidChar
, "Unexpected character");
1480 auto id
= parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
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}");
1488 id
= parseUUID("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
1489 //multiple trailing/leading characters
1490 id
= parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||");
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!
1499 import std
.conv
: to
;
1500 import std
.exception
;
1503 struct TestRange(bool forward
)
1507 @property dchar front()
1517 @property bool empty()
1524 @property TestRange
!true save()
1530 alias TestInputRange
= TestRange
!false;
1531 alias TestForwardRange
= TestRange
!true;
1533 assert(isInputRange
!TestInputRange
);
1534 assert(is(ElementType
!TestInputRange
== dchar));
1535 assert(isInputRange
!TestForwardRange
);
1536 assert(isForwardRange
!TestForwardRange
);
1537 assert(is(ElementType
!TestForwardRange
== dchar));
1539 //Helper function for unittests - Need to pass ranges by ref
1540 UUID
parseHelper(T
)(string input
)
1542 static if (is(T
== TestInputRange
) ||
is(T
== TestForwardRange
))
1544 T range
= T(to
!dstring(input
));
1545 return parseUUID(range
);
1548 return parseUUID(to
!T(input
));
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
))
1558 auto id
= parseHelper
!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
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}");
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
);
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]);
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]);
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]);
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
);
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
);
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
);
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"));
1609 except
= collectException
!UUIDParsingException(
1610 parseHelper
!S("8-ab3060e2cba-4f23-b74c-b52db3bdfb46"));
1611 assert(except
&& except
.reason
== UUIDParsingException
.Reason
.invalidChar
);
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"));
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]));
1626 assert(parseHelper
!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü")
1627 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1629 //multiple trailing/leading characters
1630 assert(parseHelper
!S("///8ab3060e2cba4f23b74cb52db3bdfb46||")
1631 == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
1634 // Test input range with non-dchar element type.
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]);
1643 * Default namespace from RFC 4122
1645 * Name string is a fully-qualified domain name
1647 enum dnsNamespace
= UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
1650 * Default namespace from RFC 4122
1652 * Name string is a URL
1654 enum urlNamespace
= UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
1657 * Default namespace from RFC 4122
1659 * Name string is an ISO OID
1661 enum oidNamespace
= UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
1664 * Default namespace from RFC 4122
1666 * Name string is an X.500 DN (in DER or a text output format)
1668 enum x500Namespace
= UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8");
1671 * Regex string to extract UUIDs from text.
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}";
1679 import std
.algorithm
;
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.";
1689 auto r
= regex(uuidRegex
, "g");
1691 foreach (c
; match(test, r
))
1693 found
~= UUID(c
.hit
);
1696 UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"),
1697 UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"),
1702 * This exception is thrown if an error occurs when parsing a UUID
1705 public class UUIDParsingException
: Exception
1708 * The reason why parsing the UUID string failed (if known)
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
1720 ///The original input string which should have been parsed.
1722 ///The position in the input string where the error occurred.
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
1728 import std
.array
: replace
;
1729 import std
.format
: format
;
1731 this.position
= pos
;
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
);
1743 import std
.exception
: collectException
;
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
);
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
);