aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.core/uuid.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.core/uuid.ccm')
-rw-r--r--nihil.core/uuid.ccm768
1 files changed, 768 insertions, 0 deletions
diff --git a/nihil.core/uuid.ccm b/nihil.core/uuid.ccm
new file mode 100644
index 0000000..b1b5d5f
--- /dev/null
+++ b/nihil.core/uuid.ccm
@@ -0,0 +1,768 @@
+// From https://github.com/mariusbancila/stduuid
+//
+// Copyright (c) 2017
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+export module nihil.core:uuid;
+
+import nihil.std;
+
+namespace nihil {
+
+template <typename TChar>
+[[nodiscard]] constexpr auto hex2char(TChar const ch) noexcept -> unsigned char
+{
+ if (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9'))
+ return static_cast<unsigned char>(ch - static_cast<TChar>('0'));
+
+ if (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f'))
+ return static_cast<unsigned char>(10 + ch - static_cast<TChar>('a'));
+
+ if (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F'))
+ return static_cast<unsigned char>(10 + ch - static_cast<TChar>('A'));
+
+ return 0;
+}
+
+template <typename TChar>
+[[nodiscard]] constexpr auto is_hex(TChar const ch) noexcept -> bool
+{
+ return (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) ||
+ (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) ||
+ (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F'));
+}
+
+template <typename TChar>
+[[nodiscard]] constexpr auto
+to_string_view(TChar const *str) noexcept -> std::basic_string_view<TChar>
+{
+ if (str)
+ return str;
+ return {};
+}
+
+template <typename StringType>
+[[nodiscard]] constexpr auto to_string_view(StringType const &str) noexcept
+ -> std::basic_string_view<typename StringType::value_type, typename StringType::traits_type>
+{
+ return str;
+}
+
+struct sha1
+{
+ using digest32_t = std::array<std::uint32_t, 5>;
+ using digest8_t = std::array<std::uint8_t, 20>;
+
+ static constexpr unsigned int block_bytes = 64;
+
+ sha1()
+ {
+ reset();
+ }
+
+ [[nodiscard]] static auto
+ left_rotate(std::uint32_t value, std::size_t const count) noexcept -> std::uint32_t
+ {
+ return (value << count) ^ (value >> (32 - count));
+ }
+
+ auto reset(this sha1 &self) noexcept -> void
+ {
+ self.m_digest[0] = 0x67452301;
+ self.m_digest[1] = 0xEFCDAB89;
+ self.m_digest[2] = 0x98BADCFE;
+ self.m_digest[3] = 0x10325476;
+ self.m_digest[4] = 0xC3D2E1F0;
+ self.m_blockByteIndex = 0;
+ self.m_byteCount = 0;
+ }
+
+ auto process_byte(this sha1 &self, std::uint8_t octet) -> void
+ {
+ self.m_block.at(self.m_blockByteIndex++) = octet;
+ ++self.m_byteCount;
+
+ if (self.m_blockByteIndex == block_bytes) {
+ self.m_blockByteIndex = 0;
+ self.process_block();
+ }
+ }
+
+ auto process_block(this sha1 &self, void const *const start, void const *const end) -> void
+ {
+ auto const *first = static_cast<std::uint8_t const *>(start);
+ auto const *last = static_cast<std::uint8_t const *>(end);
+
+ while (first != last) {
+ self.process_byte(*first);
+ first++;
+ }
+ }
+
+ auto process_bytes(this sha1 &self, void const *const data, std::size_t const len) -> void
+ {
+ auto *block = static_cast<std::uint8_t const *>(data);
+ self.process_block(block, block + len);
+ }
+
+ auto get_digest(this sha1 &self) -> digest32_t
+ {
+ auto const bit_count = self.m_byteCount * 8;
+
+ self.process_byte(0x80);
+ if (self.m_blockByteIndex > 56) {
+ while (self.m_blockByteIndex != 0)
+ self.process_byte(0);
+
+ while (self.m_blockByteIndex < 56)
+ self.process_byte(0);
+ } else {
+ while (self.m_blockByteIndex < 56)
+ self.process_byte(0);
+ }
+
+ self.process_byte(0);
+ self.process_byte(0);
+ self.process_byte(0);
+ self.process_byte(0);
+ self.process_byte(static_cast<unsigned char>((bit_count >> 24U) & 0xFFU));
+ self.process_byte(static_cast<unsigned char>((bit_count >> 16U) & 0xFFU));
+ self.process_byte(static_cast<unsigned char>((bit_count >> 8U) & 0xFFU));
+ self.process_byte(static_cast<unsigned char>((bit_count) & 0xFFU));
+
+ return self.m_digest;
+ }
+
+ auto get_digest_bytes(this sha1 &self) -> digest8_t
+ {
+ auto d32 = self.get_digest();
+
+ return {
+ static_cast<std::uint8_t>(d32[0] >> 24U),
+ static_cast<std::uint8_t>(d32[0] >> 16U),
+ static_cast<std::uint8_t>(d32[0] >> 8U),
+ static_cast<std::uint8_t>(d32[0] >> 0U),
+
+ static_cast<std::uint8_t>(d32[1] >> 24U),
+ static_cast<std::uint8_t>(d32[1] >> 16U),
+ static_cast<std::uint8_t>(d32[1] >> 8U),
+ static_cast<std::uint8_t>(d32[1] >> 0U),
+
+ static_cast<std::uint8_t>(d32[2] >> 24U),
+ static_cast<std::uint8_t>(d32[2] >> 16U),
+ static_cast<std::uint8_t>(d32[2] >> 8U),
+ static_cast<std::uint8_t>(d32[2] >> 0U),
+
+ static_cast<std::uint8_t>(d32[3] >> 24U),
+ static_cast<std::uint8_t>(d32[3] >> 16U),
+ static_cast<std::uint8_t>(d32[3] >> 8U),
+ static_cast<std::uint8_t>(d32[3] >> 0U),
+
+ static_cast<std::uint8_t>(d32[4] >> 24U),
+ static_cast<std::uint8_t>(d32[4] >> 16U),
+ static_cast<std::uint8_t>(d32[4] >> 8U),
+ static_cast<std::uint8_t>(d32[4] >> 0U),
+ };
+ }
+
+private:
+ auto process_block(this sha1 &self) -> void
+ {
+ auto w = std::array<std::uint32_t, 80>{};
+
+ for (std::size_t i = 0; i < 16; i++) {
+ w.at(i) = static_cast<std::uint32_t>(self.m_block.at((i * 4) + 0)) << 24U;
+ w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 1)) << 16U;
+ w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 2)) << 8U;
+ w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 3));
+ }
+
+ for (std::size_t i = 16; i < 80; i++) {
+ w.at(i) = left_rotate(
+ (w.at(i - 3) ^ w.at(i - 8) ^ w.at(i - 14) ^ w.at(i - 16)), 1);
+ }
+
+ auto a = self.m_digest[0];
+ auto b = self.m_digest[1];
+ auto c = self.m_digest[2];
+ auto d = self.m_digest[3];
+ auto e = self.m_digest[4];
+
+ for (std::size_t i = 0; i < 80; ++i) {
+ auto f = std::uint32_t{0};
+ auto k = std::uint32_t{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;
+ }
+
+ auto temp = std::uint32_t{left_rotate(a, 5) + f + e + k + w.at(i)};
+ e = d;
+ d = c;
+ c = left_rotate(b, 30);
+ b = a;
+ a = temp;
+ }
+
+ self.m_digest[0] += a;
+ self.m_digest[1] += b;
+ self.m_digest[2] += c;
+ self.m_digest[3] += d;
+ self.m_digest[4] += e;
+ }
+
+ digest32_t m_digest{};
+ std::array<std::uint8_t, 64> m_block{};
+ std::size_t m_blockByteIndex{};
+ std::size_t m_byteCount{};
+};
+
+template <typename CharT>
+inline constexpr std::string_view empty_guid = "00000000-0000-0000-0000-000000000000";
+
+template <>
+inline constexpr std::wstring_view empty_guid<wchar_t> = L"00000000-0000-0000-0000-000000000000";
+
+template <typename CharT>
+inline constexpr std::string_view guid_encoder = "0123456789abcdef";
+
+template <>
+inline constexpr std::wstring_view guid_encoder<wchar_t> = L"0123456789abcdef";
+
+// ---------------------------------------------------------------------
+// UUID format https://tools.ietf.org/html/rfc4122
+// ---------------------------------------------------------------------
+
+// ---------------------------------------------------------------------
+// Field NDR Data Type Octet # Note
+// 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
+export enum struct uuid_variant : std::uint8_t {
+ // 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
+export enum struct uuid_version : std::uint8_t {
+ // only possible for nil or invalid uuids
+ none = 0,
+ // The time-based version specified in RFC 4122
+ time_based = 1,
+ // DCE Security version, with embedded POSIX UIDs.
+ dce_security = 2,
+ // The name-based version specified in RFS 4122 with MD5 hashing
+ name_based_md5 = 3,
+ // The randomly or pseudo-randomly generated version specified in RFS 4122
+ random_number_based = 4,
+ // The name-based version specified in RFS 4122 with SHA1 hashing
+ name_based_sha1 = 5
+};
+
+// Forward declare uuid and to_string so that we can declare to_string as a
+// friend later.
+export struct uuid;
+export template <typename CharT = char, typename Traits = std::char_traits<CharT>,
+ typename Allocator = std::allocator<CharT>>
+auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>;
+
+// --------------------------------------------------------------------------------------------------------------------------
+// uuid class
+// --------------------------------------------------------------------------------------------------------------------------
+export struct uuid
+{
+ using value_type = std::uint8_t;
+
+ constexpr uuid() noexcept = default;
+
+ uuid(value_type (&arr)[16]) noexcept // NOLINT
+ {
+ std::ranges::copy(arr, std::ranges::begin(data));
+ }
+
+ explicit constexpr uuid(std::array<value_type, 16> const &arr) noexcept
+ : data{arr}
+ {
+ }
+
+ explicit uuid(std::span<value_type, 16> bytes)
+ {
+ std::ranges::copy(bytes, std::ranges::begin(data));
+ }
+
+ explicit uuid(std::span<value_type> bytes)
+ {
+ if (bytes.size() != 16)
+ throw std::logic_error("wrong size for uuid");
+ std::ranges::copy(bytes, std::ranges::begin(data));
+ }
+
+ template <typename ForwardIterator>
+ explicit uuid(ForwardIterator first, ForwardIterator last)
+ {
+ if (std::distance(first, last) != 16)
+ throw std::logic_error("wrong size for uuid");
+
+ std::copy(first, last, std::begin(data));
+ }
+
+ [[nodiscard]] constexpr auto variant() const noexcept -> uuid_variant
+ {
+ if ((data[8] & 0x80U) == 0x00U)
+ return uuid_variant::ncs;
+ else if ((data[8] & 0xC0U) == 0x80U)
+ return uuid_variant::rfc;
+ else if ((data[8] & 0xE0U) == 0xC0U)
+ return uuid_variant::microsoft;
+ else
+ return uuid_variant::reserved;
+ }
+
+ [[nodiscard]] constexpr auto version() const noexcept -> uuid_version
+ {
+ if ((data[6] & 0xF0U) == 0x10U)
+ return uuid_version::time_based;
+ else if ((data[6] & 0xF0U) == 0x20U)
+ return uuid_version::dce_security;
+ else if ((data[6] & 0xF0U) == 0x30U)
+ return uuid_version::name_based_md5;
+ else if ((data[6] & 0xF0U) == 0x40U)
+ return uuid_version::random_number_based;
+ else if ((data[6] & 0xF0U) == 0x50U)
+ return uuid_version::name_based_sha1;
+ else
+ return uuid_version::none;
+ }
+
+ [[nodiscard]] constexpr auto is_nil() const noexcept -> bool
+ {
+ return std::ranges::all_of(data, [](auto i) { return i == 0; });
+ }
+
+ auto swap(uuid &other) noexcept -> void
+ {
+ data.swap(other.data);
+ }
+
+ [[nodiscard]] auto as_bytes() const -> std::span<std::byte const, 16>
+ {
+ return std::span<std::byte const, 16>(
+ reinterpret_cast<std::byte const *>(data.data()), 16);
+ }
+
+ template <typename StringType>
+ [[nodiscard]] constexpr static auto is_valid_uuid(StringType const &in_str) noexcept -> bool
+ {
+ auto str = to_string_view(in_str);
+ auto firstDigit = true;
+ auto hasBraces = std::size_t{0};
+ auto index = std::size_t{0};
+
+ if (str.empty())
+ return false;
+
+ if (str.front() == '{')
+ hasBraces = 1;
+
+ if (hasBraces && str.back() != '}')
+ return false;
+
+ for (std::size_t i = hasBraces; i < str.size() - hasBraces; ++i) {
+ if (str[i] == '-')
+ continue;
+
+ if (index >= 16 || !is_hex(str[i]))
+ return false;
+
+ if (firstDigit) {
+ firstDigit = false;
+ } else {
+ index++;
+ firstDigit = true;
+ }
+ }
+
+ if (index < 16)
+ return false;
+
+ return true;
+ }
+
+ template <typename StringType>
+ [[nodiscard]] constexpr static auto
+ from_string(StringType const &in_str) -> std::optional<uuid>
+ {
+ auto str = to_string_view(in_str);
+ bool firstDigit = true;
+ auto hasBraces = std::size_t{0};
+ auto index = std::size_t{0};
+
+ auto data = std::array<std::uint8_t, 16>{};
+
+ if (str.empty())
+ return {};
+
+ if (str.front() == '{')
+ hasBraces = 1;
+ if (hasBraces && str.back() != '}')
+ return {};
+
+ for (std::size_t i = hasBraces; i < str.size() - hasBraces; ++i) {
+ if (str[i] == '-')
+ continue;
+
+ if (index >= 16 || !is_hex(str[i])) {
+ return {};
+ }
+
+ if (firstDigit) {
+ data.at(index) = static_cast<std::uint8_t>(hex2char(str[i]) << 4);
+ firstDigit = false;
+ } else {
+ data.at(index) =
+ static_cast<std::uint8_t>(data.at(index) | hex2char(str[i]));
+ index++;
+ firstDigit = true;
+ }
+ }
+
+ if (index < 16) {
+ return {};
+ }
+
+ return uuid{data};
+ }
+
+private:
+ std::array<value_type, 16> data{{0}};
+
+ friend auto operator==(uuid const &, uuid const &) noexcept -> bool;
+ friend auto operator<(uuid const &, uuid const &) noexcept -> bool;
+
+ template <class Elem, class Traits>
+ friend auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id)
+ -> std::basic_ostream<Elem, Traits> &;
+
+ template <class CharT, class Traits, class Allocator>
+ friend auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>;
+
+ friend std::hash<uuid>;
+};
+
+// --------------------------------------------------------------------------------------------------------------------------
+// operators and non-member functions
+// --------------------------------------------------------------------------------------------------------------------------
+
+export [[nodiscard]]
+auto operator==(uuid const &lhs, uuid const &rhs) noexcept -> bool
+{
+ return lhs.data == rhs.data;
+}
+
+export [[nodiscard]]
+auto operator!=(uuid const &lhs, uuid const &rhs) noexcept -> bool
+{
+ return !(lhs == rhs);
+}
+
+export [[nodiscard]]
+auto operator<(uuid const &lhs, uuid const &rhs) noexcept -> bool
+{
+ return lhs.data < rhs.data;
+}
+
+export template <typename CharT, typename Traits, typename Allocator>
+[[nodiscard]] auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>
+{
+ auto uustr =
+ std::basic_string<CharT, Traits, Allocator>(std::from_range, empty_guid<CharT>);
+
+ for (std::size_t i = 0, index = 0; i < 36; ++i) {
+ if (i == 8 || i == 13 || i == 18 || i == 23)
+ continue;
+
+ uustr[i] = guid_encoder<CharT>[id.data.at(index) >> 4U & 0x0FU];
+ uustr[++i] = guid_encoder<CharT>[id.data.at(index) & 0x0FU];
+ index++;
+ }
+
+ return uustr;
+}
+
+export template <class Elem, class Traits>
+auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id)
+ -> std::basic_ostream<Elem, Traits> &
+{
+ return s << to_string(id);
+}
+
+export auto swap(uuid &lhs, uuid &rhs) noexcept -> void
+{
+ lhs.swap(rhs);
+}
+
+/***********************************************************************
+ * namespace IDs that could be used for generating name-based uuids
+ */
+
+// Name string is a fully-qualified domain name
+export 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
+export 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)
+export 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)
+export uuid uuid_namespace_x500{
+ {0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
+ 0xc8}
+};
+
+/***********************************************************************
+ * uuid generators
+ */
+
+export template <typename UniformRandomNumberGenerator>
+struct basic_uuid_random_generator
+{
+ 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]] auto operator()() -> uuid
+ {
+ auto bytes = std::array<std::uint8_t, 16>{};
+ std::ranges::generate(bytes, [&] { return distribution(*generator); });
+
+ // variant must be 10xxxxxx
+ bytes[8] &= 0xBFU;
+ bytes[8] |= 0x80U;
+
+ // version must be 0100xxxx
+ bytes[6] &= 0x4FU;
+ bytes[6] |= 0x40U;
+
+ return uuid{std::begin(bytes), std::end(bytes)};
+ }
+
+private:
+ std::uniform_int_distribution<std::uint32_t> distribution;
+ std::shared_ptr<UniformRandomNumberGenerator> generator;
+};
+
+export using uuid_random_generator = basic_uuid_random_generator<std::mt19937>;
+
+export struct uuid_name_generator
+{
+ explicit uuid_name_generator(uuid const &namespace_uuid) noexcept
+ : nsuuid(namespace_uuid)
+ {
+ }
+
+ template <typename StringType>
+ [[nodiscard]] auto operator()(StringType const &name) -> uuid
+ {
+ reset();
+ process_characters(to_string_view(name));
+ return make_uuid();
+ }
+
+private:
+ auto reset() -> void
+ {
+ hasher.reset();
+
+ auto nsbytes = nsuuid.as_bytes();
+
+ auto bytes = std::array<std::byte, 16>();
+ std::ranges::copy(nsbytes, std::ranges::begin(bytes));
+
+ hasher.process_bytes(bytes.data(), bytes.size());
+ }
+
+ template <typename CharT, typename Traits>
+ auto process_characters(std::basic_string_view<CharT, Traits> const str) -> void
+ {
+ for (std::uint32_t c : str) {
+ hasher.process_byte(static_cast<std::uint8_t>(c & 0xFFU));
+ if constexpr (!std::is_same_v<CharT, char>) {
+ hasher.process_byte(static_cast<std::uint8_t>((c >> 8U) & 0xFFU));
+ hasher.process_byte(static_cast<std::uint8_t>((c >> 16U) & 0xFFU));
+ hasher.process_byte(static_cast<std::uint8_t>((c >> 24U) & 0xFFU));
+ }
+ }
+ }
+
+ [[nodiscard]] auto make_uuid() -> uuid
+ {
+ auto digest = hasher.get_digest_bytes();
+
+ // variant must be 0b10xxxxxx
+ digest[8] &= 0xBFU;
+ digest[8] |= 0x80U;
+
+ // version must be 0b0101xxxx
+ digest[6] &= 0x5FU;
+ digest[6] |= 0x50U;
+
+ return uuid(std::span(digest).subspan(0, 16));
+ }
+
+ uuid nsuuid;
+ sha1 hasher;
+};
+
+/*
+ * Create a random UUID.
+ */
+export auto random_uuid() -> uuid
+{
+ auto rd = std::random_device();
+ auto seed_data = std::array<int, std::mt19937::state_size>{};
+ std::ranges::generate(seed_data, std::ref(rd));
+
+ auto seq = std::seed_seq(std::ranges::begin(seed_data), std::ranges::end(seed_data));
+ auto generator = std::mt19937(seq);
+ auto gen = uuid_random_generator{generator};
+
+ return gen();
+}
+
+} // namespace nihil
+
+namespace std {
+
+export template <>
+struct hash<nihil::uuid>
+{
+ using argument_type = nihil::uuid;
+ using result_type = std::size_t;
+
+ [[nodiscard]] auto operator()(argument_type const &uuid) const noexcept -> result_type
+ {
+ auto const l = static_cast<uint64_t>(uuid.data[0]) << 56U |
+ static_cast<uint64_t>(uuid.data[1]) << 48U |
+ static_cast<uint64_t>(uuid.data[2]) << 40U |
+ static_cast<uint64_t>(uuid.data[3]) << 32U |
+ static_cast<uint64_t>(uuid.data[4]) << 24U |
+ static_cast<uint64_t>(uuid.data[5]) << 16U |
+ static_cast<uint64_t>(uuid.data[6]) << 8U |
+ static_cast<uint64_t>(uuid.data[7]);
+
+ auto const h = static_cast<uint64_t>(uuid.data[8]) << 56U |
+ static_cast<uint64_t>(uuid.data[9]) << 48U |
+ static_cast<uint64_t>(uuid.data[10]) << 40U |
+ static_cast<uint64_t>(uuid.data[11]) << 32U |
+ static_cast<uint64_t>(uuid.data[12]) << 24U |
+ static_cast<uint64_t>(uuid.data[13]) << 16U |
+ static_cast<uint64_t>(uuid.data[14]) << 8U |
+ static_cast<uint64_t>(uuid.data[15]);
+
+ return std::hash<std::uint64_t>{}(l ^ h);
+ }
+};
+
+} // namespace std