diff options
| author | Rutger Broekhoff | 2026-08-28 18:03:05 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2026-08-28 18:03:05 +0200 |
| commit | 973aec43ea54bbf95b64fbcb636403401d1ca60e (patch) | |
| tree | 41b7911c420766a9b463245b9296f44c5bf35258 /server/src/util.cppm | |
| download | routemon-973aec43ea54bbf95b64fbcb636403401d1ca60e.tar.gz routemon-973aec43ea54bbf95b64fbcb636403401d1ca60e.zip | |
Import from e4b104792206ee7ea64bf39c6b7d2c0c230f9d14
Diffstat (limited to 'server/src/util.cppm')
| -rw-r--r-- | server/src/util.cppm | 254 |
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. | ||
| 3 | export module routemon:util; | ||
| 4 | |||
| 5 | import std; | ||
| 6 | |||
| 7 | namespace 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 | ||