Add cUUID class (#3871)
This commit is contained in:
committed by
Alexander Harkness
parent
86d52c3e17
commit
f4f2fc7c3d
283
src/UUID.cpp
Normal file
283
src/UUID.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
// UUID.h
|
||||
|
||||
// Defines the cUUID class representing a Universally Unique Identifier
|
||||
|
||||
#include "Globals.h"
|
||||
#include "UUID.h"
|
||||
|
||||
#include "polarssl/md5.h"
|
||||
|
||||
|
||||
/** UUID normalised in textual form. */
|
||||
struct sShortUUID
|
||||
{
|
||||
char Data[32]{};
|
||||
bool IsValid = false;
|
||||
};
|
||||
|
||||
/** Returns the given UUID in shortened form with IsValid indicating success.
|
||||
Doesn't check digits are hexadecimal but does check dashes if long form. */
|
||||
static sShortUUID ShortenUUID(const AString & a_StringUUID)
|
||||
{
|
||||
sShortUUID UUID;
|
||||
switch (a_StringUUID.size())
|
||||
{
|
||||
case 32:
|
||||
{
|
||||
// Already a short UUID
|
||||
std::memcpy(UUID.Data, a_StringUUID.data(), 32);
|
||||
UUID.IsValid = true;
|
||||
break;
|
||||
}
|
||||
case 36:
|
||||
{
|
||||
// Long UUID, confirm dashed
|
||||
if (
|
||||
(a_StringUUID[ 8] != '-') ||
|
||||
(a_StringUUID[13] != '-') ||
|
||||
(a_StringUUID[18] != '-') ||
|
||||
(a_StringUUID[23] != '-')
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy everying but the dashes from the string
|
||||
std::memcpy(UUID.Data, a_StringUUID.data(), 8);
|
||||
std::memcpy(UUID.Data + 8, a_StringUUID.data() + 9, 4);
|
||||
std::memcpy(UUID.Data + 12, a_StringUUID.data() + 14, 4);
|
||||
std::memcpy(UUID.Data + 16, a_StringUUID.data() + 19, 4);
|
||||
std::memcpy(UUID.Data + 20, a_StringUUID.data() + 24, 12);
|
||||
UUID.IsValid = true;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return UUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns the integer value of the hex digit or 0xff if invalid. */
|
||||
static Byte FromHexDigit(char a_Hex)
|
||||
{
|
||||
if (('0' <= a_Hex) && (a_Hex <= '9'))
|
||||
{
|
||||
return static_cast<Byte>(a_Hex - '0');
|
||||
}
|
||||
if (('a' <= a_Hex) && (a_Hex <= 'f'))
|
||||
{
|
||||
return static_cast<Byte>(a_Hex - 'a');
|
||||
}
|
||||
if (('A' <= a_Hex) && (a_Hex <= 'F'))
|
||||
{
|
||||
return static_cast<Byte>(a_Hex - 'A');
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** From a number in the range [0, 16), returns the corresponding hex digit in lowercase. */
|
||||
static char ToHexDigit(UInt8 a_Nibble)
|
||||
{
|
||||
ASSERT((a_Nibble & 0xf0) == 0);
|
||||
return static_cast<char>(
|
||||
(a_Nibble < 10) ?
|
||||
('0' + a_Nibble) :
|
||||
('a' + (a_Nibble - 10))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cUUID:
|
||||
|
||||
bool cUUID::FromString(const AString & a_StringUUID)
|
||||
{
|
||||
sShortUUID Norm = ShortenUUID(a_StringUUID);
|
||||
if (!Norm.IsValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<Byte, 16> ParsedUUID{{0}};
|
||||
for (size_t i = 0; i != m_UUID.size(); ++i)
|
||||
{
|
||||
Byte HighNibble = FromHexDigit(Norm.Data[2 * i ]);
|
||||
Byte LowNibble = FromHexDigit(Norm.Data[2 * i + 1]);
|
||||
if ((HighNibble > 0x0f) || (LowNibble > 0x0f))
|
||||
{
|
||||
// Invalid hex digit
|
||||
return false;
|
||||
}
|
||||
|
||||
ParsedUUID[i] = static_cast<Byte>((HighNibble << 4) | LowNibble);
|
||||
}
|
||||
|
||||
// Parsed successfully
|
||||
m_UUID = ParsedUUID;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cUUID::ToShortString() const
|
||||
{
|
||||
AString ShortString(32, '\0');
|
||||
for (size_t i = 0; i != m_UUID.size(); ++i)
|
||||
{
|
||||
Byte HighNibble = (m_UUID[i] >> 4) & 0x0f;
|
||||
Byte LowNibble = m_UUID[i] & 0x0f;
|
||||
|
||||
ShortString[2 * i ] = ToHexDigit(HighNibble);
|
||||
ShortString[2 * i + 1] = ToHexDigit(LowNibble);
|
||||
}
|
||||
return ShortString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cUUID::ToLongString() const
|
||||
{
|
||||
AString LongString = ToShortString();
|
||||
LongString.reserve(36);
|
||||
|
||||
// Convert to long form by inserting the dashes
|
||||
auto First = LongString.begin();
|
||||
LongString.insert(First + 8, '-');
|
||||
LongString.insert(First + 13, '-');
|
||||
LongString.insert(First + 18, '-');
|
||||
LongString.insert(First + 23, '-');
|
||||
|
||||
return LongString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UInt8 cUUID::Version() const
|
||||
{
|
||||
return static_cast<UInt8>((m_UUID[6] >> 4) & 0x0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UInt8 cUUID::Variant() const
|
||||
{
|
||||
const Byte VariantBits = static_cast<Byte>((m_UUID[9] >> 5) & 0x07);
|
||||
|
||||
/* Variant bits format:
|
||||
bits | variant | Description
|
||||
-----|---------|----------------------
|
||||
0xx | 0 | Obsolete
|
||||
10x | 1 | Standard UUID
|
||||
110 | 2 | Microsoft Legacy GUID
|
||||
111 | 3 | Reserved
|
||||
*/
|
||||
|
||||
if ((VariantBits & 0x04) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if ((VariantBits & 0x02) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if ((VariantBits & 0x01) == 0)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::array<Byte, 16> cUUID::ToRaw() const
|
||||
{
|
||||
std::array<Byte, 16> Raw(m_UUID);
|
||||
if (Variant() == 2)
|
||||
{
|
||||
// Convert to microsoft mixed-endian format
|
||||
// First 3 components are host-endian, last 2 are network
|
||||
auto First = reinterpret_cast<UInt32 *>(Raw.data());
|
||||
*First = ntohl(*First);
|
||||
|
||||
auto Second = reinterpret_cast<UInt16 *>(&Raw[4]);
|
||||
*Second = ntohs(*Second);
|
||||
|
||||
auto Third = Second + 1;
|
||||
*Third = ntohs(*Third);
|
||||
}
|
||||
|
||||
return Raw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cUUID::FromRaw(const std::array<Byte, 16> & a_Raw)
|
||||
{
|
||||
m_UUID = a_Raw;
|
||||
if (Variant() != 2)
|
||||
{
|
||||
// Standard big-endian formats
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert from microsoft mixed-endian format
|
||||
// First 3 components are host-endian, last 2 are network
|
||||
auto First = reinterpret_cast<UInt32 *>(m_UUID.data());
|
||||
*First = htonl(*First);
|
||||
|
||||
auto Second = reinterpret_cast<UInt16 *>(&m_UUID[4]);
|
||||
*Second = htons(*Second);
|
||||
|
||||
auto Third = Second + 1;
|
||||
*Third = htons(*Third);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cUUID cUUID::GenerateVersion3(const AString & a_Name)
|
||||
{
|
||||
cUUID UUID;
|
||||
// Generate an md5 checksum, and use it as base for the ID:
|
||||
const Byte * ByteString = reinterpret_cast<const Byte *>(a_Name.data());
|
||||
md5(ByteString, a_Name.length(), UUID.m_UUID.data());
|
||||
|
||||
// Insert version number
|
||||
UUID.m_UUID[6] = (UUID.m_UUID[6] & 0x0f) | 0x30;
|
||||
|
||||
/* Insert variant number
|
||||
Note that by using 1000 instead of 10xx we are losing 2 bits
|
||||
but this is needed for compatibility with the old string uuid generator */
|
||||
UUID.m_UUID[8] = (UUID.m_UUID[8] & 0x0f) | 0x80;
|
||||
|
||||
return UUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user