/* * Copyright (c) 2017-2023 zhllxt * * author : zhllxt * email : 37792738@qq.com * * 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) * * boost/uuid * https://lowrey.me/guid-generation-in-c-11/ * https://github.com/mariusbancila/stduuid * https://stackoverflow.com/questions/13445688/how-to-generate-a-random-number-in-c */ #ifndef __ASIO2_UUID_IMPL_HPP__ #define __ASIO2_UUID_IMPL_HPP__ #if defined(_MSC_VER) && (_MSC_VER >= 1200) #pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include #include #include #include #include #include #include #include namespace asio2 { /** * UUID Generation in C++11 */ class uuid { protected: struct random_enginer { std::seed_seq sseq_; std::mt19937 engine_; std::uniform_int_distribution distribution_; template explicit random_enginer(InputIt begin, InputIt end) : sseq_(begin, end) , engine_(sseq_) { } inline std::uint32_t get() { return distribution_(engine_); } }; public: uuid() { std::random_device rd{}; std::array sb{}; std::generate(std::begin(sb), std::end(sb), std::ref(rd)); this->random_enginer_ = std::make_unique(std::begin(sb), std::end(sb)); } ~uuid() = default; uuid(const uuid&) = delete; uuid(uuid&&) noexcept = default; uuid& operator=(const uuid&) = delete; uuid& operator=(uuid&&) noexcept = default; inline uuid& operator()() { return generate(); } inline uuid& next() { return generate(); } inline uuid& generate() { for (std::size_t i = 0; i < sizeof(data); i += sizeof(std::uint32_t)) { *reinterpret_cast(data + i) = this->random_enginer_->get(); } // set variant // must be 0b10xxxxxx *(data + 8) &= 0xBF; *(data + 8) |= 0x80; // set version // must be 0b0100xxxx *(data + 6) &= 0x4F; //0b01001111 *(data + 6) |= 0x40; //0b01000000 return (*this); } /** * @brief convert the uuid bytes to std::string */ inline std::string str(bool upper = false, bool group = true) noexcept { std::string result; if (group) { // 00000000-0000-0000-0000-000000000000 result.reserve(36); for (std::size_t i = 0; i < sizeof(data); ) { int n = static_cast(result.size()); if (n == 8 || n == 13 || n == 18 || n == 23) { result += '-'; continue; } const int hi = ((*(data + i)) >> 4) & 0x0F; result += to_char(hi, upper); const int lo = (*(data + i)) & 0x0F; result += to_char(lo, upper); ++i; } } else { // 00000000000000000000000000000000 result.reserve(32); for (std::size_t i = 0; i < sizeof(data); ++i) { const int hi = ((*(data + i)) >> 4) & 0x0F; result += to_char(hi, upper); const int lo = (*(data + i)) & 0x0F; result += to_char(lo, upper); } } return result; } // This is equivalent to boost::hash_range(u.begin(), u.end()); inline std::size_t hash() noexcept { std::size_t seed = 0; for (std::size_t i = 0; i < sizeof(data); ++i) { seed ^= static_cast(*(data + i)) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } return seed; } enum class variant_type { ncs, // NCS backward compatibility rfc, // defined in RFC 4122 document microsoft, // Microsoft Corporation backward compatibility future // future definition }; inline variant_type variant() const noexcept { // variant is stored in octet 7 // which is index 8, since indexes count backwards unsigned char octet7 = data[8]; // octet 7 is array index 8 if ((octet7 & 0x80) == 0x00) { // 0b0xxxxxxx return variant_type::ncs; } else if ((octet7 & 0xC0) == 0x80) { // 0b10xxxxxx return variant_type::rfc; } else if ((octet7 & 0xE0) == 0xC0) { // 0b110xxxxx return variant_type::microsoft; } else { //assert( (octet7 & 0xE0) == 0xE0 ) // 0b111xxxx return variant_type::future; } } enum class version_type { unknown = 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 }; inline version_type version() const noexcept { // version is stored in octet 9 // which is index 6, since indexes count backwards uint8_t octet9 = data[6]; if ((octet9 & 0xF0) == 0x10) { return version_type::time_based; } else if ((octet9 & 0xF0) == 0x20) { return version_type::dce_security; } else if ((octet9 & 0xF0) == 0x30) { return version_type::name_based_md5; } else if ((octet9 & 0xF0) == 0x40) { return version_type::random_number_based; } else if ((octet9 & 0xF0) == 0x50) { return version_type::name_based_sha1; } else { return version_type::unknown; } } inline std::string short_uuid(int bytes) { // use base64 chars as the short uuid chars static std::string const base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; std::string result; result.reserve(bytes); std::uniform_int_distribution d(0, int(base64_chars.size() - 1)); for (int i = 0; i < bytes; ++i) { result += base64_chars[d(this->random_enginer_->engine_)]; } return result; } template constexpr inline unsigned char hex2char(CharT const ch) { if (ch >= static_cast('0') && ch <= static_cast('9')) return static_cast(ch - static_cast('0')); if (ch >= static_cast('a') && ch <= static_cast('f')) return static_cast(10 + ch - static_cast('a')); if (ch >= static_cast('A') && ch <= static_cast('F')) return static_cast(10 + ch - static_cast('A')); return 0; } template constexpr inline bool is_hex(CharT 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')); } protected: inline char to_char(int i, bool upper) noexcept { if (i <= 9) { return static_cast('0' + i); } else { return static_cast((upper ? 'A' : 'a') + (i - 10)); } } public: std::uint8_t data[16]{}; protected: std::unique_ptr random_enginer_; }; } #endif // !__ASIO2_UUID_IMPL_HPP__