// 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