/* MIT License Copyright (c) 2018 Xavier "Crashoz" Launey */ #pragma once #include #include #include #include #include #include #include #include namespace UUIDv4 { /* Converts a 128-bit unsigned int to a UUIDv4 string representation. */ void inline uint128_to_uuid_string(const uint64_t* data, char* res) { static const char* hex = "0123456789abcdef"; for (int i = 0, j = 0; i < 16; ++i) { uint8_t byte = ((uint8_t*)data)[i]; res[j++] = hex[byte >> 4]; res[j++] = hex[byte & 0x0F]; if (j == 8 || j == 13 || j == 18 || j == 23) { res[j++] = '-'; } } res[36] = '\0'; } /* Converts a UUIDv4 string representation to a 128-bit unsigned int. */ void inline uuid_string_to_uint128(const char* str, uint64_t* data) { uint8_t* bytes = (uint8_t*)data; for (int i = 0, j = 0; i < 16; ++i, j += 2) { if (str[j] == '-') ++j; char c1 = str[j]; char c2 = str[j + 1]; bytes[i] = ((c1 >= '0' && c1 <= '9') ? (c1 - '0') : (c1 - 'a' + 10)) << 4; bytes[i] |= (c2 >= '0' && c2 <= '9') ? (c2 - '0') : (c2 - 'a' + 10); } } /* * UUIDv4 (random 128-bit) RFC-4122 */ class UUID { public: UUID() {} UUID(const UUID &other) { std::memcpy(data, other.data, sizeof(data)); } /* Builds a 128-bit UUID */ UUID(uint64_t x, uint64_t y) { data[0] = x; data[1] = y; } UUID(const uint8_t* bytes) { std::memcpy(data, bytes, sizeof(data)); } /* Builds a UUID from a byte string (16 bytes long) */ explicit UUID(const std::string &bytes) { std::memcpy(data, bytes.data(), sizeof(data)); } /* Static factory to parse a UUID from its string representation */ static UUID fromStrFactory(const std::string &s) { return fromStrFactory(s.c_str()); } static UUID fromStrFactory(const char* raw) { UUID uuid; uuid_string_to_uint128(raw, uuid.data); return uuid; } void fromStr(const char* raw) { uuid_string_to_uint128(raw, data); } UUID& operator=(const UUID &other) { if (&other == this) { return *this; } std::memcpy(data, other.data, sizeof(data)); return *this; } friend bool operator==(const UUID &lhs, const UUID &rhs) { return lhs.data[0] == rhs.data[0] && lhs.data[1] == rhs.data[1]; } friend bool operator<(const UUID &lhs, const UUID &rhs) { return std::memcmp(lhs.data, rhs.data, sizeof(lhs.data)) < 0; } friend bool operator!=(const UUID &lhs, const UUID &rhs) { return !(lhs == rhs); } friend bool operator>(const UUID &lhs, const UUID &rhs) { return rhs < lhs; } friend bool operator<=(const UUID &lhs, const UUID &rhs) { return !(lhs > rhs); } friend bool operator>=(const UUID &lhs, const UUID &rhs) { return !(lhs < rhs); } /* Serializes the uuid to a byte string (16 bytes) */ std::string bytes() const { return std::string((char*)data, sizeof(data)); } void bytes(std::string &out) const { out.assign((char*)data, sizeof(data)); } void bytes(char* bytes) const { std::memcpy(bytes, data, sizeof(data)); } /* Converts the uuid to its string representation */ std::string str() const { char mem[37]; uint128_to_uuid_string(data, mem); return std::string(mem, 36); } void str(std::string &s) const { char mem[37]; uint128_to_uuid_string(data, mem); s.assign(mem, 36); } void str(char *res) const { uint128_to_uuid_string(data, res); } friend std::ostream& operator<<(std::ostream& stream, const UUID& uuid) { return stream << uuid.str(); } friend std::istream& operator>>(std::istream& stream, UUID& uuid) { std::string s; stream >> s; uuid = fromStrFactory(s); return stream; } size_t hash() const { const uint64_t a = data[0]; const uint64_t b = data[1]; return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2)); } private: alignas(16) uint64_t data[2]; }; /* Generates UUIDv4 from a provided random generator (C++11 module) std::mt19937_64 is highly recommended as it has a SIMD implementation that makes it very fast and it produces high quality randomness. */ template class UUIDGenerator { public: UUIDGenerator() : generator(new RNG(std::random_device()())), distribution(std::numeric_limits::min(), std::numeric_limits::max()) {} UUIDGenerator(uint64_t seed) : generator(new RNG(seed)), distribution(std::numeric_limits::min(), std::numeric_limits::max()) {} UUIDGenerator(RNG &gen) : generator(gen), distribution(std::numeric_limits::min(), std::numeric_limits::max()) {} /* Generates a new UUID */ UUID getUUID() { uint64_t x = distribution(*generator); uint64_t y = distribution(*generator); // The two masks set the uuid version (4) and variant (1) x = (x & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL; y = (y & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL; return UUID(x, y); } private: std::shared_ptr generator; std::uniform_int_distribution distribution; }; } namespace std { template <> struct hash { size_t operator()(const UUIDv4::UUID &uuid) const { return uuid.hash(); } }; }