uuid.hpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. *
  10. * boost/uuid
  11. * https://lowrey.me/guid-generation-in-c-11/
  12. * https://github.com/mariusbancila/stduuid
  13. * https://stackoverflow.com/questions/13445688/how-to-generate-a-random-number-in-c
  14. */
  15. #ifndef __ASIO2_UUID_IMPL_HPP__
  16. #define __ASIO2_UUID_IMPL_HPP__
  17. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  18. #pragma once
  19. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  20. #include <cstring>
  21. #include <cstdint>
  22. #include <cstddef>
  23. #include <string>
  24. #include <random>
  25. #include <limits>
  26. #include <memory>
  27. #include <array>
  28. #include <algorithm>
  29. #include <iterator>
  30. namespace asio2
  31. {
  32. /**
  33. * UUID Generation in C++11
  34. */
  35. class uuid
  36. {
  37. protected:
  38. struct random_enginer
  39. {
  40. std::seed_seq sseq_;
  41. std::mt19937 engine_;
  42. std::uniform_int_distribution<std::uint32_t> distribution_;
  43. template<class InputIt>
  44. explicit random_enginer(InputIt begin, InputIt end)
  45. : sseq_(begin, end)
  46. , engine_(sseq_)
  47. {
  48. }
  49. inline std::uint32_t get()
  50. {
  51. return distribution_(engine_);
  52. }
  53. };
  54. public:
  55. uuid()
  56. {
  57. std::random_device rd{};
  58. std::array<std::uint32_t, std::mt19937::state_size> sb{};
  59. std::generate(std::begin(sb), std::end(sb), std::ref(rd));
  60. this->random_enginer_ = std::make_unique<random_enginer>(std::begin(sb), std::end(sb));
  61. }
  62. ~uuid() = default;
  63. uuid(const uuid&) = delete;
  64. uuid(uuid&&) noexcept = default;
  65. uuid& operator=(const uuid&) = delete;
  66. uuid& operator=(uuid&&) noexcept = default;
  67. inline uuid& operator()()
  68. {
  69. return generate();
  70. }
  71. inline uuid& next()
  72. {
  73. return generate();
  74. }
  75. inline uuid& generate()
  76. {
  77. for (std::size_t i = 0; i < sizeof(data); i += sizeof(std::uint32_t))
  78. {
  79. *reinterpret_cast<std::uint32_t*>(data + i) = this->random_enginer_->get();
  80. }
  81. // set variant
  82. // must be 0b10xxxxxx
  83. *(data + 8) &= 0xBF;
  84. *(data + 8) |= 0x80;
  85. // set version
  86. // must be 0b0100xxxx
  87. *(data + 6) &= 0x4F; //0b01001111
  88. *(data + 6) |= 0x40; //0b01000000
  89. return (*this);
  90. }
  91. /**
  92. * @brief convert the uuid bytes to std::string
  93. */
  94. inline std::string str(bool upper = false, bool group = true) noexcept
  95. {
  96. std::string result;
  97. if (group)
  98. {
  99. // 00000000-0000-0000-0000-000000000000
  100. result.reserve(36);
  101. for (std::size_t i = 0; i < sizeof(data); )
  102. {
  103. int n = static_cast<int>(result.size());
  104. if (n == 8 || n == 13 || n == 18 || n == 23)
  105. {
  106. result += '-';
  107. continue;
  108. }
  109. const int hi = ((*(data + i)) >> 4) & 0x0F;
  110. result += to_char(hi, upper);
  111. const int lo = (*(data + i)) & 0x0F;
  112. result += to_char(lo, upper);
  113. ++i;
  114. }
  115. }
  116. else
  117. {
  118. // 00000000000000000000000000000000
  119. result.reserve(32);
  120. for (std::size_t i = 0; i < sizeof(data); ++i)
  121. {
  122. const int hi = ((*(data + i)) >> 4) & 0x0F;
  123. result += to_char(hi, upper);
  124. const int lo = (*(data + i)) & 0x0F;
  125. result += to_char(lo, upper);
  126. }
  127. }
  128. return result;
  129. }
  130. // This is equivalent to boost::hash_range(u.begin(), u.end());
  131. inline std::size_t hash() noexcept
  132. {
  133. std::size_t seed = 0;
  134. for (std::size_t i = 0; i < sizeof(data); ++i)
  135. {
  136. seed ^= static_cast<std::size_t>(*(data + i)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  137. }
  138. return seed;
  139. }
  140. enum class variant_type
  141. {
  142. ncs, // NCS backward compatibility
  143. rfc, // defined in RFC 4122 document
  144. microsoft, // Microsoft Corporation backward compatibility
  145. future // future definition
  146. };
  147. inline variant_type variant() const noexcept
  148. {
  149. // variant is stored in octet 7
  150. // which is index 8, since indexes count backwards
  151. unsigned char octet7 = data[8]; // octet 7 is array index 8
  152. if ((octet7 & 0x80) == 0x00)
  153. {
  154. // 0b0xxxxxxx
  155. return variant_type::ncs;
  156. }
  157. else if ((octet7 & 0xC0) == 0x80)
  158. {
  159. // 0b10xxxxxx
  160. return variant_type::rfc;
  161. }
  162. else if ((octet7 & 0xE0) == 0xC0)
  163. {
  164. // 0b110xxxxx
  165. return variant_type::microsoft;
  166. }
  167. else
  168. {
  169. //assert( (octet7 & 0xE0) == 0xE0 ) // 0b111xxxx
  170. return variant_type::future;
  171. }
  172. }
  173. enum class version_type
  174. {
  175. unknown = 0, // only possible for nil or invalid uuids
  176. time_based = 1, // The time-based version specified in RFC 4122
  177. dce_security = 2, // DCE Security version, with embedded POSIX UIDs.
  178. name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing
  179. random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122
  180. name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing
  181. };
  182. inline version_type version() const noexcept
  183. {
  184. // version is stored in octet 9
  185. // which is index 6, since indexes count backwards
  186. uint8_t octet9 = data[6];
  187. if ((octet9 & 0xF0) == 0x10)
  188. {
  189. return version_type::time_based;
  190. }
  191. else if ((octet9 & 0xF0) == 0x20)
  192. {
  193. return version_type::dce_security;
  194. }
  195. else if ((octet9 & 0xF0) == 0x30)
  196. {
  197. return version_type::name_based_md5;
  198. }
  199. else if ((octet9 & 0xF0) == 0x40)
  200. {
  201. return version_type::random_number_based;
  202. }
  203. else if ((octet9 & 0xF0) == 0x50)
  204. {
  205. return version_type::name_based_sha1;
  206. }
  207. else
  208. {
  209. return version_type::unknown;
  210. }
  211. }
  212. inline std::string short_uuid(int bytes)
  213. {
  214. // use base64 chars as the short uuid chars
  215. static std::string const base64_chars =
  216. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  217. "abcdefghijklmnopqrstuvwxyz"
  218. "0123456789+/";
  219. std::string result;
  220. result.reserve(bytes);
  221. std::uniform_int_distribution<int> d(0, int(base64_chars.size() - 1));
  222. for (int i = 0; i < bytes; ++i)
  223. {
  224. result += base64_chars[d(this->random_enginer_->engine_)];
  225. }
  226. return result;
  227. }
  228. template <typename CharT>
  229. constexpr inline unsigned char hex2char(CharT const ch)
  230. {
  231. if (ch >= static_cast<CharT>('0') && ch <= static_cast<CharT>('9'))
  232. return static_cast<unsigned char>(ch - static_cast<CharT>('0'));
  233. if (ch >= static_cast<CharT>('a') && ch <= static_cast<CharT>('f'))
  234. return static_cast<unsigned char>(10 + ch - static_cast<CharT>('a'));
  235. if (ch >= static_cast<CharT>('A') && ch <= static_cast<CharT>('F'))
  236. return static_cast<unsigned char>(10 + ch - static_cast<CharT>('A'));
  237. return 0;
  238. }
  239. template <typename CharT>
  240. constexpr inline bool is_hex(CharT const ch)
  241. {
  242. return
  243. (ch >= static_cast<CharT>('0') && ch <= static_cast<CharT>('9')) ||
  244. (ch >= static_cast<CharT>('a') && ch <= static_cast<CharT>('f')) ||
  245. (ch >= static_cast<CharT>('A') && ch <= static_cast<CharT>('F'));
  246. }
  247. protected:
  248. inline char to_char(int i, bool upper) noexcept
  249. {
  250. if (i <= 9)
  251. {
  252. return static_cast<char>('0' + i);
  253. }
  254. else
  255. {
  256. return static_cast<char>((upper ? 'A' : 'a') + (i - 10));
  257. }
  258. }
  259. public:
  260. std::uint8_t data[16]{};
  261. protected:
  262. std::unique_ptr<random_enginer> random_enginer_;
  263. };
  264. }
  265. #endif // !__ASIO2_UUID_IMPL_HPP__