Something went wrong. Try again.
Time-based Universally Unique Identifier (UUIDs v7) for C++.
Something went wrong. Try again.
3.2 kB · 104 lines · Created 1mo ago
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104#pragma once
#include <array>#include <random>#include <chrono>#include <format>#include <string_view>
#ifdef uuid_t#undef uuid_t#endif
struct uuid_t{ std::array<std::uint8_t, 16> bytes{};
constexpr auto& operator[](this auto& self, size_t index) noexcept { return self.bytes[index]; }
[[nodiscard]] friend constexpr bool operator==(const uuid_t&, const uuid_t&) noexcept = default;};
template <typename CharT>struct std::formatter<uuid_t, CharT>{ bool uppercase = false;
constexpr auto parse(std::basic_format_parse_context<CharT>& ctx) { auto it = ctx.begin(); if (it != ctx.end() && (*it == CharT{'x'} || *it == CharT{'X'})) { uppercase = (*it == CharT{'X'}); ++it; } if (it != ctx.end() && *it != CharT{'}'}) throw std::format_error{"Invalid format specifier for 'uuid_t'"}; return it; }
template <typename FormatContext> auto format(const uuid_t& uuid, FormatContext& ctx) const { constexpr std::string_view lower = "0123456789abcdef"; constexpr std::string_view upper = "0123456789ABCDEF"; const std::string_view digits = uppercase ? upper : lower;
auto out = ctx.out(); for (size_t i{}; i < uuid.bytes.size(); i++) { if (i == 4 || i == 6 || i == 8 || i == 10) *out++ = CharT{'-'}; *out++ = static_cast<CharT>(digits[uuid[i] >> 4]); *out++ = static_cast<CharT>(digits[uuid[i] & 0x0F]); } return out; }};
/* * Generate a time-based Universally Unique Identifier (UUID v7). * https://mariusbancila.ro/blog/2025/10/24/time-based-universally-unique-identifiers-uuids-v7 */inline uuid_t GenerateUUID7(){ uuid_t uuid;
// Get current Unix timestamp, in milliseconds. const auto duration = std::chrono::system_clock::now().time_since_epoch(); const uint64_t ms_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
// Fill with random bytes. std::random_device rd; std::mt19937_64 gen(rd()); std::uniform_int_distribution<uint64_t> dist; const auto rand_hi = dist(gen), rand_lo = dist(gen); for (size_t i{}; i < 8; i++) uuid[i] = (rand_hi >> ((7 - i) * 8)) & 0xFF; for (size_t i{}; i < 8; i++) uuid[i+8] = (rand_lo >> ((7 - i) * 8)) & 0xFF;
// Overwrite the first 6 bytes with timestamp (big-endian). uuid[0] = (ms_since_epoch >> 0x28) & 0xFF; uuid[1] = (ms_since_epoch >> 0x20) & 0xFF; uuid[2] = (ms_since_epoch >> 0x18) & 0xFF; uuid[3] = (ms_since_epoch >> 0x10) & 0xFF; uuid[4] = (ms_since_epoch >> 0x08) & 0xFF; uuid[5] = (ms_since_epoch) & 0xFF;
// Set version (0b0111) and variant (RFC 4122). uuid[6] = (uuid[6] & 0x0F) | 0x70; uuid[8] = (uuid[8] & 0x3F) | 0x80;
return uuid;}
inline std::wstring StringFromUUID(const uuid_t& id){ return std::format( L"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", 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] );}