phobos/std/uuid.d
2012-07-01 14:44:36 +02:00

1581 lines
53 KiB
D

/**
<script type="text/javascript">inhibitQuickIndex = 1</script>
$(BOOKTABLE ,
$(TR $(TH Category) $(TH Functions)
)
$(TR $(TDNW Parsing UUIDs) $(TD $(MYREF parseUUID) $(MYREF
UUID(string)) $(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) )
)
)
* 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.
*
* 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: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>
* Authors: Johannes Pfau
* Source: $(PHOBOSSRC std/_uuid.d)
*
* Macros:
* MYREF = <font face='Consolas, "Bitstream Vera Sans Mono", "Andale Mono", Monaco, "DejaVu Sans Mono", "Lucida Console", monospace'><a href="#$1">$1</a>&nbsp;</font>
* MYREF2 = <font face='Consolas, "Bitstream Vera Sans Mono", "Andale Mono", Monaco, "DejaVu Sans Mono", "Lucida Console", monospace'><a href="#$2">$1</a>&nbsp;</font>
* 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.algorithm, std.array, std.ascii;
import std.conv, std.random, std.range, std.string, std.traits, std.md5;
//import std.crypto.hash.sha;
//import std.crypto.hash.md5;
/**
*
*/
public struct UUID
{
private:
@safe nothrow pure char toChar(size_t i) const
{
if(i <= 9)
return cast(char)('0' + i);
else
return cast(char)('a' + (i-10));
}
@safe nothrow pure 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;
}
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");
}
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). This has nothing
* to do 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.
*/
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.
*
* Examples:
* -------------------------
* ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
* auto tmp = UUID(data);
* assert(tmp.data == data);
* -------------------------
*/
@safe pure nothrow this()(ubyte[16] uuidData)
{
data = uuidData;
}
unittest
{
ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
auto tmp = UUID(data);
assert(tmp.data == data);
enum UUID ctfeID = UUID(cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15]);
assert(ctfeID == tmp);
}
/+
Not Working! DMD interprets the ubyte literals as ints, then complains the int can't
be converted to ubyte!
/**
* 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.
*
* Examples:
* -------------------------
* 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 pure nothrow this()(ubyte[16] uuidData...)
{
data = uuidData;
}
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!
* -------------------------
*
* BUGS: Could be pure, but this depends on parse!(string, 16).
*/
@trusted this(T)(T[] uuid) if(isSomeChar!(Unqual!T))
{
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
{
data2[element++] = parse!ubyte(uuid[pairStart .. pos+1], 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;
}
unittest
{
import std.exception;
import std.typetuple;
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}
*
* Examples:
* -------------------------
* UUID id;
* assert(id.empty);
* id = UUID("00000000-0000-0000-0000-000000000001");
* assert(!id.empty);
* -------------------------
*/
@trusted pure nothrow @property bool empty() const
{
if(__ctfe)
return find!"a!=0"(data[]).empty; //simple
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");
}
unittest
{
UUID id;
assert(id.empty);
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). This has nothing
* to do with $(XREF _variant, _Variant). The type of this property is
* $(MYREF3 std.uuid.UUID.Variant, _Variant).
*
* See_Also:
* $(MYREF3 UUID.Variant, Variant)
*
* Examples:
* ------------------------
* assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
* == UUID.Variant.rfc4122);
* ------------------------
*/
@safe pure nothrow @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;
}
}
//Verify Example.
unittest
{
assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
== UUID.Variant.rfc4122);
}
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)
*
* Examples:
* ----------------------------
* assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
* == UUID.Version.randomNumberBased);
* ----------------------------
*/
@safe pure nothrow @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;
}
//Verify Example.
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.
*
* Note: linear complexity
*
* Examples:
* ----------------------------
* UUID u1;
* auto u2 = UUID(cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
* u1.swap(u2);
*
* assert(u1.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
* assert(u2.data == cast(ubyte[16])[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
* ----------------------------
*/
@safe nothrow void swap(ref UUID rhs)
{
std.algorithm.swap(this.data, rhs.data);
}
unittest
{
UUID u1;
auto u2 = UUID(cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
u1.swap(u2);
auto values1 = cast(ubyte[16])[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
auto values2 = cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
assert(u1.data == values2);
assert(u2.data == values1);
u1.swap(u2);
assert(u2.data == values2);
assert(u1.data == values1);
}
/**
* All of the standard numeric operators are defined for
* the UUID struct.
*
* Examples:
* -------------------------
* //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);
* -------------------------
*/
@safe pure nothrow bool opEquals(const UUID s) const
{
return s.data == this.data;
}
/**
* ditto
*/
@safe pure nothrow bool opEquals(ref const UUID s) const
{
return s.data == this.data;
}
/**
* ditto
*/
@safe pure nothrow int opCmp(ref const UUID s) const
{
return cmp(this.data[], s.data[]);
}
/**
* ditto
*/
@safe pure nothrow int opCmp(const UUID s) const
{
return cmp(this.data[], s.data[]);
}
/**
* ditto
*/
@safe pure nothrow 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.
*
* Examples:
* ----------------------------------
* auto id = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
* assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
* ----------------------------------
*/
void toString(scope void delegate(const(char)[]) sink) const
{
sink(_toString());
}
///ditto
@safe pure nothrow string toString() const
{
//@@@BUG@@@ workaround for bugzilla 5700
try
return _toString().idup;
catch(Exception)
assert(0, "It should be impossible for idup to throw.");
}
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");
}
}
unittest
{
assert(UUID.init.empty);
}
/**
* 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 currently not supported as $(D std.md5) doesn't work in CTFE.
*
* Examples:
* ---------------------------------------
* //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);
* ---------------------------------------
*
* 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!).
*
* BUGS: Could be pure, but this depends on the MD5 hash code.
*/
@safe UUID md5UUID(const(char[]) name, const UUID namespace = UUID.init)
{
return md5UUID(cast(const(ubyte[]))name, namespace);
}
/**
* ditto
*/
@trusted UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init)
{
MD5_CTX 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.update(namespace.data);
hash.update(data);
UUID u;
hash.finish(u.data);
//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
{
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);
}
/+
FIXME: need 3 more unittests: have to check simlpeID.data and id.data (to make sure we have the same
result on all systems, especially considering endianess). id.data needs to be checked for the ubyte
case as well
/**
* 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:
* As long as Phobos has no standard SHA-1 implementation, CTFE support
* for this function can't be guaranteed. CTFE support will depend on
* whether the SHA-1 implementation supports CTFE.
*
* Examples:
* ---------------------------------------
* //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);
* ---------------------------------------
*
* 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!).
*
* BUGS: Could be pure, but this depends on the SHA-1 hash code.
*/
@trusted UUID sha1UUID(const(char[]) name, const UUID namespace = UUID.init)
{
return sha1UUID(cast(const(ubyte[]))name, namespace);
}
/**
* ditto
*/
@trusted UUID sha1UUID(const(ubyte[]) data, const UUID namespace = UUID.init)
{
SHA1 sha = new SHA1();
/*
* 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);
UUID u;
sha.finish(u.data[]);
//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
{
auto simpleID = sha1UUID("test.uuid.any.string");
auto namespace = sha1UUID("my.app");
auto id = sha1UUID("some-description", namespace);
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);
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.
*
* Examples:
* ------------------------------------------
* //simple call
* auto uuid = randomUUID();
*
* //provide a custom RNG. Must be seeded manually.
* Xorshift192 gen;
*
* gen.seed(unpredictableSeed());
* auto uuid3 = randomUUID(gen);
* ------------------------------------------
*/
@trusted UUID randomUUID()()
{
return randomUUID(rndGen);
}
/*
* 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.
*/
static assert(is(typeof(rndGen) == Mt19937));
/**
* ditto
*/
UUID randomUUID(RNG)(ref RNG randomGen) if(isUniformRNG!(RNG) &&
isIntegral!(typeof(RNG.front)))
{
enum size_t elemSize = typeof(RNG.front).sizeof;
static assert(elemSize <= 16);
UUID u;
foreach(size_t i; iota(cast(size_t)0, cast(size_t)16, elemSize))
{
randomGen.popFront();
immutable randomValue = randomGen.front;
u.data[i .. i + elemSize] = *cast(ubyte[elemSize]*)&randomValue;
}
//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;
}
unittest
{
import std.random;
//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])
* )
*
* 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.
*
* Examples:
* -------------------------
* 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!
* -------------------------
*
* BUGS: Could be pure, but this depends on parse!(string, 16).
*/
@trusted 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))
{
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)
{
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)
{
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");
}
result.data[element++] = parse!ubyte(uuidRange[0 .. 2], 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;
result.data[element++] = parse!ubyte(copyBuf[], 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
{
import std.exception;
import std.typetuple;
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 TestRange!false TestInputRange;
alias TestRange!true TestForwardRange;
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.
*
* Examples:
* -------------------
* 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);
* }
*
* writeln(found);
* -------------------
*/
enum uuidRegex = r"[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.length == 2);
assert(canFind(found, UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8")));
assert(canFind(found, 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
{
public:
/**
* 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__)
{
input = input;
position = pos;
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);
}
}