-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathTrapLabel.h
150 lines (117 loc) · 4.24 KB
/
TrapLabel.h
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#pragma once
#include <bit>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include <binlog/binlog.hpp>
#include <cmath>
#include <charconv>
#include <concepts>
namespace codeql {
struct UndefinedTrapLabel {};
constexpr UndefinedTrapLabel undefined_label{};
class UntypedTrapLabel {
uint64_t id_;
friend class std::hash<UntypedTrapLabel>;
template <typename Tag>
friend class TrapLabel;
BINLOG_ADAPT_STRUCT_FRIEND;
static constexpr uint64_t undefined = 0xffffffffffffffff;
public:
UntypedTrapLabel() : id_{undefined} {}
explicit UntypedTrapLabel(uint64_t id) : id_{id} { assert(id != undefined); }
UntypedTrapLabel(UndefinedTrapLabel) : UntypedTrapLabel() {}
bool valid() const { return id_ != undefined; }
explicit operator bool() const { return valid(); }
friend std::ostream& operator<<(std::ostream& out, UntypedTrapLabel l) {
out << '#' << std::hex << l.id_ << std::dec;
return out;
}
std::string str() const {
assert(valid() && "outputting an undefined label!");
std::string ret(strSize(), '\0');
ret[0] = '#';
std::to_chars(ret.data() + 1, ret.data() + ret.size(), id_, 16);
return ret;
}
friend bool operator==(UntypedTrapLabel lhs, UntypedTrapLabel rhs) { return lhs.id_ == rhs.id_; }
private:
size_t strSize() const {
if (id_ == 0) return 2; // #0
// Number of hex digits is ceil(bit_width(id) / 4), but C++ integer division can only do floor.
return /* # */ 1 + /* hex digits */ 1 + (std::bit_width(id_) - 1) / 4;
}
friend bool operator!=(UntypedTrapLabel lhs, UntypedTrapLabel rhs) { return lhs.id_ != rhs.id_; }
};
template <typename TagParam>
class TrapLabel : public UntypedTrapLabel {
template <typename OtherTag>
friend class TrapLabel;
using UntypedTrapLabel::UntypedTrapLabel;
public:
using Tag = TagParam;
TrapLabel() = default;
TrapLabel(UndefinedTrapLabel) : TrapLabel() {}
TrapLabel& operator=(UndefinedTrapLabel) {
*this = TrapLabel{};
return *this;
}
// The caller is responsible for ensuring ID uniqueness.
static TrapLabel unsafeCreateFromExplicitId(uint64_t id) { return TrapLabel{id}; }
static TrapLabel unsafeCreateFromUntyped(UntypedTrapLabel label) { return TrapLabel{label.id_}; }
template <typename SourceTag>
requires std::derived_from<SourceTag, Tag>
TrapLabel(const TrapLabel<SourceTag>& other) : UntypedTrapLabel(other) {}
};
// wrapper class to allow directly assigning a vector of TrapLabel<A> to a vector of
// TrapLabel<B> if B is a base of A, using move semantics rather than copying
template <typename TagParam>
struct TrapLabelVectorWrapper {
using Tag = TagParam;
std::vector<TrapLabel<TagParam>> data;
template <typename DestinationTag>
requires std::derived_from<Tag, DestinationTag>
operator std::vector<TrapLabel<DestinationTag>>() && {
// reinterpret_cast is safe because TrapLabel instances differ only on the type, not the
// underlying data
return std::move(reinterpret_cast<std::vector<TrapLabel<DestinationTag>>&>(data));
}
};
inline auto trapQuoted(const std::string_view& s) {
return std::quoted(s, '"', '"');
}
} // namespace codeql
namespace std {
template <>
struct hash<codeql::UntypedTrapLabel> {
size_t operator()(const codeql::UntypedTrapLabel& l) const noexcept {
return std::hash<uint64_t>{}(l.id_);
}
};
} // namespace std
namespace mserialize {
// log labels using their string representation, using binlog/mserialize internal plumbing
template <>
struct CustomTag<codeql::UntypedTrapLabel, void> : detail::BuiltinTag<std::string> {
using T = codeql::UntypedTrapLabel;
};
template <typename Tag>
struct CustomTag<codeql::TrapLabel<Tag>, void> : detail::BuiltinTag<std::string> {
using T = codeql::TrapLabel<Tag>;
};
template <>
struct CustomSerializer<codeql::UntypedTrapLabel, void> {
template <typename OutputStream>
static void serialize(codeql::UntypedTrapLabel label, OutputStream& out) {
mserialize::serialize(label.str(), out);
}
static size_t serialized_size(codeql::UntypedTrapLabel label) {
return sizeof(std::uint32_t) + label.strSize();
}
};
template <typename Tag>
struct CustomSerializer<codeql::TrapLabel<Tag>, void> : CustomSerializer<codeql::UntypedTrapLabel> {
};
} // namespace mserialize