diff --git a/.cspell/words.txt b/.cspell/words.txt index b48ccfd8..1f6d0e0c 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -210,6 +210,7 @@ smatch sopen stbuf stdc +stduuid stod stoi stoll diff --git a/3rd_party/stduuid/stduuid.h b/3rd_party/stduuid/stduuid.h new file mode 100644 index 00000000..2212a096 --- /dev/null +++ b/3rd_party/stduuid/stduuid.h @@ -0,0 +1,876 @@ +#ifndef STDUUID_H +#define STDUUID_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus + +#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define LIBUUID_CPP20_OR_GREATER +#endif + +#endif + +#ifdef LIBUUID_CPP20_OR_GREATER +#include +#else +#include +#endif + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#ifdef UUID_TIME_GENERATOR +#include +#pragma comment(lib, "IPHLPAPI.lib") +#endif + +#elif defined(__linux__) || defined(__unix__) + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#elif defined(__APPLE__) + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#endif + +namespace uuids { +#ifdef __cpp_lib_span +template +using span = std::span; +#else +template +using span = gsl::span; +#endif + +namespace detail { +template +[[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept { + 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 +[[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept { + 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 +[[nodiscard]] constexpr std::basic_string_view +to_string_view(TChar const *str) noexcept { + if (str) + return str; + return {}; +} + +template +[[nodiscard]] constexpr std::basic_string_view +to_string_view(StringType const &str) noexcept { + return str; +} + +class sha1 { +public: + using digest32_t = uint32_t[5]; + using digest8_t = uint8_t[20]; + + static constexpr unsigned int block_bytes = 64; + + [[nodiscard]] inline static uint32_t + left_rotate(uint32_t value, size_t const count) noexcept { + return (value << count) ^ (value >> (32 - count)); + } + + sha1() { reset(); } + + void reset() noexcept { + 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++] = static_cast(d32[0] >> 24); + digest[di++] = static_cast(d32[0] >> 16); + digest[di++] = static_cast(d32[0] >> 8); + digest[di++] = static_cast(d32[0] >> 0); + + digest[di++] = static_cast(d32[1] >> 24); + digest[di++] = static_cast(d32[1] >> 16); + digest[di++] = static_cast(d32[1] >> 8); + digest[di++] = static_cast(d32[1] >> 0); + + digest[di++] = static_cast(d32[2] >> 24); + digest[di++] = static_cast(d32[2] >> 16); + digest[di++] = static_cast(d32[2] >> 8); + digest[di++] = static_cast(d32[2] >> 0); + + digest[di++] = static_cast(d32[3] >> 24); + digest[di++] = static_cast(d32[3] >> 16); + digest[di++] = static_cast(d32[3] >> 8); + digest[di++] = static_cast(d32[3] >> 0); + + digest[di++] = static_cast(d32[4] >> 24); + digest[di++] = static_cast(d32[4] >> 16); + digest[di++] = static_cast(d32[4] >> 8); + digest[di++] = static_cast(d32[4] >> 0); + + return digest; + } + +private: + void process_block() { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) { + w[i] = static_cast(m_block[i * 4 + 0] << 24); + w[i] |= static_cast(m_block[i * 4 + 1] << 16); + w[i] |= static_cast(m_block[i * 4 + 2] << 8); + w[i] |= static_cast(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; +}; + +template +inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; + +template <> +inline constexpr wchar_t empty_guid[37] = + L"00000000-0000-0000-0000-000000000000"; + +template +inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; + +template <> +inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; +} // namespace detail + +// -------------------------------------------------------------------------------------------------------------------------- +// 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 +}; + +// Forward declare uuid & to_string so that we can declare to_string as a friend +// later. +class uuid; +template , + class Allocator = std::allocator> +std::basic_string to_string(uuid const &id); + +// -------------------------------------------------------------------------------------------------------------------------- +// uuid class +// -------------------------------------------------------------------------------------------------------------------------- +class uuid { +public: + using value_type = uint8_t; + + constexpr uuid() noexcept = default; + + uuid(value_type (&arr)[16]) noexcept { + std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); + } + + constexpr uuid(std::array const &arr) noexcept : data{arr} {} + + explicit uuid(span bytes) { + std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); + } + + template + explicit uuid(ForwardIterator first, ForwardIterator last) { + if (std::distance(first, last) == 16) + std::copy(first, last, std::begin(data)); + } + + [[nodiscard]] 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; + } + + [[nodiscard]] 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; + } + + [[nodiscard]] 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); } + + [[nodiscard]] inline span as_bytes() const { + return span( + reinterpret_cast(data.data()), 16); + } + + template + [[nodiscard]] constexpr static bool + is_valid_uuid(StringType const &in_str) noexcept { + auto str = detail::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + if (str.empty()) + return false; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return false; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) { + if (str[i] == '-') + continue; + + if (index >= 16 || !detail::is_hex(str[i])) { + return false; + } + + if (firstDigit) { + firstDigit = false; + } else { + index++; + firstDigit = true; + } + } + + if (index < 16) { + return false; + } + + return true; + } + + template + [[nodiscard]] constexpr static std::optional + from_string(StringType const &in_str) noexcept { + auto str = detail::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + std::array data{{0}}; + + if (str.empty()) + return {}; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return {}; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) { + if (str[i] == '-') + continue; + + if (index >= 16 || !detail::is_hex(str[i])) { + return {}; + } + + if (firstDigit) { + data[index] = static_cast(detail::hex2char(str[i]) << 4); + firstDigit = false; + } else { + data[index] = + static_cast(data[index] | detail::hex2char(str[i])); + index++; + firstDigit = true; + } + } + + if (index < 16) { + return {}; + } + + return uuid{data}; + } + +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 + friend std::basic_ostream & + operator<<(std::basic_ostream &s, uuid const &id); + + template + friend std::basic_string to_string(uuid const &id); + + friend std::hash; +}; + +// -------------------------------------------------------------------------------------------------------------------------- +// operators and non-member functions +// -------------------------------------------------------------------------------------------------------------------------- + +[[nodiscard]] inline bool operator==(uuid const &lhs, + uuid const &rhs) noexcept { + return lhs.data == rhs.data; +} + +[[nodiscard]] inline bool operator!=(uuid const &lhs, + uuid const &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline bool operator<(uuid const &lhs, uuid const &rhs) noexcept { + return lhs.data < rhs.data; +} + +template +[[nodiscard]] inline std::basic_string +to_string(uuid const &id) { + std::basic_string uustr{detail::empty_guid}; + + for (size_t i = 0, index = 0; i < 36; ++i) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + continue; + } + uustr[i] = detail::guid_encoder[id.data[index] >> 4 & 0x0f]; + uustr[++i] = detail::guid_encoder[id.data[index] & 0x0f]; + index++; + } + + return uustr; +} + +template +std::basic_ostream & +operator<<(std::basic_ostream &s, uuid const &id) { + s << to_string(id); + return s; +} + +inline void swap(uuids::uuid &lhs, uuids::uuid &rhs) noexcept { lhs.swap(rhs); } + +// -------------------------------------------------------------------------------------------------------------------------- +// namespace IDs that could be used for generating name-based uuids +// -------------------------------------------------------------------------------------------------------------------------- + +// 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}}; + +// 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}}; + +// 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}}; + +// 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}}; + +// -------------------------------------------------------------------------------------------------------------------------- +// uuid generators +// -------------------------------------------------------------------------------------------------------------------------- + +#ifdef UUID_SYSTEM_GENERATOR +class uuid_system_generator { +public: + using result_type = uuid; + + uuid operator()() { +#ifdef _WIN32 + + GUID newId; + HRESULT hr = ::CoCreateGuid(&newId); + + if (FAILED(hr)) { + throw std::system_error(hr, std::system_category(), + "CoCreateGuid failed"); + } + + std::array bytes = { + {static_cast((newId.Data1 >> 24) & 0xFF), + static_cast((newId.Data1 >> 16) & 0xFF), + static_cast((newId.Data1 >> 8) & 0xFF), + static_cast((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 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 + +template +class basic_uuid_random_generator { +public: + using engine_type = UniformRandomNumberGenerator; + + explicit basic_uuid_random_generator(engine_type &gen) + : generator(&gen, [](auto) {}) {} + explicit basic_uuid_random_generator(engine_type *gen) + : generator(gen, [](auto) {}) {} + + [[nodiscard]] uuid operator()() { + alignas(uint32_t) 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; + +class uuid_name_generator { +public: + explicit uuid_name_generator(uuid const &namespace_uuid) noexcept + : nsuuid(namespace_uuid) {} + + template + [[nodiscard]] uuid operator()(StringType const &name) { + reset(); + process_characters(detail::to_string_view(name)); + return make_uuid(); + } + +private: + void reset() { + hasher.reset(); + std::byte bytes[16]; + auto nsbytes = nsuuid.as_bytes(); + std::copy(std::cbegin(nsbytes), std::cend(nsbytes), bytes); + hasher.process_bytes(bytes, 16); + } + + template + void process_characters(std::basic_string_view const str) { + for (uint32_t c : str) { + hasher.process_byte(static_cast(c & 0xFF)); + if constexpr (!std::is_same_v) { + hasher.process_byte(static_cast((c >> 8) & 0xFF)); + hasher.process_byte(static_cast((c >> 16) & 0xFF)); + hasher.process_byte(static_cast((c >> 24) & 0xFF)); + } + } + } + + [[nodiscard]] uuid make_uuid() { + detail::sha1::digest8_t digest; + hasher.get_digest_bytes(digest); + + // variant must be 0b10xxxxxx + digest[8] &= 0xBF; + digest[8] |= 0x80; + + // version must be 0b0101xxxx + digest[6] &= 0x5F; + digest[6] |= 0x50; + + return uuid{digest, digest + 16}; + } + +private: + uuid nsuuid; + detail::sha1 hasher; +}; + +#ifdef UUID_TIME_GENERATOR +// !!! DO NOT USE THIS IN PRODUCTION +// this implementation is unreliable for good uuids +class uuid_time_generator { + using mac_address = std::array; + + std::optional device_address; + + [[nodiscard]] bool get_mac_address() { + if (device_address.has_value()) { + return true; + } + +#ifdef _WIN32 + DWORD len = 0; + auto ret = GetAdaptersInfo(nullptr, &len); + if (ret != ERROR_BUFFER_OVERFLOW) + return false; + std::vector buf(len); + auto pips = reinterpret_cast(&buf.front()); + ret = GetAdaptersInfo(pips, &len); + if (ret != ERROR_SUCCESS) + return false; + mac_address addr; + std::copy(pips->Address, pips->Address + 6, std::begin(addr)); + device_address = addr; +#endif + + return device_address.has_value(); + } + + [[nodiscard]] long long get_time_intervals() { + auto start = std::chrono::system_clock::from_time_t(time_t(-12219292800)); + auto diff = std::chrono::system_clock::now() - start; + auto ns = + std::chrono::duration_cast(diff).count(); + return ns / 100; + } + + [[nodiscard]] static unsigned short get_clock_sequence() { + static std::mt19937 clock_gen(std::random_device{}()); + static std::uniform_int_distribution clock_dis; + static std::atomic_ushort clock_sequence = clock_dis(clock_gen); + return clock_sequence++; + } + +public: + [[nodiscard]] uuid operator()() { + if (get_mac_address()) { + std::array data; + + auto tm = get_time_intervals(); + + auto clock_seq = get_clock_sequence(); + + auto ptm = reinterpret_cast(&tm); + + memcpy(&data[0], ptm + 4, 4); + memcpy(&data[4], ptm + 2, 2); + memcpy(&data[6], ptm, 2); + + memcpy(&data[8], &clock_seq, 2); + + // variant must be 0b10xxxxxx + data[8] &= 0xBF; + data[8] |= 0x80; + + // version must be 0b0001xxxx + data[6] &= 0x1F; + data[6] |= 0x10; + + memcpy(&data[10], &device_address.value()[0], 6); + + return uuids::uuid{std::cbegin(data), std::cend(data)}; + } + + return {}; + } +}; +#endif +} // namespace uuids + +namespace std { +template <> struct hash { + using argument_type = uuids::uuid; + using result_type = std::size_t; + + [[nodiscard]] result_type operator()(argument_type const &uuid) const { +#ifdef UUID_HASH_STRING_BASED + std::hash hasher; + return static_cast(hasher(uuids::to_string(uuid))); +#else + uint64_t l = static_cast(uuid.data[0]) << 56 | + static_cast(uuid.data[1]) << 48 | + static_cast(uuid.data[2]) << 40 | + static_cast(uuid.data[3]) << 32 | + static_cast(uuid.data[4]) << 24 | + static_cast(uuid.data[5]) << 16 | + static_cast(uuid.data[6]) << 8 | + static_cast(uuid.data[7]); + uint64_t h = static_cast(uuid.data[8]) << 56 | + static_cast(uuid.data[9]) << 48 | + static_cast(uuid.data[10]) << 40 | + static_cast(uuid.data[11]) << 32 | + static_cast(uuid.data[12]) << 24 | + static_cast(uuid.data[13]) << 16 | + static_cast(uuid.data[14]) << 8 | + static_cast(uuid.data[15]); + + if constexpr (sizeof(result_type) > 4) { + return result_type(l ^ h); + } else { + uint64_t hash64 = l ^ h; + return result_type(uint32_t(hash64 >> 32) ^ uint32_t(hash64)); + } +#endif + } +}; +} // namespace std + +#endif /* STDUUID_H */ diff --git a/CHANGELOG.md b/CHANGELOG.md index 72d17d60..8b1530f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Changes from v2.0.0-rc +* Require `c++20` * Removed MSVC compilation support (MinGW-64 should be used) * Upgraded `boost` to v1.83.0 * Upgraded `curl` to v8.4.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 33bb2fde..66f4c6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,7 +217,6 @@ include(cmake/zlib.cmake) include(cmake/openssl.cmake) include(cmake/curl.cmake) include(cmake/boost.cmake) -include(cmake/libuuid.cmake) include(cmake/rocksdb.cmake) include(cmake/libsodium.cmake) @@ -225,12 +224,12 @@ include_directories(include) include_directories(SYSTEM ${Boost_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/cpp-httplib + ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/stduuid ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/json ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/pugixml/src ${CURL_INCLUDE_DIRS} ${LIBFUSE2_INCLUDE_DIRS} ${LIBFUSE3_INCLUDE_DIRS} - ${LIBUUID_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} ${ROCKSDB_INCLUDE_DIRS} ) @@ -239,7 +238,6 @@ set(REPERTORY_LINK_LIBRARIES ${ROCKSDB_LIBRARIES} ${LIBFUSE2_LIBRARIES} ${LIBFUSE3_LIBRARIES} - ${LIBUUID_LIBRARIES} ${Boost_LIBRARIES} ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} diff --git a/README.md b/README.md index f98b149a..ccc552cf 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ on Windows. * [RocksDB](https://rocksdb.org/) * [ScPrime](https://scpri.me/) * [Sia Decentralized Cloud Storage](https://sia.tech/) +* [stduuid](https://github.com/mariusbancila/stduuid) * [WinFSP - FUSE for Windows](https://github.com/billziss-gh/winfsp) * [zlib](https://zlib.net/) diff --git a/cmake/librepertory.cmake b/cmake/librepertory.cmake index beca34bc..59370e67 100644 --- a/cmake/librepertory.cmake +++ b/cmake/librepertory.cmake @@ -20,7 +20,3 @@ add_dependencies(librepertory rocksdb_project zlib_project ) - -if (LINUX) - add_dependencies(librepertory libuuid_project) -endif() diff --git a/cmake/libuuid.cmake b/cmake/libuuid.cmake deleted file mode 100644 index 712cfd57..00000000 --- a/cmake/libuuid.cmake +++ /dev/null @@ -1,20 +0,0 @@ -if (LINUX) - set(LIBUUID_PROJECT_NAME libuuid_${LIBUUID_VERSION}) - set(LIBUUID_BUILD_ROOT ${EXTERNAL_BUILD_ROOT}/builds/${LIBUUID_PROJECT_NAME}) - #URL "https://www.mirrorservice.org/sites/ftp.ossp.org/pkg/lib/uuid/uuid-${LIBUUID_VERSION}.tar.gz" - ExternalProject_Add(libuuid_project - DOWNLOAD_NO_PROGRESS 1 - PREFIX ${LIBUUID_BUILD_ROOT} - URL https://src.fedoraproject.org/repo/pkgs/uuid/uuid-${LIBUUID_VERSION}.tar.gz/5db0d43a9022a6ebbbc25337ae28942f/uuid-${LIBUUID_VERSION}.tar.gz - BUILD_IN_SOURCE 1 - CONFIGURE_COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/config.guess ./config.guess && - cp ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/config.sub ./config.sub && - CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER};./configure --disable-shared --enable-static --prefix=${EXTERNAL_BUILD_ROOT} - BUILD_COMMAND CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER};make - INSTALL_COMMAND make install - ) - set(LIBUUID_LIBRARIES libuuid.a) - - add_dependencies(libuuid_project zlib_project) -endif() - diff --git a/cmake/settings.cmake b/cmake/settings.cmake index ccd5f7ff..955986c6 100644 --- a/cmake/settings.cmake +++ b/cmake/settings.cmake @@ -8,7 +8,7 @@ else() endif() set(CMAKE_COLOR_MAKEFILE OFF) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(THREADS_PREFER_PTHREAD_FLAG ON) diff --git a/include/common.hpp b/include/common.hpp index 24556966..8bb8b96b 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -59,11 +59,6 @@ #include #include #endif -#if __linux__ -#include -#else -#include -#endif #endif #include @@ -109,6 +104,7 @@ template #include #include #include +#include #ifdef _WIN32 #include diff --git a/include/utils/utils.hpp b/include/utils/utils.hpp index d3106552..fa6d8317 100644 --- a/include/utils/utils.hpp +++ b/include/utils/utils.hpp @@ -107,8 +107,8 @@ void spin_wait_for_mutex(std::function complete, void spin_wait_for_mutex(bool &complete, std::condition_variable &cond, std::mutex &mtx, const std::string &text = ""); -template -[[nodiscard]] auto to_hex_string(const t &val) -> std::string; +template +[[nodiscard]] auto to_hex_string(const collection_t &collection) -> std::string; // template implementations template @@ -154,29 +154,27 @@ template return begin + repertory_rand() % ((end + 1) - begin); } -template -void remove_element_from(t &collection, const typename t::value_type &value) { +template +void remove_element_from(collection_t &collection, + const typename collection_t::value_type &value) { collection.erase(std::remove(collection.begin(), collection.end(), value), collection.end()); } -template -[[nodiscard]] auto to_hex_string(const t &value) -> std::string { - std::string ret{}; +template +[[nodiscard]] auto to_hex_string(const collection_t &collection) + -> std::string { + static_assert(sizeof(typename collection_t::value_type) == 1U, + "value_type must be 1 byte in size"); + static constexpr const auto mask = 0xFF; - std::array tmp{}; - for (const auto &num : value) { -#ifdef _WIN32 - sprintf_s(h.data(), h.size() - 1U, "%x", static_cast(num)); -#else - sprintf(tmp.data(), "%x", static_cast(num)); -#endif - - ret += - (strlen(tmp.data()) == 1) ? std::string("0") + tmp.data() : tmp.data(); + std::stringstream stream; + for (const auto &val : collection) { + stream << std::setfill('0') << std::setw(2) << std::hex + << (static_cast(val) & mask); } - return ret; + return stream.str(); } } // namespace repertory::utils diff --git a/include/utils/uuid++.hh b/include/utils/uuid++.hh deleted file mode 100644 index d5821508..00000000 --- a/include/utils/uuid++.hh +++ /dev/null @@ -1,331 +0,0 @@ -/* -** MODIFIED BY -** - Modifications for C++11 -** - Memory leak avoidance -** -** OSSP uuid - Universally Unique Identifier -** Copyright (c) 2004-2008 Ralf S. Engelschall -** Copyright (c) 2004-2008 The OSSP Project -** -** This file is part of OSSP uuid, a library for the generation -** of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/ -** -** Permission to use, copy, modify, and distribute this software for -** any purpose with or without fee is hereby granted, provided that -** the above copyright notice and this permission notice appear in all -** copies. -** -** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR -** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -** SUCH DAMAGE. -** -** uuid++.hh: library C++ API definition -*/ -#if __linux__ - -#ifndef __UUIDXX_HH__ -#define __UUIDXX_HH__ -#include -#include -#include -#include -#include - -namespace repertory { -/* UUID exception class */ -class uuid_error_t : public virtual std::exception { -public: - uuid_error_t() : rc(UUID_RC_OK){}; - - explicit uuid_error_t(const uuid_rc_t &code) : rc(code) {} - -private: - const uuid_rc_t rc; - -public: - uuid_rc_t code() const { return rc; }; - - const char *what() const noexcept override { - static std::string ret; - if (ret.empty()) { - auto *p = uuid_error(rc); - ret = std::string(p, strlen(p)); - free(p); - } - return ret.c_str(); - } -}; - -/* UUID object class */ -class uuid { -public: - /* construction & destruction */ - - /* standard constructor */ - uuid() { - uuid_rc_t rc; - if ((rc = uuid_create(&ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* copy constructor */ - uuid(const uuid &obj) { - /* Notice: the copy constructor is the same as the assignment - operator (with the object as the argument) below, except that - (1) no check for self-assignment is required, (2) no existing - internals have to be destroyed and (3) no return value is given back. */ - uuid_rc_t rc; - if ((rc = uuid_clone(obj.ctx, &ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* import constructor */ - explicit uuid(const uuid_t *obj) { - uuid_rc_t rc; - if (obj == nullptr) - throw uuid_error_t(UUID_RC_ARG); - if ((rc = uuid_clone(obj, &ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* import constructor */ - explicit uuid(const std::vector &bin) { - uuid_rc_t rc; - if (bin.empty()) - throw uuid_error_t(UUID_RC_ARG); - if ((rc = uuid_create(&ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - import(bin); - } - - /* import constructor */ - explicit uuid(const std::string &str) { - uuid_rc_t rc; - if (str.empty()) - throw uuid_error_t(UUID_RC_ARG); - if ((rc = uuid_create(&ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - import(str); - } - - /* move constructor */ - uuid(uuid &&obj) noexcept : ctx(obj.ctx) {} - - /* destructor */ - ~uuid() { uuid_destroy(ctx); } - -private: - uuid_t *ctx = nullptr; - -public: - /* copying & cloning */ - - /* copy assignment operator */ - uuid &operator=(const uuid &obj) { - uuid_rc_t rc; - if (this != &obj) { - if ((rc = uuid_destroy(ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - if ((rc = uuid_clone(obj.ctx, &ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - } - return *this; - } - - /* move assignment operator */ - uuid &operator=(uuid &&obj) { - if (this != &obj) { - ctx = obj.ctx; - } - return *this; - } - - /* import assignment operator */ - uuid &operator=(const uuid_t *obj) { - uuid_rc_t rc; - if (obj == nullptr) - throw uuid_error_t(UUID_RC_ARG); - if ((rc = uuid_clone(obj, &ctx)) != UUID_RC_OK) - throw uuid_error_t(rc); - return *this; - } - - /* import assignment operator */ - uuid &operator=(const std::vector &bin) { - if (bin.empty()) - throw uuid_error_t(UUID_RC_ARG); - import(bin); - return *this; - } - - /* import assignment operator */ - uuid &operator=(const std::string &str) { - if (str.empty()) - throw uuid_error_t(UUID_RC_ARG); - import(str); - return *this; - } - - /* regular method */ - uuid clone() { return uuid(*this); } - - /* content generation */ - - /* regular method */ - void load(const std::string &name) { - uuid_rc_t rc; - if (name.empty()) - throw uuid_error_t(UUID_RC_ARG); - if ((rc = uuid_load(ctx, &name[0])) != UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* regular method */ - void make(unsigned int mode, ...) { - uuid_rc_t rc; - va_list ap; - - va_start(ap, mode); - if ((mode & UUID_MAKE_V3) || (mode & UUID_MAKE_V5)) { - const uuid *ns = (const uuid *)va_arg(ap, const uuid *); - const char *name = (const char *)va_arg(ap, char *); - if (ns == nullptr || name == nullptr) - throw uuid_error_t(UUID_RC_ARG); - rc = uuid_make(ctx, mode, ns->ctx, name); - } else - rc = uuid_make(ctx, mode); - va_end(ap); - if (rc != UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* content comparison */ - - /* regular method */ - int isnil() { - uuid_rc_t rc; - int rv; - - if ((rc = uuid_isnil(ctx, &rv)) != UUID_RC_OK) - throw uuid_error_t(rc); - return rv; - } - - /* regular method */ - int compare(const uuid &obj) { - uuid_rc_t rc; - int rv; - - if ((rc = uuid_compare(ctx, obj.ctx, &rv)) != UUID_RC_OK) - throw uuid_error_t(rc); - return rv; - } - - /* comparison operator */ - int operator==(const uuid &obj) { return (compare(obj) == 0); } - - /* comparison operator */ - int operator!=(const uuid &obj) { return (compare(obj) != 0); } - - /* comparison operator */ - int operator<(const uuid &obj) { return (compare(obj) < 0); } - - /* comparison operator */ - int operator<=(const uuid &obj) { return (compare(obj) <= 0); } - - /* comparison operator */ - int operator>(const uuid &obj) { return (compare(obj) > 0); } - - /* comparison operator */ - int operator>=(const uuid &obj) { return (compare(obj) >= 0); } - - /* content importing & exporting */ - - /* regular method */ - void import(const std::vector &bin) { - uuid_rc_t rc; - if ((rc = uuid_import(ctx, UUID_FMT_BIN, &bin[0], UUID_LEN_BIN)) != - UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* regular method */ - void import(const std::string &str) { - uuid_rc_t rc; - if ((rc = uuid_import(ctx, UUID_FMT_STR, &str[0], UUID_LEN_STR)) != - UUID_RC_OK) - if ((rc = uuid_import(ctx, UUID_FMT_SIV, &str[0], UUID_LEN_SIV)) != - UUID_RC_OK) - throw uuid_error_t(rc); - } - - /* regular method */ - std::vector binary() { - uuid_rc_t rc; - void *bin = nullptr; - if ((rc = uuid_export(ctx, UUID_FMT_BIN, &bin, nullptr)) != UUID_RC_OK) - throw uuid_error_t(rc); - - std::vector data; - data.resize(UUID_LEN_BIN); - memcpy(&data[0], bin, UUID_LEN_BIN); - free(bin); - return data; - } - - /* regular method */ - std::string string() { - uuid_rc_t rc; - char *str = nullptr; - if ((rc = uuid_export(ctx, UUID_FMT_STR, (void **)&str, nullptr)) != - UUID_RC_OK) - throw uuid_error_t(rc); - std::string data; - data.resize(UUID_LEN_STR + 1); - memcpy(&data[0], str, UUID_LEN_STR); - free(str); - return &data[0]; - } - - /* regular method */ - std::string integer() { - uuid_rc_t rc; - char *str = nullptr; - if ((rc = uuid_export(ctx, UUID_FMT_SIV, (void **)&str, nullptr)) != - UUID_RC_OK) - throw uuid_error_t(rc); - std::string data; - data.resize(UUID_LEN_SIV + 1); - memcpy(&data[0], str, UUID_LEN_SIV); - free(str); - return &data[0]; - } - - /* regular method */ - std::string summary() { - uuid_rc_t rc; - char *txt = nullptr; - if ((rc = uuid_export(ctx, UUID_FMT_TXT, (void **)&txt, nullptr)) != - UUID_RC_OK) - throw uuid_error_t(rc); - std::string data(txt, strlen(txt)); - free(txt); - return data; - } - - /* regular method */ - unsigned long version() { return uuid_version(); } -}; -} // namespace repertory -#endif /* __UUIDXX_HH__ */ - -#endif diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index d684dc87..52d17748 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -119,33 +119,15 @@ auto create_curl() -> CURL * { } auto create_uuid_string() -> std::string { -#ifdef _WIN32 - UUID guid{}; - UuidCreate(&guid); + std::random_device random_device; + auto seed_data = std::array{}; + std::generate(std::begin(seed_data), std::end(seed_data), + std::ref(random_device)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + uuids::uuid_random_generator gen{generator}; - unsigned char *s; - UuidToStringA(&guid, &s); - - std::string ret(reinterpret_cast(s)); - RpcStringFreeA(&s); - - return ret; -#else -#if __linux__ - uuid guid; - guid.make(UUID_MAKE_V4); - return guid.string(); -#else - uuid_t guid; - uuid_generate_random(guid); - - std::string ret; - ret.resize(37); - uuid_unparse(guid, &ret[0]); - - return ret.c_str(); -#endif -#endif + return uuids::to_string(gen()); } auto create_volume_label(const provider_type &prov) -> std::string { @@ -220,23 +202,12 @@ auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD { } auto get_environment_variable(const std::string &variable) -> std::string { -#ifdef _WIN32 - std::string val; - auto sz = ::GetEnvironmentVariable(&variable[0], nullptr, 0); - if (sz > 0) { - val.resize(sz); - ::GetEnvironmentVariable(&variable[0], &val[0], sz); - } - - return value.c_str(); -#else static std::mutex mtx{}; mutex_lock lock{mtx}; const auto *val = std::getenv(variable.c_str()); auto ret = std::string(val == nullptr ? "" : val); return ret; -#endif } auto get_file_time_now() -> std::uint64_t {