#pragma once #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #elif defined(__linux__) || defined(__unix__) #include #elif defined(__APPLE__) #include #endif namespace uuids { // 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) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // 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 }; struct uuid { struct uuid_const_iterator { typedef uuid_const_iterator self_type; typedef uint8_t value_type; typedef uint8_t const & reference; typedef uint8_t const * pointer; typedef std::random_access_iterator_tag iterator_category; typedef ptrdiff_t difference_type; protected: pointer ptr = nullptr; size_t index = 0; bool compatible(self_type const & other) const noexcept { return ptr == other.ptr; } public: explicit uuid_const_iterator(pointer ptr, size_t const index) : ptr(ptr), index(index) { } uuid_const_iterator(uuid_const_iterator const & o) = default; uuid_const_iterator& operator=(uuid_const_iterator const & o) = default; ~uuid_const_iterator() = default; self_type & operator++ () { if (index >= uuid::state_size) throw std::out_of_range("Iterator cannot be incremented past the end of the data."); ++index; return *this; } self_type operator++ (int) { self_type tmp = *this; ++*this; return tmp; } bool operator== (self_type const & other) const { assert(compatible(other)); return index == other.index; } bool operator!= (self_type const & other) const { return !(*this == other); } reference operator* () const { if (ptr == nullptr) throw std::bad_function_call(); return *(ptr + index); } reference operator-> () const { if (ptr == nullptr) throw std::bad_function_call(); return *(ptr + index); } uuid_const_iterator() = default; self_type & operator--() { if (index <= 0) throw std::out_of_range("Iterator cannot be decremented past the beginning of the data."); --index; return *this; } self_type operator--(int) { self_type tmp = *this; --*this; return tmp; } self_type operator+(difference_type offset) const { self_type tmp = *this; return tmp += offset; } self_type operator-(difference_type offset) const { self_type tmp = *this; return tmp -= offset; } difference_type operator-(self_type const & other) const { assert(compatible(other)); return (index - other.index); } bool operator<(self_type const & other) const { assert(compatible(other)); return index < other.index; } bool operator>(self_type const & other) const { return other < *this; } bool operator<=(self_type const & other) const { return !(other < *this); } bool operator>=(self_type const & other) const { return !(*this < other); } self_type & operator+=(difference_type const offset) { if (index + offset < 0 || index + offset > uuid::state_size) throw std::out_of_range("Iterator cannot be incremented past the end of the data."); index += offset; return *this; } self_type & operator-=(difference_type const offset) { return *this += -offset; } value_type const & operator[](difference_type const offset) const { return (*(*this + offset)); } }; struct uuid_iterator : public uuid_const_iterator { typedef uuid_iterator self_type; typedef uint8_t value_type; typedef uint8_t& reference; typedef uint8_t* pointer; typedef std::random_access_iterator_tag iterator_category; typedef ptrdiff_t difference_type; protected: pointer ptr = nullptr; size_t index = 0; bool compatible(self_type const & other) const noexcept { return ptr == other.ptr; } public: explicit uuid_iterator(pointer ptr, size_t const index) : ptr(ptr), index(index) { } uuid_iterator(uuid_iterator const & o) = default; uuid_iterator& operator=(uuid_iterator const & o) = default; ~uuid_iterator() = default; self_type & operator++ () { if (index >= uuid::state_size) throw std::out_of_range("Iterator cannot be incremented past the end of the data."); ++index; return *this; } self_type operator++ (int) { self_type tmp = *this; ++*this; return tmp; } bool operator== (self_type const & other) const { assert(compatible(other)); return index == other.index; } bool operator!= (self_type const & other) const { return !(*this == other); } reference operator* () const { if (ptr == nullptr) throw std::bad_function_call(); return *(ptr + index); } reference operator-> () const { if (ptr == nullptr) throw std::bad_function_call(); return *(ptr + index); } uuid_iterator() = default; self_type & operator--() { if (index <= 0) throw std::out_of_range("Iterator cannot be decremented past the beginning of the data."); --index; return *this; } self_type operator--(int) { self_type tmp = *this; --*this; return tmp; } self_type operator+(difference_type offset) const { self_type tmp = *this; return tmp += offset; } self_type operator-(difference_type offset) const { self_type tmp = *this; return tmp -= offset; } difference_type operator-(self_type const & other) const { assert(compatible(other)); return (index - other.index); } bool operator<(self_type const & other) const { assert(compatible(other)); return index < other.index; } bool operator>(self_type const & other) const { return other < *this; } bool operator<=(self_type const & other) const { return !(other < *this); } bool operator>=(self_type const & other) const { return !(*this < other); } self_type & operator+=(difference_type const offset) { if (index + offset < 0 || index + offset > uuid::state_size) throw std::out_of_range("Iterator cannot be incremented past the end of the data."); index += offset; return *this; } self_type & operator-=(difference_type const offset) { return *this += -offset; } value_type & operator[](difference_type const offset) { return (*(*this + offset)); } value_type const & operator[](difference_type const offset) const { return (*(*this + offset)); } }; public: typedef uint8_t value_type; typedef uint8_t& reference; typedef uint8_t const& const_reference; typedef uuid_iterator iterator; typedef uuid_const_iterator const_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; static constexpr size_t state_size = 16; public: constexpr uuid() noexcept {} template explicit uuid(ForwardIterator first, ForwardIterator last) { if (std::distance(first, last) == 16) std::copy(first, last, std::begin(data)); } explicit uuid(std::string_view str); explicit uuid(std::wstring_view str); 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 std::size_t size() const noexcept { return state_size; } constexpr bool 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); } friend void swap(uuid& lhs, uuid& rhs) noexcept { std::swap(lhs.data, rhs.data); } iterator begin() noexcept { return uuid_iterator(&data[0], 0); } const_iterator begin() const noexcept { return uuid_const_iterator(&data[0], 0); } iterator end() noexcept { return uuid_iterator(&data[0], state_size); } const_iterator end() const noexcept { return uuid_const_iterator(&data[0], state_size); } private: std::array data{ { 0 } }; friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; template void create(TChar const * const str, size_t const size); template friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); }; inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept { return lhs.data == rhs.data; } inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept { return !(lhs == rhs); } inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept { return lhs.data < rhs.data; } template std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id) { return 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]; } std::string to_string(uuid const & id) { std::stringstream sstr; sstr << id; return sstr.str(); } std::wstring to_wstring(uuid const & id) { std::wstringstream sstr; sstr << id; return sstr.str(); } class uuid_default_generator { public: typedef uuid result_type; uuid operator()() { #ifdef _WIN32 GUID newId; ::CoCreateGuid(&newId); 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), (unsigned char)((newId.Data2 >> 8) & 0xFF), (unsigned char)((newId.Data2) & 0xFF), (unsigned char)((newId.Data3 >> 8) & 0xFF), (unsigned char)((newId.Data3) & 0xFF), newId.Data4[0], newId.Data4[1], newId.Data4[2], newId.Data4[3], newId.Data4[4], newId.Data4[5], newId.Data4[6], newId.Data4[7] } }; return uuid{ std::begin(bytes), std::end(bytes) }; #elif defined(__linux__) || defined(__unix__) uuid_t id; uuid_generate(id); 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] } }; return uuid{ std::begin(bytes), std::end(bytes) }; #elif defined(__APPLE__) auto newId = CFUUIDCreate(NULL); auto bytes = CFUUIDGetUUIDBytes(newId); CFRelease(newId); std::array bytes = { { 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(bytes), std::end(bytes) }; #elif return uuid{}; #endif } }; template class basic_uuid_random_generator { public: typedef uuid result_type; basic_uuid_random_generator() :generator(new UniformRandomNumberGenerator) { std::random_device rd; generator->seed(rd()); } explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) : generator(&gen, [](auto) {}) {} explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen) : generator(gen, [](auto) {}) {} uuid operator()() { uint8_t bytes[16]; for (int i = 0; i < 16; i += 4) *reinterpret_cast(bytes + i) = distribution(*generator); // variant must be 10xxxxxx bytes[8] &= 0xBF; bytes[8] |= 0x80; // version must be 0100xxxx bytes[6] &= 0x4F; bytes[6] |= 0x40; return uuid{std::begin(bytes), std::end(bytes)}; } private: std::uniform_int_distribution distribution; std::shared_ptr generator; }; using uuid_random_generator = basic_uuid_random_generator; 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); } } uuid::uuid(std::string_view str) { create(str.data(), str.size()); } uuid::uuid(std::wstring_view str) { create(str.data(), str.size()); } template void uuid::create(TChar const * const str, size_t const size) { TChar digit = 0; bool firstdigit = true; size_t index = 0; for (size_t i = 0; i < size; ++i) { if (str[i] == static_cast('-')) continue; if (index >= 16 || !detail::is_hex(str[i])) { std::fill(std::begin(data), std::end(data), 0); return; } if (firstdigit) { digit = str[i]; firstdigit = false; } else { data[index++] = detail::hexpair2char(digit, str[i]); firstdigit = true; } } if (index < 16) { std::fill(std::begin(data), std::end(data), 0); } } } namespace std { template <> void swap(uuids::uuid & lhs, uuids::uuid & rhs) { lhs.swap(rhs); } template <> struct hash { typedef uuids::uuid argument_type; typedef std::size_t result_type; result_type operator()(argument_type const &uuid) const { std::hash hasher; return static_cast(hasher(uuids::to_string(uuid))); } }; }