diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-07-02 03:28:45 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-07-02 03:28:45 +0100 |
| commit | 712edacbc75e6dc98c11b3c5f6c52f823c747e2f (patch) | |
| tree | 1cd3710635e0ec27d12845d21ef774102d13b938 /nihil.uuid | |
| parent | a4607e29540a9352c35afff17193ceeab137cc9d (diff) | |
| download | nihil-712edacbc75e6dc98c11b3c5f6c52f823c747e2f.tar.gz nihil-712edacbc75e6dc98c11b3c5f6c52f823c747e2f.tar.bz2 | |
move uuid to util
Diffstat (limited to 'nihil.uuid')
| -rw-r--r-- | nihil.uuid/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | nihil.uuid/test.cc | 923 | ||||
| -rw-r--r-- | nihil.uuid/uuid.ccm | 768 |
3 files changed, 0 insertions, 1713 deletions
diff --git a/nihil.uuid/CMakeLists.txt b/nihil.uuid/CMakeLists.txt deleted file mode 100644 index a210322..0000000 --- a/nihil.uuid/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# This source code is released into the public domain. - -add_library(nihil.uuid STATIC) -target_link_libraries(nihil.uuid PRIVATE nihil.std) -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 - ) - - include(CTest) - include(Catch) - catch_discover_tests(nihil.uuid.test) -endif() diff --git a/nihil.uuid/test.cc b/nihil.uuid/test.cc deleted file mode 100644 index 551c491..0000000 --- a/nihil.uuid/test.cc +++ /dev/null @@ -1,923 +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 <catch2/catch_test_macros.hpp> - -import nihil.std; -import nihil.uuid; - -// NOLINTBEGIN(bugprone-unchecked-optional-access) - -namespace { -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html -template <typename EngineT, std::size_t StateSize = EngineT::state_size> -void seed_rng(EngineT &engine) -{ - using engine_type = typename EngineT::result_type; - using device_type = std::random_device::result_type; - using seedseq_type = std::seed_seq::result_type; - - constexpr auto bytes_needed = StateSize * sizeof(engine_type); - constexpr auto numbers_needed = (sizeof(device_type) < sizeof(seedseq_type)) - ? (bytes_needed / sizeof(device_type)) - : (bytes_needed / sizeof(seedseq_type)); - - auto numbers = std::array<device_type, numbers_needed>{}; - auto rnddev = std::random_device{}; - std::ranges::generate(numbers, std::ref(rnddev)); - - auto seedseq = std::seed_seq(std::cbegin(numbers), std::cend(numbers)); - engine.seed(seedseq); -} - -using namespace nihil; - -TEST_CASE("uuid: Test multiple default generators", "[uuid]") -{ - uuid id1; - uuid id2; - - { - std::random_device rd; - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - id1 = uuid_random_generator{generator}(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - } - - { - std::random_device rd; - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - id2 = uuid_random_generator{generator}(); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - } - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test default generator", "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - uuid const guid = uuid_random_generator{generator}(); - REQUIRE(!guid.is_nil()); - REQUIRE(guid.version() == uuid_version::random_number_based); - REQUIRE(guid.variant() == uuid_variant::rfc); -} - -TEST_CASE("uuid: Test random generator (conversion ctor w/ smart ptr)", "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - uuid_random_generator dgen(&generator); - auto id1 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen(); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test random generator (conversion ctor w/ ptr)", "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - auto generator = std::make_unique<std::mt19937>(seq); - - uuid_random_generator dgen(generator.get()); - auto id1 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test random generator (conversion ctor w/ ref)", "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - uuid_random_generator dgen(generator); - auto id1 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen(); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ptr) " - "w/ ranlux48_base", - "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, 6>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::ranlux48_base generator(seq); - - basic_uuid_random_generator<std::ranlux48_base> dgen(&generator); - auto id1 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen(); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test basic random generator (conversion ctor w/ smart ptr) " - "w/ ranlux48_base", - "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, 6>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - auto generator = std::make_unique<std::ranlux48_base>(seq); - - basic_uuid_random_generator<std::ranlux48_base> dgen(generator.get()); - auto id1 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen(); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ref) " - "w/ ranlux48_base", - "[uuid]") -{ - std::random_device rd; - auto seed_data = std::array<int, 6>{}; - std::ranges::generate(seed_data, std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::ranlux48_base generator(seq); - - basic_uuid_random_generator<std::ranlux48_base> dgen(generator); - auto id1 = dgen(); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::random_number_based); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen(); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::random_number_based); - REQUIRE(id2.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); -} - -TEST_CASE("uuid: Test namespaces", "[uuid]") -{ - REQUIRE(uuid_namespace_dns == uuid::from_string("6ba7b810-9dad-11d1-80b4-00c04fd430c8")); - REQUIRE(uuid_namespace_url == uuid::from_string("6ba7b811-9dad-11d1-80b4-00c04fd430c8")); - REQUIRE(uuid_namespace_oid == uuid::from_string("6ba7b812-9dad-11d1-80b4-00c04fd430c8")); - REQUIRE(uuid_namespace_x500 == uuid::from_string("6ba7b814-9dad-11d1-80b4-00c04fd430c8")); -} - -TEST_CASE("uuid: Test name generator (char*)", "[uuid]") -{ - uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); - - auto id1 = dgen("john"); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::name_based_sha1); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen("jane"); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::name_based_sha1); - REQUIRE(id2.variant() == uuid_variant::rfc); - - auto id3 = dgen("jane"); - REQUIRE(!id3.is_nil()); - REQUIRE(id3.version() == uuid_version::name_based_sha1); - REQUIRE(id3.variant() == uuid_variant::rfc); - - auto id4 = dgen(L"jane"); - REQUIRE(!id4.is_nil()); - REQUIRE(id4.version() == uuid_version::name_based_sha1); - REQUIRE(id4.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); - REQUIRE(id2 == id3); - REQUIRE(id3 != id4); -} - -TEST_CASE("uuid: Test name generator (std::string)", "[uuid]") -{ - using namespace std::string_literals; - - uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); - auto id1 = dgen("john"s); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::name_based_sha1); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen("jane"s); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::name_based_sha1); - REQUIRE(id2.variant() == uuid_variant::rfc); - - auto id3 = dgen("jane"s); - REQUIRE(!id3.is_nil()); - REQUIRE(id3.version() == uuid_version::name_based_sha1); - REQUIRE(id3.variant() == uuid_variant::rfc); - - auto id4 = dgen(L"jane"s); - REQUIRE(!id4.is_nil()); - REQUIRE(id4.version() == uuid_version::name_based_sha1); - REQUIRE(id4.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); - REQUIRE(id2 == id3); - REQUIRE(id3 != id4); -} - -TEST_CASE("uuid: Test name generator (std::string_view)", "[uuid]") -{ - using namespace std::string_view_literals; - - uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); - auto id1 = dgen("john"sv); - REQUIRE(!id1.is_nil()); - REQUIRE(id1.version() == uuid_version::name_based_sha1); - REQUIRE(id1.variant() == uuid_variant::rfc); - - auto id2 = dgen("jane"sv); - REQUIRE(!id2.is_nil()); - REQUIRE(id2.version() == uuid_version::name_based_sha1); - REQUIRE(id2.variant() == uuid_variant::rfc); - - auto id3 = dgen("jane"sv); - REQUIRE(!id3.is_nil()); - REQUIRE(id3.version() == uuid_version::name_based_sha1); - REQUIRE(id3.variant() == uuid_variant::rfc); - - auto id4 = dgen(L"jane"sv); - REQUIRE(!id4.is_nil()); - REQUIRE(id4.version() == uuid_version::name_based_sha1); - REQUIRE(id4.variant() == uuid_variant::rfc); - - REQUIRE(id1 != id2); - REQUIRE(id2 == id3); - REQUIRE(id3 != id4); -} - -TEST_CASE("uuid: Test name generator equality (char const*, std::string, " - "std::string_view)", - "[uuid]") -{ - using namespace std::literals; - - auto dgen = uuid_name_generator( - uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); - auto id1 = dgen("john"); - auto id2 = dgen("john"s); - auto id3 = dgen("john"sv); - - REQUIRE(id1 == id2); - REQUIRE(id2 == id3); -} - -TEST_CASE("uuid: Test default constructor", "[uuid]") -{ - auto empty = uuid(); - REQUIRE(empty.is_nil()); -} - -TEST_CASE("uuid: Test string conversion", "[uuid]") -{ - auto empty = uuid(); - REQUIRE(to_string(empty) == "00000000-0000-0000-0000-000000000000"); - REQUIRE(to_string<wchar_t>(empty) == L"00000000-0000-0000-0000-000000000000"); -} - -TEST_CASE("uuid: Test is_valid_uuid(char*)", "[uuid]") -{ - REQUIRE(uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43")); - REQUIRE(uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}")); - REQUIRE(uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43")); - REQUIRE(uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}")); - REQUIRE(uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000")); - REQUIRE(uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}")); - REQUIRE(uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000")); - REQUIRE(uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}")); -} - -TEST_CASE("uuid: Test is_valid_uuid(basic_string)", "[uuid]") -{ - using namespace std::string_literals; - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = L"{47183823-2574-4bfd-b411-99ed177d3e43}"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = "00000000-0000-0000-0000-000000000000"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = "{00000000-0000-0000-0000-000000000000}"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = L"00000000-0000-0000-0000-000000000000"s; - REQUIRE(uuid::is_valid_uuid(str)); - } - - { - auto str = L"{00000000-0000-0000-0000-000000000000}"s; - REQUIRE(uuid::is_valid_uuid(str)); - } -} - -TEST_CASE("uuid: Test is_valid_uuid(basic_string_view)", "[uuid]") -{ - using namespace std::string_view_literals; - - REQUIRE(uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43"sv)); - REQUIRE(uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); - REQUIRE(uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43"sv)); - REQUIRE(uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); - REQUIRE(uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000"sv)); - REQUIRE(uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}"sv)); - REQUIRE(uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000"sv)); - REQUIRE(uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}"sv)); -} - -TEST_CASE("uuid: Test is_valid_uuid(char*) invalid format", "[uuid]") -{ - REQUIRE(!uuid::is_valid_uuid("")); - REQUIRE(!uuid::is_valid_uuid("{}")); - REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4")); - REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430")); - REQUIRE(!uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43")); - REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}")); -} - -TEST_CASE("uuid: Test is_valid_uuid(basic_string) invalid format", "[uuid]") -{ - using namespace std::string_literals; - - { - auto str = ""s; - REQUIRE(!uuid::is_valid_uuid(str)); - } - - { - auto str = "{}"s; - REQUIRE(!uuid::is_valid_uuid(str)); - } - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; - REQUIRE(!uuid::is_valid_uuid(str)); - } - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; - REQUIRE(!uuid::is_valid_uuid(str)); - } - - { - auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; - REQUIRE(!uuid::is_valid_uuid(str)); - } - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; - REQUIRE(!uuid::is_valid_uuid(str)); - } -} - -TEST_CASE("uuid: Test is_valid_uuid(basic_string_view) invalid format", "[uuid]") -{ - using namespace std::string_view_literals; - - REQUIRE(!uuid::is_valid_uuid(""sv)); - REQUIRE(!uuid::is_valid_uuid("{}"sv)); - REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4"sv)); - REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430"sv)); - REQUIRE(!uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43"sv)); - REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}"sv)); -} - -TEST_CASE("uuid: Test from_string(char*)", "[uuid]") -{ - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e43"; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string(guid) == str); - } - - { - auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); - } - - { - auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); - REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); - } - - { - auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string<wchar_t>(guid) == str); - } - - { - auto str = "4718382325744bfdb41199ed177d3e43"; - REQUIRE_NOTHROW(uuid::from_string(str)); - REQUIRE(uuid::from_string(str).has_value()); - } - - { - auto str = "00000000-0000-0000-0000-000000000000"; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = "{00000000-0000-0000-0000-000000000000}"; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = L"00000000-0000-0000-0000-000000000000"; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = L"{00000000-0000-0000-0000-000000000000}"; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } -} - -TEST_CASE("uuid: Test from_string(basic_string)", "[uuid]") -{ - using namespace std::string_literals; - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string(guid) == str); - } - - { - auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); - } - - { - auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"s).value(); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); - REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); - } - - { - auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string<wchar_t>(guid) == str); - } - - { - auto str = "4718382325744bfdb41199ed177d3e43"s; - REQUIRE_NOTHROW(uuid::from_string(str)); - REQUIRE(uuid::from_string(str).has_value()); - } - - { - auto str = "00000000-0000-0000-0000-000000000000"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = "{00000000-0000-0000-0000-000000000000}"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = L"00000000-0000-0000-0000-000000000000"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = L"{00000000-0000-0000-0000-000000000000}"s; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } -} - -TEST_CASE("uuid: Test from_string(basic_string_view)", "[uuid]") -{ - using namespace std::string_view_literals; - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e43"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string(guid) == str); - } - - { - auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); - } - - { - auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"sv).value(); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); - REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); - } - - { - auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(to_string<wchar_t>(guid) == str); - } - - { - auto str = "4718382325744bfdb41199ed177d3e43"sv; - REQUIRE_NOTHROW(uuid::from_string(str)); - REQUIRE(uuid::from_string(str).has_value()); - } - - { - auto str = "00000000-0000-0000-0000-000000000000"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = "{00000000-0000-0000-0000-000000000000}"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = L"00000000-0000-0000-0000-000000000000"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } - - { - auto str = L"{00000000-0000-0000-0000-000000000000}"sv; - auto guid = uuid::from_string(str).value(); - REQUIRE(guid.is_nil()); - } -} - -TEST_CASE("uuid: Test constexpr from_string", "[uuid]") -{ - constexpr uuid value = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); - static_assert(!value.is_nil()); - static_assert(value.variant() == uuid_variant::rfc); - static_assert(value.version() != uuid_version::none); -} - -TEST_CASE("uuid: Test from_string(char*) invalid format", "[uuid]") -{ - REQUIRE(!uuid::from_string("").has_value()); - REQUIRE(!uuid::from_string("{}").has_value()); - REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4").has_value()); - REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430").has_value()); - REQUIRE(!uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43").has_value()); - REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}").has_value()); -} - -TEST_CASE("uuid: Test from_string(basic_string) invalid format", "[uuid]") -{ - using namespace std::string_literals; - - { - auto str = ""s; - REQUIRE(!uuid::from_string(str).has_value()); - } - - { - auto str = "{}"s; - REQUIRE(!uuid::from_string(str).has_value()); - } - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; - REQUIRE(!uuid::from_string(str).has_value()); - } - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; - REQUIRE(!uuid::from_string(str).has_value()); - } - - { - auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; - REQUIRE(!uuid::from_string(str).has_value()); - } - - { - auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; - REQUIRE(!uuid::from_string(str).has_value()); - } -} - -TEST_CASE("uuid: Test from_string(basic_string_view) invalid format", "[uuid]") -{ - using namespace std::string_view_literals; - - REQUIRE(!uuid::from_string(""sv).has_value()); - REQUIRE(!uuid::from_string("{}"sv).has_value()); - REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4"sv).has_value()); - REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430"sv).has_value()); - REQUIRE(!uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43"sv).has_value()); - REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}"sv).has_value()); -} - -TEST_CASE("uuid: Test iterators constructor", "[uuid]") -{ - using namespace std::string_literals; - - { - std::array<uuid::value_type, 16> arr{ - {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, - 0x17, 0x7d, 0x3e, 0x43} - }; - - auto const guid = uuid(std::begin(arr), std::end(arr)); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); - } - - { - uuid::value_type arr[16] = {// NOLINT - 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, - 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}; - - auto const guid = uuid(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; - - { - auto const guid = uuid { - {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); - } - - { - auto arr = std::array<uuid::value_type, 16>{ - {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, - 0x17, 0x7d, 0x3e, 0x43} - }; - - auto const guid = uuid(arr); - REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); - } - - { - uuid::value_type arr[16]{// NOLINT - 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, - 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}; - - auto const guid = uuid(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); - - auto gen = uuid_random_generator{engine}; - auto id = gen(); - - REQUIRE(empty < id); - - auto ids = std::set{uuid{}, gen(), gen(), gen(), gen()}; - - REQUIRE(ids.size() == 5); - REQUIRE(ids.contains(uuid{}) == true); -} - -TEST_CASE("uuid: Test hashing", "[uuid]") -{ - using namespace std::string_literals; - - auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; - auto guid = uuid::from_string(str).value(); - - auto h1 = std::hash<std::string>{}; - auto h2 = std::hash<uuid>{}; - REQUIRE(h1(str) != h2(guid)); - - auto engine = uuid_random_generator::engine_type{}; - seed_rng(engine); - uuid_random_generator gen{engine}; - - std::unordered_set<uuid> ids{uuid{}, gen(), gen(), gen(), gen()}; - - REQUIRE(ids.size() == 5); - REQUIRE(ids.find(uuid{}) != ids.end()); -} - -TEST_CASE("uuid: Test swap", "[uuid]") -{ - uuid empty; - - auto engine = uuid_random_generator::engine_type{}; - seed_rng(engine); - uuid guid = uuid_random_generator{engine}(); - - REQUIRE(empty.is_nil()); - REQUIRE(!guid.is_nil()); - - std::swap(empty, guid); - - REQUIRE(!empty.is_nil()); - REQUIRE(guid.is_nil()); - - empty.swap(guid); - - REQUIRE(empty.is_nil()); - REQUIRE(!guid.is_nil()); -} - -TEST_CASE("uuid: Test constexpr", "[uuid]") -{ - constexpr uuid empty; - static_assert(empty.is_nil()); - static_assert(empty.variant() == uuid_variant::ncs); - static_assert(empty.version() == uuid_version::none); -} - -TEST_CASE("uuid: Test size", "[uuid]") -{ - REQUIRE(sizeof(uuid) == 16); -} - -TEST_CASE("uuid: Test assignment", "[uuid]") -{ - auto id1 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); - auto id2 = id1; - REQUIRE(id1 == id2); - - id1 = uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}").value(); - REQUIRE(id1 != id2); - - auto id3 = std::move(id2); - REQUIRE(to_string(id3) == "47183823-2574-4bfd-b411-99ed177d3e43"); -} - -TEST_CASE("uuid: Test trivial", "[uuid]") -{ - REQUIRE(std::is_trivially_copyable_v<uuid>); -} - -TEST_CASE("uuid: Test as_bytes", "[uuid]") -{ - std::array<uuid::value_type, 16> arr{ - {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, - 0x3e, 0x43} - }; - - { - uuid id{arr}; - REQUIRE(!id.is_nil()); - - auto view = id.as_bytes(); - REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); - } - - { - const uuid id{arr}; - REQUIRE(!id.is_nil()); - - auto view = id.as_bytes(); - REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); - } -} -} // anonymous namespace - -// NOLINTEND(bugprone-unchecked-optional-access) diff --git a/nihil.uuid/uuid.ccm b/nihil.uuid/uuid.ccm deleted file mode 100644 index a7a4770..0000000 --- a/nihil.uuid/uuid.ccm +++ /dev/null @@ -1,768 +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. - -export module nihil.uuid; - -import nihil.std; - -namespace nihil { - -template <typename TChar> -[[nodiscard]] constexpr auto hex2char(TChar const ch) noexcept -> unsigned char -{ - if (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) - return static_cast<unsigned char>(ch - static_cast<TChar>('0')); - - if (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) - return static_cast<unsigned char>(10 + ch - static_cast<TChar>('a')); - - if (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F')) - return static_cast<unsigned char>(10 + ch - static_cast<TChar>('A')); - - return 0; -} - -template <typename TChar> -[[nodiscard]] constexpr auto is_hex(TChar const ch) noexcept -> bool -{ - return (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) || - (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) || - (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F')); -} - -template <typename TChar> -[[nodiscard]] constexpr auto -to_string_view(TChar const *str) noexcept -> std::basic_string_view<TChar> -{ - if (str) - return str; - return {}; -} - -template <typename StringType> -[[nodiscard]] constexpr auto to_string_view(StringType const &str) noexcept - -> std::basic_string_view<typename StringType::value_type, typename StringType::traits_type> -{ - return str; -} - -struct sha1 -{ - using digest32_t = std::array<std::uint32_t, 5>; - using digest8_t = std::array<std::uint8_t, 20>; - - static constexpr unsigned int block_bytes = 64; - - sha1() - { - reset(); - } - - [[nodiscard]] static auto - left_rotate(std::uint32_t value, std::size_t const count) noexcept -> std::uint32_t - { - return (value << count) ^ (value >> (32 - count)); - } - - auto reset(this sha1 &self) noexcept -> void - { - self.m_digest[0] = 0x67452301; - self.m_digest[1] = 0xEFCDAB89; - self.m_digest[2] = 0x98BADCFE; - self.m_digest[3] = 0x10325476; - self.m_digest[4] = 0xC3D2E1F0; - self.m_blockByteIndex = 0; - self.m_byteCount = 0; - } - - auto process_byte(this sha1 &self, std::uint8_t octet) -> void - { - self.m_block.at(self.m_blockByteIndex++) = octet; - ++self.m_byteCount; - - if (self.m_blockByteIndex == block_bytes) { - self.m_blockByteIndex = 0; - self.process_block(); - } - } - - auto process_block(this sha1 &self, void const *const start, void const *const end) -> void - { - auto const *first = static_cast<std::uint8_t const *>(start); - auto const *last = static_cast<std::uint8_t const *>(end); - - while (first != last) { - self.process_byte(*first); - first++; - } - } - - auto process_bytes(this sha1 &self, void const *const data, std::size_t const len) -> void - { - auto *block = static_cast<std::uint8_t const *>(data); - self.process_block(block, block + len); - } - - auto get_digest(this sha1 &self) -> digest32_t - { - auto const bit_count = self.m_byteCount * 8; - - self.process_byte(0x80); - if (self.m_blockByteIndex > 56) { - while (self.m_blockByteIndex != 0) - self.process_byte(0); - - while (self.m_blockByteIndex < 56) - self.process_byte(0); - } else { - while (self.m_blockByteIndex < 56) - self.process_byte(0); - } - - self.process_byte(0); - self.process_byte(0); - self.process_byte(0); - self.process_byte(0); - self.process_byte(static_cast<unsigned char>((bit_count >> 24U) & 0xFFU)); - self.process_byte(static_cast<unsigned char>((bit_count >> 16U) & 0xFFU)); - self.process_byte(static_cast<unsigned char>((bit_count >> 8U) & 0xFFU)); - self.process_byte(static_cast<unsigned char>((bit_count) & 0xFFU)); - - return self.m_digest; - } - - auto get_digest_bytes(this sha1 &self) -> digest8_t - { - auto d32 = self.get_digest(); - - return { - static_cast<std::uint8_t>(d32[0] >> 24U), - static_cast<std::uint8_t>(d32[0] >> 16U), - static_cast<std::uint8_t>(d32[0] >> 8U), - static_cast<std::uint8_t>(d32[0] >> 0U), - - static_cast<std::uint8_t>(d32[1] >> 24U), - static_cast<std::uint8_t>(d32[1] >> 16U), - static_cast<std::uint8_t>(d32[1] >> 8U), - static_cast<std::uint8_t>(d32[1] >> 0U), - - static_cast<std::uint8_t>(d32[2] >> 24U), - static_cast<std::uint8_t>(d32[2] >> 16U), - static_cast<std::uint8_t>(d32[2] >> 8U), - static_cast<std::uint8_t>(d32[2] >> 0U), - - static_cast<std::uint8_t>(d32[3] >> 24U), - static_cast<std::uint8_t>(d32[3] >> 16U), - static_cast<std::uint8_t>(d32[3] >> 8U), - static_cast<std::uint8_t>(d32[3] >> 0U), - - static_cast<std::uint8_t>(d32[4] >> 24U), - static_cast<std::uint8_t>(d32[4] >> 16U), - static_cast<std::uint8_t>(d32[4] >> 8U), - static_cast<std::uint8_t>(d32[4] >> 0U), - }; - } - -private: - auto process_block(this sha1 &self) -> void - { - auto w = std::array<std::uint32_t, 80>{}; - - for (std::size_t i = 0; i < 16; i++) { - w.at(i) = static_cast<std::uint32_t>(self.m_block.at((i * 4) + 0)) << 24U; - w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 1)) << 16U; - w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 2)) << 8U; - w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 3)); - } - - for (std::size_t i = 16; i < 80; i++) { - w.at(i) = left_rotate( - (w.at(i - 3) ^ w.at(i - 8) ^ w.at(i - 14) ^ w.at(i - 16)), 1); - } - - auto a = self.m_digest[0]; - auto b = self.m_digest[1]; - auto c = self.m_digest[2]; - auto d = self.m_digest[3]; - auto e = self.m_digest[4]; - - for (std::size_t i = 0; i < 80; ++i) { - auto f = std::uint32_t{0}; - auto k = std::uint32_t{0}; - - if (i < 20) { - f = (b & c) | (~b & d); - k = 0x5A827999; - } else if (i < 40) { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } else if (i < 60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } else { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - - auto temp = std::uint32_t{left_rotate(a, 5) + f + e + k + w.at(i)}; - e = d; - d = c; - c = left_rotate(b, 30); - b = a; - a = temp; - } - - self.m_digest[0] += a; - self.m_digest[1] += b; - self.m_digest[2] += c; - self.m_digest[3] += d; - self.m_digest[4] += e; - } - - digest32_t m_digest{}; - std::array<std::uint8_t, 64> m_block{}; - std::size_t m_blockByteIndex{}; - std::size_t m_byteCount{}; -}; - -template <typename CharT> -inline constexpr std::string_view empty_guid = "00000000-0000-0000-0000-000000000000"; - -template <> -inline constexpr std::wstring_view empty_guid<wchar_t> = L"00000000-0000-0000-0000-000000000000"; - -template <typename CharT> -inline constexpr std::string_view guid_encoder = "0123456789abcdef"; - -template <> -inline constexpr std::wstring_view guid_encoder<wchar_t> = L"0123456789abcdef"; - -// --------------------------------------------------------------------- -// UUID format https://tools.ietf.org/html/rfc4122 -// --------------------------------------------------------------------- - -// --------------------------------------------------------------------- -// Field NDR Data Type Octet # Note -// Note -// --------------------------------------------------------------------- -// time_low unsigned long 0 - 3 -// The low field of the timestamp. -// time_mid unsigned short 4 - 5 -// The middle field of the timestamp. -// time_hi_and_version unsigned short 6 - 7 -// The high field of the timestamp multiplexed with the version number. -// clock_seq_hi_and_reserved unsigned small 8 -// The high field of the clock sequence multiplexed with the variant. -// clock_seq_low unsigned small 9 -// The low field of the clock sequence. -// node character 10 - 15 -// The spatially unique node identifier. -// --------------------------------------------------------------------- -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | time_low | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | time_mid | time_hi_and_version | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |clk_seq_hi_res | clk_seq_low | node (0-1) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | node (2-5) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -// --------------------------------------------------------------------- -// enumerations -// --------------------------------------------------------------------- - -// indicated by a bit pattern in octet 8, marked with N in -// xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx -export enum struct uuid_variant : std::uint8_t { - // NCS backward compatibility (with the obsolete Apollo Network - // Computing System 1.5 UUID format). - // N bit pattern: 0xxx - // > the first 6 octets of the UUID are a 48-bit timestamp (the number - // of 4 microsecond units of time since 1 Jan 1980 UTC); - // > the next 2 octets are reserved; - // > the next octet is the "address family"; - // > the final 7 octets are a 56-bit host ID in the form specified by - // the address family - ncs, - - // RFC 4122/DCE 1.1 - // N bit pattern: 10xx - // > big-endian byte order - rfc, - - // Microsoft Corporation backward compatibility - // N bit pattern: 110x - // > little endian byte order - // > formely used in the Component Object Model (COM) library - microsoft, - - // reserved for possible future definition - // N bit pattern: 111x - reserved -}; - -// indicated by a bit pattern in octet 6, marked with M in -// xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx -export enum struct uuid_version : std::uint8_t { - // only possible for nil or invalid uuids - none = 0, - // The time-based version specified in RFC 4122 - time_based = 1, - // DCE Security version, with embedded POSIX UIDs. - dce_security = 2, - // The name-based version specified in RFS 4122 with MD5 hashing - name_based_md5 = 3, - // The randomly or pseudo-randomly generated version specified in RFS 4122 - random_number_based = 4, - // The name-based version specified in RFS 4122 with SHA1 hashing - name_based_sha1 = 5 -}; - -// Forward declare uuid and to_string so that we can declare to_string as a -// friend later. -export struct uuid; -export template <typename CharT = char, typename Traits = std::char_traits<CharT>, - typename Allocator = std::allocator<CharT>> -auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>; - -// -------------------------------------------------------------------------------------------------------------------------- -// uuid class -// -------------------------------------------------------------------------------------------------------------------------- -export struct uuid -{ - using value_type = std::uint8_t; - - constexpr uuid() noexcept = default; - - uuid(value_type (&arr)[16]) noexcept // NOLINT - { - std::ranges::copy(arr, std::ranges::begin(data)); - } - - explicit constexpr uuid(std::array<value_type, 16> const &arr) noexcept - : data{arr} - { - } - - explicit uuid(std::span<value_type, 16> bytes) - { - std::ranges::copy(bytes, std::ranges::begin(data)); - } - - explicit uuid(std::span<value_type> bytes) - { - if (bytes.size() != 16) - throw std::logic_error("wrong size for uuid"); - std::ranges::copy(bytes, std::ranges::begin(data)); - } - - template <typename ForwardIterator> - explicit uuid(ForwardIterator first, ForwardIterator last) - { - if (std::distance(first, last) != 16) - throw std::logic_error("wrong size for uuid"); - - std::copy(first, last, std::begin(data)); - } - - [[nodiscard]] constexpr auto variant() const noexcept -> uuid_variant - { - if ((data[8] & 0x80U) == 0x00U) - return uuid_variant::ncs; - else if ((data[8] & 0xC0U) == 0x80U) - return uuid_variant::rfc; - else if ((data[8] & 0xE0U) == 0xC0U) - return uuid_variant::microsoft; - else - return uuid_variant::reserved; - } - - [[nodiscard]] constexpr auto version() const noexcept -> uuid_version - { - if ((data[6] & 0xF0U) == 0x10U) - return uuid_version::time_based; - else if ((data[6] & 0xF0U) == 0x20U) - return uuid_version::dce_security; - else if ((data[6] & 0xF0U) == 0x30U) - return uuid_version::name_based_md5; - else if ((data[6] & 0xF0U) == 0x40U) - return uuid_version::random_number_based; - else if ((data[6] & 0xF0U) == 0x50U) - return uuid_version::name_based_sha1; - else - return uuid_version::none; - } - - [[nodiscard]] constexpr auto is_nil() const noexcept -> bool - { - return std::ranges::all_of(data, [](auto i) { return i == 0; }); - } - - auto swap(uuid &other) noexcept -> void - { - data.swap(other.data); - } - - [[nodiscard]] auto as_bytes() const -> std::span<std::byte const, 16> - { - return std::span<std::byte const, 16>( - reinterpret_cast<std::byte const *>(data.data()), 16); - } - - template <typename StringType> - [[nodiscard]] constexpr static auto is_valid_uuid(StringType const &in_str) noexcept -> bool - { - auto str = to_string_view(in_str); - auto firstDigit = true; - auto hasBraces = std::size_t{0}; - auto index = std::size_t{0}; - - if (str.empty()) - return false; - - if (str.front() == '{') - hasBraces = 1; - - if (hasBraces && str.back() != '}') - return false; - - for (std::size_t i = hasBraces; i < str.size() - hasBraces; ++i) { - if (str[i] == '-') - continue; - - if (index >= 16 || !is_hex(str[i])) - return false; - - if (firstDigit) { - firstDigit = false; - } else { - index++; - firstDigit = true; - } - } - - if (index < 16) - return false; - - return true; - } - - template <typename StringType> - [[nodiscard]] constexpr static auto - from_string(StringType const &in_str) -> std::optional<uuid> - { - auto str = to_string_view(in_str); - bool firstDigit = true; - auto hasBraces = std::size_t{0}; - auto index = std::size_t{0}; - - auto data = std::array<std::uint8_t, 16>{}; - - if (str.empty()) - return {}; - - if (str.front() == '{') - hasBraces = 1; - if (hasBraces && str.back() != '}') - return {}; - - for (std::size_t i = hasBraces; i < str.size() - hasBraces; ++i) { - if (str[i] == '-') - continue; - - if (index >= 16 || !is_hex(str[i])) { - return {}; - } - - if (firstDigit) { - data.at(index) = static_cast<std::uint8_t>(hex2char(str[i]) << 4); - firstDigit = false; - } else { - data.at(index) = - static_cast<std::uint8_t>(data.at(index) | hex2char(str[i])); - index++; - firstDigit = true; - } - } - - if (index < 16) { - return {}; - } - - return uuid{data}; - } - -private: - std::array<value_type, 16> data{{0}}; - - friend auto operator==(uuid const &, uuid const &) noexcept -> bool; - friend auto operator<(uuid const &, uuid const &) noexcept -> bool; - - template <class Elem, class Traits> - friend auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id) - -> std::basic_ostream<Elem, Traits> &; - - template <class CharT, class Traits, class Allocator> - friend auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>; - - friend std::hash<uuid>; -}; - -// -------------------------------------------------------------------------------------------------------------------------- -// operators and non-member functions -// -------------------------------------------------------------------------------------------------------------------------- - -export [[nodiscard]] -auto operator==(uuid const &lhs, uuid const &rhs) noexcept -> bool -{ - return lhs.data == rhs.data; -} - -export [[nodiscard]] -auto operator!=(uuid const &lhs, uuid const &rhs) noexcept -> bool -{ - return !(lhs == rhs); -} - -export [[nodiscard]] -auto operator<(uuid const &lhs, uuid const &rhs) noexcept -> bool -{ - return lhs.data < rhs.data; -} - -export template <typename CharT, typename Traits, typename Allocator> -[[nodiscard]] auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator> -{ - auto uustr = - std::basic_string<CharT, Traits, Allocator>(std::from_range, empty_guid<CharT>); - - for (std::size_t i = 0, index = 0; i < 36; ++i) { - if (i == 8 || i == 13 || i == 18 || i == 23) - continue; - - uustr[i] = guid_encoder<CharT>[id.data.at(index) >> 4U & 0x0FU]; - uustr[++i] = guid_encoder<CharT>[id.data.at(index) & 0x0FU]; - index++; - } - - return uustr; -} - -export template <class Elem, class Traits> -auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id) - -> std::basic_ostream<Elem, Traits> & -{ - return s << to_string(id); -} - -export auto swap(uuid &lhs, uuid &rhs) noexcept -> void -{ - lhs.swap(rhs); -} - -/*********************************************************************** - * namespace IDs that could be used for generating name-based uuids - */ - -// Name string is a fully-qualified domain name -export uuid uuid_namespace_dns{ - {0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, - 0xc8} -}; - -// Name string is a URL -export uuid uuid_namespace_url{ - {0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, - 0xc8} -}; - -// Name string is an ISO OID (See https://oidref.com/, -// https://en.wikipedia.org/wiki/Object_identifier) -export uuid uuid_namespace_oid{ - {0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, - 0xc8} -}; - -// Name string is an X.500 DN, in DER or a text output format (See -// https://en.wikipedia.org/wiki/X.500, -// https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) -export uuid uuid_namespace_x500{ - {0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, - 0xc8} -}; - -/*********************************************************************** - * uuid generators - */ - -export template <typename UniformRandomNumberGenerator> -struct basic_uuid_random_generator -{ - using engine_type = UniformRandomNumberGenerator; - - explicit basic_uuid_random_generator(engine_type &gen) - : generator(&gen, [](auto) {}) - { - } - - explicit basic_uuid_random_generator(engine_type *gen) - : generator(gen, [](auto) {}) - { - } - - [[nodiscard]] auto operator()() -> uuid - { - auto bytes = std::array<std::uint8_t, 16>{}; - std::ranges::generate(bytes, [&] { return distribution(*generator); }); - - // variant must be 10xxxxxx - bytes[8] &= 0xBFU; - bytes[8] |= 0x80U; - - // version must be 0100xxxx - bytes[6] &= 0x4FU; - bytes[6] |= 0x40U; - - return uuid{std::begin(bytes), std::end(bytes)}; - } - -private: - std::uniform_int_distribution<std::uint32_t> distribution; - std::shared_ptr<UniformRandomNumberGenerator> generator; -}; - -export using uuid_random_generator = basic_uuid_random_generator<std::mt19937>; - -export struct uuid_name_generator -{ - explicit uuid_name_generator(uuid const &namespace_uuid) noexcept - : nsuuid(namespace_uuid) - { - } - - template <typename StringType> - [[nodiscard]] auto operator()(StringType const &name) -> uuid - { - reset(); - process_characters(to_string_view(name)); - return make_uuid(); - } - -private: - auto reset() -> void - { - hasher.reset(); - - auto nsbytes = nsuuid.as_bytes(); - - auto bytes = std::array<std::byte, 16>(); - std::ranges::copy(nsbytes, std::ranges::begin(bytes)); - - hasher.process_bytes(bytes.data(), bytes.size()); - } - - template <typename CharT, typename Traits> - auto process_characters(std::basic_string_view<CharT, Traits> const str) -> void - { - for (std::uint32_t c : str) { - hasher.process_byte(static_cast<std::uint8_t>(c & 0xFFU)); - if constexpr (!std::is_same_v<CharT, char>) { - hasher.process_byte(static_cast<std::uint8_t>((c >> 8U) & 0xFFU)); - hasher.process_byte(static_cast<std::uint8_t>((c >> 16U) & 0xFFU)); - hasher.process_byte(static_cast<std::uint8_t>((c >> 24U) & 0xFFU)); - } - } - } - - [[nodiscard]] auto make_uuid() -> uuid - { - auto digest = hasher.get_digest_bytes(); - - // variant must be 0b10xxxxxx - digest[8] &= 0xBFU; - digest[8] |= 0x80U; - - // version must be 0b0101xxxx - digest[6] &= 0x5FU; - digest[6] |= 0x50U; - - return uuid(std::span(digest).subspan(0, 16)); - } - - uuid nsuuid; - sha1 hasher; -}; - -/* - * Create a random UUID. - */ -export auto random_uuid() -> uuid -{ - auto rd = std::random_device(); - auto seed_data = std::array<int, std::mt19937::state_size>{}; - std::ranges::generate(seed_data, std::ref(rd)); - - auto seq = std::seed_seq(std::ranges::begin(seed_data), std::ranges::end(seed_data)); - auto generator = std::mt19937(seq); - auto gen = uuid_random_generator{generator}; - - return gen(); -} - -} // namespace nihil - -namespace std { - -export template <> -struct hash<nihil::uuid> -{ - using argument_type = nihil::uuid; - using result_type = std::size_t; - - [[nodiscard]] auto operator()(argument_type const &uuid) const noexcept -> result_type - { - auto const l = static_cast<uint64_t>(uuid.data[0]) << 56U | - static_cast<uint64_t>(uuid.data[1]) << 48U | - static_cast<uint64_t>(uuid.data[2]) << 40U | - static_cast<uint64_t>(uuid.data[3]) << 32U | - static_cast<uint64_t>(uuid.data[4]) << 24U | - static_cast<uint64_t>(uuid.data[5]) << 16U | - static_cast<uint64_t>(uuid.data[6]) << 8U | - static_cast<uint64_t>(uuid.data[7]); - - auto const h = static_cast<uint64_t>(uuid.data[8]) << 56U | - static_cast<uint64_t>(uuid.data[9]) << 48U | - static_cast<uint64_t>(uuid.data[10]) << 40U | - static_cast<uint64_t>(uuid.data[11]) << 32U | - static_cast<uint64_t>(uuid.data[12]) << 24U | - static_cast<uint64_t>(uuid.data[13]) << 16U | - static_cast<uint64_t>(uuid.data[14]) << 8U | - static_cast<uint64_t>(uuid.data[15]); - - return std::hash<std::uint64_t>{}(l ^ h); - } -}; - -} // namespace std |
