From f226d46ee02b57dd76a4793593aa8d66e1c58353 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sat, 28 Jun 2025 17:43:28 +0100 Subject: nihil: move uuid to its own module --- CMakeLists.txt | 1 + nihil.uuid/CMakeLists.txt | 23 ++ nihil.uuid/test.cc | 997 +++++++++++++++++++++++++++++++++++++++++++++ nihil.uuid/uuid.ccm | 760 ++++++++++++++++++++++++++++++++++ nihil/CMakeLists.txt | 1 - nihil/nihil.ccm | 1 - nihil/tests/CMakeLists.txt | 1 - nihil/tests/uuid.cc | 997 --------------------------------------------- nihil/uuid.ccm | 760 ---------------------------------- 9 files changed, 1781 insertions(+), 1760 deletions(-) create mode 100644 nihil.uuid/CMakeLists.txt create mode 100644 nihil.uuid/test.cc create mode 100644 nihil.uuid/uuid.ccm delete mode 100644 nihil/tests/uuid.cc delete mode 100644 nihil/uuid.ccm diff --git a/CMakeLists.txt b/CMakeLists.txt index 435f7ee..9a2b6ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ add_compile_options(-Werror) add_compile_options(-Wpedantic) add_subdirectory(nihil.flagset) +add_subdirectory(nihil.uuid) add_subdirectory(nihil) if(NIHIL_UCL) diff --git a/nihil.uuid/CMakeLists.txt b/nihil.uuid/CMakeLists.txt new file mode 100644 index 0000000..fe037e7 --- /dev/null +++ b/nihil.uuid/CMakeLists.txt @@ -0,0 +1,23 @@ +# This source code is released into the public domain. + +add_library(nihil.uuid STATIC) +target_sources(nihil.uuid + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + uuid.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.uuid.test test.cc) + target_link_libraries(nihil.uuid.test PRIVATE + nihil.uuid + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.uuid.test) +endif() diff --git a/nihil.uuid/test.cc b/nihil.uuid/test.cc new file mode 100644 index 0000000..04a6ab5 --- /dev/null +++ b/nihil.uuid/test.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 +#include +#include +#include + +#include + +namespace +{ + +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html +template +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{}; + 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.uuid; + +using namespace nihil; + +TEST_CASE("uuid: Test multiple default generators", "[uuid]") +{ + uuid id1; + uuid id2; + + { + std::random_device rd; + auto seed_data = std::array {}; + 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 {}; + 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 {}; + 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 {}; + 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 {}; + 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(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 {}; + 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 {}; + 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 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 {}; + 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(seq); + + basic_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(!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 {}; + 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 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(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(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(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(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(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(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(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 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 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 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{}; + auto h2 = std::hash{}; + REQUIRE(h1(str) != h2(guid)); + + auto engine = uuid_random_generator::engine_type{}; + seed_rng(engine); + uuid_random_generator gen{ engine }; + + std::unordered_set 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); +} + +TEST_CASE("uuid: Test as_bytes", "[uuid]") +{ + std::array 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/uuid.ccm b/nihil.uuid/uuid.ccm new file mode 100644 index 0000000..0c46b60 --- /dev/null +++ b/nihil.uuid/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +export module nihil.uuid; + +namespace nihil { + +template +[[nodiscard]] constexpr unsigned char hex2char(TChar const ch) noexcept +{ + if (ch >= static_cast('0') && ch <= static_cast('9')) + return static_cast(ch - static_cast('0')); + if (ch >= static_cast('a') && ch <= static_cast('f')) + return static_cast(10 + ch - static_cast('a')); + if (ch >= static_cast('A') && ch <= static_cast('F')) + return static_cast(10 + ch - static_cast('A')); + return 0; +} + +template +[[nodiscard]] constexpr bool is_hex(TChar const ch) noexcept +{ + return + (ch >= static_cast('0') && ch <= static_cast('9')) || + (ch >= static_cast('a') && ch <= static_cast('f')) || + (ch >= static_cast('A') && ch <= static_cast('F')); +} + +template +[[nodiscard]] constexpr std::basic_string_view to_string_view(TChar const * str) noexcept +{ + if (str) + return str; + return {}; +} + +template +[[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(start); + auto *finish = static_cast(end); + while (begin != finish) { + process_byte(*begin); + begin++; + } + } + + void process_bytes(void const * const data, size_t const len) + { + auto *block = static_cast(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((bitCount >> 24) & 0xFF)); + process_byte(static_cast((bitCount >> 16) & 0xFF)); + process_byte(static_cast((bitCount >> 8) & 0xFF)); + process_byte(static_cast((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(d32[0] >> 24); + digest[di++] = static_cast(d32[0] >> 16); + digest[di++] = static_cast(d32[0] >> 8); + digest[di++] = static_cast(d32[0] >> 0); + + digest[di++] = static_cast(d32[1] >> 24); + digest[di++] = static_cast(d32[1] >> 16); + digest[di++] = static_cast(d32[1] >> 8); + digest[di++] = static_cast(d32[1] >> 0); + + digest[di++] = static_cast(d32[2] >> 24); + digest[di++] = static_cast(d32[2] >> 16); + digest[di++] = static_cast(d32[2] >> 8); + digest[di++] = static_cast(d32[2] >> 0); + + digest[di++] = static_cast(d32[3] >> 24); + digest[di++] = static_cast(d32[3] >> 16); + digest[di++] = static_cast(d32[3] >> 8); + digest[di++] = static_cast(d32[3] >> 0); + + digest[di++] = static_cast(d32[4] >> 24); + digest[di++] = static_cast(d32[4] >> 16); + digest[di++] = static_cast(d32[4] >> 8); + digest[di++] = static_cast(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(m_block[i * 4 + 0] << 24); + w[i] |= static_cast(m_block[i * 4 + 1] << 16); + w[i] |= static_cast(m_block[i * 4 + 2] << 8); + w[i] |= static_cast(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 +inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; + +template <> +inline constexpr wchar_t empty_guid[37] = L"00000000-0000-0000-0000-000000000000"; + +template +inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; + +template <> +inline constexpr wchar_t guid_encoder[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 Allocator = std::allocator> +std::basic_string 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 const & arr) noexcept + : data{arr} + { + } + + explicit uuid(std::span bytes) + { + std::ranges::copy(bytes, std::ranges::begin(data)); + } + + template + 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 as_bytes() const + { + return std::span(reinterpret_cast(data.data()), 16); + } + + template + [[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 + [[nodiscard]] constexpr static std::optional 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 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(hex2char(str[i]) << 4); + firstDigit = false; + } else { + data[index] = static_cast(data[index] | hex2char(str[i])); + index++; + firstDigit = true; + } + } + + if (index < 16) { + return {}; + } + + return uuid{data}; + } + +private: + std::array data{ { 0 } }; + + friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; + friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; + + template + friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); + + template + friend std::basic_string to_string(uuid const& id); + + friend std::hash; +}; + +// -------------------------------------------------------------------------------------------------------------------------- +// 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 +[[nodiscard]] auto to_string(uuid const &id) + -> std::basic_string +{ + std::basic_string uustr{empty_guid}; + + for (size_t i = 0, index = 0; i < 36; ++i) { + if (i == 8 || i == 13 || i == 18 || i == 23) + continue; + + uustr[i] = guid_encoder[id.data[index] >> 4 & 0x0f]; + uustr[++i] = guid_encoder[id.data[index] & 0x0f]; + index++; + } + + return uustr; +} + +export template +auto operator<<(std::basic_ostream& s, uuid const &id) + -> std::basic_ostream& +{ + 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 +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(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 distribution; + std::shared_ptr generator; +}; + +export using uuid_random_generator = basic_uuid_random_generator; + +export struct uuid_name_generator +{ + explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + : nsuuid(namespace_uuid) + { + } + + template + [[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 + void process_characters(std::basic_string_view const str) + { + for (uint32_t c : str) { + hasher.process_byte(static_cast(c & 0xFF)); + if constexpr (!std::is_same_v) { + hasher.process_byte(static_cast((c >> 8) & 0xFF)); + hasher.process_byte(static_cast((c >> 16) & 0xFF)); + hasher.process_byte(static_cast((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 {}; + + 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 +{ + 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(uuid.data[0]) << 56 | + static_cast(uuid.data[1]) << 48 | + static_cast(uuid.data[2]) << 40 | + static_cast(uuid.data[3]) << 32 | + static_cast(uuid.data[4]) << 24 | + static_cast(uuid.data[5]) << 16 | + static_cast(uuid.data[6]) << 8 | + static_cast(uuid.data[7]); + uint64_t h = + static_cast(uuid.data[8]) << 56 | + static_cast(uuid.data[9]) << 48 | + static_cast(uuid.data[10]) << 40 | + static_cast(uuid.data[11]) << 32 | + static_cast(uuid.data[12]) << 24 | + static_cast(uuid.data[13]) << 16 | + static_cast(uuid.data[14]) << 8 | + static_cast(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 diff --git a/nihil/CMakeLists.txt b/nihil/CMakeLists.txt index 49955c8..64dbe6a 100644 --- a/nihil/CMakeLists.txt +++ b/nihil/CMakeLists.txt @@ -29,7 +29,6 @@ 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 7d2ac7f..cbcadaf 100644 --- a/nihil/nihil.ccm +++ b/nihil/nihil.ccm @@ -31,5 +31,4 @@ 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 a57d844..dd2d376 100644 --- a/nihil/tests/CMakeLists.txt +++ b/nihil/tests/CMakeLists.txt @@ -14,7 +14,6 @@ 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 deleted file mode 100644 index dabbca6..0000000 --- a/nihil/tests/uuid.cc +++ /dev/null @@ -1,997 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include - -namespace -{ - -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html -template -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{}; - 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 {}; - 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 {}; - 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 {}; - 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 {}; - 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 {}; - 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(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 {}; - 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 {}; - 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 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 {}; - 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(seq); - - basic_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(!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 {}; - 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 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(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(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(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(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(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(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(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 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 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 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{}; - auto h2 = std::hash{}; - REQUIRE(h1(str) != h2(guid)); - - auto engine = uuid_random_generator::engine_type{}; - seed_rng(engine); - uuid_random_generator gen{ engine }; - - std::unordered_set 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); -} - -TEST_CASE("uuid: Test as_bytes", "[uuid]") -{ - std::array 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 deleted file mode 100644 index 68968ae..0000000 --- a/nihil/uuid.ccm +++ /dev/null @@ -1,760 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -export module nihil:uuid; - -namespace nihil { - -template -[[nodiscard]] constexpr unsigned char hex2char(TChar const ch) noexcept -{ - if (ch >= static_cast('0') && ch <= static_cast('9')) - return static_cast(ch - static_cast('0')); - if (ch >= static_cast('a') && ch <= static_cast('f')) - return static_cast(10 + ch - static_cast('a')); - if (ch >= static_cast('A') && ch <= static_cast('F')) - return static_cast(10 + ch - static_cast('A')); - return 0; -} - -template -[[nodiscard]] constexpr bool is_hex(TChar const ch) noexcept -{ - return - (ch >= static_cast('0') && ch <= static_cast('9')) || - (ch >= static_cast('a') && ch <= static_cast('f')) || - (ch >= static_cast('A') && ch <= static_cast('F')); -} - -template -[[nodiscard]] constexpr std::basic_string_view to_string_view(TChar const * str) noexcept -{ - if (str) - return str; - return {}; -} - -template -[[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(start); - auto *finish = static_cast(end); - while (begin != finish) { - process_byte(*begin); - begin++; - } - } - - void process_bytes(void const * const data, size_t const len) - { - auto *block = static_cast(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((bitCount >> 24) & 0xFF)); - process_byte(static_cast((bitCount >> 16) & 0xFF)); - process_byte(static_cast((bitCount >> 8) & 0xFF)); - process_byte(static_cast((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(d32[0] >> 24); - digest[di++] = static_cast(d32[0] >> 16); - digest[di++] = static_cast(d32[0] >> 8); - digest[di++] = static_cast(d32[0] >> 0); - - digest[di++] = static_cast(d32[1] >> 24); - digest[di++] = static_cast(d32[1] >> 16); - digest[di++] = static_cast(d32[1] >> 8); - digest[di++] = static_cast(d32[1] >> 0); - - digest[di++] = static_cast(d32[2] >> 24); - digest[di++] = static_cast(d32[2] >> 16); - digest[di++] = static_cast(d32[2] >> 8); - digest[di++] = static_cast(d32[2] >> 0); - - digest[di++] = static_cast(d32[3] >> 24); - digest[di++] = static_cast(d32[3] >> 16); - digest[di++] = static_cast(d32[3] >> 8); - digest[di++] = static_cast(d32[3] >> 0); - - digest[di++] = static_cast(d32[4] >> 24); - digest[di++] = static_cast(d32[4] >> 16); - digest[di++] = static_cast(d32[4] >> 8); - digest[di++] = static_cast(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(m_block[i * 4 + 0] << 24); - w[i] |= static_cast(m_block[i * 4 + 1] << 16); - w[i] |= static_cast(m_block[i * 4 + 2] << 8); - w[i] |= static_cast(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 -inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; - -template <> -inline constexpr wchar_t empty_guid[37] = L"00000000-0000-0000-0000-000000000000"; - -template -inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; - -template <> -inline constexpr wchar_t guid_encoder[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 Allocator = std::allocator> -std::basic_string 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 const & arr) noexcept - : data{arr} - { - } - - explicit uuid(std::span bytes) - { - std::ranges::copy(bytes, std::ranges::begin(data)); - } - - template - 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 as_bytes() const - { - return std::span(reinterpret_cast(data.data()), 16); - } - - template - [[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 - [[nodiscard]] constexpr static std::optional 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 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(hex2char(str[i]) << 4); - firstDigit = false; - } else { - data[index] = static_cast(data[index] | hex2char(str[i])); - index++; - firstDigit = true; - } - } - - if (index < 16) { - return {}; - } - - return uuid{data}; - } - -private: - std::array data{ { 0 } }; - - friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; - friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; - - template - friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); - - template - friend std::basic_string to_string(uuid const& id); - - friend std::hash; -}; - -// -------------------------------------------------------------------------------------------------------------------------- -// 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 -[[nodiscard]] auto to_string(uuid const &id) - -> std::basic_string -{ - std::basic_string uustr{empty_guid}; - - for (size_t i = 0, index = 0; i < 36; ++i) { - if (i == 8 || i == 13 || i == 18 || i == 23) - continue; - - uustr[i] = guid_encoder[id.data[index] >> 4 & 0x0f]; - uustr[++i] = guid_encoder[id.data[index] & 0x0f]; - index++; - } - - return uustr; -} - -export template -auto operator<<(std::basic_ostream& s, uuid const &id) - -> std::basic_ostream& -{ - 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 -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(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 distribution; - std::shared_ptr generator; -}; - -export using uuid_random_generator = basic_uuid_random_generator; - -export struct uuid_name_generator -{ - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept - : nsuuid(namespace_uuid) - { - } - - template - [[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 - void process_characters(std::basic_string_view const str) - { - for (uint32_t c : str) { - hasher.process_byte(static_cast(c & 0xFF)); - if constexpr (!std::is_same_v) { - hasher.process_byte(static_cast((c >> 8) & 0xFF)); - hasher.process_byte(static_cast((c >> 16) & 0xFF)); - hasher.process_byte(static_cast((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 {}; - - 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 -{ - 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(uuid.data[0]) << 56 | - static_cast(uuid.data[1]) << 48 | - static_cast(uuid.data[2]) << 40 | - static_cast(uuid.data[3]) << 32 | - static_cast(uuid.data[4]) << 24 | - static_cast(uuid.data[5]) << 16 | - static_cast(uuid.data[6]) << 8 | - static_cast(uuid.data[7]); - uint64_t h = - static_cast(uuid.data[8]) << 56 | - static_cast(uuid.data[9]) << 48 | - static_cast(uuid.data[10]) << 40 | - static_cast(uuid.data[11]) << 32 | - static_cast(uuid.data[12]) << 24 | - static_cast(uuid.data[13]) << 16 | - static_cast(uuid.data[14]) << 8 | - static_cast(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 -- cgit v1.2.3