aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README1
-rw-r--r--nihil/CMakeLists.txt1
-rw-r--r--nihil/nihil.ccm1
-rw-r--r--nihil/tests/CMakeLists.txt1
-rw-r--r--nihil/tests/uuid.cc997
-rw-r--r--nihil/uuid.ccm760
6 files changed, 1761 insertions, 0 deletions
diff --git a/README b/README
index bf4ca68..42471d0 100644
--- a/README
+++ b/README
@@ -13,6 +13,7 @@ all of nihil is in the public domain, with the exception of:
- nihil/generator.ccm (BSL)
- nihil/monad.ccm (MIT)
+- nihil/uuid.ccm (MIT)
requirements
------------
diff --git a/nihil/CMakeLists.txt b/nihil/CMakeLists.txt
index ae2f49d..c84cd32 100644
--- a/nihil/CMakeLists.txt
+++ b/nihil/CMakeLists.txt
@@ -25,6 +25,7 @@ target_sources(nihil
spawn.ccm
tabulate.ccm
usage_error.ccm
+ uuid.ccm
write_file.ccm
PRIVATE
diff --git a/nihil/nihil.ccm b/nihil/nihil.ccm
index 85050e1..ac3b13f 100644
--- a/nihil/nihil.ccm
+++ b/nihil/nihil.ccm
@@ -27,4 +27,5 @@ export import :skipws;
export import :spawn;
export import :tabulate;
export import :usage_error;
+export import :uuid;
export import :write_file;
diff --git a/nihil/tests/CMakeLists.txt b/nihil/tests/CMakeLists.txt
index 25111c2..71de926 100644
--- a/nihil/tests/CMakeLists.txt
+++ b/nihil/tests/CMakeLists.txt
@@ -13,6 +13,7 @@ add_executable(nihil.test
skipws.cc
spawn.cc
tabulate.cc
+ uuid.cc
)
target_link_libraries(nihil.test PRIVATE
diff --git a/nihil/tests/uuid.cc b/nihil/tests/uuid.cc
new file mode 100644
index 0000000..dabbca6
--- /dev/null
+++ b/nihil/tests/uuid.cc
@@ -0,0 +1,997 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <random>
+#include <set>
+#include <unordered_set>
+
+#include <catch2/catch_test_macros.hpp>
+
+namespace
+{
+
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html
+template <typename EngineT, std::size_t StateSize = EngineT::state_size>
+void seed_rng(EngineT& engine)
+{
+ using engine_type = typename EngineT::result_type;
+ using device_type = std::random_device::result_type;
+ using seedseq_type = std::seed_seq::result_type;
+
+ constexpr auto bytes_needed = StateSize * sizeof(engine_type);
+ constexpr auto numbers_needed =
+ (sizeof(device_type) < sizeof(seedseq_type))
+ ? (bytes_needed / sizeof(device_type))
+ : (bytes_needed / sizeof(seedseq_type));
+
+ auto numbers = std::array<device_type, numbers_needed>{};
+ auto rnddev = std::random_device{};
+ std::ranges::generate(numbers, std::ref(rnddev));
+
+ auto seedseq = std::seed_seq(std::cbegin(numbers),
+ std::cend(numbers));
+ engine.seed(seedseq);
+}
+
+} // anonymous namespace
+
+import nihil;
+
+using namespace nihil;
+
+TEST_CASE("uuid: Test multiple default generators", "[uuid]")
+{
+ uuid id1;
+ uuid id2;
+
+ {
+ std::random_device rd;
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::mt19937 generator(seq);
+
+ id1 = uuid_random_generator{ generator }();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+ }
+
+ {
+ std::random_device rd;
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::mt19937 generator(seq);
+
+ id2 = uuid_random_generator{ generator }();
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+ }
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test default generator", "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::mt19937 generator(seq);
+
+ uuid const guid = uuid_random_generator{generator}();
+ REQUIRE(!guid.is_nil());
+ REQUIRE(guid.version() == uuid_version::random_number_based);
+ REQUIRE(guid.variant() == uuid_variant::rfc);
+}
+
+TEST_CASE("uuid: Test random generator (conversion ctor w/ smart ptr)",
+ "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::mt19937 generator(seq);
+
+ uuid_random_generator dgen(&generator);
+ auto id1 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen();
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test random generator (conversion ctor w/ ptr)", "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ auto generator = std::make_unique<std::mt19937>(seq);
+
+ uuid_random_generator dgen(generator.get());
+ auto id1 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test random generator (conversion ctor w/ ref)", "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::mt19937 generator(seq);
+
+ uuid_random_generator dgen(generator);
+ auto id1 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen();
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ptr) "
+ "w/ ranlux48_base", "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, 6> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::ranlux48_base generator(seq);
+
+ basic_uuid_random_generator<std::ranlux48_base> dgen(&generator);
+ auto id1 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen();
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test basic random generator (conversion ctor w/ smart ptr) "
+ "w/ ranlux48_base", "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, 6> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ auto generator = std::make_unique<std::ranlux48_base>(seq);
+
+ basic_uuid_random_generator<std::ranlux48_base> dgen(generator.get());
+ auto id1 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen();
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ref) "
+ "w/ ranlux48_base", "[uuid]")
+{
+ std::random_device rd;
+ auto seed_data = std::array<int, 6> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::ranlux48_base generator(seq);
+
+ basic_uuid_random_generator<std::ranlux48_base> dgen(generator);
+ auto id1 = dgen();
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::random_number_based);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen();
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::random_number_based);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+}
+
+TEST_CASE("uuid: Test namespaces", "[uuid]")
+{
+ REQUIRE(uuid_namespace_dns == uuid::from_string("6ba7b810-9dad-11d1-80b4-00c04fd430c8"));
+ REQUIRE(uuid_namespace_url == uuid::from_string("6ba7b811-9dad-11d1-80b4-00c04fd430c8"));
+ REQUIRE(uuid_namespace_oid == uuid::from_string("6ba7b812-9dad-11d1-80b4-00c04fd430c8"));
+ REQUIRE(uuid_namespace_x500 == uuid::from_string("6ba7b814-9dad-11d1-80b4-00c04fd430c8"));
+}
+
+TEST_CASE("uuid: Test name generator (char*)", "[uuid]")
+{
+ uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
+
+ auto id1 = dgen("john");
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::name_based_sha1);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen("jane");
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::name_based_sha1);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ auto id3 = dgen("jane");
+ REQUIRE(!id3.is_nil());
+ REQUIRE(id3.version() == uuid_version::name_based_sha1);
+ REQUIRE(id3.variant() == uuid_variant::rfc);
+
+ auto id4 = dgen(L"jane");
+ REQUIRE(!id4.is_nil());
+ REQUIRE(id4.version() == uuid_version::name_based_sha1);
+ REQUIRE(id4.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+ REQUIRE(id2 == id3);
+ REQUIRE(id3 != id4);
+}
+
+TEST_CASE("uuid: Test name generator (std::string)", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
+ auto id1 = dgen("john"s);
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::name_based_sha1);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen("jane"s);
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::name_based_sha1);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ auto id3 = dgen("jane"s);
+ REQUIRE(!id3.is_nil());
+ REQUIRE(id3.version() == uuid_version::name_based_sha1);
+ REQUIRE(id3.variant() == uuid_variant::rfc);
+
+ auto id4 = dgen(L"jane"s);
+ REQUIRE(!id4.is_nil());
+ REQUIRE(id4.version() == uuid_version::name_based_sha1);
+ REQUIRE(id4.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+ REQUIRE(id2 == id3);
+ REQUIRE(id3 != id4);
+}
+
+TEST_CASE("uuid: Test name generator (std::string_view)", "[uuid]")
+{
+ using namespace std::string_view_literals;
+
+ uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
+ auto id1 = dgen("john"sv);
+ REQUIRE(!id1.is_nil());
+ REQUIRE(id1.version() == uuid_version::name_based_sha1);
+ REQUIRE(id1.variant() == uuid_variant::rfc);
+
+ auto id2 = dgen("jane"sv);
+ REQUIRE(!id2.is_nil());
+ REQUIRE(id2.version() == uuid_version::name_based_sha1);
+ REQUIRE(id2.variant() == uuid_variant::rfc);
+
+ auto id3 = dgen("jane"sv);
+ REQUIRE(!id3.is_nil());
+ REQUIRE(id3.version() == uuid_version::name_based_sha1);
+ REQUIRE(id3.variant() == uuid_variant::rfc);
+
+ auto id4 = dgen(L"jane"sv);
+ REQUIRE(!id4.is_nil());
+ REQUIRE(id4.version() == uuid_version::name_based_sha1);
+ REQUIRE(id4.variant() == uuid_variant::rfc);
+
+ REQUIRE(id1 != id2);
+ REQUIRE(id2 == id3);
+ REQUIRE(id3 != id4);
+}
+
+TEST_CASE("uuid: Test name generator equality (char const*, std::string, "
+ "std::string_view)", "[uuid]")
+{
+ using namespace std::literals;
+
+ uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
+ auto id1 = dgen("john");
+ auto id2 = dgen("john"s);
+ auto id3 = dgen("john"sv);
+
+ REQUIRE(id1 == id2);
+ REQUIRE(id2 == id3);
+}
+
+TEST_CASE("uuid: Test default constructor", "[uuid]")
+{
+ auto empty = uuid();
+ REQUIRE(empty.is_nil());
+}
+
+TEST_CASE("uuid: Test string conversion", "[uuid]")
+{
+ auto empty = uuid();
+ REQUIRE(to_string(empty) ==
+ "00000000-0000-0000-0000-000000000000");
+ REQUIRE(to_string<wchar_t>(empty) ==
+ L"00000000-0000-0000-0000-000000000000");
+}
+
+TEST_CASE("uuid: Test is_valid_uuid(char*)", "[uuid]")
+{
+ REQUIRE(uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e43"));
+ REQUIRE(uuid::is_valid_uuid(
+ "{47183823-2574-4bfd-b411-99ed177d3e43}"));
+ REQUIRE(uuid::is_valid_uuid(
+ L"47183823-2574-4bfd-b411-99ed177d3e43"));
+ REQUIRE(uuid::is_valid_uuid(
+ L"{47183823-2574-4bfd-b411-99ed177d3e43}"));
+ REQUIRE(uuid::is_valid_uuid(
+ "00000000-0000-0000-0000-000000000000"));
+ REQUIRE(uuid::is_valid_uuid(
+ "{00000000-0000-0000-0000-000000000000}"));
+ REQUIRE(uuid::is_valid_uuid(
+ L"00000000-0000-0000-0000-000000000000"));
+ REQUIRE(uuid::is_valid_uuid(
+ L"{00000000-0000-0000-0000-000000000000}"));
+}
+
+TEST_CASE("uuid: Test is_valid_uuid(basic_string)", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = L"{47183823-2574-4bfd-b411-99ed177d3e43}"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "00000000-0000-0000-0000-000000000000"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "{00000000-0000-0000-0000-000000000000}"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = L"00000000-0000-0000-0000-000000000000"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = L"{00000000-0000-0000-0000-000000000000}"s;
+ REQUIRE(uuid::is_valid_uuid(str));
+ }
+}
+
+TEST_CASE("uuid: Test is_valid_uuid(basic_string_view)", "[uuid]")
+{
+ using namespace std::string_view_literals;
+
+ REQUIRE(uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e43"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ "{47183823-2574-4bfd-b411-99ed177d3e43}"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ L"47183823-2574-4bfd-b411-99ed177d3e43"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ L"{47183823-2574-4bfd-b411-99ed177d3e43}"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ "00000000-0000-0000-0000-000000000000"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ "{00000000-0000-0000-0000-000000000000}"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ L"00000000-0000-0000-0000-000000000000"sv));
+ REQUIRE(uuid::is_valid_uuid(
+ L"{00000000-0000-0000-0000-000000000000}"sv));
+}
+
+TEST_CASE("uuid: Test is_valid_uuid(char*) invalid format", "[uuid]")
+{
+ REQUIRE(!uuid::is_valid_uuid(""));
+ REQUIRE(!uuid::is_valid_uuid("{}"));
+ REQUIRE(!uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e4"));
+ REQUIRE(!uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e430"));
+ REQUIRE(!uuid::is_valid_uuid(
+ "{47183823-2574-4bfd-b411-99ed177d3e43"));
+ REQUIRE(!uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e43}"));
+}
+
+TEST_CASE("uuid: Test is_valid_uuid(basic_string) invalid format", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ {
+ auto str = ""s;
+ REQUIRE(!uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "{}"s;
+ REQUIRE(!uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s;
+ REQUIRE(!uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s;
+ REQUIRE(!uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s;
+ REQUIRE(!uuid::is_valid_uuid(str));
+ }
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s;
+ REQUIRE(!uuid::is_valid_uuid(str));
+ }
+}
+
+TEST_CASE("uuid: Test is_valid_uuid(basic_string_view) invalid format",
+ "[uuid]")
+{
+ using namespace std::string_view_literals;
+
+ REQUIRE(!uuid::is_valid_uuid(""sv));
+ REQUIRE(!uuid::is_valid_uuid("{}"sv));
+ REQUIRE(!uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e4"sv));
+ REQUIRE(!uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e430"sv));
+ REQUIRE(!uuid::is_valid_uuid(
+ "{47183823-2574-4bfd-b411-99ed177d3e43"sv));
+ REQUIRE(!uuid::is_valid_uuid(
+ "47183823-2574-4bfd-b411-99ed177d3e43}"sv));
+}
+
+TEST_CASE("uuid: Test from_string(char*)", "[uuid]")
+{
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string(guid) == str);
+ }
+
+ {
+ auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
+ }
+
+ {
+ auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
+ REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
+ REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
+ }
+
+ {
+ auto str = L"47183823-2574-4bfd-b411-99ed177d3e43";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string<wchar_t>(guid) == str);
+ }
+
+ {
+ auto str = "4718382325744bfdb41199ed177d3e43";
+ REQUIRE_NOTHROW(uuid::from_string(str));
+ REQUIRE(uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "00000000-0000-0000-0000-000000000000";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = "{00000000-0000-0000-0000-000000000000}";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = L"00000000-0000-0000-0000-000000000000";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = L"{00000000-0000-0000-0000-000000000000}";
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+}
+
+TEST_CASE("uuid: Test from_string(basic_string)", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string(guid) == str);
+ }
+
+ {
+ auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
+ }
+
+ {
+ auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"s).value();
+ REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
+ REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
+ }
+
+ {
+ auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string<wchar_t>(guid) == str);
+ }
+
+ {
+ auto str = "4718382325744bfdb41199ed177d3e43"s;
+ REQUIRE_NOTHROW(uuid::from_string(str));
+ REQUIRE(uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "00000000-0000-0000-0000-000000000000"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = "{00000000-0000-0000-0000-000000000000}"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = L"00000000-0000-0000-0000-000000000000"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = L"{00000000-0000-0000-0000-000000000000}"s;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+}
+
+TEST_CASE("uuid: Test from_string(basic_string_view)", "[uuid]")
+{
+ using namespace std::string_view_literals;
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string(guid) == str);
+ }
+
+ {
+ auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
+ }
+
+ {
+ auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"sv).value();
+ REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
+ REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
+ }
+
+ {
+ auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(to_string<wchar_t>(guid) == str);
+ }
+
+ {
+ auto str = "4718382325744bfdb41199ed177d3e43"sv;
+ REQUIRE_NOTHROW(uuid::from_string(str));
+ REQUIRE(uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "00000000-0000-0000-0000-000000000000"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = "{00000000-0000-0000-0000-000000000000}"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = L"00000000-0000-0000-0000-000000000000"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+
+ {
+ auto str = L"{00000000-0000-0000-0000-000000000000}"sv;
+ auto guid = uuid::from_string(str).value();
+ REQUIRE(guid.is_nil());
+ }
+}
+
+TEST_CASE("uuid: Test constexpr from_string", "[uuid]")
+{
+ constexpr uuid value = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
+ static_assert(!value.is_nil());
+ static_assert(value.variant() == uuid_variant::rfc);
+ static_assert(value.version() != uuid_version::none);
+}
+
+TEST_CASE("uuid: Test from_string(char*) invalid format", "[uuid]")
+{
+ REQUIRE(!uuid::from_string("").has_value());
+ REQUIRE(!uuid::from_string("{}").has_value());
+ REQUIRE(!uuid::from_string(
+ "47183823-2574-4bfd-b411-99ed177d3e4").has_value());
+ REQUIRE(!uuid::from_string(
+ "47183823-2574-4bfd-b411-99ed177d3e430").has_value());
+ REQUIRE(!uuid::from_string(
+ "{47183823-2574-4bfd-b411-99ed177d3e43").has_value());
+ REQUIRE(!uuid::from_string(
+ "47183823-2574-4bfd-b411-99ed177d3e43}").has_value());
+}
+
+TEST_CASE("uuid: Test from_string(basic_string) invalid format", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ {
+ auto str = ""s;
+ REQUIRE(!uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "{}"s;
+ REQUIRE(!uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s;
+ REQUIRE(!uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s;
+ REQUIRE(!uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s;
+ REQUIRE(!uuid::from_string(str).has_value());
+ }
+
+ {
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s;
+ REQUIRE(!uuid::from_string(str).has_value());
+ }
+}
+
+TEST_CASE("uuid: Test from_string(basic_string_view) invalid format", "[uuid]")
+{
+ using namespace std::string_view_literals;
+
+ REQUIRE(!uuid::from_string(""sv).has_value());
+ REQUIRE(!uuid::from_string("{}"sv).has_value());
+ REQUIRE(!uuid::from_string(
+ "47183823-2574-4bfd-b411-99ed177d3e4"sv).has_value());
+ REQUIRE(!uuid::from_string(
+ "47183823-2574-4bfd-b411-99ed177d3e430"sv).has_value());
+ REQUIRE(!uuid::from_string(
+ "{47183823-2574-4bfd-b411-99ed177d3e43"sv).has_value());
+ REQUIRE(!uuid::from_string(
+ "47183823-2574-4bfd-b411-99ed177d3e43}"sv).has_value());
+}
+
+TEST_CASE("uuid: Test iterators constructor", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ {
+ std::array<uuid::value_type, 16> arr{{
+ 0x47, 0x18, 0x38, 0x23,
+ 0x25, 0x74,
+ 0x4b, 0xfd,
+ 0xb4, 0x11,
+ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
+ }};
+
+ uuid guid(std::begin(arr), std::end(arr));
+ REQUIRE(to_string(guid) ==
+ "47183823-2574-4bfd-b411-99ed177d3e43"s);
+ }
+
+ {
+ uuid::value_type arr[16] = {
+ 0x47, 0x18, 0x38, 0x23,
+ 0x25, 0x74,
+ 0x4b, 0xfd,
+ 0xb4, 0x11,
+ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
+ };
+
+ uuid guid(std::begin(arr), std::end(arr));
+ REQUIRE(to_string(guid) ==
+ "47183823-2574-4bfd-b411-99ed177d3e43"s);
+ }
+}
+
+TEST_CASE("uuid: Test array constructors", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ {
+ uuid guid{{
+ 0x47, 0x18, 0x38, 0x23,
+ 0x25, 0x74,
+ 0x4b, 0xfd,
+ 0xb4, 0x11,
+ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
+ }};
+
+ REQUIRE(to_string(guid) ==
+ "47183823-2574-4bfd-b411-99ed177d3e43"s);
+ }
+
+ {
+ std::array<uuid::value_type, 16> arr{{
+ 0x47, 0x18, 0x38, 0x23,
+ 0x25, 0x74,
+ 0x4b, 0xfd,
+ 0xb4, 0x11,
+ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
+ }};
+
+ uuid guid(arr);
+ REQUIRE(to_string(guid) ==
+ "47183823-2574-4bfd-b411-99ed177d3e43"s);
+ }
+
+ {
+ uuid::value_type arr[16] {
+ 0x47, 0x18, 0x38, 0x23,
+ 0x25, 0x74,
+ 0x4b, 0xfd,
+ 0xb4, 0x11,
+ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
+ };
+
+ uuid guid(arr);
+ REQUIRE(to_string(guid) ==
+ "47183823-2574-4bfd-b411-99ed177d3e43"s);
+ }
+}
+
+TEST_CASE("uuid: Test equality", "[uuid]")
+{
+ uuid empty;
+
+ auto engine = uuid_random_generator::engine_type{};
+ seed_rng(engine);
+ uuid guid = uuid_random_generator{engine}();
+
+ REQUIRE(empty == empty);
+ REQUIRE(guid == guid);
+ REQUIRE(empty != guid);
+}
+
+TEST_CASE("Test comparison", "[uuid]")
+{
+ auto empty = uuid{};
+
+ auto engine = uuid_random_generator::engine_type{};
+ seed_rng(engine);
+
+ uuid_random_generator gen{engine};
+ auto id = gen();
+
+ REQUIRE(empty < id);
+
+ std::set<uuid> ids{
+ uuid{},
+ gen(),
+ gen(),
+ gen(),
+ gen()
+ };
+
+ REQUIRE(ids.size() == 5);
+ REQUIRE(ids.find(uuid{}) != ids.end());
+}
+
+TEST_CASE("uuid: Test hashing", "[uuid]")
+{
+ using namespace std::string_literals;
+
+ auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
+ auto guid = uuid::from_string(str).value();
+
+ auto h1 = std::hash<std::string>{};
+ auto h2 = std::hash<uuid>{};
+ REQUIRE(h1(str) != h2(guid));
+
+ auto engine = uuid_random_generator::engine_type{};
+ seed_rng(engine);
+ uuid_random_generator gen{ engine };
+
+ std::unordered_set<uuid> ids{
+ uuid{},
+ gen(),
+ gen(),
+ gen(),
+ gen()
+ };
+
+ REQUIRE(ids.size() == 5);
+ REQUIRE(ids.find(uuid{}) != ids.end());
+}
+
+TEST_CASE("uuid: Test swap", "[uuid]")
+{
+ uuid empty;
+
+ auto engine = uuid_random_generator::engine_type{};
+ seed_rng(engine);
+ uuid guid = uuid_random_generator{engine}();
+
+ REQUIRE(empty.is_nil());
+ REQUIRE(!guid.is_nil());
+
+ std::swap(empty, guid);
+
+ REQUIRE(!empty.is_nil());
+ REQUIRE(guid.is_nil());
+
+ empty.swap(guid);
+
+ REQUIRE(empty.is_nil());
+ REQUIRE(!guid.is_nil());
+}
+
+TEST_CASE("uuid: Test constexpr", "[uuid]")
+{
+ constexpr uuid empty;
+ static_assert(empty.is_nil());
+ static_assert(empty.variant() == uuid_variant::ncs);
+ static_assert(empty.version() == uuid_version::none);
+}
+
+TEST_CASE("uuid: Test size", "[uuid]")
+{
+ REQUIRE(sizeof(uuid) == 16);
+}
+
+TEST_CASE("uuid: Test assignment", "[uuid]")
+{
+ auto id1 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
+ auto id2 = id1;
+ REQUIRE(id1 == id2);
+
+ id1 = uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}").value();
+ REQUIRE(id1 != id2);
+
+ auto id3 = std::move(id2);
+ REQUIRE(to_string(id3) == "47183823-2574-4bfd-b411-99ed177d3e43");
+}
+
+TEST_CASE("uuid: Test trivial", "[uuid]")
+{
+ REQUIRE(std::is_trivially_copyable_v<uuid>);
+}
+
+TEST_CASE("uuid: Test as_bytes", "[uuid]")
+{
+ std::array<uuid::value_type, 16> arr{{
+ 0x47, 0x18, 0x38, 0x23,
+ 0x25, 0x74,
+ 0x4b, 0xfd,
+ 0xb4, 0x11,
+ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
+ }};
+
+ {
+ uuid id{ arr };
+ REQUIRE(!id.is_nil());
+
+ auto view = id.as_bytes();
+ REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0);
+ }
+
+ {
+ const uuid id{ arr };
+ REQUIRE(!id.is_nil());
+
+ auto view = id.as_bytes();
+ REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0);
+ }
+}
+
diff --git a/nihil/uuid.ccm b/nihil/uuid.ccm
new file mode 100644
index 0000000..68968ae
--- /dev/null
+++ b/nihil/uuid.ccm
@@ -0,0 +1,760 @@
+/*
+ * 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.
+ */
+
+module;
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <chrono>
+#include <cstring>
+#include <functional>
+#include <iomanip>
+#include <iterator>
+#include <memory>
+#include <numeric>
+#include <optional>
+#include <random>
+#include <ranges>
+#include <span>
+#include <string>
+#include <sstream>
+#include <string_view>
+#include <type_traits>
+
+export module nihil:uuid;
+
+namespace nihil {
+
+template <typename TChar>
+[[nodiscard]] constexpr unsigned char hex2char(TChar const ch) noexcept
+{
+ 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 bool is_hex(TChar const ch) noexcept
+{
+ 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 std::basic_string_view<TChar> to_string_view(TChar const * str) noexcept
+{
+ if (str)
+ return str;
+ return {};
+}
+
+template <typename StringType>
+[[nodiscard]]
+constexpr std::basic_string_view<
+ typename StringType::value_type,
+ typename StringType::traits_type>
+to_string_view(StringType const & str) noexcept
+{
+ return str;
+}
+
+struct sha1
+{
+ 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)
+ {
+ auto *begin = static_cast<const uint8_t*>(start);
+ auto *finish = static_cast<const uint8_t*>(end);
+ while (begin != finish) {
+ process_byte(*begin);
+ begin++;
+ }
+ }
+
+ void process_bytes(void const * const data, size_t const len)
+ {
+ auto *block = static_cast<const uint8_t*>(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<unsigned char>((bitCount >> 24) & 0xFF));
+ process_byte(static_cast<unsigned char>((bitCount >> 16) & 0xFF));
+ process_byte(static_cast<unsigned char>((bitCount >> 8) & 0xFF));
+ process_byte(static_cast<unsigned char>((bitCount) & 0xFF));
+
+ std::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<uint8_t>(d32[0] >> 24);
+ digest[di++] = static_cast<uint8_t>(d32[0] >> 16);
+ digest[di++] = static_cast<uint8_t>(d32[0] >> 8);
+ digest[di++] = static_cast<uint8_t>(d32[0] >> 0);
+
+ digest[di++] = static_cast<uint8_t>(d32[1] >> 24);
+ digest[di++] = static_cast<uint8_t>(d32[1] >> 16);
+ digest[di++] = static_cast<uint8_t>(d32[1] >> 8);
+ digest[di++] = static_cast<uint8_t>(d32[1] >> 0);
+
+ digest[di++] = static_cast<uint8_t>(d32[2] >> 24);
+ digest[di++] = static_cast<uint8_t>(d32[2] >> 16);
+ digest[di++] = static_cast<uint8_t>(d32[2] >> 8);
+ digest[di++] = static_cast<uint8_t>(d32[2] >> 0);
+
+ digest[di++] = static_cast<uint8_t>(d32[3] >> 24);
+ digest[di++] = static_cast<uint8_t>(d32[3] >> 16);
+ digest[di++] = static_cast<uint8_t>(d32[3] >> 8);
+ digest[di++] = static_cast<uint8_t>(d32[3] >> 0);
+
+ digest[di++] = static_cast<uint8_t>(d32[4] >> 24);
+ digest[di++] = static_cast<uint8_t>(d32[4] >> 16);
+ digest[di++] = static_cast<uint8_t>(d32[4] >> 8);
+ digest[di++] = static_cast<uint8_t>(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<uint32_t>(m_block[i * 4 + 0] << 24);
+ w[i] |= static_cast<uint32_t>(m_block[i * 4 + 1] << 16);
+ w[i] |= static_cast<uint32_t>(m_block[i * 4 + 2] << 8);
+ w[i] |= static_cast<uint32_t>(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;
+ }
+
+ digest32_t m_digest;
+ uint8_t m_block[64];
+ size_t m_blockByteIndex;
+ size_t m_byteCount;
+};
+
+template <typename CharT>
+inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000";
+
+template <>
+inline constexpr wchar_t empty_guid<wchar_t>[37] = L"00000000-0000-0000-0000-000000000000";
+
+template <typename CharT>
+inline constexpr CharT guid_encoder[17] = "0123456789abcdef";
+
+template <>
+inline constexpr wchar_t guid_encoder<wchar_t>[17] = L"0123456789abcdef";
+
+// --------------------------------------------------------------------------------------------------------------------------
+// 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
+export enum struct 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
+export enum struct 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.
+export struct uuid;
+export template <typename CharT = char,
+ typename Traits = std::char_traits<CharT>,
+ typename Allocator = std::allocator<CharT>>
+std::basic_string<CharT, Traits, Allocator> to_string(uuid const &id);
+
+// --------------------------------------------------------------------------------------------------------------------------
+// uuid class
+// --------------------------------------------------------------------------------------------------------------------------
+export struct uuid {
+ using value_type = uint8_t;
+
+ constexpr uuid() noexcept = default;
+
+ uuid(value_type(&arr)[16]) noexcept
+ {
+ std::ranges::copy(arr, std::ranges::begin(data));
+ }
+
+ 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));
+ }
+
+ template<typename ForwardIterator>
+ 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 std::span<std::byte const, 16> as_bytes() const
+ {
+ return std::span<std::byte const, 16>(reinterpret_cast<std::byte const*>(data.data()), 16);
+ }
+
+ template <typename StringType>
+ [[nodiscard]] constexpr static bool is_valid_uuid(StringType const & in_str) noexcept
+ {
+ auto str = 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 || !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 std::optional<uuid> from_string(StringType const & in_str) noexcept
+ {
+ auto str = to_string_view(in_str);
+ bool firstDigit = true;
+ size_t hasBraces = 0;
+ size_t index = 0;
+
+ std::array<uint8_t, 16> 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 || !is_hex(str[i])) {
+ return {};
+ }
+
+ if (firstDigit) {
+ data[index] = static_cast<uint8_t>(hex2char(str[i]) << 4);
+ firstDigit = false;
+ } else {
+ data[index] = static_cast<uint8_t>(data[index] | hex2char(str[i]));
+ index++;
+ firstDigit = true;
+ }
+ }
+
+ if (index < 16) {
+ return {};
+ }
+
+ return uuid{data};
+ }
+
+private:
+ std::array<value_type, 16> data{ { 0 } };
+
+ friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept;
+ friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept;
+
+ template <class Elem, class Traits>
+ friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
+
+ template<class CharT, class Traits, class Allocator>
+ friend std::basic_string<CharT, Traits, Allocator> to_string(uuid const& id);
+
+ 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>
+{
+ std::basic_string<CharT, Traits, Allocator> uustr{empty_guid<CharT>};
+
+ for (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[index] >> 4 & 0x0f];
+ uustr[++i] = guid_encoder<CharT>[id.data[index] & 0x0f];
+ 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>&
+{
+ s << to_string(id);
+ return s;
+}
+
+export void swap(uuid & lhs, 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
+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]] uuid operator()()
+ {
+ alignas(uint32_t) uint8_t bytes[16];
+ for (int i = 0; i < 16; i += 4)
+ *reinterpret_cast<uint32_t*>(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<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]] uuid operator()(StringType const & name)
+ {
+ reset();
+ process_characters(to_string_view(name));
+ return make_uuid();
+ }
+
+private:
+ void reset()
+ {
+ hasher.reset();
+ std::byte bytes[16];
+ auto nsbytes = nsuuid.as_bytes();
+ std::ranges::copy(nsbytes, bytes);
+ hasher.process_bytes(bytes, 16);
+ }
+
+ template <typename CharT, typename Traits>
+ void process_characters(std::basic_string_view<CharT, Traits> const str)
+ {
+ for (uint32_t c : str) {
+ hasher.process_byte(static_cast<uint8_t>(c & 0xFF));
+ if constexpr (!std::is_same_v<CharT, char>) {
+ hasher.process_byte(static_cast<uint8_t>((c >> 8) & 0xFF));
+ hasher.process_byte(static_cast<uint8_t>((c >> 16) & 0xFF));
+ hasher.process_byte(static_cast<uint8_t>((c >> 24) & 0xFF));
+ }
+ }
+ }
+
+ [[nodiscard]] uuid make_uuid()
+ {
+ 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;
+ 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::begin(seed_data), std::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]] result_type operator()(argument_type const &uuid) const
+ {
+ uint64_t l =
+ static_cast<uint64_t>(uuid.data[0]) << 56 |
+ static_cast<uint64_t>(uuid.data[1]) << 48 |
+ static_cast<uint64_t>(uuid.data[2]) << 40 |
+ static_cast<uint64_t>(uuid.data[3]) << 32 |
+ static_cast<uint64_t>(uuid.data[4]) << 24 |
+ static_cast<uint64_t>(uuid.data[5]) << 16 |
+ static_cast<uint64_t>(uuid.data[6]) << 8 |
+ static_cast<uint64_t>(uuid.data[7]);
+ uint64_t h =
+ static_cast<uint64_t>(uuid.data[8]) << 56 |
+ static_cast<uint64_t>(uuid.data[9]) << 48 |
+ static_cast<uint64_t>(uuid.data[10]) << 40 |
+ static_cast<uint64_t>(uuid.data[11]) << 32 |
+ static_cast<uint64_t>(uuid.data[12]) << 24 |
+ static_cast<uint64_t>(uuid.data[13]) << 16 |
+ static_cast<uint64_t>(uuid.data[14]) << 8 |
+ static_cast<uint64_t>(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));
+ }
+ }
+};
+
+} // namespace std