diff options
| author | Morph <39850852+Morph1984@users.noreply.github.com> | 2022-02-04 23:44:02 -0500 | 
|---|---|---|
| committer | Morph <39850852+Morph1984@users.noreply.github.com> | 2022-02-05 13:18:31 -0500 | 
| commit | 3271099fea41d5adc0003c02c8481b334772296a (patch) | |
| tree | 10006c106d3a317639d330b45369973fa29af562 | |
| parent | 5cb1c2ad843c66856fab6aa964c91f0efd1d5b80 (diff) | |
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.
| -rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/common/new_uuid.cpp | 182 | ||||
| -rw-r--r-- | src/common/new_uuid.h | 138 | 
3 files changed, 322 insertions, 0 deletions
| 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 <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 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 <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 | 
