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.
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
							
								
								
									
										182
									
								
								src/common/new_uuid.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/common/new_uuid.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | ||||
| // Copyright 2022 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <bit> | ||||
| #include <random> | ||||
|  | ||||
| #include <fmt/format.h> | ||||
|  | ||||
| #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<u8>(c - '0'); | ||||
|     } | ||||
|     if (c >= 'a' && c <= 'f') { | ||||
|         return static_cast<u8>(c - 'a' + 10); | ||||
|     } | ||||
|     if (c >= 'A' && c <= 'F') { | ||||
|         return static_cast<u8>(c - 'A' + 10); | ||||
|     } | ||||
|     ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); | ||||
|     return u8{0}; | ||||
| } | ||||
|  | ||||
| std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) { | ||||
|     std::array<u8, 0x10> uuid; | ||||
|  | ||||
|     for (size_t i = 0; i < RawStringSize; i += 2) { | ||||
|         uuid[i / 2] = | ||||
|             static_cast<u8>((HexCharToByte(raw_string[i]) << 4) | HexCharToByte(raw_string[i + 1])); | ||||
|     } | ||||
|  | ||||
|     return uuid; | ||||
| } | ||||
|  | ||||
| std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { | ||||
|     std::array<u8, 0x10> uuid; | ||||
|  | ||||
|     size_t i = 0; | ||||
|  | ||||
|     // Process the first 8 characters. | ||||
|     const auto* str = formatted_string.data(); | ||||
|  | ||||
|     for (; i < 4; ++i) { | ||||
|         uuid[i] = static_cast<u8>((HexCharToByte(*(str++)) << 4)); | ||||
|         uuid[i] |= HexCharToByte(*(str++)); | ||||
|     } | ||||
|  | ||||
|     // Process the next 4 characters. | ||||
|     ++str; | ||||
|  | ||||
|     for (; i < 6; ++i) { | ||||
|         uuid[i] = static_cast<u8>((HexCharToByte(*(str++)) << 4)); | ||||
|         uuid[i] |= HexCharToByte(*(str++)); | ||||
|     } | ||||
|  | ||||
|     // Process the next 4 characters. | ||||
|     ++str; | ||||
|  | ||||
|     for (; i < 8; ++i) { | ||||
|         uuid[i] = static_cast<u8>((HexCharToByte(*(str++)) << 4)); | ||||
|         uuid[i] |= HexCharToByte(*(str++)); | ||||
|     } | ||||
|  | ||||
|     // Process the next 4 characters. | ||||
|     ++str; | ||||
|  | ||||
|     for (; i < 10; ++i) { | ||||
|         uuid[i] = static_cast<u8>((HexCharToByte(*(str++)) << 4)); | ||||
|         uuid[i] |= HexCharToByte(*(str++)); | ||||
|     } | ||||
|  | ||||
|     // Process the last 12 characters. | ||||
|     ++str; | ||||
|  | ||||
|     for (; i < 16; ++i) { | ||||
|         uuid[i] = static_cast<u8>((HexCharToByte(*(str++)) << 4)); | ||||
|         uuid[i] |= HexCharToByte(*(str++)); | ||||
|     } | ||||
|  | ||||
|     return uuid; | ||||
| } | ||||
|  | ||||
| std::array<u8, 0x10> 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 | ||||
							
								
								
									
										138
									
								
								src/common/new_uuid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/common/new_uuid.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <array> | ||||
| #include <functional> | ||||
| #include <string> | ||||
| #include <string_view> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Common { | ||||
|  | ||||
| struct NewUUID { | ||||
|     std::array<u8, 0x10> 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<u8, 16>& 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<u8, 0x10>{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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<Common::NewUUID> { | ||||
|     size_t operator()(const Common::NewUUID& uuid) const noexcept { | ||||
|         return uuid.Hash(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace std | ||||
		Reference in New Issue
	
	Block a user
	 Morph
					Morph