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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
// Stuff that doesn't really have a place right now, but that is
// broadly useful.
export module routemon:util;
import std;
namespace routemon::util {
// For use with e.g. std::visit (on std::variant).
template <class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
export constexpr auto parse_double(
std::string_view s,
std::chars_format fmt = std::chars_format::general) noexcept
-> std::optional<double>
{
auto x = 0.0;
auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt);
if (ec == std::errc{})
{
return x;
}
else
{
return std::nullopt;
}
}
export constexpr auto parse_float(
std::string_view s,
std::chars_format fmt = std::chars_format::general) noexcept
-> std::optional<float>
{
auto x = 0.0;
auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt);
if (ec == std::errc{})
{
return x;
}
else
{
return std::nullopt;
}
}
export template <class T>
class aolist : public std::enable_shared_from_this<aolist<T>>
{
T v_;
std::shared_ptr<aolist<T> const> next_;
explicit aolist(T v, std::shared_ptr<aolist<T> const> next)
: v_{v}, next_{next}
{
}
public:
static auto nil() -> std::shared_ptr<aolist<T>> { return nullptr; }
static auto cons(T v, std::shared_ptr<aolist<T> const> l)
-> std::shared_ptr<aolist<T>>
{
return std::shared_ptr<aolist<T>>{new aolist<T>{v, l}};
}
auto next() const -> std::shared_ptr<aolist<T> const> { return next_; }
auto value() const noexcept -> T const& { return v_; }
};
export constexpr auto size_from_int(int x) -> std::optional<std::size_t>
{
static_assert(
sizeof(int) <= sizeof(std::size_t),
"cannot cast int to smaller size_t type");
if (x < 0)
return std::nullopt;
return static_cast<std::size_t>(x);
}
export constexpr auto int_from_size(std::size_t x) -> std::optional<int>
{
constexpr auto int_max = size_from_int(std::numeric_limits<int>::max());
static_assert(int_max.has_value());
if (x > *int_max)
return std::nullopt;
return static_cast<int>(x);
}
export class zstring_view
{
char const* s_;
std::size_t length_;
public:
constexpr explicit zstring_view(char const* s, std::size_t length)
: s_{s}, length_{length}
{
}
constexpr zstring_view(char const* s)
: zstring_view{s, std::char_traits<char>::length(s)}
{
}
auto length() const -> std::size_t { return length_; }
auto c_str() const -> char const* { return s_; }
operator std::string_view() const { return std::string_view{s_, length_}; }
operator char const*() const { return s_; }
};
// View for null-terminated strings for which we might not
// necessarily be interested in the length. String length is only
// calculated on demand, at most once on each thread (on more than
// one thread when racing). May be null.
//
// It is undefined behavior to assign to a lazy_zstring_view when
// it is in use by other threads.
export class lazy_zstring_view
{
static constexpr auto unset_length = std::numeric_limits<std::size_t>::max();
char const* s_; // nullable
mutable std::atomic<std::size_t> length_;
static_assert(decltype(length_)::is_always_lock_free);
public:
constexpr explicit lazy_zstring_view(char const* s)
: s_{s}, length_{s ? unset_length : 0}
{
}
~lazy_zstring_view() = default;
lazy_zstring_view(lazy_zstring_view const& sv) noexcept
: s_{sv.s_}, length_{sv.length_.load(std::memory_order_acquire)}
{
}
lazy_zstring_view(lazy_zstring_view&& sv) noexcept
: s_{sv.s_}, length_{sv.length_.load(std::memory_order_acquire)}
{
}
auto operator=(lazy_zstring_view const& rhs) noexcept -> lazy_zstring_view&
{
if (this != &rhs)
{
s_ = rhs.s_;
length_.store(
rhs.length_.load(std::memory_order_acquire),
std::memory_order_release);
}
return *this;
}
auto operator=(lazy_zstring_view&& rhs) noexcept -> lazy_zstring_view&
{
return *this = rhs; // use the copy assignment operator
}
auto length() const noexcept -> std::size_t
{
if (auto v = length_.load(std::memory_order_acquire); v != unset_length)
return v;
auto l = std::char_traits<char>::length(s_);
length_.store(l, std::memory_order_release);
return l;
}
auto c_str() const noexcept -> char const* { return s_; }
operator std::string_view() const noexcept
{
return std::string_view{s_, length()};
}
operator char const*() const noexcept { return s_; }
auto operator==(std::string_view sv) const noexcept -> bool
{
if (auto v = length_.load(std::memory_order_acquire); v != unset_length)
if (sv.length() != v)
return false;
auto res = std::char_traits<char>::compare(s_, sv.data(), sv.length());
if (res != 0)
return false;
// Strings are equal for sv.length() characters.
if (s_[sv.length()] != '\0')
return false;
// Strings are actually equal, and we have just found out the
// length of this string, so we might as well set it.
length_.store(sv.length(), std::memory_order_release);
return true;
}
};
constexpr auto operator""_zsv(char const* s, std::size_t length) noexcept
-> zstring_view
{
return zstring_view{s, length};
}
auto operator==(zstring_view lhs, zstring_view rhs) -> bool
{
return std::string_view{lhs} == std::string_view{rhs};
}
export constexpr auto split_on(std::string_view s, char c)
-> std::pair<std::string_view, std::optional<std::string_view>>
{
if (auto i = s.find(c); i != std::string_view::npos)
return std::make_pair(s.substr(0, i), s.substr(i + 1));
return std::make_pair(s, std::nullopt);
}
export template <class T>
class not_null;
export template <class T>
class not_null<T*>
{
T* p_;
struct guaranteed_not_null_t
{
};
explicit not_null(T* p, guaranteed_not_null_t) noexcept : p_{p} {}
public:
explicit not_null(T* p) : p_{p}
{
if (!p_)
throw std::runtime_error{"not_null constructed with null pointer"};
}
~not_null() = default;
not_null(not_null const& other) = default;
not_null(not_null&& other) noexcept = default;
auto operator=(not_null const& rhs) noexcept -> not_null& = default;
auto operator=(not_null&& rhs) noexcept -> not_null& = default;
friend auto make_not_null(T* p) noexcept -> std::optional<not_null>
{
if (p)
return not_null(p, guaranteed_not_null_t{});
else
return std::nullopt;
}
[[nodiscard]] auto get() const noexcept -> T* { return p_; }
auto operator*() const noexcept -> std::add_lvalue_reference_t<T>
{
return *p_;
}
auto operator->() const noexcept -> T* { return p_; }
};
export template <class T>
explicit not_null(T*) -> not_null<T*>;
export template <>
class not_null<lazy_zstring_view>
{
lazy_zstring_view s_;
public:
explicit not_null(lazy_zstring_view s) : s_{std::move(s)}
{
if (!s_)
throw std::runtime_error{"not_null constructed with null pointer"};
}
[[nodiscard]] auto get() const noexcept -> lazy_zstring_view { return s_; }
operator lazy_zstring_view() const noexcept { return s_; }
operator std::string_view() const noexcept { return s_; }
operator char const*() const noexcept { return s_; }
};
export explicit not_null(lazy_zstring_view s) -> not_null<lazy_zstring_view>;
} // namespace routemon::util
|