diff options
Diffstat (limited to 'compat')
-rw-r--r-- | compat/CMakeLists.txt | 2 | ||||
-rw-r--r-- | compat/functional.hpp | 86 | ||||
-rw-r--r-- | compat/meta.hpp | 22 | ||||
-rw-r--r-- | compat/time.hpp | 25 | ||||
-rw-r--r-- | compat/timer-resolution.cpp | 55 | ||||
-rw-r--r-- | compat/timer-resolution.hpp | 45 | ||||
-rw-r--r-- | compat/timer.cpp | 32 | ||||
-rw-r--r-- | compat/timer.hpp | 18 | ||||
-rw-r--r-- | compat/util.hpp | 33 |
9 files changed, 264 insertions, 54 deletions
diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index 40850862..7655a223 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -5,5 +5,5 @@ if(NOT WIN32 AND NOT APPLE) endif() if(CMAKE_COMPILER_IS_GNUCXX) - set_property(SOURCE nan.cpp APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-lto -fno-fast-math -fno-finite-math-only -O0") + set_property(SOURCE nan.cpp APPEND_STRING PROPERTY COMPILE_FLAGS " -fno-lto -fno-fast-math -fno-finite-math-only -O0") endif() diff --git a/compat/functional.hpp b/compat/functional.hpp new file mode 100644 index 00000000..893fe1a0 --- /dev/null +++ b/compat/functional.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include <algorithm> +#include <iterator> +#include <type_traits> + +template<typename t> +using remove_qualifiers = std::remove_reference_t<std::remove_cv_t<t>>; + +namespace functools +{ + +template<typename seq_, typename = void> +struct reserver_ +{ + static inline void maybe_reserve_space(seq_&, unsigned) + { + //qDebug() << "nada"; + } +}; + +template<typename seq_> +struct reserver_<seq_, decltype(std::declval<seq_>().reserve(0u), (void)0)> +{ + static inline void maybe_reserve_space(seq_& seq, unsigned sz) + { + seq.reserve(sz); + } +}; + +template<typename seq_> +inline void maybe_reserve_space(seq_& seq, unsigned sz) +{ + reserver_<seq_, void>::maybe_reserve_space(seq, sz); +} + +} // ns + +template<typename t, t value_> +struct constant final +{ + using type = t; + constexpr type operator()() const noexcept + { + return value_; + } + static constexpr type value = value_; + + constant() = delete; +}; + +template<typename seq_, typename F> +auto map(F&& fun, const seq_& seq) +{ + using seq_type = remove_qualifiers<seq_>; + + seq_type ret; + std::back_insert_iterator<seq_type> it = std::back_inserter(ret); + + for (const auto& elt : seq) + it = fun(elt); + + return ret; +} + +template<typename seq_, typename F> +auto remove_if_not(F&& fun, const seq_& seq) +{ + using namespace functools; + + using seq_type = remove_qualifiers<seq_>; + using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<seq_>()))>::value_type; + using fun_ret_type = decltype(fun(std::declval<const value_type&>())); + static_assert(std::is_convertible<fun_ret_type, bool>::value, "must return bool"); + + seq_type ret; + maybe_reserve_space(ret, seq.size()); + + std::back_insert_iterator<seq_type> it = std::back_inserter(ret); + + for (const value_type& elt : seq) + if (fun(elt)) + it = elt; + + return ret; +} diff --git a/compat/meta.hpp b/compat/meta.hpp index b1c1dd1a..900c515f 100644 --- a/compat/meta.hpp +++ b/compat/meta.hpp @@ -1,6 +1,8 @@ #pragma once +#include <type_traits> #include <tuple> +#include <cstddef> namespace meta { @@ -29,7 +31,20 @@ namespace detail { { using type = inst<xs...>; }; -} + + template<typename N, N max, N pos, typename... xs> + struct index_sequence_ + { + using part = std::integral_constant<N, pos>; + using type = typename index_sequence_<N, max, pos+1, xs..., part>::type; + }; + + template<typename N, N max, typename... xs> + struct index_sequence_<N, max, max, xs...> + { + using type = std::tuple<xs...>; + }; +} // ns detail template<typename... xs> @@ -50,4 +65,7 @@ using butlast = reverse<rest<reverse<xs...>>>; template<typename... xs> using last = lift<first, reverse<xs...>>; -} +template<std::size_t max> +using index_sequence = typename detail::index_sequence_<std::size_t, max, std::size_t(0)>::type; + +} // ns meta diff --git a/compat/time.hpp b/compat/time.hpp new file mode 100644 index 00000000..fbe7469a --- /dev/null +++ b/compat/time.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <chrono> +#include <type_traits> + +namespace time_units { + +template<typename repr, typename ratio> +using duration = std::chrono::duration<repr, ratio>; + +template<typename t, typename u> +static inline constexpr auto time_cast(const u& in) +{ + return std::chrono::duration_cast<t>(in); +} + +using secs = duration<double, std::ratio<1, 1>>; +using secs_ = duration<long long, std::ratio<1, 1>>; +using ms = duration<double, std::milli>; +using ms_ = duration<long long, std::milli>; +using us = duration<double, std::micro>; +using us_ = duration<long long, std::micro>; +using ns = duration<long long, std::nano>; + +} // ns time_units diff --git a/compat/timer-resolution.cpp b/compat/timer-resolution.cpp index 597ee91e..d59c250d 100644 --- a/compat/timer-resolution.cpp +++ b/compat/timer-resolution.cpp @@ -8,12 +8,11 @@ #include "timer-resolution.hpp" #if defined _WIN32 -# include <windows.h> - # include <QLibrary> # include <QDebug> -typedef LONG (__stdcall *funptr_NtSetTimerResolution) (ULONG, BOOLEAN, PULONG); +using namespace timer_impl; + static funptr_NtSetTimerResolution init_timer_resolution_funptr(); static funptr_NtSetTimerResolution get_funptr(); @@ -41,62 +40,72 @@ static funptr_NtSetTimerResolution init_timer_resolution_funptr() static funptr_NtSetTimerResolution get_funptr() { + // starting with C++11 static initializers are fully + // thread-safe and run the first time function is called + + // cf. http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11 + static auto ret = init_timer_resolution_funptr(); return ret; } -timer_resolution::timer_resolution(int msecs) : old_value(-1) +timer_resolution::timer_resolution(int msecs) : old_value(fail_()) { - if (msecs <= 0 || msecs > 30) + if (msecs <= 0 || msecs > 100) { qDebug() << "can't set timer resolution to" << msecs << "ms"; return; } - funptr_NtSetTimerResolution f = get_funptr(); - if (f == nullptr) + funptr_NtSetTimerResolution set_timer_res = get_funptr(); + if (set_timer_res == nullptr) return; // hundredth of a nanosecond - const ULONG value = msecs * ULONG(10000); - NTSTATUS error = f(value, TRUE, &old_value); - ULONG old_value_ = -1; + const ulong_ value = msecs * ulong_(10000); + ntstatus_ res; - if (error != 0) + res = set_timer_res(value, true_(), &old_value); + + if (res < 0) { - old_value = -1; - qDebug() << "NtSetTimerResolution erred with" << error; + old_value = fail_(); + qDebug() << "NtSetTimerResolution erred with" << res; return; } // see if it stuck - error = f(value, TRUE, &old_value_); - if (error != 0) + ulong_ old_value_ = fail_(); + + res = set_timer_res(value, true_(), &old_value_); + if (res < 0) { - old_value = -1; - qDebug() << "NtSetTimerResolution check erred with" << error; + old_value = fail_(); + qDebug() << "NtSetTimerResolution check erred with" << res; return; } if (old_value_ != old_value) { + using t = long long; + qDebug() << "NtSetTimerResolution:" << "old value didn't stick" - << "current resolution" << old_value_ - << "* 100 ns"; - old_value = -1; + << "current resolution" << (t(old_value_) * t(100)) + << "ns"; + old_value = fail_(); return; } } timer_resolution::~timer_resolution() { - if (old_value != ULONG(-1)) + if (old_value != fail_()) { funptr_NtSetTimerResolution f = get_funptr(); - ULONG fuzz = -1; - (void) f(old_value, TRUE, &fuzz); + ulong_ fuzz = fail_(); + (void) f(old_value, true_(), &fuzz); } } diff --git a/compat/timer-resolution.hpp b/compat/timer-resolution.hpp index 5db877c0..8c18a600 100644 --- a/compat/timer-resolution.hpp +++ b/compat/timer-resolution.hpp @@ -3,14 +3,57 @@ #if defined _WIN32 # include "export.hpp" +#include <type_traits> + +namespace timer_impl +{ + +// cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 +// for NTSTATUS, see http://stackoverflow.com/questions/3378622/how-to-understand-the-ntstatus-nt-success-typedef-in-windows-ddk + +using ulong_ = unsigned long; +using long_ = long; +using byte_ = unsigned char; +using boolean_ = byte_; + +using true_ = std::integral_constant<int, 1>; + +using fail_ = std::integral_constant<byte_, byte_(-1)>; + +// cf. http://stackoverflow.com/questions/3378622/how-to-understand-the-ntstatus-nt-success-typedef-in-windows-ddk + +// typedef __success(return >= 0) LONG NTSTATUS; + +// __success(expr) T f() : indicates whether function f succeeded or +// not. If is true at exit, all the function's guarantees (as given +// by other annotations) must hold. If is false at exit, the caller +// should not expect any of the function's guarantees to hold. If not used, +// the function must always satisfy its guarantees. Added automatically to +// functions that indicate success in standard ways, such as by returning an +// HRESULT. + +using ntstatus_ = long_; + +// finally what we want +// this is equivalent to typedef syntax + +using funptr_NtSetTimerResolution = ntstatus_ (__stdcall *)(ulong_, boolean_, ulong_*); + +// RAII wrapper + class OTR_COMPAT_EXPORT timer_resolution final { - unsigned long old_value; + ulong_ old_value; public: timer_resolution(int msecs); ~timer_resolution(); }; + +} + +using timer_impl::timer_resolution; + #else struct timer_resolution final { diff --git a/compat/timer.cpp b/compat/timer.cpp index d5052ccb..3ecdf528 100644 --- a/compat/timer.cpp +++ b/compat/timer.cpp @@ -7,6 +7,7 @@ */ #include "timer.hpp" +#include <cmath> Timer::Timer() { @@ -15,17 +16,20 @@ Timer::Timer() void Timer::start() { - wrap_gettime(&state); + gettime(&state); } // common -void Timer::wrap_gettime(timespec* state) +void Timer::gettime(timespec* state) { #if defined(_WIN32) || defined(__MACH__) otr_clock_gettime(state); +#elif defined CLOCK_MONOTONIC + const int res = clock_gettime(CLOCK_MONOTONIC, state); + assert(res == 0 && "must support CLOCK_MONOTONIC"); #else - (void) clock_gettime(CLOCK_MONOTONIC, state); +# error "timer query method not known" #endif } @@ -33,14 +37,13 @@ void Timer::wrap_gettime(timespec* state) long long Timer::elapsed_nsecs() const { - struct timespec cur = {}; - wrap_gettime(&cur); + timespec cur{}; + gettime(&cur); return conv_nsecs(cur); } long long Timer::conv_nsecs(const timespec& cur) const { - // this can and will overflow return (cur.tv_sec - state.tv_sec) * 1000000000LL + (cur.tv_nsec - state.tv_nsec); } @@ -48,8 +51,8 @@ long long Timer::conv_nsecs(const timespec& cur) const double Timer::elapsed_usecs() const { - struct timespec cur = {}; - wrap_gettime(&cur); + timespec cur{}; + gettime(&cur); const long long nsecs = conv_nsecs(cur); return nsecs * 1e-3; } @@ -73,27 +76,26 @@ double Timer::elapsed_seconds() const #if defined _WIN32 LARGE_INTEGER Timer::otr_get_clock_frequency() { - LARGE_INTEGER freq = {}; + LARGE_INTEGER freq{}; (void) QueryPerformanceFrequency(&freq); return freq; } void Timer::otr_clock_gettime(timespec* ts) { - static LARGE_INTEGER freq = otr_get_clock_frequency(); + static const LARGE_INTEGER freq = otr_get_clock_frequency(); LARGE_INTEGER d; (void) QueryPerformanceCounter(&d); using ll = long long; - using ld = long double; - const long long part = ll(d.QuadPart / ld(freq.QuadPart) * 1000000000.L); + const ll part = ll(std::roundl((d.QuadPart * 1000000000.L) / ll(freq.QuadPart))); using t_s = decltype(ts->tv_sec); using t_ns = decltype(ts->tv_nsec); - ts->tv_sec = t_s(part / 1000000000LL); - ts->tv_nsec = t_ns(part % 1000000000LL); + ts->tv_sec = t_s(part / 1000000000); + ts->tv_nsec = t_ns(part % 1000000000); } #elif defined __MACH__ @@ -106,7 +108,7 @@ mach_timebase_info_data_t Timer::otr_get_mach_frequency() double Timer::otr_clock_gettime(timespec* ts) { - static mach_timebase_info_data_t timebase_info = otr_get_mach_frequency(); + static const mach_timebase_info_data_t timebase_info = otr_get_mach_frequency(); uint64_t state, nsec; state = mach_absolute_time(); nsec = state * timebase_info.numer / timebase_info.denom; diff --git a/compat/timer.hpp b/compat/timer.hpp index e9efba79..58e1c7d6 100644 --- a/compat/timer.hpp +++ b/compat/timer.hpp @@ -20,7 +20,9 @@ #include <ctime> #include <tuple> -class OTR_COMPAT_EXPORT Timer +#include "time.hpp" + +class OTR_COMPAT_EXPORT Timer final { struct timespec state; long long conv_nsecs(const struct timespec& cur) const; @@ -31,16 +33,22 @@ class OTR_COMPAT_EXPORT Timer static mach_timebase_info_data_t otr_get_mach_frequency(); #endif - static void wrap_gettime(struct timespec* state); + static void gettime(struct timespec* state); + using ns = time_units::ns; public: Timer(); - void start(); + + template<typename t> + t elapsed() const + { + using namespace time_units; + return static_cast<const t&>(ns(elapsed_nsecs())); + } + long long elapsed_nsecs() const; double elapsed_usecs() const; double elapsed_ms() const; double elapsed_seconds() const; }; - - diff --git a/compat/util.hpp b/compat/util.hpp index b904978b..823b83d1 100644 --- a/compat/util.hpp +++ b/compat/util.hpp @@ -3,6 +3,7 @@ #include "ndebug-guard.hpp" #include "run-in-thread.hpp" #include "meta.hpp" +#include "functional.hpp" #include <memory> #include <cmath> @@ -12,10 +13,14 @@ #include <QDebug> #define progn(...) ([&]() { __VA_ARGS__ }()) +#define prog1(x, ...) (([&]() { auto _ret1324 = (x); do { __VA_ARGS__; } while (0); return _ret1324; })()) #define once_only(...) progn(static bool once = false; if (!once) { once = true; __VA_ARGS__; }) - -#define load_time_value(x) progn(static const auto _value132((x)); return _value132;) +#define load_time_value(x) \ + progn( \ + static const auto _value132((x)); \ + return static_cast<decltype(_value132)&>(value132); \ + ) template<typename t> using mem = std::shared_ptr<t>; template<typename t> using ptr = std::unique_ptr<t>; @@ -40,20 +45,34 @@ template<typename t> using ptr = std::unique_ptr<t>; # define unused_on_unix(t, i) t i #endif +#if defined __GNUC__ +# define likely(x) __builtin_expect((x),1) +# define unlikely(x) __builtin_expect((x),0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + template<typename t> -int iround(const t& val) +inline int iround(const t& val) { return int(std::round(val)); } +template<typename t> +inline unsigned uround(const t& val) +{ + return std::round(std::fmax(t(0), val)); +} + namespace util_detail { template<typename n> inline auto clamp_(n val, n min, n max) -> n { - if (val > max) + if (unlikely(val > max)) return max; - if (val < min) + if (unlikely(val < min)) return min; return val; } @@ -61,9 +80,9 @@ inline auto clamp_(n val, n min, n max) -> n } template<typename t, typename u, typename w> -inline auto clamp(const t& val, const u& min, const w& max) -> decltype(val * min * max) +inline auto clamp(const t& val, const u& min, const w& max) -> decltype(val + min + max) { - return ::util_detail::clamp_<decltype(val * min * max)>(val, min, max); + return ::util_detail::clamp_<decltype(val + min + max)>(val, min, max); } template<typename t, typename... xs> |