aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/uuid.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std/uuid.d
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'libphobos/src/std/uuid.d')
-rw-r--r--libphobos/src/std/uuid.d1731
1 files changed, 1731 insertions, 0 deletions
diff --git a/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d
new file mode 100644
index 0000000..c804e8e
--- /dev/null
+++ b/libphobos/src/std/uuid.d
@@ -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);
+}