/* * charybdis: 21st Century IRC++d * util.h: Miscellaneous utilities * * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #pragma once #define HAVE_IRCD_UTIL_H /// Tools for developers namespace ircd::util { } namespace ircd { inline namespace util { #define IRCD_EXPCAT(a, b) a ## b #define IRCD_CONCAT(a, b) IRCD_EXPCAT(a, b) #define IRCD_UNIQUE(a) IRCD_CONCAT(a, __COUNTER__) #define IRCD_OVERLOAD(NAME) \ static constexpr struct NAME##_t {} NAME {}; #define IRCD_USING_OVERLOAD(ALIAS, ORIGIN) \ static constexpr const auto &ALIAS{ORIGIN} #define IRCD_WEAK_TYPEDEF(TYPE, NAME) \ struct NAME \ :TYPE \ { \ using TYPE::TYPE; \ }; #define IRCD_STRONG_TYPEDEF(TYPE, NAME) \ struct NAME \ { \ TYPE val; \ \ explicit operator const TYPE &() const { return val; } \ explicit operator TYPE &() { return val; } \ }; #define IRCD_WEAK_T(TYPE) \ IRCD_WEAK_TYPEDEF(TYPE, IRCD_UNIQUE(weak_t)) // ex: using foo_t = IRCD_STRONG_T(int) #define IRCD_STRONG_T(TYPE) \ IRCD_STRONG_TYPEDEF(TYPE, IRCD_UNIQUE(strong_t)) /* Output the sizeof a structure at compile time. * This stops the compiler with an error (good) containing the size of the target * in the message. * * example: struct foo {}; IRCD_TEST_SIZEOF(foo) */ template struct _TEST_SIZEOF_; #define IRCD_TEST_SIZEOF(name) \ ircd::util::_TEST_SIZEOF_ _test_; // for complex static initialization (try to avoid this though) enum class init_priority { FIRST = 101, STD_CONTAINER = 102, }; #define IRCD_INIT_PRIORITY(name) \ __attribute__((init_priority(int(ircd::init_priority::name)))) /// /// C++14 user defined literals /// /// These are very useful for dealing with space. Simply write 8_MiB and it's /// as if a macro turned that into (8 * 1024 * 1024) at compile time. /// /// (Internal) Defines a unit literal with an unsigned long long basis. /// #define IRCD_UNIT_LITERAL_UL(name, morphism) \ constexpr auto \ operator"" _ ## name(const unsigned long long val) \ { \ return (morphism); \ } /// (Internal) Defines a unit literal with a signed long long basis /// #define IRCD_UNIT_LITERAL_LL(name, morphism) \ constexpr auto \ operator"" _ ## name(const long long val) \ { \ return (morphism); \ } /// (Internal) Defines a unit literal with a long double basis /// #define IRCD_UNIT_LITERAL_LD(name, morphism) \ constexpr auto \ operator"" _ ## name(const long double val) \ { \ return (morphism); \ } // IEC unit literals IRCD_UNIT_LITERAL_UL( B, val ) IRCD_UNIT_LITERAL_UL( KiB, val * 1024LL ) IRCD_UNIT_LITERAL_UL( MiB, val * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_UL( GiB, val * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_UL( TiB, val * 1024LL * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_UL( PiB, val * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_UL( EiB, val * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_LD( B, val ) IRCD_UNIT_LITERAL_LD( KiB, val * 1024.0L ) IRCD_UNIT_LITERAL_LD( MiB, val * 1024.0L * 1024.0L ) IRCD_UNIT_LITERAL_LD( GiB, val * 1024.0L * 1024.0L * 1024.0L ) IRCD_UNIT_LITERAL_LD( TiB, val * 1024.0L * 1024.0L * 1024.0L * 1024.0L ) IRCD_UNIT_LITERAL_LD( PiB, val * 1024.0L * 1024.0L * 1024.0L * 1024.0L * 1024.0L ) IRCD_UNIT_LITERAL_LD( EiB, val * 1024.0L * 1024.0L * 1024.0L * 1024.0L * 1024.0L * 1024.0L ) // SI unit literals IRCD_UNIT_LITERAL_UL( KB, val * 1000LL ) IRCD_UNIT_LITERAL_UL( MB, val * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_UL( GB, val * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_UL( TB, val * 1000LL * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_UL( PB, val * 1000LL * 1000LL * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_UL( EB, val * 1000LL * 1000LL * 1000LL * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_LD( KB, val * 1000.0L ) IRCD_UNIT_LITERAL_LD( MB, val * 1000.0L * 1000.0L ) IRCD_UNIT_LITERAL_LD( GB, val * 1000.0L * 1000.0L * 1000.0L ) IRCD_UNIT_LITERAL_LD( TB, val * 1000.0L * 1000.0L * 1000.0L * 1000.0L ) IRCD_UNIT_LITERAL_LD( PB, val * 1000.0L * 1000.0L * 1000.0L * 1000.0L * 1000.0L ) IRCD_UNIT_LITERAL_LD( EB, val * 1000.0L * 1000.0L * 1000.0L * 1000.0L * 1000.0L * 1000.0L ) /// /// Fundamental scope-unwind utilities establishing actions during destruction /// /// Unconditionally executes the provided code when the object goes out of scope. /// struct unwind { struct nominal; struct exceptional; const std::function func; template unwind(F &&func) :func{std::forward(func)} {} unwind(const unwind &) = delete; unwind &operator=(const unwind &) = delete; ~unwind() noexcept { func(); } }; /// Executes function only if the unwind takes place without active exception /// /// The function is expected to be executed and the likely() should pipeline /// that branch and make this device cheaper to use under normal circumstances. /// struct unwind::nominal { const std::function func; template nominal(F &&func) :func{std::forward(func)} {} ~nominal() noexcept { if(likely(!std::uncaught_exception())) func(); } nominal(const nominal &) = delete; }; /// Executes function only if unwind is taking place because exception thrown /// /// The unlikely() intends for the cost of a branch misprediction to be paid /// for fetching and executing this function. This is because we strive to /// optimize the pipeline for the nominal path, making this device as cheap /// as possible to use. /// struct unwind::exceptional { const std::function func; template exceptional(F &&func) :func{std::forward(func)} {} ~exceptional() noexcept { if(unlikely(std::uncaught_exception())) func(); } exceptional(const exceptional &) = delete; }; template using custom_ptr = std::unique_ptr>; // For conforming enums include a _NUM_ as the last element, // then num_of() works template constexpr typename std::underlying_type::type num_of() { return static_cast::type>(Enum::_NUM_); } // Iteration of a num_of() conforming enum template typename std::enable_if::value, void>::type for_each(const std::function &func) { for(size_t i(0); i < num_of(); ++i) func(static_cast(i)); } /** * flag-enum utilities * * This relaxes the strong typing of enums to allow bitflags with operations on the elements * with intuitive behavior. * * If the project desires absolute guarantees on the strong enum typing then this can be tucked * away in some namespace and imported into select scopes instead. */ template constexpr typename std::enable_if::value, Enum>::type operator~(const Enum &a) { using enum_t = typename std::underlying_type::type; return static_cast(~static_cast(a)); } template constexpr typename std::enable_if::value, bool>::type operator!(const Enum &a) { using enum_t = typename std::underlying_type::type; return !static_cast(a); } template constexpr typename std::enable_if::value, Enum>::type operator|(const Enum &a, const Enum &b) { using enum_t = typename std::underlying_type::type; return static_cast(static_cast(a) | static_cast(b)); } template constexpr typename std::enable_if::value, Enum>::type operator&(const Enum &a, const Enum &b) { using enum_t = typename std::underlying_type::type; return static_cast(static_cast(a) & static_cast(b)); } template constexpr typename std::enable_if::value, Enum>::type operator^(const Enum &a, const Enum &b) { using enum_t = typename std::underlying_type::type; return static_cast(static_cast(a) ^ static_cast(b)); } template constexpr typename std::enable_if::value, Enum &>::type operator|=(Enum &a, const Enum &b) { using enum_t = typename std::underlying_type::type; return (a = (a | b)); } template constexpr typename std::enable_if::value, Enum &>::type operator&=(Enum &a, const Enum &b) { using enum_t = typename std::underlying_type::type; return (a = (a & b)); } template constexpr typename std::enable_if::value, Enum &>::type operator^=(Enum &a, const Enum &b) { using enum_t = typename std::underlying_type::type; return (a = (a ^ b)); } template typename std::enable_if::value, typename std::underlying_type::type>::type combine_flags(const it &begin, const it &end) { using type = typename std::underlying_type::type; return std::accumulate(begin, end, type(0), [] (auto ret, const auto &val) { return ret |= type(val); }); } template typename std::enable_if::value, typename std::underlying_type::type>::type combine_flags(const std::initializer_list &list) { return combine_flags(begin(list), end(list)); } inline size_t size(std::ostream &s) { const auto cur(s.tellp()); s.seekp(0, std::ios::end); const auto ret(s.tellp()); s.seekp(cur, std::ios::beg); return ret; } template constexpr size_t size(const char (&buf)[SIZE]) { return SIZE; } template constexpr size_t size(const std::array &buf) { return SIZE; } template constexpr size_t size(const std::array &buf) { return SIZE; } template constexpr typename std::enable_if::value, size_t>::type size(const T &val) { return sizeof(T); } template constexpr const char * data(const char (&buf)[SIZE]) { return buf; } template constexpr char * data(char (&buf)[SIZE]) { return buf; } template constexpr typename std::enable_if::value, const uint8_t *>::type data(const T &val) { return reinterpret_cast(&val); } template constexpr typename std::enable_if::value, uint8_t *>::type data(T &val) { return reinterpret_cast(&val); } template auto string(const T &s) { std::stringstream ss; return static_cast(ss << s).str(); } inline auto string(const char *const &buf, const size_t &size) { return std::string{buf, size}; } inline auto string(const uint8_t *const &buf, const size_t &size) { return string(reinterpret_cast(buf), size); } // // stringstream buffer set macros // inline std::string & pubsetbuf(std::stringstream &ss, std::string &s) { auto *const &data { const_cast(s.data()) }; ss.rdbuf()->pubsetbuf(data, s.size()); return s; } inline std::string & pubsetbuf(std::stringstream &ss, std::string &s, const size_t &size) { s.resize(size, char{}); return pubsetbuf(ss, s); } inline std::string & resizebuf(std::stringstream &ss, std::string &s) { s.resize(ss.tellp()); return s; } /* This is a template alternative to nothrow overloads, which * allows keeping the function arguments sanitized of the thrownness. */ template constexpr bool is_nothrow() { return std::is_same::value; } template using nothrow_overload = typename std::enable_if(), return_t>::type; template using throw_overload = typename std::enable_if(), return_t>::type; // // Test if type is forward declared or complete // template struct is_complete :std::false_type {}; template struct is_complete :std::true_type {}; // // Test if type is a specialization of a template // template class> struct is_specialization_of :std::false_type {}; template class T, class... args> struct is_specialization_of, T> :std::true_type {}; // // Convenience constexprs for iterators // template constexpr auto is_iterator() { return std::is_base_of::value_type, It>::value; } template constexpr auto is_forward_iterator() { return std::is_base_of::iterator_category>::value; } template constexpr auto is_input_iterator() { return std::is_base_of::iterator_category>::value; } // std::next with out_of_range exception template typename std::enable_if() || is_input_iterator(), It>::type at(It &&start, It &&stop, ssize_t i) { for(; start != stop; --i, std::advance(start, 1)) if(!i) return start; throw std::out_of_range("at(a, b, i): 'i' out of range"); } // // Some functors for STL // template struct keys { auto &operator()(typename container::reference v) const { return v.first; } }; template struct values { auto &operator()(typename container::reference v) const { return v.second; } }; // // Error-checking closure for POSIX system calls. Note the usage is // syscall(read, foo, bar, baz) not a macro like syscall(read(foo, bar, baz)); // template auto syscall(function&& f, args&&... a) -> typename std::enable_if::value, int>::type { const int ret { f(std::forward(a)...) }; if(unlikely(ret == ERROR_CODE)) throw std::system_error(errno, std::system_category()); return ret; } // // Error-checking closure for POSIX system calls. Note the usage is // syscall(read, foo, bar, baz) not a macro like syscall(read(foo, bar, baz)); // template auto uninterruptible_syscall(function&& f, args&&... a) -> typename std::enable_if::value, int>::type { int ret; do { ret = f(std::forward(a)...); } while(unlikely(ret == ERROR_CODE && errno == EINTR)); if(unlikely(ret == ERROR_CODE)) throw std::system_error(errno, std::system_category()); return ret; } // // Similar to a va_list, but conveying std-c++ type data acquired from a variadic template // parameter pack acting as the ...) elipsis. This is used to implement fmt::snprintf(), // exceptions and logger et al in their respective translation units rather than the header // files. // // Limitations: The choice of a fixed array of N is because std::initializer_list doesn't // work here and other containers may be heavy in this context. Ideas to improve this are // welcome. // const size_t VA_RTTI_MAX_SIZE = 12; struct va_rtti :std::array, VA_RTTI_MAX_SIZE> { using base_type = std::array; static constexpr size_t max_size() { return std::tuple_size(); } size_t argc; const size_t &size() const { return argc; } template va_rtti(Args&&... args) :base_type {{ std::make_pair(std::addressof(args), std::addressof(typeid(Args)))... }} ,argc { sizeof...(args) } { assert(argc <= max_size()); } }; static_assert ( sizeof(va_rtti) == (va_rtti::max_size() * 16) + 8, "va_rtti should be (8 + 8) * N + 8;" " where 8 + 8 are the two pointers carrying the argument and its type data;" " where N is the max arguments;" " where the final + 8 bytes holds the actual number of arguments passed;" ); // // To collapse pairs of iterators down to a single type // template struct iterpair :std::pair { using std::pair::pair; }; template T & begin(iterpair &i) { return std::get<0>(i); } template T & end(iterpair &i) { return std::get<1>(i); } template const T & begin(const iterpair &i) { return std::get<0>(i); } template const T & end(const iterpair &i) { return std::get<1>(i); } // // To collapse pairs of iterators down to a single type // typed by an object with iterator typedefs. // template using iterators = std::pair; template using const_iterators = std::pair; template typename T::iterator begin(const iterators &i) { return i.first; } template typename T::iterator end(const iterators &i) { return i.second; } template typename T::const_iterator begin(const const_iterators &ci) { return ci.first; } template typename T::const_iterator end(const const_iterators &ci) { return ci.second; } // // For objects using the pattern of adding their own instance to a container // in their constructor, storing an iterator as a member, and then removing // themselves using the iterator in their destructor. It is unsafe to do that. // Use this instead; or better, use ircd::instance_list<> // template struct unique_iterator { container *c; iterator it; unique_iterator(container &c, iterator it) :c{&c} ,it{std::move(it)} {} unique_iterator() :c{nullptr} {} unique_iterator(const unique_iterator &) = delete; unique_iterator(unique_iterator &&o) noexcept :c{std::move(o.c)} ,it{std::move(o.it)} { o.c = nullptr; } unique_iterator &operator=(const unique_iterator &) = delete; unique_iterator &operator=(unique_iterator &&o) noexcept { this->~unique_iterator(); c = std::move(o.c); it = std::move(o.it); o.c = nullptr; return *this; } ~unique_iterator() noexcept { if(c) c->erase(it); } }; template struct unique_const_iterator :unique_iterator { using iterator_type = typename container::const_iterator; using unique_iterator::unique_iterator; }; /// The instance_list pattern is where every instance of a class registers /// itself in a static list of all instances and removes itself on dtor. /// IRCd Ex. All clients use instance_list so all clients can be listed for /// an administrator or be interrupted and disconnected on server shutdown. /// /// `struct myobj : ircd::instance_list {};` /// /// * The creator of the class no longer has to manually specify what is /// defined here using unique_iterator; however, one still must provide /// linkage for the static list. /// /// * The container pointer used by unique_iterator is eliminated here /// because of the static list. /// template struct instance_list { static std::list list; protected: typename decltype(list)::iterator it; instance_list(typename decltype(list)::iterator it) :it{std::move(it)} {} instance_list() :it{list.emplace(end(list), static_cast(this))} {} instance_list(const instance_list &) = delete; instance_list(instance_list &&o) noexcept :it{std::move(o.it)} { o.it = end(list); } instance_list &operator=(const instance_list &) = delete; instance_list &operator=(instance_list &&o) noexcept { std::swap(it, o.it); return *this; } ~instance_list() noexcept { if(it != end(list)) list.erase(it); } }; // // Compile-time comparison of string literals // constexpr bool _constexpr_equal(const char *a, const char *b) { return *a == *b && (*a == '\0' || _constexpr_equal(a + 1, b + 1)); } inline auto operator!(const std::string &str) { return str.empty(); } inline auto operator!(const std::string_view &str) { return str.empty(); } // // Iterator based until() matching std::for_each except the function // returns a bool to continue rather than void. // template bool until(it_a a, const it_b &b, boolean_function&& f) { for(; a != b; ++a) if(!f(*a)) return false; return true; } /// Convenience loop to test std::is* on a character sequence template ssize_t ctype(const char *begin, const char *const &end) { size_t i(0); for(; begin != end; ++begin, ++i) if(!test(static_cast(*begin))) return i; return -1; } template struct unlock_guard { lockable &l; unlock_guard(lockable &l) :l{l} { l.unlock(); } ~unlock_guard() noexcept { l.lock(); } }; template constexpr bool is_bool() { using type = typename std::remove_reference::type; return std::is_same::value; } template constexpr bool is_number() { using type = typename std::remove_reference::type; return std::is_arithmetic::value; } template constexpr bool is_floating() { using type = typename std::remove_reference::type; return is_number() && std::is_floating_point(); } template constexpr bool is_integer() { return is_number() && !is_floating(); } struct is_zero { template typename std::enable_if < is_bool(), bool>::type test(const bool &value) const { return !value; } template typename std::enable_if < is_integer() && !is_bool(), bool>::type test(const size_t &value) const { return value == 0; } template typename std::enable_if < is_floating(), bool>::type test(const double &value) const { return !(value > 0.0 || value < 0.0); } template bool operator()(T&& t) const { return test(std::forward(t)); } }; constexpr bool is_powerof2(const long long v) { return v && !(v & (v - 1LL)); } template T & bswap(T *const &val) { assert(val != nullptr); std::reverse(data(*val), data(*val) + size(*val)); return *val; } template T bswap(const T &val) { T ret; std::reverse_copy(data(val), data(val) + size(val), data(ret)); return ret; } } // namespace util } // namespace ircd