summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/uuid.cpp208
-rw-r--r--src/common/uuid.h166
2 files changed, 276 insertions, 98 deletions
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index d7435a6e9..2b6a530e3 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -1,23 +1,25 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6#include <optional>
5#include <random> 7#include <random>
6 8
7#include <fmt/format.h> 9#include <fmt/format.h>
8 10
9#include "common/assert.h" 11#include "common/assert.h"
12#include "common/tiny_mt.h"
10#include "common/uuid.h" 13#include "common/uuid.h"
11 14
12namespace Common { 15namespace Common {
13 16
14namespace { 17namespace {
15 18
16bool IsHexDigit(char c) { 19constexpr size_t RawStringSize = sizeof(UUID) * 2;
17 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); 20constexpr size_t FormattedStringSize = RawStringSize + 4;
18}
19 21
20u8 HexCharToByte(char c) { 22std::optional<u8> HexCharToByte(char c) {
21 if (c >= '0' && c <= '9') { 23 if (c >= '0' && c <= '9') {
22 return static_cast<u8>(c - '0'); 24 return static_cast<u8>(c - '0');
23 } 25 }
@@ -28,60 +30,184 @@ u8 HexCharToByte(char c) {
28 return static_cast<u8>(c - 'A' + 10); 30 return static_cast<u8>(c - 'A' + 10);
29 } 31 }
30 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); 32 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
31 return u8{0}; 33 return std::nullopt;
32} 34}
33 35
34} // Anonymous namespace 36std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
37 std::array<u8, 0x10> uuid;
38
39 for (size_t i = 0; i < RawStringSize; i += 2) {
40 const auto upper = HexCharToByte(raw_string[i]);
41 const auto lower = HexCharToByte(raw_string[i + 1]);
42 if (!upper || !lower) {
43 return {};
44 }
45 uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
46 }
47
48 return uuid;
49}
35 50
36u128 HexStringToU128(std::string_view hex_string) { 51std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
37 const size_t length = hex_string.length(); 52 std::array<u8, 0x10> uuid;
38 53
39 // Detect "0x" prefix. 54 size_t i = 0;
40 const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
41 const size_t offset = has_0x_prefix ? 2 : 0;
42 55
43 // Check length. 56 // Process the first 8 characters.
44 if (length > 32 + offset) { 57 const auto* str = formatted_string.data();
45 ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!"); 58
46 return INVALID_UUID; 59 for (; i < 4; ++i) {
60 const auto upper = HexCharToByte(*(str++));
61 const auto lower = HexCharToByte(*(str++));
62 if (!upper || !lower) {
63 return {};
64 }
65 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
66 }
67
68 // Process the next 4 characters.
69 ++str;
70
71 for (; i < 6; ++i) {
72 const auto upper = HexCharToByte(*(str++));
73 const auto lower = HexCharToByte(*(str++));
74 if (!upper || !lower) {
75 return {};
76 }
77 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
47 } 78 }
48 79
49 u64 lo = 0; 80 // Process the next 4 characters.
50 u64 hi = 0; 81 ++str;
51 for (size_t i = 0; i < length - offset; ++i) { 82
52 const char c = hex_string[length - 1 - i]; 83 for (; i < 8; ++i) {
53 if (!IsHexDigit(c)) { 84 const auto upper = HexCharToByte(*(str++));
54 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); 85 const auto lower = HexCharToByte(*(str++));
55 return INVALID_UUID; 86 if (!upper || !lower) {
87 return {};
56 } 88 }
57 if (i < 16) { 89 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
58 lo |= u64{HexCharToByte(c)} << (i * 4); 90 }
91
92 // Process the next 4 characters.
93 ++str;
94
95 for (; i < 10; ++i) {
96 const auto upper = HexCharToByte(*(str++));
97 const auto lower = HexCharToByte(*(str++));
98 if (!upper || !lower) {
99 return {};
59 } 100 }
60 if (i >= 16) { 101 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
61 hi |= u64{HexCharToByte(c)} << ((i - 16) * 4); 102 }
103
104 // Process the last 12 characters.
105 ++str;
106
107 for (; i < 16; ++i) {
108 const auto upper = HexCharToByte(*(str++));
109 const auto lower = HexCharToByte(*(str++));
110 if (!upper || !lower) {
111 return {};
62 } 112 }
113 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
114 }
115
116 return uuid;
117}
118
119std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) {
120 const auto length = uuid_string.length();
121
122 if (length == 0) {
123 return {};
124 }
125
126 // Check if the input string contains 32 hexadecimal characters.
127 if (length == RawStringSize) {
128 return ConstructFromRawString(uuid_string);
129 }
130
131 // Check if the input string has the length of a RFC 4122 formatted UUID string.
132 if (length == FormattedStringSize) {
133 return ConstructFromFormattedString(uuid_string);
63 } 134 }
64 return u128{lo, hi}; 135
136 ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length);
137
138 return {};
139}
140
141} // Anonymous namespace
142
143UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {}
144
145std::string UUID::RawString() const {
146 return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"
147 "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
148 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
149 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
150 uuid[15]);
151}
152
153std::string UUID::FormattedString() const {
154 return fmt::format("{:02x}{:02x}{:02x}{:02x}"
155 "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
156 "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
157 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
158 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
159 uuid[15]);
160}
161
162size_t UUID::Hash() const noexcept {
163 u64 upper_hash;
164 u64 lower_hash;
165
166 std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
167 std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
168
169 return upper_hash ^ std::rotl(lower_hash, 1);
65} 170}
66 171
67UUID UUID::Generate() { 172u128 UUID::AsU128() const {
173 u128 uuid_old;
174 std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
175 return uuid_old;
176}
177
178UUID UUID::MakeRandom() {
68 std::random_device device; 179 std::random_device device;
69 std::mt19937 gen(device()); 180
70 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 181 return MakeRandomWithSeed(device());
71 return UUID{distribution(gen), distribution(gen)};
72} 182}
73 183
74std::string UUID::Format() const { 184UUID UUID::MakeRandomWithSeed(u32 seed) {
75 return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]); 185 // Create and initialize our RNG.
186 TinyMT rng;
187 rng.Initialize(seed);
188
189 UUID uuid;
190
191 // Populate the UUID with random bytes.
192 rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
193
194 return uuid;
76} 195}
77 196
78std::string UUID::FormatSwitch() const { 197UUID UUID::MakeRandomRFC4122V4() {
79 std::array<u8, 16> s{}; 198 auto uuid = MakeRandom();
80 std::memcpy(s.data(), uuid.data(), sizeof(u128)); 199
81 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" 200 // According to Proposed Standard RFC 4122 Section 4.4, we must:
82 ":02x}{:02x}{:02x}{:02x}{:02x}", 201
83 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], 202 // 1. Set the two most significant bits (bits 6 and 7) of the
84 s[12], s[13], s[14], s[15]); 203 // clock_seq_hi_and_reserved to zero and one, respectively.
204 uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
205
206 // 2. Set the four most significant bits (bits 12 through 15) of the
207 // time_hi_and_version field to the 4-bit version number from Section 4.1.3.
208 uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF);
209
210 return uuid;
85} 211}
86 212
87} // namespace Common 213} // 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 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <functional>
7#include <string> 9#include <string>
8#include <string_view> 10#include <string_view>
9 11
@@ -11,69 +13,119 @@
11 13
12namespace Common { 14namespace Common {
13 15
14constexpr u128 INVALID_UUID{{0, 0}};
15
16/**
17 * Converts a hex string to a 128-bit unsigned integer.
18 *
19 * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
20 *
21 * This function will assert and return INVALID_UUID under the following conditions:
22 * - If the hex string is more than 32 characters long
23 * - If the hex string contains non-hexadecimal characters
24 *
25 * @param hex_string Hexadecimal string
26 *
27 * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
28 */
29[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
30
31struct UUID { 16struct UUID {
32 // UUIDs which are 0 are considered invalid! 17 std::array<u8, 0x10> uuid{};
33 u128 uuid; 18
34 UUID() = default; 19 /// Constructs an invalid UUID.
35 constexpr explicit UUID(const u128& id) : uuid{id} {} 20 constexpr UUID() = default;
36 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 21
37 explicit UUID(std::string_view hex_string) { 22 /// Constructs a UUID from a reference to a 128 bit array.
38 uuid = HexStringToU128(hex_string); 23 constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {}
39 } 24
40 25 /**
41 [[nodiscard]] constexpr explicit operator bool() const { 26 * Constructs a UUID from either:
42 return uuid != INVALID_UUID; 27 * 1. A 32 hexadecimal character string representing the bytes of the UUID
43 } 28 * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
44 29 *
45 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const { 30 * The input string may contain uppercase or lowercase characters, but they must:
46 return uuid == rhs.uuid; 31 * 1. Contain valid hexadecimal characters (0-9, a-f, A-F)
47 } 32 * 2. Not contain the "0x" hexadecimal prefix
48 33 *
49 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const { 34 * Should the input string not meet the above requirements,
50 return !operator==(rhs); 35 * an assert will be triggered and an invalid UUID is set instead.
51 } 36 */
52 37 explicit UUID(std::string_view uuid_string);
53 // TODO(ogniK): Properly generate uuids based on RFC-4122 38
54 [[nodiscard]] static UUID Generate(); 39 ~UUID() = default;
55 40
56 // Set the UUID to {0,0} to be considered an invalid user 41 constexpr UUID(const UUID&) noexcept = default;
57 constexpr void Invalidate() { 42 constexpr UUID(UUID&&) noexcept = default;
58 uuid = INVALID_UUID; 43
44 constexpr UUID& operator=(const UUID&) noexcept = default;
45 constexpr UUID& operator=(UUID&&) noexcept = default;
46
47 /**
48 * Returns whether the stored UUID is valid or not.
49 *
50 * @returns True if the stored UUID is valid, false otherwise.
51 */
52 constexpr bool IsValid() const {
53 return uuid != std::array<u8, 0x10>{};
59 } 54 }
60 55
61 [[nodiscard]] constexpr bool IsInvalid() const { 56 /**
62 return uuid == INVALID_UUID; 57 * Returns whether the stored UUID is invalid or not.
63 } 58 *
64 [[nodiscard]] constexpr bool IsValid() const { 59 * @returns True if the stored UUID is invalid, false otherwise.
65 return !IsInvalid(); 60 */
61 constexpr bool IsInvalid() const {
62 return !IsValid();
66 } 63 }
67 64
68 // TODO(ogniK): Properly generate a Nintendo ID 65 /**
69 [[nodiscard]] constexpr u64 GetNintendoID() const { 66 * Returns a 32 hexadecimal character string representing the bytes of the UUID.
70 return uuid[0]; 67 *
68 * @returns A 32 hexadecimal character string of the UUID.
69 */
70 std::string RawString() const;
71
72 /**
73 * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
74 *
75 * @returns A RFC 4122 formatted UUID string.
76 */
77 std::string FormattedString() const;
78
79 /**
80 * Returns a 64-bit hash of the UUID for use in hash table data structures.
81 *
82 * @returns A 64-bit hash of the UUID.
83 */
84 size_t Hash() const noexcept;
85
86 /// DO NOT USE. Copies the contents of the UUID into a u128.
87 u128 AsU128() const;
88
89 /**
90 * Creates a default UUID "yuzu Default UID".
91 *
92 * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID".
93 */
94 static constexpr UUID MakeDefault() {
95 return UUID{
96 {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'},
97 };
71 } 98 }
72 99
73 [[nodiscard]] std::string Format() const; 100 /**
74 [[nodiscard]] std::string FormatSwitch() const; 101 * Creates a random UUID.
102 *
103 * @returns A random UUID.
104 */
105 static UUID MakeRandom();
106
107 /**
108 * Creates a random UUID with a seed.
109 *
110 * @param seed A seed to initialize the Mersenne-Twister RNG
111 *
112 * @returns A random UUID.
113 */
114 static UUID MakeRandomWithSeed(u32 seed);
115
116 /**
117 * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
118 *
119 * @returns A random UUID that is RFC 4122 Version 4 compliant.
120 */
121 static UUID MakeRandomRFC4122V4();
122
123 friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
75}; 124};
76static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 125static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
126
127/// An invalid UUID. This UUID has all its bytes set to 0.
128constexpr UUID InvalidUUID = {};
77 129
78} // namespace Common 130} // namespace Common
79 131
@@ -82,7 +134,7 @@ namespace std {
82template <> 134template <>
83struct hash<Common::UUID> { 135struct hash<Common::UUID> {
84 size_t operator()(const Common::UUID& uuid) const noexcept { 136 size_t operator()(const Common::UUID& uuid) const noexcept {
85 return uuid.uuid[1] ^ uuid.uuid[0]; 137 return uuid.Hash();
86 } 138 }
87}; 139};
88 140