summaryrefslogtreecommitdiffstats
path: root/server/src/util.cppm
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/util.cppm')
-rw-r--r--server/src/util.cppm254
1 files changed, 254 insertions, 0 deletions
diff --git a/server/src/util.cppm b/server/src/util.cppm
new file mode 100644
index 0000000..65f4d67
--- /dev/null
+++ b/server/src/util.cppm
@@ -0,0 +1,254 @@
1// Stuff that doesn't really have a place right now, but that is
2// broadly useful.
3export module routemon:util;
4
5import std;
6
7namespace routemon::util {
8
9 // For use with e.g. std::visit (on std::variant).
10 template<class... Ts>
11 struct overloaded : Ts... {
12 using Ts::operator()...;
13 };
14
15 export constexpr auto parse_double(std::string_view s, std::chars_format fmt = std::chars_format::general) noexcept -> std::optional<double> {
16 auto x = 0.0;
17 auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt);
18 if (ec == std::errc{}) {
19 return x;
20 } else {
21 return std::nullopt;
22 }
23 }
24
25 export constexpr auto parse_float(std::string_view s, std::chars_format fmt = std::chars_format::general) noexcept -> std::optional<float> {
26 auto x = 0.0;
27 auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt);
28 if (ec == std::errc{}) {
29 return x;
30 } else {
31 return std::nullopt;
32 }
33 }
34
35 export template<class T>
36 class aolist : public std::enable_shared_from_this<aolist<T>> {
37 T v_;
38 std::shared_ptr<aolist<T> const> next_;
39
40 explicit aolist(T v, std::shared_ptr<aolist<T> const> next)
41 : v_{v}, next_{next}
42 {}
43
44 public:
45 static auto nil() -> std::shared_ptr<aolist<T>> {
46 return nullptr;
47 }
48
49 static auto cons(T v, std::shared_ptr<aolist<T> const> l) -> std::shared_ptr<aolist<T>> {
50 return std::shared_ptr<aolist<T>>{new aolist<T>{v, l}};
51 }
52
53 auto next() const -> std::shared_ptr<aolist<T> const> {
54 return next_;
55 }
56
57 auto value() const noexcept -> T const& {
58 return v_;
59 }
60 };
61
62 export constexpr auto size_from_int(int x) -> std::optional<std::size_t> {
63 static_assert(sizeof(int) <= sizeof(std::size_t), "cannot cast int to smaller size_t type");
64 if (x < 0)
65 return std::nullopt;
66 return static_cast<std::size_t>(x);
67 }
68
69 export constexpr auto int_from_size(std::size_t x) -> std::optional<int> {
70 constexpr auto int_max = size_from_int(std::numeric_limits<int>::max());
71 static_assert(int_max.has_value());
72 if (x > *int_max)
73 return std::nullopt;
74 return static_cast<int>(x);
75 }
76
77 export class zstring_view {
78 char const* s_;
79 std::size_t length_;
80
81 public:
82 constexpr explicit zstring_view(char const* s, std::size_t length) :
83 s_{s}, length_{length}
84 {}
85
86 constexpr zstring_view(char const* s) :
87 zstring_view{s, std::char_traits<char>::length(s)}
88 {}
89
90 auto length() const -> std::size_t {
91 return length_;
92 }
93
94 auto c_str() const -> char const* {
95 return s_;
96 }
97
98 operator std::string_view() const {
99 return std::string_view{s_, length_};
100 }
101
102 operator char const*() const {
103 return s_;
104 }
105 };
106
107 // View for null-terminated strings for which we might not
108 // necessarily be interested in the length. String length is only
109 // calculated on demand, at most once on each thread (on more than
110 // one thread when racing). May be null.
111 //
112 // It is undefined behavior to assign to a lazy_zstring_view when
113 // it is in use by other threads.
114 export class lazy_zstring_view {
115 static constexpr auto unset_length = std::numeric_limits<std::size_t>::max();
116
117 char const* s_; // nullable
118 mutable std::atomic<std::size_t> length_;
119 static_assert(decltype(length_)::is_always_lock_free);
120
121 public:
122 constexpr explicit lazy_zstring_view(char const* s) :
123 s_{s}, length_{s ? unset_length : 0}
124 {}
125 ~lazy_zstring_view() = default;
126
127 lazy_zstring_view(lazy_zstring_view const& sv) noexcept
128 : s_{sv.s_}, length_{sv.length_.load(std::memory_order_acquire)}
129 {}
130 lazy_zstring_view(lazy_zstring_view&& sv) noexcept
131 : s_{sv.s_}, length_{sv.length_.load(std::memory_order_acquire)}
132 {}
133 auto operator=(lazy_zstring_view const& rhs) noexcept -> lazy_zstring_view& {
134 if (this != &rhs) {
135 s_ = rhs.s_;
136 length_.store(rhs.length_.load(std::memory_order_acquire), std::memory_order_release);
137 }
138 return *this;
139 }
140 auto operator=(lazy_zstring_view&& rhs) noexcept -> lazy_zstring_view& {
141 return *this = rhs; // use the copy assignment operator
142 }
143
144 auto length() const noexcept -> std::size_t {
145 if (auto v = length_.load(std::memory_order_acquire); v != unset_length)
146 return v;
147 auto l = std::char_traits<char>::length(s_);
148 length_.store(l, std::memory_order_release);
149 return l;
150 }
151
152 auto c_str() const noexcept -> char const* {
153 return s_;
154 }
155
156 operator std::string_view() const noexcept {
157 return std::string_view{s_, length()};
158 }
159
160 operator char const*() const noexcept {
161 return s_;
162 }
163
164 auto operator==(std::string_view sv) const noexcept -> bool {
165 if (auto v = length_.load(std::memory_order_acquire); v != unset_length)
166 if (sv.length() != v)
167 return false;
168 auto res = std::char_traits<char>::compare(s_, sv.data(), sv.length());
169 if (res != 0)
170 return false;
171 // Strings are equal for sv.length() characters.
172 if (s_[sv.length()] != '\0')
173 return false;
174 // Strings are actually equal, and we have just found out the
175 // length of this string, so we might as well set it.
176 length_.store(sv.length(), std::memory_order_release);
177 return true;
178 }
179 };
180
181 constexpr auto operator""_zsv(char const* s, std::size_t length) noexcept -> zstring_view {
182 return zstring_view{s, length};
183 }
184
185 auto operator==(zstring_view lhs, zstring_view rhs) -> bool {
186 return std::string_view{lhs} == std::string_view{rhs};
187 }
188
189 export constexpr auto split_on(std::string_view s, char c) -> std::pair<std::string_view, std::optional<std::string_view>> {
190 if (auto i = s.find(c); i != std::string_view::npos)
191 return std::make_pair(s.substr(0, i), s.substr(i + 1));
192 return std::make_pair(s, std::nullopt);
193 }
194
195 export template<class T>
196 class not_null;
197
198 export template<class T>
199 class not_null<T*> {
200 T* p_;
201
202 struct guaranteed_not_null_t {};
203 explicit not_null(T* p, guaranteed_not_null_t) noexcept : p_{p} {}
204
205 public:
206 explicit not_null(T* p)
207 : p_{p}
208 { if (!p_) throw std::runtime_error{"not_null constructed with null pointer"}; }
209 ~not_null() = default;
210
211 not_null(not_null const& other) = default;
212 not_null(not_null&& other) noexcept = default;
213 auto operator=(not_null const& rhs) noexcept -> not_null& = default;
214 auto operator=(not_null&& rhs) noexcept -> not_null& = default;
215
216 friend auto make_not_null(T* p) noexcept -> std::optional<not_null> {
217 if (p) return not_null(p, guaranteed_not_null_t{});
218 else return std::nullopt;
219 }
220
221 [[nodiscard]] auto get() const noexcept -> T* {
222 return p_;
223 }
224
225 auto operator*() const noexcept -> std::add_lvalue_reference_t<T> {
226 return *p_;
227 }
228
229 auto operator->() const noexcept -> T* {
230 return p_;
231 }
232 };
233 export template<class T> explicit not_null(T*) -> not_null<T*>;
234
235 export template<>
236 class not_null<lazy_zstring_view> {
237 lazy_zstring_view s_;
238
239 public:
240 explicit not_null(lazy_zstring_view s)
241 : s_{std::move(s)}
242 { if (!s_) throw std::runtime_error{"not_null constructed with null pointer"}; }
243
244 [[nodiscard]] auto get() const noexcept -> lazy_zstring_view {
245 return s_;
246 }
247
248 operator lazy_zstring_view() const noexcept { return s_; }
249 operator std::string_view() const noexcept { return s_; }
250 operator char const*() const noexcept { return s_; }
251 };
252 export explicit not_null(lazy_zstring_view s) -> not_null<lazy_zstring_view>;
253
254} // namespace routemon::util