From 973aec43ea54bbf95b64fbcb636403401d1ca60e Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Fri, 28 Aug 2026 18:03:05 +0200 Subject: Import from e4b104792206ee7ea64bf39c6b7d2c0c230f9d14 --- server/src/util.cppm | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 server/src/util.cppm (limited to 'server/src/util.cppm') 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 @@ +// 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 + 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 { + 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 { + 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 aolist : public std::enable_shared_from_this> { + T v_; + std::shared_ptr const> next_; + + explicit aolist(T v, std::shared_ptr const> next) + : v_{v}, next_{next} + {} + + public: + static auto nil() -> std::shared_ptr> { + return nullptr; + } + + static auto cons(T v, std::shared_ptr const> l) -> std::shared_ptr> { + return std::shared_ptr>{new aolist{v, l}}; + } + + auto next() const -> std::shared_ptr const> { + return next_; + } + + auto value() const noexcept -> T const& { + return v_; + } + }; + + export constexpr auto size_from_int(int x) -> std::optional { + 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(x); + } + + export constexpr auto int_from_size(std::size_t x) -> std::optional { + constexpr auto int_max = size_from_int(std::numeric_limits::max()); + static_assert(int_max.has_value()); + if (x > *int_max) + return std::nullopt; + return static_cast(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::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::max(); + + char const* s_; // nullable + mutable std::atomic 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::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::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> { + 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 not_null; + + export template + class not_null { + 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 { + 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 { + return *p_; + } + + auto operator->() const noexcept -> T* { + return p_; + } + }; + export template explicit not_null(T*) -> not_null; + + export template<> + class not_null { + 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; + +} // namespace routemon::util -- cgit v1.3