From d72ac9cc73cd3b7edaa2b5a26a53237a34fae409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Mon, 7 Mar 2022 12:54:54 +0100 Subject: [PATCH] Core/Uuid: Add FromString builder method --- include/Nazara/Core/Uuid.hpp | 7 +-- include/Nazara/Core/Uuid.inl | 20 ++++----- src/Nazara/Core/Uuid.cpp | 78 ++++++++++++++++++++++++++++------ tests/Engine/Core/UuidTest.cpp | 21 +++++++++ 4 files changed, 99 insertions(+), 27 deletions(-) diff --git a/include/Nazara/Core/Uuid.hpp b/include/Nazara/Core/Uuid.hpp index ee9b353d2..f984241b6 100644 --- a/include/Nazara/Core/Uuid.hpp +++ b/include/Nazara/Core/Uuid.hpp @@ -20,7 +20,7 @@ namespace Nz { public: inline Uuid(); - inline Uuid(const std::array guid); + inline Uuid(const std::array uuid); Uuid(const Uuid&) = default; Uuid(Uuid&& generator) = default; ~Uuid() = default; @@ -34,13 +34,14 @@ namespace Nz Uuid& operator=(const Uuid&) = default; Uuid& operator=(Uuid&&) = default; + static Uuid FromString(std::string_view str); static Uuid Generate(); private: - std::array m_guid; + std::array m_uuid; }; - NAZARA_CORE_API std::ostream& operator<<(std::ostream& out, const Uuid& guid); + NAZARA_CORE_API std::ostream& operator<<(std::ostream& out, const Uuid& uuid); inline bool operator==(const Uuid& lhs, const Uuid& rhs); inline bool operator!=(const Uuid& lhs, const Uuid& rhs); inline bool operator<(const Uuid& lhs, const Uuid& rhs); diff --git a/include/Nazara/Core/Uuid.inl b/include/Nazara/Core/Uuid.inl index 5fc081512..77d4cd8cb 100644 --- a/include/Nazara/Core/Uuid.inl +++ b/include/Nazara/Core/Uuid.inl @@ -9,30 +9,30 @@ namespace Nz { inline Uuid::Uuid() { - m_guid.fill(0); + m_uuid.fill(0); } - inline Uuid::Uuid(const std::array guid) : - m_guid(guid) + inline Uuid::Uuid(const std::array uuid) : + m_uuid(uuid) { } inline bool Uuid::IsNull() const { - Uuid NullGuid; - return *this == NullGuid; + Uuid nullUuid; + return *this == nullUuid; } inline const std::array& Uuid::ToArray() const { - return m_guid; + return m_uuid; } inline std::string Uuid::ToString() const { - std::array guidStr = ToStringArray(); + std::array uuidStr = ToStringArray(); - return std::string(guidStr.data(), guidStr.size() - 1); + return std::string(uuidStr.data(), uuidStr.size() - 1); } bool operator==(const Uuid& lhs, const Uuid& rhs) @@ -93,12 +93,12 @@ namespace std template <> struct hash { - size_t operator()(const Nz::Uuid& guid) const + size_t operator()(const Nz::Uuid& uuid) const { // DJB2 algorithm http://www.cse.yorku.ca/~oz/hash.html size_t h = 5381; - const array& data = guid.ToArray(); + const array& data = uuid.ToArray(); for (size_t i = 0; i < data.size(); ++i) h = ((h << 5) + h) + data[i]; diff --git a/src/Nazara/Core/Uuid.cpp b/src/Nazara/Core/Uuid.cpp index 701f603d7..a7d77f83f 100644 --- a/src/Nazara/Core/Uuid.cpp +++ b/src/Nazara/Core/Uuid.cpp @@ -15,51 +15,101 @@ namespace Nz { + namespace + { + bool ParseHexadecimalPair(Pointer& str, UInt8& number) + { + number = 0; + + for (UInt8 mul : { 0x10, 1 }) + { + if (*str >= '0' && *str <= '9') + number += (*str - '0') * mul; + else if (((*str & 0x5F) >= 'A' && (*str & 0x5F) <= 'F')) + number += ((*str & 0x5F) - 'A' + 10) * mul; + else + return false; + + str++; + } + + return true; + } + } + std::array Uuid::ToStringArray() const { - std::array guidStr; //< Including \0 - std::snprintf(guidStr.data(), guidStr.size(), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - m_guid[0], m_guid[1], m_guid[2], m_guid[3], m_guid[4], m_guid[5], m_guid[6], m_guid[7], - m_guid[8], m_guid[9], m_guid[10], m_guid[11], m_guid[12], m_guid[13], m_guid[14], m_guid[15]); + std::array uuidStr; //< Including \0 + std::snprintf(uuidStr.data(), uuidStr.size(), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + m_uuid[0], m_uuid[1], m_uuid[2], m_uuid[3], m_uuid[4], m_uuid[5], m_uuid[6], m_uuid[7], + m_uuid[8], m_uuid[9], m_uuid[10], m_uuid[11], m_uuid[12], m_uuid[13], m_uuid[14], m_uuid[15]); - return guidStr; + return uuidStr; + } + + Uuid Uuid::FromString(std::string_view str) + { + if (str.size() != 36) + return {}; + + const char* ptr = str.data(); + + std::array uuid; + UInt8* uuidPart = &uuid[0]; + + bool first = true; + for (std::size_t groupSize : { 4, 2, 2, 2, 6 }) + { + if (!first && *ptr++ != '-') + return {}; + + first = false; + + for (std::size_t i = 0; i < groupSize; ++i) + { + if (!ParseHexadecimalPair(ptr, *uuidPart++)) + return {}; + } + } + + return Uuid{ uuid }; } Uuid Uuid::Generate() { - std::array guid; + std::array uuid; #ifdef NAZARA_PLATFORM_WINDOWS GUID id; CoCreateGuid(&id); for (unsigned int i = 0; i < 4; ++i) - guid[i] = static_cast(id.Data1 >> ((3 - i) * 8 & 0xFF)); + uuid[i] = static_cast(id.Data1 >> ((3 - i) * 8 & 0xFF)); for (unsigned int i = 0; i < 2; ++i) - guid[4 + i] = static_cast(id.Data2 >> ((1 - i) * 8 & 0xFF)); + uuid[4 + i] = static_cast(id.Data2 >> ((1 - i) * 8 & 0xFF)); for (unsigned int i = 0; i < 2; ++i) - guid[6 + i] = static_cast(id.Data3 >> ((1 - i) * 8 & 0xFF)); + uuid[6 + i] = static_cast(id.Data3 >> ((1 - i) * 8 & 0xFF)); for (unsigned int i = 0; i < 8; ++i) - guid[8 + i] = static_cast(id.Data4[i]); + uuid[8 + i] = static_cast(id.Data4[i]); #elif defined(NAZARA_PLATFORM_LINUX) || defined(NAZARA_PLATFORM_MACOSX) uuid_t id; uuid_generate(id); - std::copy(std::begin(id), std::end(id), guid.begin()); + std::copy(std::begin(id), std::end(id), uuid.begin()); #else #error Missing platform support #endif - return guid; + return uuid; } std::ostream& operator<<(std::ostream& out, const Uuid& guid) { - std::array guidStr = guid.ToStringArray(); + std::array uuidStr = guid.ToStringArray(); - return out << guidStr.data(); + return out << uuidStr.data(); } } diff --git a/tests/Engine/Core/UuidTest.cpp b/tests/Engine/Core/UuidTest.cpp index 597321403..1fa786c68 100644 --- a/tests/Engine/Core/UuidTest.cpp +++ b/tests/Engine/Core/UuidTest.cpp @@ -6,6 +6,25 @@ SCENARIO("Uuid", "[CORE][UUID]") { + WHEN("Parsing UUID") + { + CHECK(Nz::Uuid::FromString("00000000-0000-0000-0000-000000000000") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid({ 0x1B, 0x0E, 0x29, 0xAF, 0xFD, 0x4A, 0x43, 0xE0, 0xBA, 0x4C, 0xE9, 0x33, 0x41, 0x83, 0xB1, 0xF1 })); + + // Testing some invalid cases + CHECK(Nz::Uuid::FromString("Nazara Engine") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("Zb0e29af-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29a\t-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4\v-43e0-ba4c-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-\r3e0-ba4c-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e\n-ba4c-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-\0a4c-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4\n-e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4c-g9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a-43e0-ba4c-e9334183b1fG") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-HELL-OWOR-LDDD-e9334183b1f1") == Nz::Uuid()); + } + WHEN("Generating a null UUID") { Nz::Uuid nullUuid; @@ -19,6 +38,7 @@ SCENARIO("Uuid", "[CORE][UUID]") CHECK_FALSE(nullUuid > Nz::Uuid{}); CHECK_FALSE(nullUuid < Nz::Uuid{}); CHECK(nullUuid != Nz::Uuid::Generate()); + CHECK(Nz::Uuid::FromString(nullUuid.ToString()) == nullUuid); } WHEN("Generating a UUID") @@ -36,6 +56,7 @@ SCENARIO("Uuid", "[CORE][UUID]") CHECK(uuid <= uuid); CHECK_FALSE(uuid > uuid); CHECK_FALSE(uuid < uuid); + CHECK(Nz::Uuid::FromString(uuid.ToString()) == uuid); } WHEN("Generating multiple UUID, they are unique")