Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1599 lines (1410 sloc) 53.103 kb
/**
* 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.
*
* Examples:
* ------------------------
* 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);
*
* ------------------------
* 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: $(WEB 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;
import std.range.primitives;
import std.traits;
/**
*
*/
public struct UUID
{
import std.typetuple : allSatisfy;
import std.traits : isIntegral;
private:
@safe pure nothrow char toChar(size_t i) const
{
if(i <= 9)
return cast(char)('0' + i);
else
return cast(char)('a' + (i-10));
}
@safe pure nothrow char[36] _toString() const
{
char[36] result;
size_t i=0;
foreach(entry; this.data)
{
const size_t hi = (entry >> 4) & 0x0F;
result[i++] = toChar(hi);
const size_t lo = (entry) & 0x0F;
result[i++] = toChar(lo);
if (i == 8 || i == 13 || i == 18 || i == 23)
{
result[i++] = '-';
}
}
return result;
}
@safe pure 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 $(XREF _variant, _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
}
/**
* 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.
*
* Examples:
* -----------------------------------------------
* auto rawData = uuid.data; //get data
* rawData[0] = 1; //modify
* uuid.data = rawData; //set data
* uuid.data[1] = 2; //modify directly
* -----------------------------------------------
*/
ubyte[16] data;
/*
* 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);
}
}
///
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]);
}
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)
*
* Examples:
* -------------------------
* 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!
* -------------------------
*/
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");
}
ubyte[16] data2; //ctfe bug
size_t element = 0, pairStart = -1;
foreach(pos, dchar character; uuid)
{
if(pos == 8 || pos == 13 || pos == 18 || pos == 23)
{
if(character != '-')
{
throw new UUIDParsingException(to!string(uuid), pos,
UUIDParsingException.Reason.invalidChar, "Expected '-'");
}
}
else
{
if(pairStart == -1)
pairStart = pos;
else
{
try
{
auto part = uuid[pairStart .. pos+1];
data2[element++] = parse!ubyte(part, 16);
pairStart = -1;
}
catch(Exception e)
{
throw new UUIDParsingException(to!string(uuid), pos,
UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte", e);
}
}
}
}
assert(element <= 16);
if(element < 16)
{
throw new UUIDParsingException(to!string(uuid), 0,
UUIDParsingException.Reason.tooLittle, "Insufficient Input");
}
this.data = data2;
}
@safe pure unittest
{
import std.exception;
import std.typetuple;
import std.conv : to;
foreach(S; TypeTuple!(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 $(XREF _variant, _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
auto 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);
}
pure unittest
{
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
auto 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;
}
///
unittest
{
assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
== UUID.Version.randomNumberBased);
}
unittest
{
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)
{
auto bck = data;
data = rhs.data;
rhs.data = bck;
}
///
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 s.data == this.data;
}
///
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 s.data == this.data;
}
/**
* ditto
*/
@safe pure nothrow @nogc int opCmp(in UUID s) const
{
import std.algorithm : cmp;
return cmp(this.data[], s.data[]);
}
/**
* ditto
*/
@safe pure nothrow @nogc int opCmp(ref in UUID s) const
{
import std.algorithm : cmp;
return cmp(this.data[], s.data[]);
}
/**
* ditto
*/
@safe pure nothrow @nogc size_t toHash() const
{
size_t seed = 0;
foreach(entry; this.data)
seed ^= cast(size_t)entry + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
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());
}
/**
* Return the UUID as a string in the canonical form.
*/
void toString(scope void delegate(const(char)[]) sink) const
{
sink(_toString());
}
///ditto
@safe pure nothrow string toString() const
{
return _toString().idup;
}
///
@safe pure unittest
{
immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
auto id = UUID(str);
assert(id.toString() == str);
}
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;
}
///
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;
}
///
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.
*
* CTFE:
* This function is not supported at compile time.
*
*/
@safe UUID randomUUID()
{
import std.random : rndGen;
return randomUUID(rndGen);
}
/**
* ditto
*/
/**
* Params:
* randomGen = uniform RNG
* See_Also: $(XREF random, isUniformRNG)
*/
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.conv : ConvException, parse;
import std.ascii : isHexDigit;
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)
{
dchar 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;
}
///
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.exception;
import std.typetuple;
import std.conv : to;
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; TypeTuple!(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}";
///
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);
}
}
///
unittest
{
auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch);
assert(ex.input == "foo");
assert(ex.position == 10);
assert(ex.reason == UUIDParsingException.Reason.tooMuch);
}
Jump to Line
Something went wrong with that request. Please try again.