pineapple/src/common/uuid.cpp

213 lines
5.7 KiB
C++
Raw Normal View History

2022-04-23 20:49:07 +02:00
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
2020-12-28 16:15:37 +01:00
2022-02-09 20:56:59 +01:00
#include <bit>
#include <optional>
2020-12-28 16:15:37 +01:00
#include <random>
#include <fmt/format.h>
2021-08-05 23:45:14 +02:00
#include "common/assert.h"
2022-02-09 20:56:59 +01:00
#include "common/tiny_mt.h"
2020-12-28 16:15:37 +01:00
#include "common/uuid.h"
namespace Common {
2021-08-05 23:45:14 +02:00
namespace {
2022-02-09 20:56:59 +01:00
constexpr size_t RawStringSize = sizeof(UUID) * 2;
constexpr size_t FormattedStringSize = RawStringSize + 4;
2021-08-05 23:45:14 +02:00
2022-02-09 20:56:59 +01:00
std::optional<u8> HexCharToByte(char c) {
2021-08-05 23:45:14 +02:00
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);
2022-02-09 20:56:59 +01:00
return std::nullopt;
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
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;
}
2021-08-05 23:45:14 +02:00
2022-02-09 20:56:59 +01:00
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
std::array<u8, 0x10> uuid;
2021-08-05 23:45:14 +02:00
2022-02-09 20:56:59 +01:00
size_t i = 0;
2021-08-05 23:45:14 +02:00
2022-02-09 20:56:59 +01:00
// 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);
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
// Process the next 4 characters.
++str;
for (; i < 8; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
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 {};
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
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 {};
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
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);
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
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 {
2022-02-12 07:27:44 +01:00
u64 upper_hash;
u64 lower_hash;
2022-02-09 20:56:59 +01:00
2022-02-12 07:27:44 +01:00
std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
2022-02-09 20:56:59 +01:00
2022-02-12 07:27:44 +01:00
return upper_hash ^ std::rotl(lower_hash, 1);
2021-08-05 23:45:14 +02:00
}
2022-02-09 20:56:59 +01:00
u128 UUID::AsU128() const {
u128 uuid_old;
std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
return uuid_old;
}
UUID UUID::MakeRandom() {
2020-12-28 16:15:37 +01:00
std::random_device device;
2022-02-09 20:56:59 +01:00
return MakeRandomWithSeed(device());
2020-12-28 16:15:37 +01:00
}
2022-02-09 20:56:59 +01:00
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;
2020-12-28 16:15:37 +01:00
}
2022-02-09 20:56:59 +01:00
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;
2020-12-28 16:15:37 +01:00
}
} // namespace Common