From 3271099fea41d5adc0003c02c8481b334772296a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 4 Feb 2022 23:44:02 -0500 Subject: common: Implement NewUUID This is a fixed and revised implementation of UUID that uses an array of bytes as its internal representation of a UUID instead of a u128 (which was an array of 2 u64s). In addition to this, the generation of RFC 4122 Version 4 compliant UUIDs is also implemented. --- src/common/CMakeLists.txt | 2 + src/common/new_uuid.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++++++ src/common/new_uuid.h | 138 +++++++++++++++++++++++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 src/common/new_uuid.cpp create mode 100644 src/common/new_uuid.h (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index adf70eb8b..3dd460191 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -99,6 +99,8 @@ add_library(common STATIC microprofile.cpp microprofile.h microprofileui.h + new_uuid.cpp + new_uuid.h nvidia_flags.cpp nvidia_flags.h page_table.cpp diff --git a/src/common/new_uuid.cpp b/src/common/new_uuid.cpp new file mode 100644 index 000000000..0442681c8 --- /dev/null +++ b/src/common/new_uuid.cpp @@ -0,0 +1,182 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include + +#include "common/assert.h" +#include "common/new_uuid.h" +#include "common/tiny_mt.h" + +namespace Common { + +namespace { + +constexpr size_t RawStringSize = sizeof(NewUUID) * 2; +constexpr size_t FormattedStringSize = RawStringSize + 4; + +u8 HexCharToByte(char c) { + if (c >= '0' && c <= '9') { + return static_cast(c - '0'); + } + if (c >= 'a' && c <= 'f') { + return static_cast(c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') { + return static_cast(c - 'A' + 10); + } + ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); + return u8{0}; +} + +std::array ConstructFromRawString(std::string_view raw_string) { + std::array uuid; + + for (size_t i = 0; i < RawStringSize; i += 2) { + uuid[i / 2] = + static_cast((HexCharToByte(raw_string[i]) << 4) | HexCharToByte(raw_string[i + 1])); + } + + return uuid; +} + +std::array ConstructFromFormattedString(std::string_view formatted_string) { + std::array uuid; + + size_t i = 0; + + // Process the first 8 characters. + const auto* str = formatted_string.data(); + + for (; i < 4; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + // Process the next 4 characters. + ++str; + + for (; i < 6; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + // Process the next 4 characters. + ++str; + + for (; i < 8; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + // Process the next 4 characters. + ++str; + + for (; i < 10; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + // Process the last 12 characters. + ++str; + + for (; i < 16; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + return uuid; +} + +std::array ConstructUUID(std::string_view uuid_string) { + const auto length = uuid_string.length(); + + if (length == 0) { + return {}; + } + + // Check if the input string contains 32 hexadecimal characters. + if (length == RawStringSize) { + return ConstructFromRawString(uuid_string); + } + + // Check if the input string has the length of a RFC 4122 formatted UUID string. + if (length == FormattedStringSize) { + return ConstructFromFormattedString(uuid_string); + } + + ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length); + + return {}; +} + +} // Anonymous namespace + +NewUUID::NewUUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {} + +std::string NewUUID::RawString() const { + return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}" + "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], + uuid[15]); +} + +std::string NewUUID::FormattedString() const { + return fmt::format("{:02x}{:02x}{:02x}{:02x}" + "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-" + "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], + uuid[15]); +} + +size_t NewUUID::Hash() const noexcept { + u64 hash; + u64 temp; + + std::memcpy(&hash, uuid.data(), sizeof(u64)); + std::memcpy(&temp, uuid.data() + 8, sizeof(u64)); + + return hash ^ std::rotl(temp, 1); +} + +NewUUID NewUUID::MakeRandom() { + std::random_device device; + + return MakeRandomWithSeed(device()); +} + +NewUUID NewUUID::MakeRandomWithSeed(u32 seed) { + // Create and initialize our RNG. + TinyMT rng; + rng.Initialize(seed); + + NewUUID uuid; + + // Populate the UUID with random bytes. + rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(NewUUID)); + + return uuid; +} + +NewUUID NewUUID::MakeRandomRFC4122V4() { + auto uuid = MakeRandom(); + + // According to Proposed Standard RFC 4122 Section 4.4, we must: + + // 1. Set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively. + uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); + + // 2. Set the four most significant bits (bits 12 through 15) of the + // time_hi_and_version field to the 4-bit version number from Section 4.1.3. + uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF); + + return uuid; +} + +} // namespace Common diff --git a/src/common/new_uuid.h b/src/common/new_uuid.h new file mode 100644 index 000000000..bd4468ad2 --- /dev/null +++ b/src/common/new_uuid.h @@ -0,0 +1,138 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { + +struct NewUUID { + std::array uuid{}; + + /// Constructs an invalid UUID. + constexpr NewUUID() = default; + + /// Constructs a UUID from a reference to a 128 bit array. + constexpr explicit NewUUID(const std::array& uuid_) : uuid{uuid_} {} + + /** + * Constructs a UUID from either: + * 1. A 32 hexadecimal character string representing the bytes of the UUID + * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * The input string may contain uppercase or lowercase characters, but they must: + * 1. Contain valid hexadecimal characters (0-9, a-f, A-F) + * 2. Not contain the "0x" hexadecimal prefix + * + * Should the input string not meet the above requirements, + * an assert will be triggered and an invalid UUID is set instead. + */ + explicit NewUUID(std::string_view uuid_string); + + ~NewUUID() = default; + + constexpr NewUUID(const NewUUID&) noexcept = default; + constexpr NewUUID(NewUUID&&) noexcept = default; + + constexpr NewUUID& operator=(const NewUUID&) noexcept = default; + constexpr NewUUID& operator=(NewUUID&&) noexcept = default; + + /** + * Returns whether the stored UUID is valid or not. + * + * @returns True if the stored UUID is valid, false otherwise. + */ + constexpr bool IsValid() const { + return uuid != std::array{}; + } + + /** + * Returns whether the stored UUID is invalid or not. + * + * @returns True if the stored UUID is invalid, false otherwise. + */ + constexpr bool IsInvalid() const { + return !IsValid(); + } + + /** + * Returns a 32 hexadecimal character string representing the bytes of the UUID. + * + * @returns A 32 hexadecimal character string of the UUID. + */ + std::string RawString() const; + + /** + * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. + * + * @returns A RFC 4122 formatted UUID string. + */ + std::string FormattedString() const; + + /** + * Returns a 64-bit hash of the UUID for use in hash table data structures. + * + * @returns A 64-bit hash of the UUID. + */ + size_t Hash() const noexcept; + + /** + * Creates a default UUID "yuzu Default UID". + * + * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID". + */ + static constexpr NewUUID MakeDefault() { + return NewUUID{ + {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, + }; + } + + /** + * Creates a random UUID. + * + * @returns A random UUID. + */ + static NewUUID MakeRandom(); + + /** + * Creates a random UUID with a seed. + * + * @param seed A seed to initialize the Mersenne-Twister RNG + * + * @returns A random UUID. + */ + static NewUUID MakeRandomWithSeed(u32 seed); + + /** + * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant. + * + * @returns A random UUID that is RFC 4122 Version 4 compliant. + */ + static NewUUID MakeRandomRFC4122V4(); + + friend constexpr bool operator==(const NewUUID& lhs, const NewUUID& rhs) = default; +}; +static_assert(sizeof(NewUUID) == 0x10, "UUID has incorrect size."); + +/// An invalid UUID. This UUID has all its bytes set to 0. +constexpr NewUUID InvalidUUID = {}; + +} // namespace Common + +namespace std { + +template <> +struct hash { + size_t operator()(const Common::NewUUID& uuid) const noexcept { + return uuid.Hash(); + } +}; + +} // namespace std -- cgit v1.2.3 From cb30fe50cd074fe05dd1d6e4b0d58116d3d98489 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 5 Feb 2022 00:40:28 -0500 Subject: input/hid: Migrate to the new UUID implementation --- src/common/input.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/common') diff --git a/src/common/input.h b/src/common/input.h index 54fcb24b0..95d30497d 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -10,8 +10,8 @@ #include #include #include "common/logging/log.h" +#include "common/new_uuid.h" #include "common/param_package.h" -#include "common/uuid.h" namespace Common::Input { @@ -97,7 +97,7 @@ struct AnalogStatus { // Button data struct ButtonStatus { - Common::UUID uuid{}; + Common::NewUUID uuid{}; bool value{}; bool inverted{}; bool toggle{}; @@ -109,7 +109,7 @@ using BatteryStatus = BatteryLevel; // Analog and digital joystick data struct StickStatus { - Common::UUID uuid{}; + Common::NewUUID uuid{}; AnalogStatus x{}; AnalogStatus y{}; bool left{}; @@ -120,7 +120,7 @@ struct StickStatus { // Analog and digital trigger data struct TriggerStatus { - Common::UUID uuid{}; + Common::NewUUID uuid{}; AnalogStatus analog{}; ButtonStatus pressed{}; }; -- cgit v1.2.3 From d94dcaefa020dcfc89d655dcf5aa8dad998e0bf2 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 5 Feb 2022 11:41:39 -0500 Subject: common: uuid: Add AsU128() This copies the internal bytes of the UUID into a u128 for backwards compatibility. This should not be used. --- src/common/new_uuid.cpp | 6 ++++++ src/common/new_uuid.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'src/common') diff --git a/src/common/new_uuid.cpp b/src/common/new_uuid.cpp index 0442681c8..f2f0077ae 100644 --- a/src/common/new_uuid.cpp +++ b/src/common/new_uuid.cpp @@ -144,6 +144,12 @@ size_t NewUUID::Hash() const noexcept { return hash ^ std::rotl(temp, 1); } +u128 NewUUID::AsU128() const { + u128 uuid_old; + std::memcpy(&uuid_old, uuid.data(), sizeof(NewUUID)); + return uuid_old; +} + NewUUID NewUUID::MakeRandom() { std::random_device device; diff --git a/src/common/new_uuid.h b/src/common/new_uuid.h index bd4468ad2..44665ad5a 100644 --- a/src/common/new_uuid.h +++ b/src/common/new_uuid.h @@ -83,6 +83,9 @@ struct NewUUID { */ size_t Hash() const noexcept; + /// DO NOT USE. Copies the contents of the UUID into a u128. + u128 AsU128() const; + /** * Creates a default UUID "yuzu Default UID". * -- cgit v1.2.3 From 25db62ce1534cbd8b93b4284869229e4bd7de54d Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 5 Feb 2022 12:35:39 -0500 Subject: general: Rename NewUUID to UUID, and remove the previous UUID impl This completes the removal of the old UUID implementation. --- src/common/CMakeLists.txt | 2 - src/common/input.h | 8 +- src/common/new_uuid.cpp | 188 ---------------------------------------------- src/common/new_uuid.h | 141 ---------------------------------- src/common/uuid.cpp | 185 ++++++++++++++++++++++++++++++++++----------- src/common/uuid.h | 166 ++++++++++++++++++++++++++-------------- 6 files changed, 256 insertions(+), 434 deletions(-) delete mode 100644 src/common/new_uuid.cpp delete mode 100644 src/common/new_uuid.h (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3dd460191..adf70eb8b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -99,8 +99,6 @@ add_library(common STATIC microprofile.cpp microprofile.h microprofileui.h - new_uuid.cpp - new_uuid.h nvidia_flags.cpp nvidia_flags.h page_table.cpp diff --git a/src/common/input.h b/src/common/input.h index 95d30497d..54fcb24b0 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -10,8 +10,8 @@ #include #include #include "common/logging/log.h" -#include "common/new_uuid.h" #include "common/param_package.h" +#include "common/uuid.h" namespace Common::Input { @@ -97,7 +97,7 @@ struct AnalogStatus { // Button data struct ButtonStatus { - Common::NewUUID uuid{}; + Common::UUID uuid{}; bool value{}; bool inverted{}; bool toggle{}; @@ -109,7 +109,7 @@ using BatteryStatus = BatteryLevel; // Analog and digital joystick data struct StickStatus { - Common::NewUUID uuid{}; + Common::UUID uuid{}; AnalogStatus x{}; AnalogStatus y{}; bool left{}; @@ -120,7 +120,7 @@ struct StickStatus { // Analog and digital trigger data struct TriggerStatus { - Common::NewUUID uuid{}; + Common::UUID uuid{}; AnalogStatus analog{}; ButtonStatus pressed{}; }; diff --git a/src/common/new_uuid.cpp b/src/common/new_uuid.cpp deleted file mode 100644 index f2f0077ae..000000000 --- a/src/common/new_uuid.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include - -#include - -#include "common/assert.h" -#include "common/new_uuid.h" -#include "common/tiny_mt.h" - -namespace Common { - -namespace { - -constexpr size_t RawStringSize = sizeof(NewUUID) * 2; -constexpr size_t FormattedStringSize = RawStringSize + 4; - -u8 HexCharToByte(char c) { - if (c >= '0' && c <= '9') { - return static_cast(c - '0'); - } - if (c >= 'a' && c <= 'f') { - return static_cast(c - 'a' + 10); - } - if (c >= 'A' && c <= 'F') { - return static_cast(c - 'A' + 10); - } - ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); - return u8{0}; -} - -std::array ConstructFromRawString(std::string_view raw_string) { - std::array uuid; - - for (size_t i = 0; i < RawStringSize; i += 2) { - uuid[i / 2] = - static_cast((HexCharToByte(raw_string[i]) << 4) | HexCharToByte(raw_string[i + 1])); - } - - return uuid; -} - -std::array ConstructFromFormattedString(std::string_view formatted_string) { - std::array uuid; - - size_t i = 0; - - // Process the first 8 characters. - const auto* str = formatted_string.data(); - - for (; i < 4; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); - } - - // Process the next 4 characters. - ++str; - - for (; i < 6; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); - } - - // Process the next 4 characters. - ++str; - - for (; i < 8; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); - } - - // Process the next 4 characters. - ++str; - - for (; i < 10; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); - } - - // Process the last 12 characters. - ++str; - - for (; i < 16; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); - } - - return uuid; -} - -std::array ConstructUUID(std::string_view uuid_string) { - const auto length = uuid_string.length(); - - if (length == 0) { - return {}; - } - - // Check if the input string contains 32 hexadecimal characters. - if (length == RawStringSize) { - return ConstructFromRawString(uuid_string); - } - - // Check if the input string has the length of a RFC 4122 formatted UUID string. - if (length == FormattedStringSize) { - return ConstructFromFormattedString(uuid_string); - } - - ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length); - - return {}; -} - -} // Anonymous namespace - -NewUUID::NewUUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {} - -std::string NewUUID::RawString() const { - return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}" - "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", - uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], - uuid[15]); -} - -std::string NewUUID::FormattedString() const { - return fmt::format("{:02x}{:02x}{:02x}{:02x}" - "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-" - "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", - uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], - uuid[15]); -} - -size_t NewUUID::Hash() const noexcept { - u64 hash; - u64 temp; - - std::memcpy(&hash, uuid.data(), sizeof(u64)); - std::memcpy(&temp, uuid.data() + 8, sizeof(u64)); - - return hash ^ std::rotl(temp, 1); -} - -u128 NewUUID::AsU128() const { - u128 uuid_old; - std::memcpy(&uuid_old, uuid.data(), sizeof(NewUUID)); - return uuid_old; -} - -NewUUID NewUUID::MakeRandom() { - std::random_device device; - - return MakeRandomWithSeed(device()); -} - -NewUUID NewUUID::MakeRandomWithSeed(u32 seed) { - // Create and initialize our RNG. - TinyMT rng; - rng.Initialize(seed); - - NewUUID uuid; - - // Populate the UUID with random bytes. - rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(NewUUID)); - - return uuid; -} - -NewUUID NewUUID::MakeRandomRFC4122V4() { - auto uuid = MakeRandom(); - - // According to Proposed Standard RFC 4122 Section 4.4, we must: - - // 1. Set the two most significant bits (bits 6 and 7) of the - // clock_seq_hi_and_reserved to zero and one, respectively. - uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); - - // 2. Set the four most significant bits (bits 12 through 15) of the - // time_hi_and_version field to the 4-bit version number from Section 4.1.3. - uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF); - - return uuid; -} - -} // namespace Common diff --git a/src/common/new_uuid.h b/src/common/new_uuid.h deleted file mode 100644 index 44665ad5a..000000000 --- a/src/common/new_uuid.h +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include - -#include "common/common_types.h" - -namespace Common { - -struct NewUUID { - std::array uuid{}; - - /// Constructs an invalid UUID. - constexpr NewUUID() = default; - - /// Constructs a UUID from a reference to a 128 bit array. - constexpr explicit NewUUID(const std::array& uuid_) : uuid{uuid_} {} - - /** - * Constructs a UUID from either: - * 1. A 32 hexadecimal character string representing the bytes of the UUID - * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - * - * The input string may contain uppercase or lowercase characters, but they must: - * 1. Contain valid hexadecimal characters (0-9, a-f, A-F) - * 2. Not contain the "0x" hexadecimal prefix - * - * Should the input string not meet the above requirements, - * an assert will be triggered and an invalid UUID is set instead. - */ - explicit NewUUID(std::string_view uuid_string); - - ~NewUUID() = default; - - constexpr NewUUID(const NewUUID&) noexcept = default; - constexpr NewUUID(NewUUID&&) noexcept = default; - - constexpr NewUUID& operator=(const NewUUID&) noexcept = default; - constexpr NewUUID& operator=(NewUUID&&) noexcept = default; - - /** - * Returns whether the stored UUID is valid or not. - * - * @returns True if the stored UUID is valid, false otherwise. - */ - constexpr bool IsValid() const { - return uuid != std::array{}; - } - - /** - * Returns whether the stored UUID is invalid or not. - * - * @returns True if the stored UUID is invalid, false otherwise. - */ - constexpr bool IsInvalid() const { - return !IsValid(); - } - - /** - * Returns a 32 hexadecimal character string representing the bytes of the UUID. - * - * @returns A 32 hexadecimal character string of the UUID. - */ - std::string RawString() const; - - /** - * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. - * - * @returns A RFC 4122 formatted UUID string. - */ - std::string FormattedString() const; - - /** - * Returns a 64-bit hash of the UUID for use in hash table data structures. - * - * @returns A 64-bit hash of the UUID. - */ - size_t Hash() const noexcept; - - /// DO NOT USE. Copies the contents of the UUID into a u128. - u128 AsU128() const; - - /** - * Creates a default UUID "yuzu Default UID". - * - * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID". - */ - static constexpr NewUUID MakeDefault() { - return NewUUID{ - {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, - }; - } - - /** - * Creates a random UUID. - * - * @returns A random UUID. - */ - static NewUUID MakeRandom(); - - /** - * Creates a random UUID with a seed. - * - * @param seed A seed to initialize the Mersenne-Twister RNG - * - * @returns A random UUID. - */ - static NewUUID MakeRandomWithSeed(u32 seed); - - /** - * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant. - * - * @returns A random UUID that is RFC 4122 Version 4 compliant. - */ - static NewUUID MakeRandomRFC4122V4(); - - friend constexpr bool operator==(const NewUUID& lhs, const NewUUID& rhs) = default; -}; -static_assert(sizeof(NewUUID) == 0x10, "UUID has incorrect size."); - -/// An invalid UUID. This UUID has all its bytes set to 0. -constexpr NewUUID InvalidUUID = {}; - -} // namespace Common - -namespace std { - -template <> -struct hash { - size_t operator()(const Common::NewUUID& uuid) const noexcept { - return uuid.Hash(); - } -}; - -} // namespace std diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index d7435a6e9..10a1b86e0 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp @@ -1,21 +1,22 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2022 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include "common/assert.h" +#include "common/tiny_mt.h" #include "common/uuid.h" namespace Common { namespace { -bool IsHexDigit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} +constexpr size_t RawStringSize = sizeof(UUID) * 2; +constexpr size_t FormattedStringSize = RawStringSize + 4; u8 HexCharToByte(char c) { if (c >= '0' && c <= '9') { @@ -31,57 +32,157 @@ u8 HexCharToByte(char c) { return u8{0}; } -} // Anonymous namespace +std::array ConstructFromRawString(std::string_view raw_string) { + std::array uuid; + + for (size_t i = 0; i < RawStringSize; i += 2) { + uuid[i / 2] = + static_cast((HexCharToByte(raw_string[i]) << 4) | HexCharToByte(raw_string[i + 1])); + } + + return uuid; +} + +std::array ConstructFromFormattedString(std::string_view formatted_string) { + std::array uuid; -u128 HexStringToU128(std::string_view hex_string) { - const size_t length = hex_string.length(); + size_t i = 0; - // Detect "0x" prefix. - const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x'; - const size_t offset = has_0x_prefix ? 2 : 0; + // Process the first 8 characters. + const auto* str = formatted_string.data(); - // Check length. - if (length > 32 + offset) { - ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!"); - return INVALID_UUID; + for (; i < 4; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); } - u64 lo = 0; - u64 hi = 0; - for (size_t i = 0; i < length - offset; ++i) { - const char c = hex_string[length - 1 - i]; - if (!IsHexDigit(c)) { - ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); - return INVALID_UUID; - } - if (i < 16) { - lo |= u64{HexCharToByte(c)} << (i * 4); - } - if (i >= 16) { - hi |= u64{HexCharToByte(c)} << ((i - 16) * 4); - } + // Process the next 4 characters. + ++str; + + for (; i < 6; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + // Process the next 4 characters. + ++str; + + for (; i < 8; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); } - return u128{lo, hi}; + + // Process the next 4 characters. + ++str; + + for (; i < 10; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + // Process the last 12 characters. + ++str; + + for (; i < 16; ++i) { + uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); + uuid[i] |= HexCharToByte(*(str++)); + } + + return uuid; } -UUID UUID::Generate() { +std::array ConstructUUID(std::string_view uuid_string) { + const auto length = uuid_string.length(); + + if (length == 0) { + return {}; + } + + // Check if the input string contains 32 hexadecimal characters. + if (length == RawStringSize) { + return ConstructFromRawString(uuid_string); + } + + // Check if the input string has the length of a RFC 4122 formatted UUID string. + if (length == FormattedStringSize) { + return ConstructFromFormattedString(uuid_string); + } + + ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length); + + return {}; +} + +} // Anonymous namespace + +UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {} + +std::string UUID::RawString() const { + return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}" + "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], + uuid[15]); +} + +std::string UUID::FormattedString() const { + return fmt::format("{:02x}{:02x}{:02x}{:02x}" + "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-" + "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], + uuid[15]); +} + +size_t UUID::Hash() const noexcept { + u64 hash; + u64 temp; + + std::memcpy(&hash, uuid.data(), sizeof(u64)); + std::memcpy(&temp, uuid.data() + 8, sizeof(u64)); + + return hash ^ std::rotl(temp, 1); +} + +u128 UUID::AsU128() const { + u128 uuid_old; + std::memcpy(&uuid_old, uuid.data(), sizeof(UUID)); + return uuid_old; +} + +UUID UUID::MakeRandom() { std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution distribution(1, std::numeric_limits::max()); - return UUID{distribution(gen), distribution(gen)}; + + return MakeRandomWithSeed(device()); } -std::string UUID::Format() const { - return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]); +UUID UUID::MakeRandomWithSeed(u32 seed) { + // Create and initialize our RNG. + TinyMT rng; + rng.Initialize(seed); + + UUID uuid; + + // Populate the UUID with random bytes. + rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID)); + + return uuid; } -std::string UUID::FormatSwitch() const { - std::array s{}; - std::memcpy(s.data(), uuid.data(), sizeof(u128)); - return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" - ":02x}{:02x}{:02x}{:02x}{:02x}", - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], - s[12], s[13], s[14], s[15]); +UUID UUID::MakeRandomRFC4122V4() { + auto uuid = MakeRandom(); + + // According to Proposed Standard RFC 4122 Section 4.4, we must: + + // 1. Set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively. + uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); + + // 2. Set the four most significant bits (bits 12 through 15) of the + // time_hi_and_version field to the 4-bit version number from Section 4.1.3. + uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF); + + return uuid; } } // namespace Common diff --git a/src/common/uuid.h b/src/common/uuid.h index 8ea01f8da..fe31e64e6 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -1,9 +1,11 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2022 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once +#include +#include #include #include @@ -11,69 +13,119 @@ namespace Common { -constexpr u128 INVALID_UUID{{0, 0}}; - -/** - * Converts a hex string to a 128-bit unsigned integer. - * - * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix. - * - * This function will assert and return INVALID_UUID under the following conditions: - * - If the hex string is more than 32 characters long - * - If the hex string contains non-hexadecimal characters - * - * @param hex_string Hexadecimal string - * - * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise. - */ -[[nodiscard]] u128 HexStringToU128(std::string_view hex_string); - struct UUID { - // UUIDs which are 0 are considered invalid! - u128 uuid; - UUID() = default; - constexpr explicit UUID(const u128& id) : uuid{id} {} - constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} - explicit UUID(std::string_view hex_string) { - uuid = HexStringToU128(hex_string); - } - - [[nodiscard]] constexpr explicit operator bool() const { - return uuid != INVALID_UUID; - } - - [[nodiscard]] constexpr bool operator==(const UUID& rhs) const { - return uuid == rhs.uuid; - } - - [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const { - return !operator==(rhs); - } - - // TODO(ogniK): Properly generate uuids based on RFC-4122 - [[nodiscard]] static UUID Generate(); - - // Set the UUID to {0,0} to be considered an invalid user - constexpr void Invalidate() { - uuid = INVALID_UUID; + std::array uuid{}; + + /// Constructs an invalid UUID. + constexpr UUID() = default; + + /// Constructs a UUID from a reference to a 128 bit array. + constexpr explicit UUID(const std::array& uuid_) : uuid{uuid_} {} + + /** + * Constructs a UUID from either: + * 1. A 32 hexadecimal character string representing the bytes of the UUID + * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * The input string may contain uppercase or lowercase characters, but they must: + * 1. Contain valid hexadecimal characters (0-9, a-f, A-F) + * 2. Not contain the "0x" hexadecimal prefix + * + * Should the input string not meet the above requirements, + * an assert will be triggered and an invalid UUID is set instead. + */ + explicit UUID(std::string_view uuid_string); + + ~UUID() = default; + + constexpr UUID(const UUID&) noexcept = default; + constexpr UUID(UUID&&) noexcept = default; + + constexpr UUID& operator=(const UUID&) noexcept = default; + constexpr UUID& operator=(UUID&&) noexcept = default; + + /** + * Returns whether the stored UUID is valid or not. + * + * @returns True if the stored UUID is valid, false otherwise. + */ + constexpr bool IsValid() const { + return uuid != std::array{}; } - [[nodiscard]] constexpr bool IsInvalid() const { - return uuid == INVALID_UUID; - } - [[nodiscard]] constexpr bool IsValid() const { - return !IsInvalid(); + /** + * Returns whether the stored UUID is invalid or not. + * + * @returns True if the stored UUID is invalid, false otherwise. + */ + constexpr bool IsInvalid() const { + return !IsValid(); } - // TODO(ogniK): Properly generate a Nintendo ID - [[nodiscard]] constexpr u64 GetNintendoID() const { - return uuid[0]; + /** + * Returns a 32 hexadecimal character string representing the bytes of the UUID. + * + * @returns A 32 hexadecimal character string of the UUID. + */ + std::string RawString() const; + + /** + * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. + * + * @returns A RFC 4122 formatted UUID string. + */ + std::string FormattedString() const; + + /** + * Returns a 64-bit hash of the UUID for use in hash table data structures. + * + * @returns A 64-bit hash of the UUID. + */ + size_t Hash() const noexcept; + + /// DO NOT USE. Copies the contents of the UUID into a u128. + u128 AsU128() const; + + /** + * Creates a default UUID "yuzu Default UID". + * + * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID". + */ + static constexpr UUID MakeDefault() { + return UUID{ + {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, + }; } - [[nodiscard]] std::string Format() const; - [[nodiscard]] std::string FormatSwitch() const; + /** + * Creates a random UUID. + * + * @returns A random UUID. + */ + static UUID MakeRandom(); + + /** + * Creates a random UUID with a seed. + * + * @param seed A seed to initialize the Mersenne-Twister RNG + * + * @returns A random UUID. + */ + static UUID MakeRandomWithSeed(u32 seed); + + /** + * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant. + * + * @returns A random UUID that is RFC 4122 Version 4 compliant. + */ + static UUID MakeRandomRFC4122V4(); + + friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; }; -static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); +static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size."); + +/// An invalid UUID. This UUID has all its bytes set to 0. +constexpr UUID InvalidUUID = {}; } // namespace Common @@ -82,7 +134,7 @@ namespace std { template <> struct hash { size_t operator()(const Common::UUID& uuid) const noexcept { - return uuid.uuid[1] ^ uuid.uuid[0]; + return uuid.Hash(); } }; -- cgit v1.2.3 From ec4d7f71fedb1cc117b0fb6c33cbffb699b98555 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 5 Feb 2022 13:07:51 -0500 Subject: common: uuid: Return an invalid UUID if conversion from string fails The string constructor of UUID states: Should the input string not meet the above requirements, an assert will be triggered and an invalid UUID is set instead. --- src/common/uuid.cpp | 53 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 14 deletions(-) (limited to 'src/common') diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index 10a1b86e0..4aab10e08 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include @@ -18,7 +19,7 @@ namespace { constexpr size_t RawStringSize = sizeof(UUID) * 2; constexpr size_t FormattedStringSize = RawStringSize + 4; -u8 HexCharToByte(char c) { +std::optional HexCharToByte(char c) { if (c >= '0' && c <= '9') { return static_cast(c - '0'); } @@ -29,15 +30,19 @@ u8 HexCharToByte(char c) { return static_cast(c - 'A' + 10); } ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); - return u8{0}; + return std::nullopt; } std::array ConstructFromRawString(std::string_view raw_string) { std::array uuid; for (size_t i = 0; i < RawStringSize; i += 2) { - uuid[i / 2] = - static_cast((HexCharToByte(raw_string[i]) << 4) | HexCharToByte(raw_string[i + 1])); + const auto upper = HexCharToByte(raw_string[i]); + const auto lower = HexCharToByte(raw_string[i + 1]); + if (!upper || !lower) { + return {}; + } + uuid[i / 2] = static_cast((*upper << 4) | *lower); } return uuid; @@ -52,40 +57,60 @@ std::array ConstructFromFormattedString(std::string_view formatted_str const auto* str = formatted_string.data(); for (; i < 4; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast((*upper << 4) | *lower); } // Process the next 4 characters. ++str; for (; i < 6; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast((*upper << 4) | *lower); } // Process the next 4 characters. ++str; for (; i < 8; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast((*upper << 4) | *lower); } // Process the next 4 characters. ++str; for (; i < 10; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast((*upper << 4) | *lower); } // Process the last 12 characters. ++str; for (; i < 16; ++i) { - uuid[i] = static_cast((HexCharToByte(*(str++)) << 4)); - uuid[i] |= HexCharToByte(*(str++)); + const auto upper = HexCharToByte(*(str++)); + const auto lower = HexCharToByte(*(str++)); + if (!upper || !lower) { + return {}; + } + uuid[i] = static_cast((*upper << 4) | *lower); } return uuid; -- cgit v1.2.3 From 3799c820ca7c5b978d47be9c5ac1318333e5d9cb Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 10 Feb 2022 15:03:49 -0500 Subject: common: uuid: Use sizeof(u64) instead of 8 in Hash() --- src/common/uuid.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/common') diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index 4aab10e08..2b6a530e3 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp @@ -160,13 +160,13 @@ std::string UUID::FormattedString() const { } size_t UUID::Hash() const noexcept { - u64 hash; - u64 temp; + u64 upper_hash; + u64 lower_hash; - std::memcpy(&hash, uuid.data(), sizeof(u64)); - std::memcpy(&temp, uuid.data() + 8, sizeof(u64)); + std::memcpy(&upper_hash, uuid.data(), sizeof(u64)); + std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64)); - return hash ^ std::rotl(temp, 1); + return upper_hash ^ std::rotl(lower_hash, 1); } u128 UUID::AsU128() const { -- cgit v1.2.3