diff options
Diffstat (limited to 'src/UUID.cpp')
-rw-r--r-- | src/UUID.cpp | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/UUID.cpp b/src/UUID.cpp new file mode 100644 index 000000000..e150b3603 --- /dev/null +++ b/src/UUID.cpp @@ -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; +} + + + + |