/* * 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 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) \ struct NAME##_t {}; \ static constexpr NAME##_t NAME {}; #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)) struct scope { struct nominal; struct exceptional; const std::function func; template scope(F &&func): func(std::forward(func)) {} scope() = default; scope(const scope &) = delete; scope &operator=(const scope &) = delete; ~scope() noexcept { func(); } }; struct scope::nominal :scope { template nominal(F &&func) :scope { [func(std::forward(func))] { if(likely(!std::uncaught_exception())) func(); } }{} nominal() = default; }; struct scope::exceptional :scope { template exceptional(F &&func) :scope { [func(std::forward(func))] { if(unlikely(std::uncaught_exception())) func(); } }{} exceptional() = default; }; template using custom_ptr = std::unique_ptr>; // // Iteration of a tuple // // for_each(tuple, [](auto&& elem) { ... }); template typename std::enable_if>::value, void>::type for_each(std::tuple &t, func&& f) {} template typename std::enable_if>::value, void>::type for_each(const std::tuple &t, func&& f) {} template typename std::enable_if>::value, void>::type for_each(const std::tuple &t, func&& f) { f(std::get(t)); for_each(t, std::forward(f)); } template typename std::enable_if>::value, void>::type for_each(std::tuple &t, func&& f) { f(std::get(t)); for_each(t, std::forward(f)); } // // Iteration of a tuple until() style: your closure returns true to continue, false // to break. until() then remains true to the end, or returns false if not. template typename std::enable_if>::value, bool>::type until(std::tuple &t, func&& f) { return true; } template typename std::enable_if>::value, bool>::type until(const std::tuple &t, func&& f) { return true; } template typename std::enable_if>::value, bool>::type until(std::tuple &t, func&& f) { return f(std::get(t))? until(t, std::forward(f)) : false; } template typename std::enable_if>::value, bool>::type until(const std::tuple &t, func&& f) { return f(std::get(t))? until(t, std::forward(f)) : false; } // 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)); } 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; } inline std::pair microtime() { struct timeval tv; gettimeofday(&tv, nullptr); return { tv.tv_sec, tv.tv_usec }; } inline ssize_t microtime(char *const &buf, const size_t &size) { const auto mt(microtime()); return snprintf(buf, size, "%zd.%06d", mt.first, mt.second); } 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); } inline auto operator!(const std::string &str) { return str.empty(); } constexpr size_t hash(const char *const &str, const size_t i = 0) { return !str[i]? 7681ULL : (hash(str, i+1) * 33ULL) ^ str[i]; } inline size_t hash(const std::string_view &str, const size_t i = 0) { return i >= str.size()? 7681ULL : (hash(str, i+1) * 33ULL) ^ str.at(i); } inline size_t hash(const std::string &str, const size_t i = 0) { return i >= str.size()? 7681ULL : (hash(str, i+1) * 33ULL) ^ str.at(i); } constexpr size_t hash(const char16_t *const &str, const size_t i = 0) { return !str[i]? 7681ULL : (hash(str, i+1) * 33ULL) ^ str[i]; } inline size_t hash(const std::u16string &str, const size_t i = 0) { return i >= str.size()? 7681ULL : (hash(str, i+1) * 33ULL) ^ str.at(i); } /*** * 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. */ #define IRCD_UNIT_LITERAL_LL(name, morphism) \ constexpr auto \ operator"" _ ## name(const unsigned long long val) \ { \ return (morphism); \ } #define IRCD_UNIT_LITERAL_LD(name, morphism) \ constexpr auto \ operator"" _ ## name(const long double val) \ { \ return (morphism); \ } // IEC unit literals IRCD_UNIT_LITERAL_LL( B, val ) IRCD_UNIT_LITERAL_LL( KiB, val * 1024LL ) IRCD_UNIT_LITERAL_LL( MiB, val * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_LL( GiB, val * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_LL( TiB, val * 1024LL * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_LL( PiB, val * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL ) IRCD_UNIT_LITERAL_LL( 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_LL( KB, val * 1000LL ) IRCD_UNIT_LITERAL_LL( MB, val * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_LL( GB, val * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_LL( TB, val * 1000LL * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_LL( PB, val * 1000LL * 1000LL * 1000LL * 1000LL * 1000LL ) IRCD_UNIT_LITERAL_LL( 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 ) /* 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_; /* 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 { }; // // 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"); } // // Customized std::string_view (experimental TS / C++17) // // This class adds iterator-based (char*, char*) construction to std::string_view which otherwise // takes traditional (char*, size_t) arguments. This allows boost::spirit grammars to create // string_view's using the raw[] directive achieving zero-copy/zero-allocation parsing. // struct string_view :std::string_view { // (non-standard) our faux insert stub void insert(const iterator &, const char &) { assert(0); } //XXX // (non-standard) our iterator-based assign string_view &assign(const char *const &begin, const char *const &end) { *this = std::string_view{begin, size_t(std::distance(begin, end))}; return *this; } // (non-standard) our iterator-based constructor string_view(const char *const &begin, const char *const &end) :std::string_view{begin, size_t(std::distance(begin, end))} {} // Required due to current instability in stdlib //string_view(const std::experimental::string_view &esv) //:std::string_view{esv} //{} // Required due to current instability in stdlib string_view(const std::experimental::fundamentals_v1::basic_string_view &bsv) :std::string_view{bsv} {} string_view() :std::string_view{} {} using std::string_view::string_view; }; template struct vector_view { T *_data { nullptr }; T *_stop { nullptr }; public: T *data() const { return _data; } size_t size() const { return std::distance(_data, _stop); } bool empty() const { return !size(); } const T *begin() const { return data(); } const T *end() const { return _stop; } const T *cbegin() { return data(); } const T *cend() { return _stop; } T *begin() { return data(); } T *end() { return _stop; } const T &operator[](const size_t &pos) const { return *(data() + pos); } T &operator[](const size_t &pos) { return *(data() + pos); } const T &at(const size_t &pos) const { if(unlikely(pos >= size())) throw std::out_of_range(); return operator[](pos); } T &at(const size_t &pos) { if(unlikely(pos >= size())) throw std::out_of_range(); return operator[](pos); } vector_view(T *const &start, T *const &stop) :_data{start} ,_stop{stop} {} vector_view(T *const &start, const size_t &size) :_data{start} ,_stop{start + size} {} template vector_view(std::vector &v) :vector_view{v.data(), v.size()} {} template vector_view(T (&buffer)[SIZE]) :vector_view{std::addressof(buffer), SIZE} {} template vector_view(std::array &array) :vector_view{array.data(), array.size()} {} vector_view() = default; }; // // 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) { const auto ret(f(a...)); if(unlikely(long(ret) == -1)) 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 12 is because std::initializer_list doesn't // seem to work and other containers may be heavy in this context. // struct va_rtti :std::array, 12> { size_t argc; size_t size() const { return argc; } template va_rtti(Args&&... args) :std::array, 12> {{ std::make_pair(std::addressof(args), std::addressof(typeid(Args)))... }} ,argc { sizeof...(args) } { assert(argc <= 8); } }; static_assert(sizeof(va_rtti) == 192 + 8, ""); } // namespace util } // namespace ircd