202 lines
5.3 KiB
C++
202 lines
5.3 KiB
C++
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2018 Xavier "Crashoz" Launey
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <random>
|
|
#include <string>
|
|
#include <limits>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <cstring>
|
|
|
|
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 <random> 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 <typename RNG>
|
|
class UUIDGenerator {
|
|
public:
|
|
UUIDGenerator() : generator(new RNG(std::random_device()())), distribution(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max()) {}
|
|
|
|
UUIDGenerator(uint64_t seed) : generator(new RNG(seed)), distribution(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max()) {}
|
|
|
|
UUIDGenerator(RNG &gen) : generator(gen), distribution(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::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<RNG> generator;
|
|
std::uniform_int_distribution<uint64_t> distribution;
|
|
};
|
|
|
|
}
|
|
|
|
namespace std {
|
|
template <> struct hash<UUIDv4::UUID>
|
|
{
|
|
size_t operator()(const UUIDv4::UUID &uuid) const {
|
|
return uuid.hash();
|
|
}
|
|
};
|
|
}
|