carb/extras/Uuid.h
File members: carb/extras/Uuid.h
// Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once
#include "../Defines.h"
#include "../cpp/StringView.h"
#include "../cpp/Bit.h"
#include "StringSafe.h"
#include "StringUtils.h"
#include <array>
#include <cctype>
#include <cinttypes>
#include <cstring>
#include <functional>
#include <iostream>
#include <random>
#include <string>
#include <type_traits>
namespace carb
{
namespace extras
{
class Uuid final
{
public:
using value_type = std::array<uint8_t, 16>;
Uuid() noexcept : m_data{ 0U }
{
}
Uuid(const std::string& uuidStr) noexcept : m_data{ 0U }
{
std::array<uint8_t, 16> data{ 0U };
carb::cpp::string_view uuidView;
if (uuidStr[0] == '{' && uuidStr.size() == 38)
{
uuidView = carb::cpp::string_view(uuidStr.c_str(), uuidStr.size()).substr(1, uuidStr.size() - 2);
}
else if (startsWith(uuidStr.c_str(), "urn:uuid:") && uuidStr.size() == 45) // RFC 4122
{
uuidView = carb::cpp::string_view(uuidStr.c_str(), uuidStr.size()).substr(9, uuidStr.size());
}
else if (uuidStr.size() == 36)
{
uuidView = carb::cpp::string_view(uuidStr.c_str(), uuidStr.size());
}
if (!uuidView.empty())
{
CARB_ASSERT(uuidView.size() == 36);
size_t i = 0;
size_t j = 0;
while (i < uuidView.size() - 1 && j < data.size())
{
if ((i == 8) || (i == 13) || (i == 18) || (i == 23))
{
if (uuidView[i] == '-')
{
++i;
continue;
}
}
if (std::isxdigit(uuidView[i]) && std::isxdigit(uuidView[i + 1]))
{
char buf[3] = { uuidView[i], uuidView[i + 1], '\0' };
data[j++] = static_cast<uint8_t>(std::strtoul(buf, nullptr, 16));
i += 2;
}
else
{
// error parsing, unknown character
break;
}
}
// if we parsed the entire view and filled the entire array, copy it
if (i == uuidView.size() && j == data.size())
{
m_data = data;
}
}
}
static Uuid createV4() noexcept
{
Uuid uuidv4;
std::random_device rd;
for (size_t i = 0; i < uuidv4.m_data.size(); i += 4)
{
// use the entire 32-bits returned by random device
uint32_t rdata = rd();
uuidv4.m_data[i + 0] = uint8_t(rdata >> 0) & 0xff;
uuidv4.m_data[i + 1] = uint8_t(rdata >> 8) & 0xff;
uuidv4.m_data[i + 2] = uint8_t(rdata >> 16) & 0xff;
uuidv4.m_data[i + 3] = uint8_t(rdata >> 24) & 0xff;
}
uuidv4.m_data[6] = (uuidv4.m_data[6] & 0x0f) | 0x40; // RFC 4122 for UUIDv4
uuidv4.m_data[8] = (uuidv4.m_data[8] & 0x3f) | 0x80; // RFC 4122 for UUIDv4
return uuidv4;
}
bool isEmpty() const noexcept
{
auto data = m_data.data();
return *data == 0 && memcmp(data, data + 1, m_data.size() - 1) == 0;
}
const value_type& data() const noexcept
{
return m_data;
}
bool operator==(const Uuid& rhs) const noexcept
{
return !memcmp(m_data.data(), rhs.m_data.data(), m_data.size());
}
bool operator!=(const Uuid& rhs) const noexcept
{
return !(*this == rhs);
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
friend std::string to_string(const Uuid& uuid) noexcept
{
// UUID format 00000000-0000-0000-0000-000000000000
static constexpr char kFmtString[] =
"%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8
"-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8;
// 32 chars + 4 dashes + 1 null termination
char strBuffer[37];
formatString(strBuffer, CARB_COUNTOF(strBuffer), kFmtString, uuid.m_data[0], uuid.m_data[1], uuid.m_data[2],
uuid.m_data[3], uuid.m_data[4], uuid.m_data[5], uuid.m_data[6], uuid.m_data[7], uuid.m_data[8],
uuid.m_data[9], uuid.m_data[10], uuid.m_data[11], uuid.m_data[12], uuid.m_data[13],
uuid.m_data[14], uuid.m_data[15]);
return std::string(strBuffer);
}
friend std::ostream& operator<<(std::ostream& os, const Uuid& uuid)
{
return os << to_string(uuid);
}
#endif // DOXYGEN_SHOULD_SKIP_THIS
private:
value_type m_data;
};
CARB_ASSERT_INTEROP_SAFE(Uuid);
static_assert(std::is_trivially_move_assignable<Uuid>::value, "Uuid must be move assignable");
static_assert(sizeof(Uuid) == 16, "Uuid must be exactly 16 bytes");
} // namespace extras
} // namespace carb
#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace std
{
template <>
struct hash<carb::extras::Uuid>
{
using argument_type = carb::extras::Uuid;
using result_type = std::size_t;
result_type operator()(argument_type const& uuid) const noexcept
{
// uuid is random bytes, so just XOR
// bit_cast() won't compile unless sizes match
auto parts = carb::cpp::bit_cast<std::array<result_type, 2>>(uuid.data());
return parts[0] ^ parts[1];
}
};
} // namespace std
#endif // DOXYGEN_SHOULD_SKIP_THIS