Просмотр исходного кода

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.
Morph 4 лет назад
Родитель
Сommit
3271099fea
3 измененных файлов с 322 добавлено и 0 удалено
  1. 2 0
      src/common/CMakeLists.txt
  2. 182 0
      src/common/new_uuid.cpp
  3. 138 0
      src/common/new_uuid.h

+ 2 - 0
src/common/CMakeLists.txt

@@ -99,6 +99,8 @@ add_library(common STATIC
     microprofile.cpp
     microprofile.cpp
     microprofile.h
     microprofile.h
     microprofileui.h
     microprofileui.h
+    new_uuid.cpp
+    new_uuid.h
     nvidia_flags.cpp
     nvidia_flags.cpp
     nvidia_flags.h
     nvidia_flags.h
     page_table.cpp
     page_table.cpp

+ 182 - 0
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 <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 - 0
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 <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