From 40bead52b714d8eb9ef52444c2c769d0fdf1ba91 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 10 Jan 2018 13:16:34 -0800 Subject: [PATCH] ircd: Move and breakup util.h into util/; move fs.h into fs/. --- include/ircd/{ => fs}/fs.h | 0 include/ircd/stdinc.h | 4 +- include/ircd/util.h | 1107 --------------------------- include/ircd/util/bswap.h | 47 ++ include/ircd/util/enum.h | 156 ++++ include/ircd/util/instance_list.h | 77 ++ include/ircd/util/reentrance.h | 44 ++ include/ircd/util/syscall.h | 123 +++ include/ircd/util/unique_iterator.h | 86 +++ include/ircd/util/unit_literal.h | 93 +++ include/ircd/util/unwind.h | 100 +++ include/ircd/util/util.h | 621 +++++++++++++++ include/ircd/util/va_rtti.h | 81 ++ 13 files changed, 1430 insertions(+), 1109 deletions(-) rename include/ircd/{ => fs}/fs.h (100%) delete mode 100644 include/ircd/util.h create mode 100644 include/ircd/util/bswap.h create mode 100644 include/ircd/util/enum.h create mode 100644 include/ircd/util/instance_list.h create mode 100644 include/ircd/util/reentrance.h create mode 100644 include/ircd/util/syscall.h create mode 100644 include/ircd/util/unique_iterator.h create mode 100644 include/ircd/util/unit_literal.h create mode 100644 include/ircd/util/unwind.h create mode 100644 include/ircd/util/util.h create mode 100644 include/ircd/util/va_rtti.h diff --git a/include/ircd/fs.h b/include/ircd/fs/fs.h similarity index 100% rename from include/ircd/fs.h rename to include/ircd/fs/fs.h diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 8b36524bb..220b28557 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -185,7 +185,7 @@ namespace ircd template std::string demangle(); } -#include "util.h" +#include "util/util.h" #include "exception.h" #include "string_view.h" #include "vector_view.h" @@ -216,7 +216,7 @@ namespace ircd #include "openssl.h" #include "http.h" #include "fmt.h" -#include "fs.h" +#include "fs/fs.h" #include "ios.h" #include "ctx/ctx.h" #include "db/db.h" diff --git a/include/ircd/util.h b/include/ircd/util.h deleted file mode 100644 index a4214422c..000000000 --- a/include/ircd/util.h +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * 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; -}; - - -/// Simple assert for reentrancy; useful when static variables are in play. -/// You have to place `entered` and give it the proper linkage you want. -template -struct reentrance_assertion -{ - reentrance_assertion() - { - assert(!entered); - entered = true; - } - - ~reentrance_assertion() - { - assert(entered); - entered = false; - } -}; - - -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, long>::type -{ - const auto 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, long>::type -{ - long 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 diff --git a/include/ircd/util/bswap.h b/include/ircd/util/bswap.h new file mode 100644 index 000000000..316df628d --- /dev/null +++ b/include/ircd/util/bswap.h @@ -0,0 +1,47 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_BSWAP_H + +namespace ircd::util +{ + template T &bswap(T *const &val); + template T bswap(const T &val); +} + +/// Reverse endian of data pointed to; return reference +template +T & +ircd::util::bswap(T *const &val) +{ + assert(val != nullptr); + std::reverse(data(*val), data(*val) + size(*val)); + return *val; +} + +/// Reverse endian of T returning value copy +template +T +ircd::util::bswap(const T &val) +{ + T ret; + std::reverse_copy(data(val), data(val) + size(val), data(ret)); + return ret; +} diff --git a/include/ircd/util/enum.h b/include/ircd/util/enum.h new file mode 100644 index 000000000..3da002ca3 --- /dev/null +++ b/include/ircd/util/enum.h @@ -0,0 +1,156 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_ENUM_H + +namespace ircd::util { + +// 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)); +} + +} // namespace ircd::util diff --git a/include/ircd/util/instance_list.h b/include/ircd/util/instance_list.h new file mode 100644 index 000000000..d12ecee05 --- /dev/null +++ b/include/ircd/util/instance_list.h @@ -0,0 +1,77 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_INSTANCE_LIST_H + +namespace ircd::util +{ + template struct instance_list; +} + +/// 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 ircd::util::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); + } +}; diff --git a/include/ircd/util/reentrance.h b/include/ircd/util/reentrance.h new file mode 100644 index 000000000..d2aa817b5 --- /dev/null +++ b/include/ircd/util/reentrance.h @@ -0,0 +1,44 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_REENTRANCE_H + +namespace ircd::util +{ + template struct reentrance_assertion; +} + +/// Simple assert for reentrancy; useful when static variables are in play. +/// You have to place `entered` and give it the proper linkage you want. +template +struct ircd::util::reentrance_assertion +{ + reentrance_assertion() + { + assert(!entered); + entered = true; + } + + ~reentrance_assertion() + { + assert(entered); + entered = false; + } +}; diff --git a/include/ircd/util/syscall.h b/include/ircd/util/syscall.h new file mode 100644 index 000000000..4ec92497c --- /dev/null +++ b/include/ircd/util/syscall.h @@ -0,0 +1,123 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_SYSCALL_H + +// Declaring this here eliminates the need to include for it +extern "C" long syscall(long, ...) noexcept; + +namespace ircd::util +{ + template long syscall(function&& f, args&&... a); + template long syscall(args&&... a); + + template long syscall_nointr(function&& f, args&&... a); + template long syscall_nointr(args&&... a); +} + +// +// 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 +long +ircd::util::syscall(function&& f, + args&&... a) +{ + const auto ret + { + f(std::forward(a)...) + }; + + if(unlikely(ret == -1)) + 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 +long +ircd::util::syscall(args&&... a) +{ + const auto ret + { + ::syscall(number, std::forward(a)...) + }; + + if(unlikely(ret == -1)) + 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 +long +ircd::util::syscall_nointr(function&& f, + args&&... a) +{ + long ret; do + { + ret = f(std::forward(a)...); + } + while(unlikely(ret == -1 && errno == EINTR)); + + if(unlikely(ret == -1)) + 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 +long +ircd::util::syscall_nointr(args&&... a) +{ + long ret; do + { + ret = ::syscall(number, std::forward(a)...); + } + while(unlikely(ret == -1 && errno == EINTR)); + + if(unlikely(ret == -1)) + throw std::system_error(errno, std::system_category()); + + return ret; +} diff --git a/include/ircd/util/unique_iterator.h b/include/ircd/util/unique_iterator.h new file mode 100644 index 000000000..56ba0ea63 --- /dev/null +++ b/include/ircd/util/unique_iterator.h @@ -0,0 +1,86 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_UNIQUE_ITERATOR_H + +namespace ircd::util +{ + template + struct unique_iterator; + + template + struct unique_const_iterator; +} + +// +// 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 ircd::util::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 ircd::util::unique_const_iterator +:unique_iterator +{ + using iterator_type = typename container::const_iterator; + using unique_iterator::unique_iterator; +}; diff --git a/include/ircd/util/unit_literal.h b/include/ircd/util/unit_literal.h new file mode 100644 index 000000000..48e492acd --- /dev/null +++ b/include/ircd/util/unit_literal.h @@ -0,0 +1,93 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_UNIT_LITERAL_H + +// +// 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); \ +} + +namespace ircd { +namespace util { + +// 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 ) + +} // namespace util +} // namespace ircd diff --git a/include/ircd/util/unwind.h b/include/ircd/util/unwind.h new file mode 100644 index 000000000..383b0e069 --- /dev/null +++ b/include/ircd/util/unwind.h @@ -0,0 +1,100 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_UNWIND_H + +namespace ircd::util +{ + struct unwind; +}; + +// +// Fundamental scope-unwind utilities establishing actions during destruction +// + +/// Unconditionally executes the provided code when the object goes out of scope. +/// +struct ircd::util::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 ircd::util::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 ircd::util::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; +}; diff --git a/include/ircd/util/util.h b/include/ircd/util/util.h new file mode 100644 index 000000000..d98992936 --- /dev/null +++ b/include/ircd/util/util.h @@ -0,0 +1,621 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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 +{ + /// Utilities for IRCd. + /// + /// This is an inline namespace: everything declared in it will be + /// accessible in ircd::. By first opening it here as inline all + /// subsequent openings of this namespace do not have to use the inline + /// keyword but will still be inlined to ircd::. + inline namespace util {} +} + +// +// Fundamental macros +// + +#define IRCD_EXPCAT(a, b) a ## b +#define IRCD_CONCAT(a, b) IRCD_EXPCAT(a, b) +#define IRCD_UNIQUE(a) IRCD_CONCAT(a, __COUNTER__) + +/// Macro to arrange a function overload scheme based on the following +/// convention: An available `name` is chosen, from this name a strong type +/// is created by appending `_t`. The name itself becomes a static constexpr +/// instance of this `name_t`. Functions can be declared with an argument +/// accepting `name_t`, and called by passing `name` +/// +/// IRCD_OVERLOAD(foo) // declare overload +/// void function(int, foo_t) {} // overloaded version +/// void function(int) { function(0, foo); } // calls overloaded version +/// function(0); // calls regular version +/// +#define IRCD_OVERLOAD(NAME) \ + static constexpr struct NAME##_t {} NAME {}; + +/// Imports an overload scheme from elsewhere without redeclaring the type_t. +#define IRCD_USING_OVERLOAD(ALIAS, ORIGIN) \ + static constexpr const auto &ALIAS{ORIGIN} + +// +// Typedef macros +// + +/// Creates a type `NAME` from original type `TYPE` by inheriting from `TYPE` +/// and passing through construction to `TYPE`. These implicit conversions +/// we consider to be a "weak" typedef +#define IRCD_WEAK_TYPEDEF(TYPE, NAME) \ +struct NAME \ +:TYPE \ +{ \ + using TYPE::TYPE; \ +}; + +/// Creates a type `NAME` by wrapping instance of `TYPE` as a member and +/// providing explicit conversions to `TYPE` and aggregate construction only. We +/// consider this a "strong" typedef which is useful for wrapping POD types +/// for overloaded functions, etc. +#define IRCD_STRONG_TYPEDEF(TYPE, NAME) \ +struct NAME \ +{ \ + TYPE val; \ + \ + explicit operator const TYPE &() const { return val; } \ + explicit operator TYPE &() { return val; } \ +}; + +/// Convenience for weak typedef statements +#define IRCD_WEAK_T(TYPE) \ + IRCD_WEAK_TYPEDEF(TYPE, IRCD_UNIQUE(weak_t)) + +/// Convenience for strong typedef statements +/// ex: using foo_t = IRCD_STRONG_T(int) +#define IRCD_STRONG_T(TYPE) \ + IRCD_STRONG_TYPEDEF(TYPE, IRCD_UNIQUE(strong_t)) + +// +// Debug size of structure at compile time. +// + +/// Internal use only +template +struct _TEST_SIZEOF_; + +/// 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) +/// +#define IRCD_TEST_SIZEOF(name) \ + ircd::util::_TEST_SIZEOF_ _test_; + + +/// A standard unique_ptr but accepting an std::function for T as its custom +/// deleter. This reduces the boilerplate burden on declaring the right +/// unique_ptr template for custom deleters every single time. +/// +template +using custom_ptr = std::unique_ptr>; + + +#include "unit_literal.h" +#include "unwind.h" +#include "reentrance.h" +#include "enum.h" +#include "syscall.h" +#include "va_rtti.h" +#include "unique_iterator.h" +#include "instance_list.h" +#include "bswap.h" + +// Unsorted section +namespace ircd { +namespace util { + +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; + } +}; + + +// +// 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; +} + + +/// +/// 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)); +} + + +} // namespace util +} // namespace ircd diff --git a/include/ircd/util/va_rtti.h b/include/ircd/util/va_rtti.h new file mode 100644 index 000000000..71baea348 --- /dev/null +++ b/include/ircd/util/va_rtti.h @@ -0,0 +1,81 @@ +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 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_VA_RTTI_H + +namespace ircd::util +{ + struct va_rtti; + + const size_t VA_RTTI_MAX_SIZE + { + 12 + }; +} + +// +// 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. +// +struct ircd::util::va_rtti +:std::array, ircd::util::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(ircd::util::va_rtti) == (ircd::util::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;" +);