From 60944ec71e35242f3bde04e402d741e772690b16 Mon Sep 17 00:00:00 2001 From: Reece Date: Sun, 27 Feb 2022 07:49:13 +0000 Subject: [PATCH] [*] Reformat because 3 space indents were doing my head in [*] Obliterate string streams because why the hell would we EVER want the overhead of streams and arbitrary allocs when merely stringifying a uuid --- include/uuid.h | 1444 ++++++++++++++++++++++-------------------------- 1 file changed, 671 insertions(+), 773 deletions(-) diff --git a/include/uuid.h b/include/uuid.h index e8b5fee..63ac373 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,848 +2,746 @@ #include #include -#include -#include #include #include #include #include -#include -#include #include -#include #include -#include - -#ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef UUID_SYSTEM_GENERATOR -#include -#endif - -#include -#include -#include -#pragma comment(lib, "IPHLPAPI.lib") - -#elif defined(__linux__) || defined(__unix__) - -#ifdef UUID_SYSTEM_GENERATOR -#include -#endif - -#elif defined(__APPLE__) - -#ifdef UUID_SYSTEM_GENERATOR -#include -#endif - -#endif namespace uuids { - namespace detail - { - template - constexpr inline unsigned char hex2char(TChar const ch) - { - if (ch >= static_cast('0') && ch <= static_cast('9')) - return ch - static_cast('0'); - if (ch >= static_cast('a') && ch <= static_cast('f')) - return 10 + ch - static_cast('a'); - if (ch >= static_cast('A') && ch <= static_cast('F')) - return 10 + ch - static_cast('A'); - return 0; - } - - template - constexpr inline bool is_hex(TChar const ch) - { - return - (ch >= static_cast('0') && ch <= static_cast('9')) || - (ch >= static_cast('a') && ch <= static_cast('f')) || - (ch >= static_cast('A') && ch <= static_cast('F')); - } - - template - constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) - { - return (hex2char(a) << 4) | hex2char(b); - } - - class sha1 - { - public: - using digest32_t = uint32_t[5]; - using digest8_t = uint8_t[20]; - - static constexpr unsigned int block_bytes = 64; - - inline static uint32_t left_rotate(uint32_t value, size_t const count) - { - return (value << count) ^ (value >> (32 - count)); - } - - sha1() { reset(); } - - void reset() - { - m_digest[0] = 0x67452301; - m_digest[1] = 0xEFCDAB89; - m_digest[2] = 0x98BADCFE; - m_digest[3] = 0x10325476; - m_digest[4] = 0xC3D2E1F0; - m_blockByteIndex = 0; - m_byteCount = 0; - } - - void process_byte(uint8_t octet) - { - this->m_block[this->m_blockByteIndex++] = octet; - ++this->m_byteCount; - if (m_blockByteIndex == block_bytes) - { - this->m_blockByteIndex = 0; - process_block(); - } - } - - void process_block(void const * const start, void const * const end) - { - const uint8_t* begin = static_cast(start); - const uint8_t* finish = static_cast(end); - while (begin != finish) - { - process_byte(*begin); - begin++; - } - } - - void process_bytes(void const * const data, size_t const len) - { - const uint8_t* block = static_cast(data); - process_block(block, block + len); - } - - uint32_t const * get_digest(digest32_t digest) - { - size_t const bitCount = this->m_byteCount * 8; - process_byte(0x80); - if (this->m_blockByteIndex > 56) { - while (m_blockByteIndex != 0) { - process_byte(0); - } - while (m_blockByteIndex < 56) { - process_byte(0); - } - } - else { - while (m_blockByteIndex < 56) { - process_byte(0); - } - } - process_byte(0); - process_byte(0); - process_byte(0); - process_byte(0); - process_byte(static_cast((bitCount >> 24) & 0xFF)); - process_byte(static_cast((bitCount >> 16) & 0xFF)); - process_byte(static_cast((bitCount >> 8) & 0xFF)); - process_byte(static_cast((bitCount) & 0xFF)); - - memcpy(digest, m_digest, 5 * sizeof(uint32_t)); - return digest; - } - - uint8_t const * get_digest_bytes(digest8_t digest) - { - digest32_t d32; - get_digest(d32); - size_t di = 0; - digest[di++] = ((d32[0] >> 24) & 0xFF); - digest[di++] = ((d32[0] >> 16) & 0xFF); - digest[di++] = ((d32[0] >> 8) & 0xFF); - digest[di++] = ((d32[0]) & 0xFF); - - digest[di++] = ((d32[1] >> 24) & 0xFF); - digest[di++] = ((d32[1] >> 16) & 0xFF); - digest[di++] = ((d32[1] >> 8) & 0xFF); - digest[di++] = ((d32[1]) & 0xFF); - - digest[di++] = ((d32[2] >> 24) & 0xFF); - digest[di++] = ((d32[2] >> 16) & 0xFF); - digest[di++] = ((d32[2] >> 8) & 0xFF); - digest[di++] = ((d32[2]) & 0xFF); - - digest[di++] = ((d32[3] >> 24) & 0xFF); - digest[di++] = ((d32[3] >> 16) & 0xFF); - digest[di++] = ((d32[3] >> 8) & 0xFF); - digest[di++] = ((d32[3]) & 0xFF); - - digest[di++] = ((d32[4] >> 24) & 0xFF); - digest[di++] = ((d32[4] >> 16) & 0xFF); - digest[di++] = ((d32[4] >> 8) & 0xFF); - digest[di++] = ((d32[4]) & 0xFF); - - return digest; - } - - private: - void process_block() - { - uint32_t w[80]; - for (size_t i = 0; i < 16; i++) { - w[i] = (m_block[i * 4 + 0] << 24); - w[i] |= (m_block[i * 4 + 1] << 16); - w[i] |= (m_block[i * 4 + 2] << 8); - w[i] |= (m_block[i * 4 + 3]); - } - for (size_t i = 16; i < 80; i++) { - w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); - } - - uint32_t a = m_digest[0]; - uint32_t b = m_digest[1]; - uint32_t c = m_digest[2]; - uint32_t d = m_digest[3]; - uint32_t e = m_digest[4]; - - for (std::size_t i = 0; i < 80; ++i) - { - uint32_t f = 0; - uint32_t k = 0; - - if (i < 20) { - f = (b & c) | (~b & d); - k = 0x5A827999; - } - else if (i < 40) { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } - else if (i < 60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } - else { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; - e = d; - d = c; - c = left_rotate(b, 30); - b = a; - a = temp; - } - - m_digest[0] += a; - m_digest[1] += b; - m_digest[2] += c; - m_digest[3] += d; - m_digest[4] += e; - } - - private: - digest32_t m_digest; - uint8_t m_block[64]; - size_t m_blockByteIndex; - size_t m_byteCount; - }; - - static std::mt19937 clock_gen(std::random_device{}()); - static std::uniform_int_distribution clock_dis{ -32768, 32767 }; - static std::atomic_short clock_sequence = clock_dis(clock_gen); - } - - // -------------------------------------------------------------------------------------------------------------------------- - // UUID format https://tools.ietf.org/html/rfc4122 - // -------------------------------------------------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------------------------------------------------- - // Field NDR Data Type Octet # Note - // -------------------------------------------------------------------------------------------------------------------------- - // time_low unsigned long 0 - 3 The low field of the timestamp. - // time_mid unsigned short 4 - 5 The middle field of the timestamp. - // time_hi_and_version unsigned short 6 - 7 The high field of the timestamp multiplexed with the version number. - // clock_seq_hi_and_reserved unsigned small 8 The high field of the clock sequence multiplexed with the variant. - // clock_seq_low unsigned small 9 The low field of the clock sequence. - // node character 10 - 15 The spatially unique node identifier. - // -------------------------------------------------------------------------------------------------------------------------- - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | time_low | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | time_mid | time_hi_and_version | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |clk_seq_hi_res | clk_seq_low | node (0-1) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | node (2-5) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - // -------------------------------------------------------------------------------------------------------------------------- - // enumerations - // -------------------------------------------------------------------------------------------------------------------------- - - // indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx - enum class uuid_variant - { - // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) - // N bit pattern: 0xxx - // > the first 6 octets of the UUID are a 48-bit timestamp (the number of 4 microsecond units of time since 1 Jan 1980 UTC); - // > the next 2 octets are reserved; - // > the next octet is the "address family"; - // > the final 7 octets are a 56-bit host ID in the form specified by the address family - ncs, - - // RFC 4122/DCE 1.1 - // N bit pattern: 10xx - // > big-endian byte order - rfc, - - // Microsoft Corporation backward compatibility - // N bit pattern: 110x - // > little endian byte order - // > formely used in the Component Object Model (COM) library - microsoft, - - // reserved for possible future definition - // N bit pattern: 111x - reserved - }; - - // indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx - enum class uuid_version - { - none = 0, // only possible for nil or invalid uuids - time_based = 1, // The time-based version specified in RFC 4122 - dce_security = 2, // DCE Security version, with embedded POSIX UIDs. - name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing - random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 - name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing - }; - - // -------------------------------------------------------------------------------------------------------------------------- - // uuid class - // -------------------------------------------------------------------------------------------------------------------------- - class uuid - { - public: - using value_type = uint8_t; - - constexpr uuid() noexcept : data({}) {}; - - //uuid(value_type(&arr)[16]) noexcept - //{ - // std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); - //} - - uuid(std::array const & arr) noexcept - { - std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); - } - - explicit uuid(value_type * bytes) - { - std::copy(bytes, bytes + 16, std::begin(data)); - } - - template - explicit uuid(ForwardIterator first, ForwardIterator last) - { - if (std::distance(first, last) == 16) - std::copy(first, last, std::begin(data)); - } - - constexpr uuid_variant variant() const noexcept - { - if ((data[8] & 0x80) == 0x00) - return uuid_variant::ncs; - else if ((data[8] & 0xC0) == 0x80) - return uuid_variant::rfc; - else if ((data[8] & 0xE0) == 0xC0) - return uuid_variant::microsoft; - else - return uuid_variant::reserved; - } - - constexpr uuid_version version() const noexcept - { - if ((data[6] & 0xF0) == 0x10) - return uuid_version::time_based; - else if ((data[6] & 0xF0) == 0x20) - return uuid_version::dce_security; - else if ((data[6] & 0xF0) == 0x30) - return uuid_version::name_based_md5; - else if ((data[6] & 0xF0) == 0x40) - return uuid_version::random_number_based; - else if ((data[6] & 0xF0) == 0x50) - return uuid_version::name_based_sha1; - else - return uuid_version::none; - } - - constexpr bool is_nil() const noexcept - { - for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false; - return true; - } - - void swap(uuid & other) noexcept - { - data.swap(other.data); - } - - inline const std::array & as_array() const - { - return data; - } - - inline const std::uint8_t * as_data() const - { - return reinterpret_cast(data.data()); - } - - template - static bool is_valid_uuid(CharT const * str) noexcept - { - CharT digit = 0; - bool firstDigit = true; - int hasBraces = 0; - size_t index = 0; - size_t size = 0; - if constexpr(std::is_same_v) - size = strlen(str); - else - size = wcslen(str); - - if (str == nullptr || size == 0) - return false; - - if (str[0] == static_cast('{')) - hasBraces = 1; - if (hasBraces && str[size - 1] != static_cast('}')) - return false; - - for (size_t i = hasBraces; i < size - hasBraces; ++i) - { - if (str[i] == static_cast('-')) continue; - - if (index >= 16 || !detail::is_hex(str[i])) - { - return false; - } - - if (firstDigit) - { - firstDigit = false; - } + namespace detail + { + constexpr inline char NibbleToChar(unsigned char nib) + { + if (nib < 10) + return '0' + nib; else + return 'A' + (nib - 10); + } + + template + constexpr inline void ByteToHex(unsigned char val, TChar *hex) + { + unsigned char lowNibble = val & 0xF; + unsigned char hiNibble = (val >> 4) & 0xF; + + hex[0] = NibbleToChar(hiNibble); + hex[1] = NibbleToChar(lowNibble); + } + + + template + constexpr inline unsigned char hex2char(TChar const ch) + { + if (ch >= static_cast('0') && ch <= static_cast('9')) + return ch - static_cast('0'); + if (ch >= static_cast('a') && ch <= static_cast('f')) + return 10 + ch - static_cast('a'); + if (ch >= static_cast('A') && ch <= static_cast('F')) + return 10 + ch - static_cast('A'); + return 0; + } + + template + constexpr inline bool is_hex(TChar const ch) + { + return + (ch >= static_cast('0') && ch <= static_cast('9')) || + (ch >= static_cast('a') && ch <= static_cast('f')) || + (ch >= static_cast('A') && ch <= static_cast('F')); + } + + template + constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) + { + return (hex2char(a) << 4) | hex2char(b); + } + + class sha1 + { + public: + using digest32_t = uint32_t[5]; + using digest8_t = uint8_t[20]; + + static constexpr unsigned int block_bytes = 64; + + inline static uint32_t left_rotate(uint32_t value, size_t const count) { - index++; - firstDigit = true; - } - } - - if (index < 16) - { - return false; - } - - return true; - } - - template, - class Allocator = std::allocator> - static bool is_valid_uuid(std::basic_string const & str) noexcept - { - return is_valid_uuid(str.c_str()); - } - - template - static std::optional from_string(CharT const * str) noexcept - { - CharT digit = 0; - bool firstDigit = true; - int hasBraces = 0; - size_t index = 0; - size_t size = 0; - if constexpr(std::is_same_v) - size = strlen(str); - else - size = wcslen(str); - - std::array data{ { 0 } }; - - if (str == nullptr || size == 0) return {}; - - if (str[0] == static_cast('{')) - hasBraces = 1; - if (hasBraces && str[size - 1] != static_cast('}')) - return {}; - - for (size_t i = hasBraces; i < size - hasBraces; ++i) - { - if (str[i] == static_cast('-')) continue; - - if (index >= 16 || !detail::is_hex(str[i])) - { - return {}; + return (value << count) ^ (value >> (32 - count)); } - if (firstDigit) + sha1() { - digit = str[i]; - firstDigit = false; + reset(); } + + void reset() + { + m_digest[0] = 0x67452301; + m_digest[1] = 0xEFCDAB89; + m_digest[2] = 0x98BADCFE; + m_digest[3] = 0x10325476; + m_digest[4] = 0xC3D2E1F0; + m_blockByteIndex = 0; + m_byteCount = 0; + } + + void process_byte(uint8_t octet) + { + this->m_block[this->m_blockByteIndex++] = octet; + ++this->m_byteCount; + if (m_blockByteIndex == block_bytes) + { + this->m_blockByteIndex = 0; + process_block(); + } + } + + void process_block(void const *const start, void const *const end) + { + const uint8_t *begin = static_cast(start); + const uint8_t *finish = static_cast(end); + while (begin != finish) + { + process_byte(*begin); + begin++; + } + } + + void process_bytes(void const *const data, size_t const len) + { + const uint8_t *block = static_cast(data); + process_block(block, block + len); + } + + uint32_t const *get_digest(digest32_t digest) + { + size_t const bitCount = this->m_byteCount * 8; + process_byte(0x80); + if (this->m_blockByteIndex > 56) + { + while (m_blockByteIndex != 0) + { + process_byte(0); + } + while (m_blockByteIndex < 56) + { + process_byte(0); + } + } + else + { + while (m_blockByteIndex < 56) + { + process_byte(0); + } + } + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(static_cast((bitCount >> 24) & 0xFF)); + process_byte(static_cast((bitCount >> 16) & 0xFF)); + process_byte(static_cast((bitCount >> 8) & 0xFF)); + process_byte(static_cast((bitCount) & 0xFF)); + + memcpy(digest, m_digest, 5 * sizeof(uint32_t)); + return digest; + } + + uint8_t const *get_digest_bytes(digest8_t digest) + { + digest32_t d32; + get_digest(d32); + size_t di = 0; + digest[di++] = ((d32[0] >> 24) & 0xFF); + digest[di++] = ((d32[0] >> 16) & 0xFF); + digest[di++] = ((d32[0] >> 8) & 0xFF); + digest[di++] = ((d32[0]) & 0xFF); + + digest[di++] = ((d32[1] >> 24) & 0xFF); + digest[di++] = ((d32[1] >> 16) & 0xFF); + digest[di++] = ((d32[1] >> 8) & 0xFF); + digest[di++] = ((d32[1]) & 0xFF); + + digest[di++] = ((d32[2] >> 24) & 0xFF); + digest[di++] = ((d32[2] >> 16) & 0xFF); + digest[di++] = ((d32[2] >> 8) & 0xFF); + digest[di++] = ((d32[2]) & 0xFF); + + digest[di++] = ((d32[3] >> 24) & 0xFF); + digest[di++] = ((d32[3] >> 16) & 0xFF); + digest[di++] = ((d32[3] >> 8) & 0xFF); + digest[di++] = ((d32[3]) & 0xFF); + + digest[di++] = ((d32[4] >> 24) & 0xFF); + digest[di++] = ((d32[4] >> 16) & 0xFF); + digest[di++] = ((d32[4] >> 8) & 0xFF); + digest[di++] = ((d32[4]) & 0xFF); + + return digest; + } + + private: + void process_block() + { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) + { + w[i] = (m_block[i * 4 + 0] << 24); + w[i] |= (m_block[i * 4 + 1] << 16); + w[i] |= (m_block[i * 4 + 2] << 8); + w[i] |= (m_block[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) + { + w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = m_digest[0]; + uint32_t b = m_digest[1]; + uint32_t c = m_digest[2]; + uint32_t d = m_digest[3]; + uint32_t e = m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) + { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) + { + f = (b & c) | (~b & d); + k = 0x5A827999; + } + else if (i < 40) + { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if (i < 60) + { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else + { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = left_rotate(b, 30); + b = a; + a = temp; + } + + m_digest[0] += a; + m_digest[1] += b; + m_digest[2] += c; + m_digest[3] += d; + m_digest[4] += e; + } + + private: + digest32_t m_digest; + uint8_t m_block[64]; + size_t m_blockByteIndex; + size_t m_byteCount; + }; + + } + + // -------------------------------------------------------------------------------------------------------------------------- + // UUID format https://tools.ietf.org/html/rfc4122 + // -------------------------------------------------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------------------------------------------------- + // Field NDR Data Type Octet # Note + // -------------------------------------------------------------------------------------------------------------------------- + // time_low unsigned long 0 - 3 The low field of the timestamp. + // time_mid unsigned short 4 - 5 The middle field of the timestamp. + // time_hi_and_version unsigned short 6 - 7 The high field of the timestamp multiplexed with the version number. + // clock_seq_hi_and_reserved unsigned small 8 The high field of the clock sequence multiplexed with the variant. + // clock_seq_low unsigned small 9 The low field of the clock sequence. + // node character 10 - 15 The spatially unique node identifier. + // -------------------------------------------------------------------------------------------------------------------------- + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_low | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_mid | time_hi_and_version | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |clk_seq_hi_res | clk_seq_low | node (0-1) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | node (2-5) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // -------------------------------------------------------------------------------------------------------------------------- + // enumerations + // -------------------------------------------------------------------------------------------------------------------------- + + // indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx + enum class uuid_variant + { + // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) + // N bit pattern: 0xxx + // > the first 6 octets of the UUID are a 48-bit timestamp (the number of 4 microsecond units of time since 1 Jan 1980 UTC); + // > the next 2 octets are reserved; + // > the next octet is the "address family"; + // > the final 7 octets are a 56-bit host ID in the form specified by the address family + ncs, + + // RFC 4122/DCE 1.1 + // N bit pattern: 10xx + // > big-endian byte order + rfc, + + // Microsoft Corporation backward compatibility + // N bit pattern: 110x + // > little endian byte order + // > formely used in the Component Object Model (COM) library + microsoft, + + // reserved for possible future definition + // N bit pattern: 111x + reserved + }; + + // indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx + enum class uuid_version + { + none = 0, // only possible for nil or invalid uuids + time_based = 1, // The time-based version specified in RFC 4122 + dce_security = 2, // DCE Security version, with embedded POSIX UIDs. + name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing + random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 + name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing + }; + + // -------------------------------------------------------------------------------------------------------------------------- + // uuid class + // -------------------------------------------------------------------------------------------------------------------------- + class uuid + { + public: + using value_type = uint8_t; + + constexpr uuid() noexcept : data({}) + {}; + + //uuid(value_type(&arr)[16]) noexcept + //{ + // std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); + //} + + uuid(std::array const &arr) noexcept + { + std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); + } + + explicit uuid(value_type *bytes) + { + std::copy(bytes, bytes + 16, std::begin(data)); + } + + template + explicit uuid(ForwardIterator first, ForwardIterator last) + { + if (std::distance(first, last) == 16) + std::copy(first, last, std::begin(data)); + } + + constexpr uuid_variant variant() const noexcept + { + if ((data[8] & 0x80) == 0x00) + return uuid_variant::ncs; + else if ((data[8] & 0xC0) == 0x80) + return uuid_variant::rfc; + else if ((data[8] & 0xE0) == 0xC0) + return uuid_variant::microsoft; else + return uuid_variant::reserved; + } + + constexpr uuid_version version() const noexcept + { + if ((data[6] & 0xF0) == 0x10) + return uuid_version::time_based; + else if ((data[6] & 0xF0) == 0x20) + return uuid_version::dce_security; + else if ((data[6] & 0xF0) == 0x30) + return uuid_version::name_based_md5; + else if ((data[6] & 0xF0) == 0x40) + return uuid_version::random_number_based; + else if ((data[6] & 0xF0) == 0x50) + return uuid_version::name_based_sha1; + else + return uuid_version::none; + } + + constexpr bool is_nil() const noexcept + { + for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false; + return true; + } + + void swap(uuid &other) noexcept + { + data.swap(other.data); + } + + inline const std::array &as_array() const + { + return data; + } + + inline const std::uint8_t *as_data() const + { + return reinterpret_cast(data.data()); + } + + template + static bool is_valid_uuid(CharT const *str) noexcept + { + CharT digit = 0; + bool firstDigit = true; + int hasBraces = 0; + size_t index = 0; + size_t size = 0; + if constexpr (std::is_same_v) + size = strlen(str); + else + size = wcslen(str); + + if (str == nullptr || size == 0) + return false; + + if (str[0] == static_cast('{')) + hasBraces = 1; + if (hasBraces && str[size - 1] != static_cast('}')) + return false; + + for (size_t i = hasBraces; i < size - hasBraces; ++i) { - data[index++] = detail::hexpair2char(digit, str[i]); - firstDigit = true; + if (str[i] == static_cast('-')) continue; + + if (index >= 16 || !detail::is_hex(str[i])) + { + return false; + } + + if (firstDigit) + { + firstDigit = false; + } + else + { + index++; + firstDigit = true; + } } - } - if (index < 16) - { - return {}; - } + if (index < 16) + { + return false; + } - return uuid{ std::cbegin(data), std::cend(data) }; - } + return true; + } - template, - class Allocator = std::allocator> - static std::optional from_string(std::basic_string const & str) noexcept - { - return from_string(str.c_str()); - } + template + static bool is_valid_uuid(const String_t &str) noexcept + { + return is_valid_uuid(str.c_str()); + } - private: - std::array data{ { 0 } }; + template + static std::optional from_string(CharT const *str) noexcept + { + CharT digit = 0; + bool firstDigit = true; + int hasBraces = 0; + size_t index = 0; + size_t size = 0; + if constexpr (std::is_same_v) + size = strlen(str); + else + size = wcslen(str); - friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; - friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; + std::array data {{ 0 }}; - template - friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); - }; + if (str == nullptr || size == 0) return {}; - // -------------------------------------------------------------------------------------------------------------------------- - // operators and non-member functions - // -------------------------------------------------------------------------------------------------------------------------- + if (str[0] == static_cast('{')) + hasBraces = 1; + if (hasBraces && str[size - 1] != static_cast('}')) + return {}; - inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept - { - return lhs.data == rhs.data; - } + for (size_t i = hasBraces; i < size - hasBraces; ++i) + { + if (str[i] == static_cast('-')) continue; - inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept - { - return !(lhs == rhs); - } + if (index >= 16 || !detail::is_hex(str[i])) + { + return {}; + } - inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept - { - return lhs.data < rhs.data; - } + if (firstDigit) + { + digit = str[i]; + firstDigit = false; + } + else + { + data[index++] = detail::hexpair2char(digit, str[i]); + firstDigit = true; + } + } - template - std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id) - { - // save current flags - std::ios_base::fmtflags f(s.flags()); - - // manipulate stream as needed - s << std::hex << std::setfill(static_cast('0')) - << std::setw(2) << (int)id.data[0] - << std::setw(2) << (int)id.data[1] - << std::setw(2) << (int)id.data[2] - << std::setw(2) << (int)id.data[3] - << '-' - << std::setw(2) << (int)id.data[4] - << std::setw(2) << (int)id.data[5] - << '-' - << std::setw(2) << (int)id.data[6] - << std::setw(2) << (int)id.data[7] - << '-' - << std::setw(2) << (int)id.data[8] - << std::setw(2) << (int)id.data[9] - << '-' - << std::setw(2) << (int)id.data[10] - << std::setw(2) << (int)id.data[11] - << std::setw(2) << (int)id.data[12] - << std::setw(2) << (int)id.data[13] - << std::setw(2) << (int)id.data[14] - << std::setw(2) << (int)id.data[15]; + if (index < 16) + { + return {}; + } - // restore original flags - s.flags(f); - - return s; - } + return uuid {std::cbegin(data), std::cend(data)}; + } - template + static std::optional from_string(const String_t &str) noexcept + { + return from_string(str.c_str()); + } + + template + inline void to_string(CharT(&out)[36]) const + { + detail::ByteToHex(data[0], out + (2 * 0)); + detail::ByteToHex(data[1], out + (2 * 1)); + detail::ByteToHex(data[2], out + (2 * 2)); + detail::ByteToHex(data[3], out + (2 * 3)); + out[(2 * 4)] = '-'; + + detail::ByteToHex(data[4], out + (2 * 4) + 1); + detail::ByteToHex(data[5], out + (2 * 5) + 1); + out[(2 * 6) + 1] = '-'; + + detail::ByteToHex(data[6], out + (2 * 6) + 2); + detail::ByteToHex(data[7], out + (2 * 7) + 2); + out[(2 * 8) + 2] = '-'; + + detail::ByteToHex(data[8], out + (2 * 8) + 3); + detail::ByteToHex(data[9], out + (2 * 9) + 3); + out[(2 * 10) + 3] = '-'; + + detail::ByteToHex(data[10], out + (2 * 10) + 4); + detail::ByteToHex(data[11], out + (2 * 11) + 4); + detail::ByteToHex(data[12], out + (2 * 12) + 4); + detail::ByteToHex(data[13], out + (2 * 13) + 4); + detail::ByteToHex(data[14], out + (2 * 14) + 4); + detail::ByteToHex(data[15], out + (2 * 15) + 4); + } + + template, class Allocator = std::allocator> - inline std::basic_string to_string(uuid const & id) - { - std::basic_stringstream sstr; - sstr << id; - return sstr.str(); - } + inline std::basic_string to_string() const + { + CharT stackStr[36]; + to_string(stackStr); + return std::basic_string(stackStr, stackStr + 36); + } - inline void swap(uuids::uuid & lhs, uuids::uuid & rhs) noexcept - { - lhs.swap(rhs); - } + private: + std::array data {{ 0 }}; - // -------------------------------------------------------------------------------------------------------------------------- - // namespace IDs that could be used for generating name-based uuids - // -------------------------------------------------------------------------------------------------------------------------- + friend bool operator==(uuid const &lhs, uuid const &rhs) noexcept; + friend bool operator<(uuid const &lhs, uuid const &rhs) noexcept; + }; - // Name string is a fully-qualified domain name - static uuid uuid_namespace_dns{ {0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + // -------------------------------------------------------------------------------------------------------------------------- + // operators and non-member functions + // -------------------------------------------------------------------------------------------------------------------------- - // Name string is a URL - static uuid uuid_namespace_url{ {0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + inline bool operator== (uuid const &lhs, uuid const &rhs) noexcept + { + return lhs.data == rhs.data; + } - // Name string is an ISO OID (See https://oidref.com/, https://en.wikipedia.org/wiki/Object_identifier) - static uuid uuid_namespace_oid{ {0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + inline bool operator!= (uuid const &lhs, uuid const &rhs) noexcept + { + return !(lhs == rhs); + } - // Name string is an X.500 DN, in DER or a text output format (See https://en.wikipedia.org/wiki/X.500, https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) - static uuid uuid_namespace_x500{ {0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + inline bool operator< (uuid const &lhs, uuid const &rhs) noexcept + { + return lhs.data < rhs.data; + } - // -------------------------------------------------------------------------------------------------------------------------- - // uuid generators - // -------------------------------------------------------------------------------------------------------------------------- -#ifdef UUID_SYSTEM_GENERATOR - class uuid_system_generator - { - public: - using result_type = uuid; + template, + class Allocator = std::allocator> + inline std::basic_string to_string(uuid const &id) + { + return id.to_string(); + } - uuid operator()() - { -#ifdef _WIN32 + inline void swap(uuids::uuid &lhs, uuids::uuid &rhs) noexcept + { + lhs.swap(rhs); + } - GUID newId; - ::CoCreateGuid(&newId); + // -------------------------------------------------------------------------------------------------------------------------- + // namespace IDs that could be used for generating name-based uuids + // -------------------------------------------------------------------------------------------------------------------------- - std::array bytes = - { { - (unsigned char)((newId.Data1 >> 24) & 0xFF), - (unsigned char)((newId.Data1 >> 16) & 0xFF), - (unsigned char)((newId.Data1 >> 8) & 0xFF), - (unsigned char)((newId.Data1) & 0xFF), + // Name string is a fully-qualified domain name + static uuid uuid_namespace_dns {{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; - (unsigned char)((newId.Data2 >> 8) & 0xFF), - (unsigned char)((newId.Data2) & 0xFF), + // Name string is a URL + static uuid uuid_namespace_url {{0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; - (unsigned char)((newId.Data3 >> 8) & 0xFF), - (unsigned char)((newId.Data3) & 0xFF), + // Name string is an ISO OID (See https://oidref.com/, https://en.wikipedia.org/wiki/Object_identifier) + static uuid uuid_namespace_oid {{0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; - newId.Data4[0], - newId.Data4[1], - newId.Data4[2], - newId.Data4[3], - newId.Data4[4], - newId.Data4[5], - newId.Data4[6], - newId.Data4[7] - } }; + // Name string is an X.500 DN, in DER or a text output format (See https://en.wikipedia.org/wiki/X.500, https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) + static uuid uuid_namespace_x500 {{0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}}; - return uuid{ std::begin(bytes), std::end(bytes) }; + // -------------------------------------------------------------------------------------------------------------------------- + // uuid generators + // -------------------------------------------------------------------------------------------------------------------------- -#elif defined(__linux__) || defined(__unix__) + template + class basic_uuid_random_generator + { + public: + using engine_type = UniformRandomNumberGenerator; - uuid_t id; - uuid_generate(id); + explicit basic_uuid_random_generator(engine_type &gen) : + generator(&gen, [](auto) + {}) + {} - std::array bytes = - { { - id[0], - id[1], - id[2], - id[3], - id[4], - id[5], - id[6], - id[7], - id[8], - id[9], - id[10], - id[11], - id[12], - id[13], - id[14], - id[15] - } }; + explicit basic_uuid_random_generator(engine_type *gen) : + generator(gen, [](auto) + {}) + {} - return uuid{ std::begin(bytes), std::end(bytes) }; + uuid operator()() + { + uint8_t bytes[16]; + for (int i = 0; i < 16; i += 4) + *reinterpret_cast(bytes + i) = distribution(*generator); -#elif defined(__APPLE__) - auto newId = CFUUIDCreate(NULL); - auto bytes = CFUUIDGetUUIDBytes(newId); - CFRelease(newId); + // variant must be 10xxxxxx + bytes[8] &= 0xBF; + bytes[8] |= 0x80; - std::array arrbytes = - { { - bytes.byte0, - bytes.byte1, - bytes.byte2, - bytes.byte3, - bytes.byte4, - bytes.byte5, - bytes.byte6, - bytes.byte7, - bytes.byte8, - bytes.byte9, - bytes.byte10, - bytes.byte11, - bytes.byte12, - bytes.byte13, - bytes.byte14, - bytes.byte15 - } }; - return uuid{ std::begin(arrbytes), std::end(arrbytes) }; -#else - return uuid{}; -#endif - } - }; -#endif + // version must be 0100xxxx + bytes[6] &= 0x4F; + bytes[6] |= 0x40; - template - class basic_uuid_random_generator - { - public: - using engine_type = UniformRandomNumberGenerator; + return uuid {std::begin(bytes), std::end(bytes)}; + } - explicit basic_uuid_random_generator(engine_type& gen) : - generator(&gen, [](auto) {}) {} - explicit basic_uuid_random_generator(engine_type* gen) : - generator(gen, [](auto) {}) {} + private: + std::uniform_int_distribution distribution; + std::shared_ptr generator; + }; - uuid operator()() - { - uint8_t bytes[16]; - for (int i = 0; i < 16; i += 4) - *reinterpret_cast(bytes + i) = distribution(*generator); + using uuid_random_generator = basic_uuid_random_generator; - // variant must be 10xxxxxx - bytes[8] &= 0xBF; - bytes[8] |= 0x80; + class uuid_name_generator + { + public: + explicit uuid_name_generator(uuid const &namespace_uuid) noexcept + : nsuuid(namespace_uuid) + {} + explicit uuid_name_generator() + : nsuuid() + {} - // version must be 0100xxxx - bytes[6] &= 0x4F; - bytes[6] |= 0x40; + template + uuid operator()(CharT const *name) + { + size_t size = 0; + if constexpr (std::is_same_v) + size = strlen(name); + else + size = wcslen(name); - return uuid{std::begin(bytes), std::end(bytes)}; - } + reset(); + process_characters(name, size); + return make_uuid(); + } - private: - std::uniform_int_distribution distribution; - std::shared_ptr generator; - }; + template, + class Allocator = std::allocator> + uuid operator()(std::basic_string const &name) + { + reset(); + process_characters(name.data(), name.size()); + return make_uuid(); + } - using uuid_random_generator = basic_uuid_random_generator; - - class uuid_name_generator - { - public: - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept - : nsuuid(namespace_uuid) - {} - explicit uuid_name_generator() - : nsuuid() - {} + private: + void reset() + { + hasher.reset(); + if (nsuuid.has_value()) + { + auto nsbytes = nsuuid.value().as_array(); + hasher.process_bytes(nsbytes.data(), 16); + } + } - template - uuid operator()(CharT const * name) - { - size_t size = 0; - if constexpr (std::is_same_v) - size = strlen(name); - else - size = wcslen(name); + template ::value>> + void process_characters(char_type const *const characters, size_t const count) + { + for (size_t i = 0; i < count * sizeof(char_type); i++) + { + hasher.process_byte(reinterpret_cast(characters)[i]); + } + } - reset(); - process_characters(name, size); - return make_uuid(); - } + void process_characters(const char *const characters, size_t const count) + { + hasher.process_bytes(characters, count); + } - template, - class Allocator = std::allocator> - uuid operator()(std::basic_string const & name) - { - reset(); - process_characters(name.data(), name.size()); - return make_uuid(); - } + uuid make_uuid() + { + detail::sha1::digest8_t digest; + hasher.get_digest_bytes(digest); - private: - void reset() - { - hasher.reset(); - if (nsuuid.has_value()) - { - auto nsbytes = nsuuid.value().as_array(); - hasher.process_bytes(nsbytes.data(), 16); - } - } - - template ::value>> - void process_characters(char_type const * const characters, size_t const count) - { - for (size_t i = 0; i < count * sizeof(char_type); i++) - { - hasher.process_byte(reinterpret_cast(characters)[i]); - } - } + // variant must be 0b10xxxxxx + digest[8] &= 0xBF; + digest[8] |= 0x80; - void process_characters(const char * const characters, size_t const count) - { - hasher.process_bytes(characters, count); - } + // version must be 0b0101xxxx + digest[6] &= 0x5F; + digest[6] |= 0x50; - uuid make_uuid() - { - detail::sha1::digest8_t digest; - hasher.get_digest_bytes(digest); + return uuid {digest, digest + 16}; + } - // variant must be 0b10xxxxxx - digest[8] &= 0xBF; - digest[8] |= 0x80; + private: + detail::sha1 hasher; + std::optional nsuuid; + }; - // version must be 0b0101xxxx - digest[6] &= 0x5F; - digest[6] |= 0x50; - - return uuid{ digest, digest + 16 }; - } - - private: - detail::sha1 hasher; - std::optional nsuuid; - }; - - static uuid gen_string(const std::string& in) - { - uuids::uuid_name_generator re; - return re(in); - } + static uuid gen_string(const std::string &in) + { + uuids::uuid_name_generator re; + return re(in); + } }; namespace std @@ -854,7 +752,7 @@ namespace std using argument_type = uuids::uuid; using result_type = std::size_t; - result_type operator()(argument_type const& uuid) const + result_type operator()(argument_type const &uuid) const { std::hash hasher; return static_cast(hasher(uuids::to_string(uuid)));