diff --git a/include/uuid.h b/include/uuid.h index d5dd65d..19f5ca0 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -82,9 +82,19 @@ namespace uuids } template - constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) + constexpr std::basic_string_view to_string_view(TChar const * str) { - return (hex2char(a) << 4) | hex2char(b); + if (str) return str; + return {}; + } + + template + constexpr std::basic_string_view< + typename StringType::value_type, + typename StringType::traits_type> + to_string_view(StringType const & str) + { + return str; } class sha1 @@ -348,17 +358,14 @@ namespace uuids public: using value_type = uint8_t; - constexpr uuid() noexcept : data({}) {}; + constexpr uuid() noexcept = default; uuid(value_type(&arr)[16]) noexcept { std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); } - uuid(std::array const & arr) noexcept - { - std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); - } + constexpr uuid(std::array const & arr) noexcept : data{arr} {} explicit uuid(span bytes) { @@ -416,29 +423,25 @@ namespace uuids return span(reinterpret_cast(data.data()), 16); } - template - static bool is_valid_uuid(CharT const * str) noexcept + template + constexpr static bool is_valid_uuid(StringType const & in_str) noexcept { + auto str = detail::to_string_view(in_str); bool firstDigit = true; int hasBraces = 0; size_t index = 0; - size_t size = 0; - if constexpr(std::is_same_v) - size = strlen(str); - else - size = wcslen(str); - if (str == nullptr || size == 0) + if (str.empty()) return false; - if (str[0] == static_cast('{')) + if (str.front() == '{') hasBraces = 1; - if (hasBraces && str[size - 1] != static_cast('}')) + if (hasBraces && str.back() != '}') return false; - for (size_t i = hasBraces; i < size - hasBraces; ++i) + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) { - if (str[i] == static_cast('-')) continue; + if (str[i] == '-') continue; if (index >= 16 || !detail::is_hex(str[i])) { @@ -464,39 +467,26 @@ namespace uuids return true; } - template, - class Allocator = std::allocator> - static bool is_valid_uuid(std::basic_string const & str) noexcept + template + constexpr static std::optional from_string(StringType const & in_str) noexcept { - return is_valid_uuid(str.c_str()); - } - - template - static std::optional from_string(CharT const * str) noexcept - { - CharT digit = 0; + auto str = detail::to_string_view(in_str); bool firstDigit = true; int hasBraces = 0; size_t index = 0; - size_t size = 0; - if constexpr(std::is_same_v) - size = strlen(str); - else - size = wcslen(str); std::array data{ { 0 } }; - if (str == nullptr || size == 0) return {}; + if (str.empty()) return {}; - if (str[0] == static_cast('{')) + if (str.front() == '{') hasBraces = 1; - if (hasBraces && str[size - 1] != static_cast('}')) + if (hasBraces && str.back() != '}') return {}; - for (size_t i = hasBraces; i < size - hasBraces; ++i) + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) { - if (str[i] == static_cast('-')) continue; + if (str[i] == '-') continue; if (index >= 16 || !detail::is_hex(str[i])) { @@ -505,12 +495,12 @@ namespace uuids if (firstDigit) { - digit = str[i]; + data[index] = detail::hex2char(str[i]) << 4; firstDigit = false; } else { - data[index++] = detail::hexpair2char(digit, str[i]); + data[index++] |= detail::hex2char(str[i]); firstDigit = true; } } @@ -520,7 +510,7 @@ namespace uuids return {}; } - return uuid{ std::cbegin(data), std::cend(data) }; + return uuid{ data }; } template - uuid operator()(CharT const * name) - { - size_t size = 0; - if constexpr (std::is_same_v) - size = strlen(name); - else - size = wcslen(name); - - reset(); - process_characters(name, size); - return make_uuid(); - } - - template, - class Allocator = std::allocator> - uuid operator()(std::basic_string const & name) + template + uuid operator()(StringType const & name) { reset(); - process_characters(name.data(), name.size()); + process_characters(detail::to_string_view(name)); return make_uuid(); } @@ -802,24 +776,20 @@ namespace uuids std::copy(std::cbegin(nsbytes), std::cend(nsbytes), bytes); hasher.process_bytes(bytes, 16); } - - template ::value>> - void process_characters(char_type const * const characters, size_t const count) - { - for (size_t i = 0; i < count; i++) - { - uint32_t c = characters[i]; - hasher.process_byte(static_cast((c >> 0) & 0xFF)); - hasher.process_byte(static_cast((c >> 8) & 0xFF)); - hasher.process_byte(static_cast((c >> 16) & 0xFF)); - hasher.process_byte(static_cast((c >> 24) & 0xFF)); - } - } - void process_characters(const char * const characters, size_t const count) + template + void process_characters(std::basic_string_view const str) { - hasher.process_bytes(characters, count); + 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)); + } + } } uuid make_uuid() diff --git a/test/test_generators.cpp b/test/test_generators.cpp index 02dda72..572bb34 100644 --- a/test/test_generators.cpp +++ b/test/test_generators.cpp @@ -254,6 +254,49 @@ TEST_CASE("Test name generator (std::string)", "[gen][name]") REQUIRE(id3 != id4); } +TEST_CASE("Test name generator (std::string_view)", "[gen][name]") +{ + using namespace std::string_view_literals; + + uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); + auto id1 = dgen("john"sv); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuids::uuid_version::name_based_sha1); + REQUIRE(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen("jane"sv); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuids::uuid_version::name_based_sha1); + REQUIRE(id2.variant() == uuids::uuid_variant::rfc); + + auto id3 = dgen("jane"sv); + REQUIRE(!id3.is_nil()); + REQUIRE(id3.version() == uuids::uuid_version::name_based_sha1); + REQUIRE(id3.variant() == uuids::uuid_variant::rfc); + + auto id4 = dgen(L"jane"sv); + REQUIRE(!id4.is_nil()); + REQUIRE(id4.version() == uuids::uuid_version::name_based_sha1); + REQUIRE(id4.variant() == uuids::uuid_variant::rfc); + + REQUIRE(id1 != id2); + REQUIRE(id2 == id3); + REQUIRE(id3 != id4); +} + +TEST_CASE("Test name generator equality (char const*, std::string, std::string_view)", "[gen][name]") +{ + using namespace std::literals; + + uuids::uuid_name_generator dgen(uuids::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); +} + #ifdef _WIN32 TEST_CASE("Test time generator", "[gen][time]") { diff --git a/test/test_uuid.cpp b/test/test_uuid.cpp index bd736f7..2a41ab6 100644 --- a/test/test_uuid.cpp +++ b/test/test_uuid.cpp @@ -100,6 +100,20 @@ TEST_CASE("Test is_valid_uuid(basic_string)", "[parse]") } } +TEST_CASE("Test is_valid_uuid(basic_string_view)", "[parse]") +{ + using namespace std::string_view_literals; + + REQUIRE(uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43"sv)); + REQUIRE(uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); + REQUIRE(uuids::uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43"sv)); + REQUIRE(uuids::uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); + REQUIRE(uuids::uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000"sv)); + REQUIRE(uuids::uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}"sv)); + REQUIRE(uuids::uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000"sv)); + REQUIRE(uuids::uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}"sv)); +} + TEST_CASE("Test is_valid_uuid(char*) invalid format", "[parse]") { REQUIRE(!uuids::uuid::is_valid_uuid("")); @@ -145,6 +159,18 @@ TEST_CASE("Test is_valid_uuid(basic_string) invalid format", "[parse]") } } +TEST_CASE("Test is_valid_uuid(basic_string_view) invalid format", "[parse]") +{ + using namespace std::string_view_literals; + + REQUIRE(!uuids::uuid::is_valid_uuid(""sv)); + REQUIRE(!uuids::uuid::is_valid_uuid("{}"sv)); + REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4"sv)); + REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430"sv)); + REQUIRE(!uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43"sv)); + REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}"sv)); +} + TEST_CASE("Test from_string(char*)", "[parse]") { { @@ -219,7 +245,7 @@ TEST_CASE("Test from_string(basic_string)", "[parse]") } { - auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); + auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"s).value(); REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); REQUIRE(uuids::to_string(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); } @@ -261,6 +287,65 @@ TEST_CASE("Test from_string(basic_string)", "[parse]") } } +TEST_CASE("Test from_string(basic_string_view)", "[parse]") +{ + using namespace std::string_view_literals; + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(uuids::to_string(guid) == str); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"sv).value(); + REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + REQUIRE(uuids::to_string(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(uuids::to_string(guid) == str); + } + + { + auto str = "4718382325744bfdb41199ed177d3e43"sv; + REQUIRE_NOTHROW(uuids::uuid::from_string(str)); + REQUIRE(uuids::uuid::from_string(str).has_value()); + } + + { + auto str = "00000000-0000-0000-0000-000000000000"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = "{00000000-0000-0000-0000-000000000000}"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"00000000-0000-0000-0000-000000000000"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"{00000000-0000-0000-0000-000000000000}"sv; + auto guid = uuids::uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } +} + TEST_CASE("Test from_string(char*) invalid format", "[parse]") { REQUIRE(!uuids::uuid::from_string("").has_value()); @@ -306,6 +391,18 @@ TEST_CASE("Test from_string(basic_string) invalid format", "[parse]") } } +TEST_CASE("Test from_string(basic_string_view) invalid format", "[parse]") +{ + using namespace std::string_view_literals; + + REQUIRE(!uuids::uuid::from_string(""sv).has_value()); + REQUIRE(!uuids::uuid::from_string("{}"sv).has_value()); + REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4"sv).has_value()); + REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430"sv).has_value()); + REQUIRE(!uuids::uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43"sv).has_value()); + REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}"sv).has_value()); +} + TEST_CASE("Test iterators constructor", "[ctors]") { using namespace std::string_literals;