diff libphobos/src/std/uuid.d @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libphobos/src/std/uuid.d	Thu Feb 13 11:34:05 2020 +0900
@@ -0,0 +1,1731 @@
+/**
+ * A $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, UUID), or
+ * $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, Universally unique identifier),
+ * is intended to uniquely identify information in a distributed environment
+ * without significant central coordination. It can be
+ * used to tag objects with very short lifetimes, or to reliably identify very
+ * persistent objects across a network.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Parsing UUIDs)
+     $(TD $(MYREF parseUUID)
+          $(MYREF UUID)
+          $(MYREF UUIDParsingException)
+          $(MYREF uuidRegex)
+          )
+     )
+$(TR $(TDNW Generating UUIDs)
+     $(TD $(MYREF sha1UUID)
+          $(MYREF randomUUID)
+          $(MYREF md5UUID)
+          )
+     )
+$(TR $(TDNW Using UUIDs)
+     $(TD $(MYREF2 UUID.uuidVersion, uuidVersion)
+          $(MYREF2 UUID.variant, variant)
+          $(MYREF2 UUID.toString, toString)
+          $(MYREF2 UUID.data, data)
+          $(MYREF2 UUID.swap, swap)
+          $(MYREF2 UUID.opEquals, opEquals)
+          $(MYREF2 UUID.opCmp, opCmp)
+          $(MYREF2 UUID.toHash, toHash)
+          )
+     )
+$(TR $(TDNW UUID namespaces)
+     $(TD $(MYREF dnsNamespace)
+          $(MYREF urlNamespace)
+          $(MYREF oidNamespace)
+          $(MYREF x500Namespace)
+          )
+     )
+)
+)
+
+ * UUIDs have many applications. Some examples follow: Databases may use UUIDs to identify
+ * rows or records in order to ensure that they are unique across different
+ * databases, or for publication/subscription services. Network messages may be
+ * identified with a UUID to ensure that different parts of a message are put back together
+ * again. Distributed computing may use UUIDs to identify a remote procedure call.
+ * Transactions and classes involved in serialization may be identified by UUIDs.
+ * Microsoft's component object model (COM) uses UUIDs to distinguish different software
+ * component interfaces. UUIDs are inserted into documents from Microsoft Office programs.
+ * UUIDs identify audio or video streams in the Advanced Systems Format (ASF). UUIDs are
+ * also a basis for OIDs (object identifiers), and URNs (uniform resource name).
+ *
+ * An attractive feature of UUIDs when compared to alternatives is their relative small size,
+ * of 128 bits, or 16 bytes. Another is that the creation of UUIDs does not require
+ * a centralized authority.
+ *
+ * When UUIDs are generated by one of the defined mechanisms, they are either guaranteed
+ * to be unique, different from all other generated UUIDs (that is, it has never been
+ * generated before and it will never be generated again), or it is extremely likely
+ * to be unique (depending on the mechanism).
+ *
+ * For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly
+ * initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to
+ * $(D UUID.init), which is a UUID with all 16 bytes set to 0.
+ * Use UUID's constructors or the UUID generator functions to get an initialized UUID.
+ *
+ * This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html,
+ * boost._uuid) from the Boost project with some minor additions and API
+ * changes for a more D-like API.
+ *
+ * Standards:
+ * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122)
+ *
+ * See_Also:
+ * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier)
+ *
+ * Copyright: Copyright Johannes Pfau 2011 - .
+ * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:   Johannes Pfau
+ * Source:    $(PHOBOSSRC std/_uuid.d)
+ *
+ * Macros:
+ * MYREF2 = <a href="#$2">$(TT $1)</a>&nbsp;
+ * MYREF3 = <a href="#$2">$(D $1)</a>
+ */
+/*          Copyright Johannes Pfau 2011 - 2012.
+ * Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ */
+module std.uuid;
+
+///
+@safe unittest
+{
+    import std.uuid;
+
+    UUID[] ids;
+    ids ~= randomUUID();
+    ids ~= md5UUID("test.name.123");
+    ids ~= sha1UUID("test.name.123");
+
+    foreach (entry; ids)
+    {
+        assert(entry.variant == UUID.Variant.rfc4122);
+    }
+    assert(ids[0].uuidVersion == UUID.Version.randomNumberBased);
+    assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd");
+    assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207,
+        234, 161, 157, 12, 205]);
+    UUID id;
+    assert(id.empty);
+}
+
+import std.range.primitives;
+import std.traits;
+
+/**
+ *
+ */
+public struct UUID
+{
+    import std.meta : AliasSeq, allSatisfy;
+
+    private:
+        alias skipSeq = AliasSeq!(8, 13, 18, 23);
+        alias byteSeq = AliasSeq!(0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34);
+
+        @safe pure nothrow @nogc Char toChar(Char)(size_t i) const
+        {
+            if (i <= 9)
+                return cast(Char)('0' + i);
+            else
+                return cast(Char)('a' + (i-10));
+        }
+
+        @safe pure nothrow unittest
+        {
+            assert(UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45,
+                179, 189, 251, 70]).toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+        }
+
+        // Reinterpret the UUID as an array of some other primitive.
+        @trusted ref T[16 / T.sizeof] asArrayOf(T)() return
+        if (isIntegral!T)
+        {
+            return *cast(typeof(return)*)&data;
+        }
+
+    public:
+        /**
+         * RFC 4122 defines different internal data layouts for UUIDs. These are
+         * the UUID formats supported by this module. It's
+         * possible to read, compare and use all these Variants, but
+         * UUIDs generated by this module will always be in rfc4122 format.
+         *
+         * Note: Do not confuse this with $(REF _Variant, std,_variant).
+         */
+        enum Variant
+        {
+            ncs, /// NCS backward compatibility
+            rfc4122, /// Defined in RFC 4122 document
+            microsoft, /// Microsoft Corporation backward compatibility
+            future ///Reserved for future use
+        }
+
+        /**
+         * RFC 4122 defines different UUID versions. The version shows
+         * how a UUID was generated, e.g. a version 4 UUID was generated
+         * from a random number, a version 3 UUID from an MD5 hash of a name.
+         *
+         * Note:
+         * All of these UUID versions can be read and processed by
+         * $(D std.uuid), but only version 3, 4 and 5 UUIDs can be generated.
+         */
+        enum Version
+        {
+            ///Unknown version
+            unknown = -1,
+            ///Version 1
+            timeBased = 1,
+            ///Version 2
+            dceSecurity = 2,
+            ///Version 3 (Name based + MD5)
+            nameBasedMD5 = 3,
+            ///Version 4 (Random)
+            randomNumberBased = 4,
+            ///Version 5 (Name based + SHA-1)
+            nameBasedSHA1 = 5
+        }
+
+        union
+        {
+            /**
+             * It is sometimes useful to get or set the 16 bytes of a UUID
+             * directly.
+             *
+             * Note:
+             * UUID uses a 16-ubyte representation for the UUID data.
+             * RFC 4122 defines a UUID as a special structure in big-endian
+             * format. These 16-ubytes always equal the big-endian structure
+             * defined in RFC 4122.
+             *
+             * Example:
+             * -----------------------------------------------
+             * auto rawData = uuid.data; //get data
+             * rawData[0] = 1; //modify
+             * uuid.data = rawData; //set data
+             * uuid.data[1] = 2; //modify directly
+             * -----------------------------------------------
+             */
+            ubyte[16] data;
+            private ulong[2] ulongs;
+            static if (size_t.sizeof == 4)
+                private uint[4] uints;
+        }
+
+        /*
+         * We could use a union here to also provide access to the
+         * fields specified in RFC 4122, but as we never have to access
+         * those (only necessary for version 1 (and maybe 2) UUIDs),
+         * that is not needed right now.
+         */
+
+        @safe pure unittest
+        {
+            UUID tmp;
+            tmp.data = cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,
+                13,14,15];
+            assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
+                12,13,14,15]);
+            tmp.data[2] = 3;
+            assert(tmp.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11,
+                12,13,14,15]);
+
+            auto tmp2 = cast(immutable UUID) tmp;
+            assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11,
+                12,13,14,15]);
+        }
+
+        /**
+         * Construct a UUID struct from the 16 byte representation
+         * of a UUID.
+         */
+        @safe pure nothrow @nogc this(ref in ubyte[16] uuidData)
+        {
+            data = uuidData;
+        }
+        /// ditto
+        @safe pure nothrow @nogc this(in ubyte[16] uuidData)
+        {
+            data = uuidData;
+        }
+
+        ///
+        @safe pure unittest
+        {
+            enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+            auto uuid = UUID(data);
+            enum ctfe = UUID(data);
+            assert(uuid.data == data);
+            assert(ctfe.data == data);
+        }
+
+        /**
+         * Construct a UUID struct from the 16 byte representation
+         * of a UUID. Variadic constructor to allow a simpler syntax, see examples.
+         * You need to pass exactly 16 ubytes.
+         */
+        @safe pure this(T...)(T uuidData)
+            if (uuidData.length == 16 && allSatisfy!(isIntegral, T))
+        {
+            import std.conv : to;
+
+            foreach (idx, it; uuidData)
+            {
+                this.data[idx] = to!ubyte(it);
+            }
+        }
+
+        ///
+        @safe unittest
+        {
+            auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+            assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
+                12,13,14,15]);
+        }
+
+        @safe unittest
+        {
+            UUID tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+            assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
+                12,13,14,15]);
+
+            enum UUID ctfeID = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+            assert(ctfeID == tmp);
+
+            //Too few arguments
+            assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))));
+
+            //Too many arguments
+            assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1))));
+        }
+
+        /**
+         * <a name="UUID(string)"></a>
+         * Parse a UUID from its canonical string form. An UUID in its
+         * canonical form looks like this: 8ab3060e-2cba-4f23-b74c-b52db3bdfb46
+         *
+         * Throws:
+         * $(LREF UUIDParsingException) if the input is invalid
+         *
+         * CTFE:
+         * This function is supported in CTFE code. Note that error messages
+         * caused by a malformed UUID parsed at compile time can be cryptic,
+         * but errors are detected and reported at
+         * compile time.
+         *
+         * Note:
+         * This is a strict parser. It only accepts the pattern above.
+         * It doesn't support any leading or trailing characters. It only
+         * accepts characters used for hex numbers and the string must have
+         * hyphens exactly like above.
+         *
+         * For a less strict parser, see $(LREF parseUUID)
+         */
+        this(T)(in T[] uuid) if (isSomeChar!(Unqual!T))
+        {
+            import std.conv : to, parse;
+            if (uuid.length < 36)
+            {
+                throw new UUIDParsingException(to!string(uuid), 0,
+                    UUIDParsingException.Reason.tooLittle, "Insufficient Input");
+            }
+            if (uuid.length > 36)
+            {
+                throw new UUIDParsingException(to!string(uuid), 35, UUIDParsingException.Reason.tooMuch,
+                    "Input is too long, need exactly 36 characters");
+            }
+            static immutable skipInd = [skipSeq];
+            foreach (pos; skipInd)
+                if (uuid[pos] != '-')
+                    throw new UUIDParsingException(to!string(uuid), pos,
+                        UUIDParsingException.Reason.invalidChar, "Expected '-'");
+
+            ubyte[16] data2; //ctfe bug
+            uint pos = void;
+
+            foreach (i, p; byteSeq)
+            {
+                enum uint s = 'a'-10-'0';
+                uint h = uuid[p];
+                uint l = uuid[p+1];
+                pos = p;
+                if (h < '0') goto Lerr;
+                if (l < '0') goto Lerr;
+                if (h > '9')
+                {
+                    h |= 0x20; //poorman's tolower
+                    if (h < 'a') goto Lerr;
+                    if (h > 'f') goto Lerr;
+                    h -= s;
+                }
+                if (l > '9')
+                {
+                    l |= 0x20; //poorman's tolower
+                    if (l < 'a') goto Lerr;
+                    if (l > 'f') goto Lerr;
+                    l -= s;
+                }
+                h -= '0';
+                l -= '0';
+
+                data2[i] = cast(ubyte)((h << 4) ^ l);
+            }
+            this.data = data2;
+            return;
+
+        Lerr: throw new UUIDParsingException(to!string(uuid), pos,
+                UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte");
+        }
+
+        ///
+        @safe pure unittest
+        {
+            auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46");
+            assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76,
+               181, 45, 179, 189, 251, 70]);
+            assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+
+            //Can also be used in CTFE, for example as UUID literals:
+            enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+            //here parsing is done at compile time, no runtime overhead!
+        }
+
+        @safe pure unittest
+        {
+            import std.conv : to;
+            import std.exception;
+            import std.meta;
+
+            foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
+                                  wchar[], const(wchar)[], immutable(wchar)[],
+                                  dchar[], const(dchar)[], immutable(dchar)[],
+                                  immutable(char[]), immutable(wchar[]), immutable(dchar[])))
+            {
+                //Test valid, working cases
+                assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty);
+
+                auto id = UUID(to!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"));
+                assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76,
+                    181, 45, 179, 189, 251, 70]);
+                assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+
+                enum UUID ctfe = UUID(to!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+                assert(ctfe == id);
+
+                assert(UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a")).data
+                    == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+
+                //Test too short UUIDS
+                auto except = collectException!UUIDParsingException(
+                    UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886")));
+                assert(except && except.reason == UUIDParsingException.Reason.tooLittle);
+
+                //Test too long UUIDS
+                except = collectException!UUIDParsingException(
+                    UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa")));
+                assert(except && except.reason == UUIDParsingException.Reason.tooMuch);
+
+                //Test dashes
+                except = collectException!UUIDParsingException(
+                    UUID(to!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46")));
+                assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+                //Test dashes 2
+                except = collectException!UUIDParsingException(
+                    UUID(to!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")));
+                assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+                //Test invalid characters
+                //make sure 36 characters in total or we'll get a 'tooMuch' reason
+                except = collectException!UUIDParsingException(
+                    UUID(to!S("{8ab3060e-2cba-4f23-b74c-b52db3bdf6}")));
+                assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+                //Boost test
+                assert(UUID(to!S("01234567-89ab-cdef-0123-456789ABCDEF"))
+                    == UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01,
+                    0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
+            }
+        }
+
+        /**
+         * Returns true if and only if the UUID is equal
+         * to {00000000-0000-0000-0000-000000000000}
+         */
+        @trusted pure nothrow @nogc @property bool empty() const
+        {
+            if (__ctfe)
+                return data == (ubyte[16]).init;
+
+            auto p = cast(const(size_t*))data.ptr;
+            static if (size_t.sizeof == 4)
+                return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0;
+            else static if (size_t.sizeof == 8)
+                return p[0] == 0 && p[1] == 0;
+            else
+                static assert(false, "nonsense, it's not 32 or 64 bit");
+        }
+
+        ///
+        @safe pure unittest
+        {
+            UUID id;
+            assert(id.empty);
+            id = UUID("00000000-0000-0000-0000-000000000001");
+            assert(!id.empty);
+        }
+
+        @safe pure unittest
+        {
+            ubyte[16] getData(size_t i)
+            {
+                ubyte[16] data;
+                data[i] = 1;
+                return data;
+            }
+
+            for (size_t i = 0; i < 16; i++)
+            {
+                assert(!UUID(getData(i)).empty);
+            }
+
+            enum ctfeEmpty = UUID.init.empty;
+            assert(ctfeEmpty);
+
+            bool ctfeTest()
+            {
+                for (size_t i = 0; i < 16; i++)
+                {
+                    auto ctfeEmpty2 = UUID(getData(i)).empty;
+                    assert(!ctfeEmpty2);
+                }
+                return true;
+            }
+            enum res = ctfeTest();
+        }
+
+        /**
+         * RFC 4122 defines different internal data layouts for UUIDs.
+         * Returns the format used by this UUID.
+         *
+         * Note: Do not confuse this with $(REF _Variant, std,_variant).
+         * The type of this property is $(MYREF3 std.uuid.UUID.Variant, _Variant).
+         *
+         * See_Also:
+         * $(MYREF3 UUID.Variant, Variant)
+         */
+        @safe pure nothrow @nogc @property Variant variant() const
+        {
+            //variant is stored in octet 7
+            //which is index 8, since indexes count backwards
+            immutable octet7 = data[8]; //octet 7 is array index 8
+
+            if ((octet7 & 0x80) == 0x00) //0b0xxxxxxx
+                return Variant.ncs;
+            else if ((octet7 & 0xC0) == 0x80) //0b10xxxxxx
+                return Variant.rfc4122;
+            else if ((octet7 & 0xE0) == 0xC0) //0b110xxxxx
+                return Variant.microsoft;
+            else
+            {
+                //assert((octet7 & 0xE0) == 0xE0, "Unknown UUID variant!") //0b111xxxx
+                return Variant.future;
+            }
+        }
+
+        ///
+        @safe pure unittest
+        {
+            assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
+               == UUID.Variant.rfc4122);
+        }
+        @system pure unittest
+        {
+            // @system due to Variant
+            Variant[ubyte] tests = cast(Variant[ubyte])[0x00 : Variant.ncs,
+                                    0x10 : Variant.ncs,
+                                    0x20 : Variant.ncs,
+                                    0x30 : Variant.ncs,
+                                    0x40 : Variant.ncs,
+                                    0x50 : Variant.ncs,
+                                    0x60 : Variant.ncs,
+                                    0x70 : Variant.ncs,
+                                    0x80 : Variant.rfc4122,
+                                    0x90 : Variant.rfc4122,
+                                    0xa0 : Variant.rfc4122,
+                                    0xb0 : Variant.rfc4122,
+                                    0xc0 : Variant.microsoft,
+                                    0xd0 : Variant.microsoft,
+                                    0xe0 : Variant.future,
+                                    0xf0 : Variant.future];
+            foreach (key, value; tests)
+            {
+                UUID u;
+                u.data[8] = key;
+                assert(u.variant == value);
+            }
+        }
+
+        /**
+         * RFC 4122 defines different UUID versions. The version shows
+         * how a UUID was generated, e.g. a version 4 UUID was generated
+         * from a random number, a version 3 UUID from an MD5 hash of a name.
+         * Returns the version used by this UUID.
+         *
+         * See_Also:
+         * $(MYREF3 UUID.Version, Version)
+         */
+        @safe pure nothrow @nogc @property Version uuidVersion() const
+        {
+            //version is stored in octet 9
+            //which is index 6, since indexes count backwards
+            immutable octet9 = data[6];
+            if ((octet9 & 0xF0) == 0x10)
+                return Version.timeBased;
+            else if ((octet9 & 0xF0) == 0x20)
+                return Version.dceSecurity;
+            else if ((octet9 & 0xF0) == 0x30)
+                return Version.nameBasedMD5;
+            else if ((octet9 & 0xF0) == 0x40)
+                return Version.randomNumberBased;
+            else if ((octet9 & 0xF0) == 0x50)
+                return Version.nameBasedSHA1;
+            else
+                return Version.unknown;
+        }
+
+        ///
+        @safe unittest
+        {
+            assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
+                == UUID.Version.randomNumberBased);
+        }
+        @system unittest
+        {
+            // @system due to cast
+            Version[ubyte] tests = cast(Version[ubyte]) [
+                0x00 : UUID.Version.unknown,
+                0x10 : UUID.Version.timeBased,
+                0x20 : UUID.Version.dceSecurity,
+                0x30 : UUID.Version.nameBasedMD5,
+                0x40 : UUID.Version.randomNumberBased,
+                0x50 : UUID.Version.nameBasedSHA1,
+                0x60 : UUID.Version.unknown,
+                0x70 : UUID.Version.unknown,
+                0x80 : UUID.Version.unknown,
+                0x90 : UUID.Version.unknown,
+                0xa0 : UUID.Version.unknown,
+                0xb0 : UUID.Version.unknown,
+                0xc0 : UUID.Version.unknown,
+                0xd0 : UUID.Version.unknown,
+                0xe0 : UUID.Version.unknown,
+                0xf0 : UUID.Version.unknown];
+            foreach (key, value; tests)
+            {
+                UUID u;
+                u.data[6] = key;
+                assert(u.uuidVersion == value);
+            }
+        }
+
+        /**
+         * Swap the data of this UUID with the data of rhs.
+         */
+        @safe pure nothrow @nogc void swap(ref UUID rhs)
+        {
+            immutable bck = data;
+            data = rhs.data;
+            rhs.data = bck;
+        }
+
+        ///
+        @safe unittest
+        {
+            immutable ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+            UUID u1;
+            UUID u2 = UUID(data);
+            u1.swap(u2);
+
+            assert(u1 == UUID(data));
+            assert(u2 == UUID.init);
+        }
+
+        /**
+         * All of the standard numeric operators are defined for
+         * the UUID struct.
+         */
+        @safe pure nothrow @nogc bool opEquals(in UUID s) const
+        {
+            return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
+        }
+
+        ///
+        @safe pure unittest
+        {
+            //compare UUIDs
+            assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init);
+
+            //UUIDs in associative arrays:
+            int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1,
+                UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
+                UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
+
+            assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
+
+            //UUIDS can be sorted:
+            import std.algorithm;
+            UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+                          UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+                          UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+            sort(ids);
+        }
+
+        /**
+         * ditto
+         */
+        @safe pure nothrow @nogc bool opEquals(ref in UUID s) const
+        {
+            return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
+        }
+
+        /**
+         * ditto
+         */
+        @safe pure nothrow @nogc int opCmp(in UUID s) const
+        {
+            import std.algorithm.comparison : cmp;
+            return cmp(this.data[], s.data[]);
+        }
+
+        /**
+         * ditto
+         */
+        @safe pure nothrow @nogc int opCmp(ref in UUID s) const
+        {
+            import std.algorithm.comparison : cmp;
+            return cmp(this.data[], s.data[]);
+        }
+
+        /**
+         * ditto
+         */
+       @safe pure nothrow @nogc UUID opAssign(in UUID s)
+        {
+            ulongs[0] = s.ulongs[0];
+            ulongs[1] = s.ulongs[1];
+            return this;
+        }
+
+        /**
+         * ditto
+         */
+        @safe pure nothrow @nogc UUID opAssign(ref in UUID s)
+        {
+            ulongs[0] = s.ulongs[0];
+            ulongs[1] = s.ulongs[1];
+            return this;
+        }
+
+        /**
+         * ditto
+         */
+        //MurmurHash2
+        @safe pure nothrow @nogc size_t toHash() const
+        {
+            static if (size_t.sizeof == 4)
+            {
+                enum uint m = 0x5bd1e995;
+                enum uint n = 16;
+                enum uint r = 24;
+
+                uint h = n;
+
+                uint k = uints[0];
+                k *= m;
+                k ^= k >> r;
+                k *= m;
+
+                h ^= k;
+                h *= m;
+
+                k = uints[1];
+                k *= m;
+                k ^= k >> r;
+                k *= m;
+
+                h ^= k;
+                h *= m;
+
+                k = uints[2];
+                k *= m;
+                k ^= k >> r;
+                k *= m;
+
+                h ^= k;
+                h *= m;
+
+                k = uints[3];
+                k *= m;
+                k ^= k >> r;
+                k *= m;
+
+                h ^= k;
+                h *= m;
+            }
+            else
+            {
+                enum ulong m = 0xc6a4a7935bd1e995UL;
+                enum ulong n = m * 16;
+                enum uint r = 47;
+
+                ulong h = n;
+
+                ulong k = ulongs[0];
+                k *= m;
+                k ^= k >> r;
+                k *= m;
+
+                h ^= k;
+                h *= m;
+
+                k = ulongs[1];
+                k *= m;
+                k ^= k >> r;
+                k *= m;
+
+                h ^= k;
+                h *= m;
+            }
+            return h;
+        }
+        @safe unittest
+        {
+            assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init);
+            int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1,
+                UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
+                UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
+
+            assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
+
+            import std.algorithm;
+            UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+                          UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+                          UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+            sort(ids);
+            auto id2 = ids.dup;
+
+            ids = [UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+                   UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+                   UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+            sort(ids);
+            assert(ids == id2);
+
+            //test comparsion
+            UUID u1;
+            UUID u2 = UUID(cast(ubyte[16])[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
+            UUID u3 = UUID(cast(ubyte[16])[255,255,255,255,255,255,255,255,255,
+                255,255,255,255,255,255,255]);
+
+            assert(u1 == u1);
+
+            assert(u1 != u2);
+
+            assert(u1 < u2);
+            assert(u2 < u3);
+
+            assert(u1 <= u1);
+            assert(u1 <= u2);
+            assert(u2 <= u3);
+
+            assert(u2 >= u2);
+            assert(u3 >= u2);
+
+            assert(u3 >= u3);
+            assert(u2 >= u1);
+            assert(u3 >= u1);
+
+            // test hash
+            assert(u1.toHash() != u2.toHash());
+            assert(u2.toHash() != u3.toHash());
+            assert(u3.toHash() != u1.toHash());
+        }
+
+
+        /**
+         * Write the UUID into `sink` as an ASCII string in the canonical form,
+         * which is 36 characters in the form "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+         * Params:
+         *      sink = OutputRange or writeable array at least 36 entries long
+         */
+        void toString(Writer)(scope Writer sink) const
+        {
+            char[36] result = void;
+            foreach (pos; skipSeq)
+                result[pos] = '-';
+            foreach (i, pos; byteSeq)
+            {
+                const uint entry = this.data[i];
+                const uint hi = entry >> 4;
+                result[pos  ] = toChar!char(hi);
+                const uint lo = (entry) & 0x0F;
+                result[pos+1] = toChar!char(lo);
+            }
+            foreach (i, c; result)
+            {
+                static if (__traits(compiles, put(sink, c)))
+                    put(sink, c);
+                else
+                    sink[i] = cast(typeof(sink[i]))c;
+            }
+        }
+
+        /**
+         * Return the UUID as a string in the canonical form.
+         */
+        @trusted pure nothrow string toString() const
+        {
+            import std.exception : assumeUnique;
+            auto result = new char[36];
+            toString(result);
+            return result.assumeUnique;
+        }
+
+        ///
+        @safe pure unittest
+        {
+            immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
+            auto id = UUID(str);
+            assert(id.toString() == str);
+        }
+
+        @safe pure nothrow @nogc unittest
+        {
+            import std.meta : AliasSeq;
+            foreach (Char; AliasSeq!(char, wchar, dchar))
+            {
+                alias String = immutable(Char)[];
+                //CTFE
+                enum String s = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
+                enum id = UUID(s);
+                static if (is(Char == char))
+                {
+                    enum p = id.toString();
+                    static assert(s == p);
+                }
+                //nogc
+                Char[36] str;
+                id.toString(str[]);
+                assert(str == s);
+            }
+        }
+
+        @system pure nothrow @nogc unittest
+        {
+            // @system due to cast
+            import std.encoding : Char = AsciiChar;
+            enum  utfstr = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
+            alias String = immutable(Char)[];
+            enum String s = cast(String) utfstr;
+            enum id = UUID(utfstr);
+            //nogc
+            Char[36] str;
+            id.toString(str[]);
+            assert(str == s);
+        }
+
+        @safe unittest
+        {
+            auto u1 = UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79,
+                35, 183, 76, 181, 45, 179, 189, 251, 70]);
+            assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+            u1 = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+            assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+
+            char[] buf;
+            void sink(const(char)[] data)
+            {
+                buf ~= data;
+            }
+            u1.toString(&sink);
+            assert(buf == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+        }
+}
+
+
+/**
+ * This function generates a name based (Version 3) UUID from a namespace UUID and a name.
+ * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used.
+ *
+ * Note:
+ * The default namespaces ($(LREF dnsNamespace), ...) defined by
+ * this module should be used when appropriate.
+ *
+ * RFC 4122 recommends to use Version 5 UUIDs (SHA-1) instead of Version 3
+ * UUIDs (MD5) for new applications.
+ *
+ * CTFE:
+ * CTFE is not supported.
+ *
+ * Note:
+ * RFC 4122 isn't very clear on how UUIDs should be generated from names.
+ * It is possible that different implementations return different UUIDs
+ * for the same input, so be warned. The implementation for UTF-8 strings
+ * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation.
+ * $(D std.uuid) guarantees that the same input to this function will generate
+ * the same output at any time, on any system (this especially means endianness
+ * doesn't matter).
+ *
+ * Note:
+ * This function does not provide overloads for wstring and dstring, as
+ * there's no clear answer on how that should be implemented. It could be
+ * argued, that string, wstring and dstring input should have the same output,
+ * but that wouldn't be compatible with Boost, which generates different output
+ * for strings and wstrings. It's always possible to pass wstrings and dstrings
+ * by using the ubyte[] function overload (but be aware of endianness issues!).
+ */
+@safe pure nothrow @nogc UUID md5UUID(const(char[]) name, const UUID namespace = UUID.init)
+{
+    return md5UUID(cast(const(ubyte[]))name, namespace);
+}
+
+/// ditto
+@safe pure nothrow @nogc UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init)
+{
+    import std.digest.md : MD5;
+
+    MD5 hash;
+    hash.start();
+
+    /*
+     * NOTE: RFC 4122 says namespace should be converted to big-endian.
+     * We always keep the UUID data in big-endian representation, so
+     * that's fine
+     */
+    hash.put(namespace.data[]);
+    hash.put(data[]);
+
+    UUID u;
+    u.data = hash.finish();
+
+    //set variant
+    //must be 0b10xxxxxx
+    u.data[8] &= 0b10111111;
+    u.data[8] |= 0b10000000;
+
+    //set version
+    //must be 0b0011xxxx
+    u.data[6] &= 0b00111111;
+    u.data[6] |= 0b00110000;
+
+    return u;
+}
+
+///
+@safe unittest
+{
+    //Use default UUID.init namespace
+    auto simpleID = md5UUID("test.uuid.any.string");
+
+    //use a name-based id as namespace
+    auto namespace = md5UUID("my.app");
+    auto id = md5UUID("some-description", namespace);
+}
+
+@safe pure unittest
+{
+    auto simpleID = md5UUID("test.uuid.any.string");
+    assert(simpleID.data == cast(ubyte[16])[126, 206, 86, 72, 29, 233, 62, 213, 178, 139, 198, 136,
+        188, 135, 153, 123]);
+    auto namespace = md5UUID("my.app");
+    auto id = md5UUID("some-description", namespace);
+    assert(id.data == cast(ubyte[16])[166, 138, 167, 79, 48, 219, 55, 166, 170, 103, 39, 73, 216,
+        150, 144, 164]);
+
+    auto constTest = md5UUID(cast(const(char)[])"test");
+    constTest = md5UUID(cast(const(char[]))"test");
+
+    char[] mutable = "test".dup;
+    id = md5UUID(mutable, namespace);
+
+    const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222];
+    id = md5UUID(data);
+    assert(id.data == cast(ubyte[16])[16, 50, 29, 247, 243, 185, 61, 178, 157, 100, 253, 236, 73,
+        76, 51, 47]);
+
+    assert(id.variant == UUID.Variant.rfc4122);
+    assert(id.uuidVersion == UUID.Version.nameBasedMD5);
+
+    auto correct = UUID("3d813cbb-47fb-32ba-91df-831e1593ac29");
+
+    auto u = md5UUID("www.widgets.com", dnsNamespace);
+    //enum ctfeId = md5UUID("www.widgets.com", dnsNamespace);
+    //assert(ctfeId == u);
+    assert(u == correct);
+    assert(u.variant == UUID.Variant.rfc4122);
+    assert(u.uuidVersion == UUID.Version.nameBasedMD5);
+}
+
+ /**
+ * This function generates a name based (Version 5) UUID from a namespace
+ * UUID and a name.
+ * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used.
+ *
+ * Note:
+ * The default namespaces ($(LREF dnsNamespace), ...) defined by
+ * this module should be used when appropriate.
+ *
+ * CTFE:
+ * CTFE is not supported.
+ *
+ * Note:
+ * RFC 4122 isn't very clear on how UUIDs should be generated from names.
+ * It is possible that different implementations return different UUIDs
+ * for the same input, so be warned. The implementation for UTF-8 strings
+ * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation.
+ * $(D std.uuid) guarantees that the same input to this function will generate
+ * the same output at any time, on any system (this especially means endianness
+ * doesn't matter).
+ *
+ * Note:
+ * This function does not provide overloads for wstring and dstring, as
+ * there's no clear answer on how that should be implemented. It could be
+ * argued, that string, wstring and dstring input should have the same output,
+ * but that wouldn't be compatible with Boost, which generates different output
+ * for strings and wstrings. It's always possible to pass wstrings and dstrings
+ * by using the ubyte[] function overload (but be aware of endianness issues!).
+ */
+@safe pure nothrow @nogc UUID sha1UUID(in char[] name, const UUID namespace = UUID.init)
+{
+    return sha1UUID(cast(const(ubyte[]))name, namespace);
+}
+
+/// ditto
+@safe pure nothrow @nogc UUID sha1UUID(in ubyte[] data, const UUID namespace = UUID.init)
+{
+    import std.digest.sha : SHA1;
+
+    SHA1 sha;
+    sha.start();
+
+    /*
+     * NOTE: RFC 4122 says namespace should be converted to big-endian.
+     * We always keep the UUID data in big-endian representation, so
+     * that's fine
+     */
+    sha.put(namespace.data[]);
+    sha.put(data[]);
+
+    auto hash = sha.finish();
+    auto u = UUID();
+    u.data[] = hash[0 .. 16];
+
+    //set variant
+    //must be 0b10xxxxxx
+    u.data[8] &= 0b10111111;
+    u.data[8] |= 0b10000000;
+
+    //set version
+    //must be 0b0101xxxx
+    u.data[6] &= 0b01011111;
+    u.data[6] |= 0b01010000;
+
+    return u;
+}
+
+///
+@safe unittest
+{
+    //Use default UUID.init namespace
+    auto simpleID = sha1UUID("test.uuid.any.string");
+
+    //use a name-based id as namespace
+    auto namespace = sha1UUID("my.app");
+    auto id = sha1UUID("some-description", namespace);
+}
+
+@safe pure unittest
+{
+    auto simpleID = sha1UUID("test.uuid.any.string");
+    assert(simpleID.data == cast(ubyte[16])[16, 209, 239, 61, 99, 12, 94, 70, 159, 79, 255, 250,
+        131, 79, 14, 147]);
+    auto namespace = sha1UUID("my.app");
+    auto id = sha1UUID("some-description", namespace);
+    assert(id.data == cast(ubyte[16])[225, 94, 195, 219, 126, 75, 83, 71, 157, 52, 247, 43, 238, 248,
+        148, 46]);
+
+    auto constTest = sha1UUID(cast(const(char)[])"test");
+    constTest = sha1UUID(cast(const(char[]))"test");
+
+    char[] mutable = "test".dup;
+    id = sha1UUID(mutable, namespace);
+
+    const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222];
+    id = sha1UUID(data);
+    assert(id.data == cast(ubyte[16])[60, 65, 92, 240, 96, 46, 95, 238, 149, 100, 12, 64, 199, 194,
+        243, 12]);
+
+    auto correct = UUID("21f7f8de-8051-5b89-8680-0195ef798b6a");
+
+    auto u = sha1UUID("www.widgets.com", dnsNamespace);
+    assert(u == correct);
+    assert(u.variant == UUID.Variant.rfc4122);
+    assert(u.uuidVersion == UUID.Version.nameBasedSHA1);
+}
+
+/**
+ * This function generates a random number based UUID from a random
+ * number generator.
+ *
+ * This function is not supported at compile time.
+ *
+ * Params:
+ *      randomGen = uniform RNG
+ * See_Also: $(REF isUniformRNG, std,random)
+ */
+@safe UUID randomUUID()
+{
+    import std.random : rndGen;
+    return randomUUID(rndGen);
+}
+
+/// ditto
+UUID randomUUID(RNG)(ref RNG randomGen)
+if (isInputRange!RNG && isIntegral!(ElementType!RNG))
+{
+    import std.random : isUniformRNG;
+    static assert(isUniformRNG!RNG, "randomGen must be a uniform RNG");
+
+    alias E = ElementEncodingType!RNG;
+    enum size_t elemSize = E.sizeof;
+    static assert(elemSize <= 16);
+    static assert(16 % elemSize == 0);
+
+    UUID u;
+    foreach (ref E e ; u.asArrayOf!E())
+    {
+        e = randomGen.front;
+        randomGen.popFront();
+    }
+
+    //set variant
+    //must be 0b10xxxxxx
+    u.data[8] &= 0b10111111;
+    u.data[8] |= 0b10000000;
+
+    //set version
+    //must be 0b0100xxxx
+    u.data[6] &= 0b01001111;
+    u.data[6] |= 0b01000000;
+
+    return u;
+}
+
+///
+@safe unittest
+{
+    import std.random : Xorshift192, unpredictableSeed;
+
+    //simple call
+    auto uuid = randomUUID();
+
+    //provide a custom RNG. Must be seeded manually.
+    Xorshift192 gen;
+
+    gen.seed(unpredictableSeed);
+    auto uuid3 = randomUUID(gen);
+}
+
+/*
+ * Original boost.uuid used Mt19937, we don't want
+ * to use anything worse than that. If Random is changed
+ * to something else, this assert and the randomUUID function
+ * have to be updated.
+ */
+@safe unittest
+{
+    import std.random : rndGen, Mt19937;
+    static assert(is(typeof(rndGen) == Mt19937));
+}
+
+@safe unittest
+{
+    import std.random : Xorshift192, unpredictableSeed;
+    //simple call
+    auto uuid = randomUUID();
+
+    //provide a custom RNG. Must be seeded manually.
+    Xorshift192 gen;
+    gen.seed(unpredictableSeed);
+    auto uuid3 = randomUUID(gen);
+
+    auto u1 = randomUUID();
+    auto u2 = randomUUID();
+    assert(u1 != u2);
+    assert(u1.variant == UUID.Variant.rfc4122);
+    assert(u1.uuidVersion == UUID.Version.randomNumberBased);
+}
+
+/**
+ * This is a less strict parser compared to the parser used in the
+ * UUID constructor. It enforces the following rules:
+ *
+ * $(UL
+ *   $(LI hex numbers are always two hexdigits([0-9a-fA-F]))
+ *   $(LI there must be exactly 16 such pairs in the input, not less, not more)
+ *   $(LI there can be exactly one dash between two hex-pairs, but not more)
+ *   $(LI there can be multiple characters enclosing the 16 hex pairs,
+ *     as long as these characters do not contain [0-9a-fA-F])
+ * )
+ *
+ * Note:
+ * Like most parsers, it consumes its argument. This means:
+ * -------------------------
+ * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46";
+ * parseUUID(s);
+ * assert(s == "");
+ * -------------------------
+ *
+ * Throws:
+ * $(LREF UUIDParsingException) if the input is invalid
+ *
+ * CTFE:
+ * This function is supported in CTFE code. Note that error messages
+ * caused by a malformed UUID parsed at compile time can be cryptic,
+ * but errors are detected and reported at compile time.
+ */
+UUID parseUUID(T)(T uuidString)
+if (isSomeString!T)
+{
+    return parseUUID(uuidString);
+}
+
+///ditto
+UUID parseUUID(Range)(ref Range uuidRange)
+if (isInputRange!Range
+    && is(Unqual!(ElementType!Range) == dchar))
+{
+    import std.ascii : isHexDigit;
+    import std.conv : ConvException, parse;
+
+    static if (isForwardRange!Range)
+        auto errorCopy = uuidRange.save;
+
+    void parserError()(size_t pos, UUIDParsingException.Reason reason, string message, Throwable next = null,
+        string file = __FILE__, size_t line = __LINE__)
+    {
+        static if (isForwardRange!Range)
+        {
+            import std.conv : to;
+            static if (isInfinite!Range)
+            {
+                throw new UUIDParsingException(to!string(take(errorCopy, pos)), pos, reason, message,
+                    next, file, line);
+            }
+            else
+            {
+                throw new UUIDParsingException(to!string(errorCopy), pos, reason, message, next, file,
+                    line);
+            }
+        }
+        else
+        {
+            throw new UUIDParsingException("", pos, reason, message, next, file, line);
+        }
+    }
+
+    static if (hasLength!Range)
+    {
+        import std.conv : to;
+        if (uuidRange.length < 32)
+        {
+            throw new UUIDParsingException(to!string(uuidRange), 0, UUIDParsingException.Reason.tooLittle,
+                "Insufficient Input");
+        }
+    }
+
+    UUID result;
+    size_t consumed;
+    size_t element = 0;
+
+    //skip garbage
+    size_t skip()()
+    {
+        size_t skipped;
+        while (!uuidRange.empty && !isHexDigit(uuidRange.front))
+        {
+            skipped++;
+            uuidRange.popFront();
+        }
+        return skipped;
+    }
+
+    consumed += skip();
+
+    if (uuidRange.empty)
+        parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input");
+
+    bool dashAllowed = false;
+
+    parseLoop: while (!uuidRange.empty)
+    {
+        immutable character = uuidRange.front;
+
+        if (character == '-')
+        {
+            if (!dashAllowed)
+                parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected '-'");
+            else
+                dashAllowed = false;
+
+            consumed++;
+        }
+        else if (!isHexDigit(character))
+        {
+            parserError(consumed, UUIDParsingException.Reason.invalidChar,
+                "Unexpected character (wanted a hexDigit)");
+        }
+        else
+        {
+            try
+            {
+                consumed += 2;
+                static if (isSomeString!Range)
+                {
+                    if (uuidRange.length < 2)
+                    {
+                        parserError(consumed, UUIDParsingException.Reason.tooLittle,
+                            "Insufficient Input");
+                    }
+                    auto part = uuidRange[0 .. 2];
+                    result.data[element++] = parse!ubyte(part, 16);
+                    uuidRange.popFront();
+                }
+                else
+                {
+                    dchar[2] copyBuf;
+                    copyBuf[0] = character;
+                    uuidRange.popFront();
+                    if (uuidRange.empty)
+                    {
+                        parserError(consumed, UUIDParsingException.Reason.tooLittle,
+                            "Insufficient Input");
+                    }
+                    copyBuf[1] = uuidRange.front;
+                    auto part = copyBuf[];
+                    result.data[element++] = parse!ubyte(part, 16);
+                }
+
+                if (element == 16)
+                {
+                    uuidRange.popFront();
+                    break parseLoop;
+                }
+
+                dashAllowed = true;
+            }
+            catch (ConvException e)
+            {
+                parserError(consumed, UUIDParsingException.Reason.invalidChar,
+                    "Couldn't parse ubyte", e);
+            }
+        }
+        uuidRange.popFront();
+    }
+    assert(element <= 16);
+
+    if (element < 16)
+        parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input");
+
+    consumed += skip();
+    if (!uuidRange.empty)
+        parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character");
+
+    return result;
+}
+
+///
+@safe unittest
+{
+    auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
+    //no dashes
+    id = parseUUID("8ab3060e2cba4f23b74cb52db3bdfb46");
+    //dashes at different positions
+    id = parseUUID("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
+    //leading / trailing characters
+    id = parseUUID("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
+    //unicode
+    id = parseUUID("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
+    //multiple trailing/leading characters
+    id = parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||");
+
+    //Can also be used in CTFE, for example as UUID literals:
+    enum ctfeID = parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+    //here parsing is done at compile time, no runtime overhead!
+}
+
+@safe pure unittest
+{
+    import std.conv : to;
+    import std.exception;
+    import std.meta;
+
+    struct TestRange(bool forward)
+    {
+        dstring input;
+
+        @property dchar front()
+        {
+            return input.front;
+        }
+
+        void popFront()
+        {
+            input.popFront();
+        }
+
+        @property bool empty()
+        {
+            return input.empty;
+        }
+
+        static if (forward)
+        {
+            @property TestRange!true save()
+            {
+                return this;
+            }
+        }
+    }
+    alias TestInputRange = TestRange!false;
+    alias TestForwardRange = TestRange!true;
+
+    assert(isInputRange!TestInputRange);
+    assert(is(ElementType!TestInputRange == dchar));
+    assert(isInputRange!TestForwardRange);
+    assert(isForwardRange!TestForwardRange);
+    assert(is(ElementType!TestForwardRange == dchar));
+
+    //Helper function for unittests - Need to pass ranges by ref
+    UUID parseHelper(T)(string input)
+    {
+        static if (is(T == TestInputRange) || is(T == TestForwardRange))
+        {
+            T range = T(to!dstring(input));
+            return parseUUID(range);
+        }
+        else
+            return parseUUID(to!T(input));
+    }
+
+    foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
+                          wchar[], const(wchar)[], immutable(wchar)[],
+                          dchar[], const(dchar)[], immutable(dchar)[],
+                          immutable(char[]), immutable(wchar[]), immutable(dchar[]),
+                          TestForwardRange, TestInputRange))
+    {
+        //Verify examples.
+        auto id = parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
+        //no dashes
+        id = parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46");
+        //dashes at different positions
+        id = parseHelper!S("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
+        //leading / trailing characters
+        id = parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
+        //unicode
+        id = parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
+        //multiple trailing/leading characters
+        id = parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||");
+        enum ctfeId = parseHelper!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+        assert(parseHelper!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46") == ctfeId);
+
+        //Test valid, working cases
+        assert(parseHelper!S("00000000-0000-0000-0000-000000000000").empty);
+        assert(parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46").data
+            == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]);
+
+        assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
+            == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+
+        //wstring / dstring
+        assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
+            == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+        assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
+            == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+
+        //Test too short UUIDS
+        auto except = collectException!UUIDParsingException(
+            parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886"));
+        assert(except && except.reason == UUIDParsingException.Reason.tooLittle);
+
+        //Test too long UUIDS
+        except = collectException!UUIDParsingException(
+            parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa"));
+        assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+        //Test too long UUIDS 2
+        except = collectException!UUIDParsingException(
+            parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a-aa"));
+        assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+        //Test dashes
+        assert(parseHelper!S("8ab3060e2cba-4f23-b74c-b52db3bdfb46")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+        assert(parseHelper!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+        assert(parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+
+        except = collectException!UUIDParsingException(
+            parseHelper!S("8-ab3060e2cba-4f23-b74c-b52db3bdfb46"));
+        assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+        //Test leading/trailing characters
+        assert(parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+        assert(parseHelper!S("{8ab3060e2cba4f23b74cb52db3bdfb46}")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+
+        //Boost test
+        auto u_increasing = UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
+            0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
+        assert(parseHelper!S("0123456789abcdef0123456789ABCDEF") == UUID(cast(ubyte[16])[0x01,
+            0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
+
+        //unicode
+        assert(parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+
+        //multiple trailing/leading characters
+        assert(parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||")
+            == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+    }
+}
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is a fully-qualified domain name
+ */
+enum dnsNamespace = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is a URL
+ */
+enum urlNamespace = UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is an ISO OID
+ */
+enum oidNamespace = UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is an X.500 DN (in DER or a text output format)
+ */
+enum x500Namespace = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Regex string to extract UUIDs from text.
+ */
+enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~
+    "-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}";
+
+///
+@safe unittest
+{
+    import std.algorithm;
+    import std.regex;
+
+    string test = "Lorem ipsum dolor sit amet, consetetur "~
+    "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~
+    "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~
+    "magna aliquyam erat, sed diam voluptua. "~
+    "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~
+    "justo duo dolores et ea rebum.";
+
+    auto r = regex(uuidRegex, "g");
+    UUID[] found;
+    foreach (c; match(test, r))
+    {
+        found ~= UUID(c.hit);
+    }
+    assert(found == [
+        UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"),
+        UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"),
+    ]);
+}
+
+/**
+ * This exception is thrown if an error occurs when parsing a UUID
+ * from a string.
+ */
+public class UUIDParsingException : Exception
+{
+    /**
+     * The reason why parsing the UUID string failed (if known)
+     */
+    enum Reason
+    {
+        unknown, ///
+        tooLittle, ///The passed in input was correct, but more input was expected.
+        tooMuch, ///The input data is too long (There's no guarantee the first part of the data is valid)
+        invalidChar, ///Encountered an invalid character
+
+    }
+    ///ditto
+    Reason reason;
+    ///The original input string which should have been parsed.
+    string input;
+    ///The position in the input string where the error occurred.
+    size_t position;
+
+    private this(string input, size_t pos, Reason why = Reason.unknown, string msg = "",
+        Throwable next = null, string file = __FILE__, size_t line = __LINE__) pure @trusted
+    {
+        import std.array : replace;
+        import std.format : format;
+        this.input = input;
+        this.position = pos;
+        this.reason = why;
+        string message = format("An error occured in the UUID parser: %s\n" ~
+          " * Input:\t'%s'\n * Position:\t%s", msg, replace(replace(input,
+          "\r", "\\r"), "\n", "\\n"), pos);
+        super(message, file, line, next);
+    }
+}
+
+///
+@safe unittest
+{
+    import std.exception : collectException;
+
+    const inputUUID = "this-is-an-invalid-uuid";
+    auto ex = collectException!UUIDParsingException(UUID(inputUUID));
+    assert(ex !is null); // check that exception was thrown
+    assert(ex.input == inputUUID);
+    assert(ex.position == 0);
+    assert(ex.reason == UUIDParsingException.Reason.tooLittle);
+}
+
+@safe unittest
+{
+    auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch);
+    assert(ex.input == "foo");
+    assert(ex.position == 10);
+    assert(ex.reason == UUIDParsingException.Reason.tooMuch);
+}