Files
nostalgia/deps/ox/src/ox/std/uuid.hpp

232 lines
5.2 KiB
C++

/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "bit.hpp"
#include "def.hpp"
#include "ignore.hpp"
#include "istring.hpp"
#include "buffer.hpp"
#include "hash.hpp"
#include "random.hpp"
#include "ranges.hpp"
#include "stringview.hpp"
#include "strops.hpp"
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
namespace ox {
using UUIDStr = ox::IString<36>;
namespace detail {
[[nodiscard]]
constexpr auto isHexChar(char c) noexcept {
return (c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F');
}
constexpr ox::Result<uint8_t> fromHex(ox::StringViewCR v) noexcept {
constexpr auto valMap = [] {
ox::Array<uint8_t, 128> out;
out['A'] = out['a'] = 10;
out['B'] = out['b'] = 11;
out['C'] = out['c'] = 12;
out['D'] = out['d'] = 13;
out['E'] = out['e'] = 14;
out['F'] = out['f'] = 15;
out['0'] = 0;
out['1'] = 1;
out['2'] = 2;
out['3'] = 3;
out['4'] = 4;
out['5'] = 5;
out['6'] = 6;
out['7'] = 7;
out['8'] = 8;
out['9'] = 9;
return out;
}();
if (!detail::isHexChar(v[0]) || !detail::isHexChar(v[1])) {
return ox::Error(1, "Invalid UUID");
}
if (v.len() != 2) {
return ox::Error(2);
}
uint8_t out = 0;
out += static_cast<uint8_t>(valMap[static_cast<unsigned char>(v[0])] * 16);
out += valMap[static_cast<unsigned char>(v[1])];
return out;
}
constexpr ox::IString<2> toHex(uint8_t v) noexcept {
constexpr ox::Array<char, 16> valMap {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
};
ox::IString<2> out;
std::ignore = out.resize(2);
out[0] = valMap[static_cast<unsigned>((v & 0xf0) / 16)];
out[1] = valMap[static_cast<unsigned>(v & 0x0f)];
out[2] = 0;
return out.data();
}
}
class UUID {
template<typename T>
friend constexpr Error model(T *io, ox::CommonPtrWith<UUID> auto *obj) noexcept;
protected:
static bool s_seeded;
static Random s_rand;
ox::Array<uint8_t, 16> m_value{};
public:
static constexpr auto TypeName = "net.drinkingtea.ox.UUID";
static constexpr auto TypeVersion = 1;
static void seedGenerator(const RandomSeed &seed) noexcept;
static ox::Result<UUID> generate() noexcept;
[[nodiscard]]
constexpr ox::Array<uint8_t, 16> const&value() const noexcept {
return m_value;
}
[[nodiscard]]
constexpr bool isNull() const noexcept {
if (std::is_constant_evaluated()) {
if (ox::all_of(m_value.begin(), m_value.end(), [](auto v) { return v == 0; })) {
return true;
}
return false;
} else {
constexpr uint64_t zero = 0;
return memcmp(&zero, m_value.data() + 0, 8) == 0
&& memcmp(&zero, m_value.data() + 8, 8) == 0;
}
}
static constexpr ox::Result<ox::UUID> fromString(ox::StringViewCR s) noexcept {
if (s.len() < 36) {
return ox::Error(1, "Insufficient data to contain a complete UUID");
}
UUID out;
auto valueI = 0u;
for (size_t i = 0; i < s.len();) {
if (s[i] == '-') {
++i;
continue;
}
const auto seg = substr(s, i, i + 2);
if (seg.len() != 2) {
return ox::Error(1, "Invalid UUID");
}
OX_REQUIRE(val, detail::fromHex(seg));
out.m_value[valueI] = val;
i += 2;
++valueI;
}
return out;
}
constexpr bool operator==(UUID const&o) const noexcept {
return m_value == o.m_value;
}
constexpr bool operator!=(UUID const&o) const noexcept {
return !operator==(o);
}
[[nodiscard]]
constexpr ox::Error toString(Writer_c auto &writer) const noexcept {
auto valueI = 0u;
constexpr auto printChars = [](
Writer_c auto &writer,
const Array<uint8_t, 16> &value,
std::size_t cnt,
unsigned &valueI) {
for (auto i = 0u; i < cnt; ++i) {
const auto v = value[valueI];
const auto h = detail::toHex(v);
std::ignore = writer.write(h.c_str(), h.len());
++valueI;
}
};
printChars(writer, m_value, 4, valueI);
OX_RETURN_ERROR(writer.put('-'));
printChars(writer, m_value, 2, valueI);
OX_RETURN_ERROR(writer.put('-'));
printChars(writer, m_value, 2, valueI);
OX_RETURN_ERROR(writer.put('-'));
printChars(writer, m_value, 2, valueI);
OX_RETURN_ERROR(writer.put('-'));
printChars(writer, m_value, 6, valueI);
return {};
}
[[nodiscard]]
constexpr UUIDStr toString() const noexcept {
UUIDStr out;
std::ignore = out.resize(UUIDStr::cap());
ox::CharBuffWriter bw{{out.data(), UUIDStr::cap()}};
std::ignore = toString(bw);
out[UUIDStr::cap()] = 0;
return out;
}
};
template<>
struct hash<ox::UUID> {
[[nodiscard]]
constexpr size_t operator()(ox::UUID v) const noexcept {
size_t out{};
if (std::is_constant_evaluated()) {
for (auto i = 0u; i < sizeof(out); ++i) {
out |= static_cast<size_t>(v.value()[i]) << (i * 8);
}
} else {
memcpy(&out, &v, sizeof(out));
}
return out;
}
};
template<typename T>
constexpr Error model(T *io, ox::CommonPtrWith<UUID> auto *obj) noexcept {
OX_RETURN_ERROR(io->template setTypeInfo<UUID>());
OX_RETURN_ERROR(io->field("value", &obj->m_value));
return {};
}
}
OX_CLANG_NOWARN_END