summaryrefslogtreecommitdiffstats
path: root/server/src/trace.cppm
blob: 35d32f30d6758fa492f78e3bea56bb4a2ef5b9bc (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
module;

// Might as well since we're using OpenSSL
#include <openssl/err.h>
#include <openssl/rand.h>

export module routemon:trace;

import std;
import :util;

namespace routemon::trace {

class uuid7
{
  std::uint64_t high_ = 0;
  std::uint64_t low_ = 0;

public:
  uuid7()
  {
    namespace chrono = std::chrono;
    auto const unix_time_ms_signed = static_cast<std::int64_t>(
        chrono::duration_cast<chrono::milliseconds>(
            chrono::system_clock::now().time_since_epoch())
            .count());
    if (unix_time_ms_signed < 0)
      throw std::runtime_error{"system time before UNIX epoch"};
    auto const unix_time_ms = static_cast<std::uint64_t>(unix_time_ms_signed);
    if (std::countl_zero(unix_time_ms) < 16)
      throw std::runtime_error{"system time too great"};

    auto rand = std::array<unsigned char, 10>{};
    int s = RAND_bytes(rand.data(), static_cast<int>(rand.size()));
    if (s != 1)
    {
      unsigned long e = ERR_get_error();
      throw std::runtime_error{std::format(
          "failed to generate UUID(v7): {} ({}, code {})",
          ERR_reason_error_string(e), ERR_lib_error_string(e), e)};
    }

    auto version = std::uint64_t{0b0111};
    auto variant = std::uint64_t{0b10};

    high_ |= unix_time_ms << 16;
    high_ |= version << 12;
    high_ |= std::uint64_t{rand[0]} << 4;
    high_ |= std::uint64_t{rand[1]};
    low_ |= variant << 62;
    low_ |= std::uint64_t{rand[2]} << 54;
    low_ |= std::uint64_t{rand[3]} << 48;
    low_ |= std::uint64_t{rand[4]} << 40;
    low_ |= std::uint64_t{rand[5]} << 32;
    low_ |= std::uint64_t{rand[6]} << 24;
    low_ |= std::uint64_t{rand[7]} << 16;
    low_ |= std::uint64_t{rand[8]} << 8;
    low_ |= std::uint64_t{rand[9]};
  }

  auto format(std::array<char, 37>& target) -> void
  {
    auto high_high = (high_ & 0xffff'ffff'0000'0000) >> 32;
    auto high_low_high = (high_ & 0x0000'0000'ffff'0000) >> 16;
    auto low_low_high = (high_ & 0x0000'0000'0000'ffff) >> 0;
    auto high_low = (low_ & 0xffff'0000'0000'0000) >> 48;
    auto low_low = (low_ & 0x0000'ffff'ffff'ffff) >> 0;

    std::format_to(
        target.begin(), "{:0>8x}-{:0>4x}-{:0>4x}-{:0>4x}-{:0>12x}", high_high,
        high_low_high, low_low_high, high_low, low_low);
    target.back() = '\0';
  }
};

export class id
{
  std::array<char, 37> chars_;

public:
  id() { uuid7{}.format(chars_); }

  auto as_string() const -> util::zstring_view
  {
    return util::zstring_view{chars_.data(), chars_.size() - 1};
  }
};

} // namespace routemon::trace