| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include <bit>
- #include <optional>
- #include <random>
- #include <fmt/format.h>
- #include "common/assert.h"
- #include "common/tiny_mt.h"
- #include "common/uuid.h"
- namespace Common {
- namespace {
- constexpr size_t RawStringSize = sizeof(UUID) * 2;
- constexpr size_t FormattedStringSize = RawStringSize + 4;
- std::optional<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 std::nullopt;
- }
- std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
- std::array<u8, 0x10> uuid;
- for (size_t i = 0; i < RawStringSize; i += 2) {
- const auto upper = HexCharToByte(raw_string[i]);
- const auto lower = HexCharToByte(raw_string[i + 1]);
- if (!upper || !lower) {
- return {};
- }
- uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
- }
- 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) {
- const auto upper = HexCharToByte(*(str++));
- const auto lower = HexCharToByte(*(str++));
- if (!upper || !lower) {
- return {};
- }
- uuid[i] = static_cast<u8>((*upper << 4) | *lower);
- }
- // Process the next 4 characters.
- ++str;
- for (; i < 6; ++i) {
- const auto upper = HexCharToByte(*(str++));
- const auto lower = HexCharToByte(*(str++));
- if (!upper || !lower) {
- return {};
- }
- uuid[i] = static_cast<u8>((*upper << 4) | *lower);
- }
- // Process the next 4 characters.
- ++str;
- for (; i < 8; ++i) {
- const auto upper = HexCharToByte(*(str++));
- const auto lower = HexCharToByte(*(str++));
- if (!upper || !lower) {
- return {};
- }
- uuid[i] = static_cast<u8>((*upper << 4) | *lower);
- }
- // Process the next 4 characters.
- ++str;
- for (; i < 10; ++i) {
- const auto upper = HexCharToByte(*(str++));
- const auto lower = HexCharToByte(*(str++));
- if (!upper || !lower) {
- return {};
- }
- uuid[i] = static_cast<u8>((*upper << 4) | *lower);
- }
- // Process the last 12 characters.
- ++str;
- for (; i < 16; ++i) {
- const auto upper = HexCharToByte(*(str++));
- const auto lower = HexCharToByte(*(str++));
- if (!upper || !lower) {
- return {};
- }
- uuid[i] = static_cast<u8>((*upper << 4) | *lower);
- }
- 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
- 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 upper_hash;
- u64 lower_hash;
- std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
- std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
- return upper_hash ^ std::rotl(lower_hash, 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;
- return MakeRandomWithSeed(device());
- }
- 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;
- }
- 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
|