Sunshine/src/utility.h

1039 lines
25 KiB
C++

/**
* @file src/utility.h
* @brief todo
*/
#pragma once
#include <algorithm>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>
#define KITTY_WHILE_LOOP(x, y, z) \
{ \
x; \
while (y) z \
}
template <typename T>
struct argument_type;
template <typename T, typename U>
struct argument_type<T(U)> {
typedef U type;
};
#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \
class move_t { \
public: \
using element_type = typename argument_type<void(t)>::type; \
\
move_t(): el { init_val } {} \
template <class... Args> \
move_t(Args &&...args): el { std::forward<Args>(args)... } {} \
move_t(const move_t &) = delete; \
\
move_t(move_t &&other) noexcept: el { std::move(other.el) } { \
other.el = element_type { init_val }; \
} \
\
move_t & \
operator=(const move_t &) = delete; \
\
move_t & \
operator=(move_t &&other) { \
std::swap(el, other.el); \
return *this; \
} \
element_type * \
operator->() { return &el; } \
const element_type * \
operator->() const { return &el; } \
\
inline element_type \
release() { \
element_type val = std::move(el); \
el = element_type { init_val }; \
return val; \
} \
\
~move_t() z \
\
element_type el; \
}
#define KITTY_DECL_CONSTR(x) \
x(x &&) noexcept = default; \
x &operator=(x &&) noexcept = default; \
x();
#define KITTY_DEFAULT_CONSTR_MOVE(x) \
x(x &&) noexcept = default; \
x &operator=(x &&) noexcept = default;
#define KITTY_DEFAULT_CONSTR_MOVE_THROW(x) \
x(x &&) = default; \
x &operator=(x &&) = default; \
x() = default;
#define KITTY_DEFAULT_CONSTR(x) \
KITTY_DEFAULT_CONSTR_MOVE(x) \
x(const x &) noexcept = default; \
x &operator=(const x &) = default;
#define TUPLE_2D(a, b, expr) \
decltype(expr) a##_##b = expr; \
auto &a = std::get<0>(a##_##b); \
auto &b = std::get<1>(a##_##b)
#define TUPLE_2D_REF(a, b, expr) \
auto &a##_##b = expr; \
auto &a = std::get<0>(a##_##b); \
auto &b = std::get<1>(a##_##b)
#define TUPLE_3D(a, b, c, expr) \
decltype(expr) a##_##b##_##c = expr; \
auto &a = std::get<0>(a##_##b##_##c); \
auto &b = std::get<1>(a##_##b##_##c); \
auto &c = std::get<2>(a##_##b##_##c)
#define TUPLE_3D_REF(a, b, c, expr) \
auto &a##_##b##_##c = expr; \
auto &a = std::get<0>(a##_##b##_##c); \
auto &b = std::get<1>(a##_##b##_##c); \
auto &c = std::get<2>(a##_##b##_##c)
#define TUPLE_EL(a, b, expr) \
decltype(expr) a##_ = expr; \
auto &a = std::get<b>(a##_)
#define TUPLE_EL_REF(a, b, expr) \
auto &a = std::get<b>(expr)
namespace util {
template <template <typename...> class X, class... Y>
struct __instantiation_of: public std::false_type {};
template <template <typename...> class X, class... Y>
struct __instantiation_of<X, X<Y...>>: public std::true_type {};
template <template <typename...> class X, class T, class... Y>
static constexpr auto instantiation_of_v = __instantiation_of<X, T, Y...>::value;
template <bool V, class X, class Y>
struct __either;
template <class X, class Y>
struct __either<true, X, Y> {
using type = X;
};
template <class X, class Y>
struct __either<false, X, Y> {
using type = Y;
};
template <bool V, class X, class Y>
using either_t = typename __either<V, X, Y>::type;
template <class... Ts>
struct overloaded: Ts... {
using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
template <class T>
class FailGuard {
public:
FailGuard() = delete;
FailGuard(T &&f) noexcept:
_func { std::forward<T>(f) } {}
FailGuard(FailGuard &&other) noexcept:
_func { std::move(other._func) } {
this->failure = other.failure;
other.failure = false;
}
FailGuard(const FailGuard &) = delete;
FailGuard &
operator=(const FailGuard &) = delete;
FailGuard &
operator=(FailGuard &&other) = delete;
~FailGuard() noexcept {
if (failure) {
_func();
}
}
void
disable() { failure = false; }
bool failure { true };
private:
T _func;
};
template <class T>
[[nodiscard]] auto
fail_guard(T &&f) {
return FailGuard<T> { std::forward<T>(f) };
}
template <class T>
void
append_struct(std::vector<uint8_t> &buf, const T &_struct) {
constexpr size_t data_len = sizeof(_struct);
buf.reserve(data_len);
auto *data = (uint8_t *) &_struct;
for (size_t x = 0; x < data_len; ++x) {
buf.push_back(data[x]);
}
}
template <class T>
class Hex {
public:
typedef T elem_type;
private:
const char _bits[16] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
char _hex[sizeof(elem_type) * 2];
public:
Hex(const elem_type &elem, bool rev) {
if (!rev) {
const uint8_t *data = reinterpret_cast<const uint8_t *>(&elem) + sizeof(elem_type) - 1;
for (auto it = begin(); it < cend();) {
*it++ = _bits[*data / 16];
*it++ = _bits[*data-- % 16];
}
}
else {
const uint8_t *data = reinterpret_cast<const uint8_t *>(&elem);
for (auto it = begin(); it < cend();) {
*it++ = _bits[*data / 16];
*it++ = _bits[*data++ % 16];
}
}
}
char *
begin() { return _hex; }
char *
end() { return _hex + sizeof(elem_type) * 2; }
const char *
begin() const { return _hex; }
const char *
end() const { return _hex + sizeof(elem_type) * 2; }
const char *
cbegin() const { return _hex; }
const char *
cend() const { return _hex + sizeof(elem_type) * 2; }
std::string
to_string() const {
return { begin(), end() };
}
std::string_view
to_string_view() const {
return { begin(), sizeof(elem_type) * 2 };
}
};
template <class T>
Hex<T>
hex(const T &elem, bool rev = false) {
return Hex<T>(elem, rev);
}
template <class It>
std::string
hex_vec(It begin, It end, bool rev = false) {
auto str_size = 2 * std::distance(begin, end);
std::string hex;
hex.resize(str_size);
const char _bits[16] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
if (rev) {
for (auto it = std::begin(hex); it < std::end(hex);) {
*it++ = _bits[((uint8_t) *begin) / 16];
*it++ = _bits[((uint8_t) *begin++) % 16];
}
}
else {
--end;
for (auto it = std::begin(hex); it < std::end(hex);) {
*it++ = _bits[((uint8_t) *end) / 16];
*it++ = _bits[((uint8_t) *end--) % 16];
}
}
return hex;
}
template <class C>
std::string
hex_vec(C &&c, bool rev = false) {
return hex_vec(std::begin(c), std::end(c), rev);
}
template <class T>
T
from_hex(const std::string_view &hex, bool rev = false) {
std::uint8_t buf[sizeof(T)];
static char constexpr shift_bit = 'a' - 'A';
auto is_convertable = [](char ch) -> bool {
if (isdigit(ch)) {
return true;
}
ch |= shift_bit;
if ('a' > ch || ch > 'z') {
return false;
}
return true;
};
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2;
auto padding = sizeof(T) - buf_size;
const char *data = hex.data() + hex.size() - 1;
auto convert = [](char ch) -> std::uint8_t {
if (ch >= '0' && ch <= '9') {
return (std::uint8_t) ch - '0';
}
return (std::uint8_t)(ch | (char) 32) - 'a' + (char) 10;
};
std::fill_n(buf + buf_size, padding, 0);
std::for_each_n(buf, buf_size, [&](auto &el) {
while (!is_convertable(*data)) { --data; }
std::uint8_t ch_r = convert(*data--);
while (!is_convertable(*data)) { --data; }
std::uint8_t ch_l = convert(*data--);
el = (ch_l << 4) | ch_r;
});
if (rev) {
std::reverse(std::begin(buf), std::end(buf));
}
return *reinterpret_cast<T *>(buf);
}
inline std::string
from_hex_vec(const std::string &hex, bool rev = false) {
std::string buf;
static char constexpr shift_bit = 'a' - 'A';
auto is_convertable = [](char ch) -> bool {
if (isdigit(ch)) {
return true;
}
ch |= shift_bit;
if ('a' > ch || ch > 'z') {
return false;
}
return true;
};
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2;
buf.resize(buf_size);
const char *data = hex.data() + hex.size() - 1;
auto convert = [](char ch) -> std::uint8_t {
if (ch >= '0' && ch <= '9') {
return (std::uint8_t) ch - '0';
}
return (std::uint8_t)(ch | (char) 32) - 'a' + (char) 10;
};
for (auto &el : buf) {
while (!is_convertable(*data)) { --data; }
std::uint8_t ch_r = convert(*data--);
while (!is_convertable(*data)) { --data; }
std::uint8_t ch_l = convert(*data--);
el = (ch_l << 4) | ch_r;
}
if (rev) {
std::reverse(std::begin(buf), std::end(buf));
}
return buf;
}
template <class T>
class hash {
public:
using value_type = T;
std::size_t
operator()(const value_type &value) const {
const auto *p = reinterpret_cast<const char *>(&value);
return std::hash<std::string_view> {}(std::string_view { p, sizeof(value_type) });
}
};
template <class T>
auto
enm(const T &val) -> const std::underlying_type_t<T> & {
return *reinterpret_cast<const std::underlying_type_t<T> *>(&val);
}
template <class T>
auto
enm(T &val) -> std::underlying_type_t<T> & {
return *reinterpret_cast<std::underlying_type_t<T> *>(&val);
}
inline std::int64_t
from_chars(const char *begin, const char *end) {
if (begin == end) {
return 0;
}
std::int64_t res {};
std::int64_t mul = 1;
while (begin != --end) {
res += (std::int64_t)(*end - '0') * mul;
mul *= 10;
}
return *begin != '-' ? res + (std::int64_t)(*begin - '0') * mul : -res;
}
inline std::int64_t
from_view(const std::string_view &number) {
return from_chars(std::begin(number), std::end(number));
}
template <class X, class Y>
class Either: public std::variant<std::monostate, X, Y> {
public:
using std::variant<std::monostate, X, Y>::variant;
constexpr bool
has_left() const {
return std::holds_alternative<X>(*this);
}
constexpr bool
has_right() const {
return std::holds_alternative<Y>(*this);
}
X &
left() {
return std::get<X>(*this);
}
Y &
right() {
return std::get<Y>(*this);
}
const X &
left() const {
return std::get<X>(*this);
}
const Y &
right() const {
return std::get<Y>(*this);
}
};
// Compared to std::unique_ptr, it adds the ability to get the address of the pointer itself
template <typename T, typename D = std::default_delete<T>>
class uniq_ptr {
public:
using element_type = T;
using pointer = element_type *;
using const_pointer = element_type const *;
using deleter_type = D;
constexpr uniq_ptr() noexcept:
_p { nullptr } {}
constexpr uniq_ptr(std::nullptr_t) noexcept:
_p { nullptr } {}
uniq_ptr(const uniq_ptr &other) noexcept = delete;
uniq_ptr &
operator=(const uniq_ptr &other) noexcept = delete;
template <class V>
uniq_ptr(V *p) noexcept:
_p { p } {
static_assert(std::is_same_v<element_type, void> || std::is_same_v<element_type, V> || std::is_base_of_v<element_type, V>, "element_type must be base class of V");
}
template <class V>
uniq_ptr(std::unique_ptr<V, deleter_type> &&uniq) noexcept:
_p { uniq.release() } {
static_assert(std::is_same_v<element_type, void> || std::is_same_v<T, V> || std::is_base_of_v<element_type, V>, "element_type must be base class of V");
}
template <class V>
uniq_ptr(uniq_ptr<V, deleter_type> &&other) noexcept:
_p { other.release() } {
static_assert(std::is_same_v<element_type, void> || std::is_same_v<T, V> || std::is_base_of_v<element_type, V>, "element_type must be base class of V");
}
template <class V>
uniq_ptr &
operator=(uniq_ptr<V, deleter_type> &&other) noexcept {
static_assert(std::is_same_v<element_type, void> || std::is_same_v<T, V> || std::is_base_of_v<element_type, V>, "element_type must be base class of V");
reset(other.release());
return *this;
}
template <class V>
uniq_ptr &
operator=(std::unique_ptr<V, deleter_type> &&uniq) noexcept {
static_assert(std::is_same_v<element_type, void> || std::is_same_v<T, V> || std::is_base_of_v<element_type, V>, "element_type must be base class of V");
reset(uniq.release());
return *this;
}
~uniq_ptr() {
reset();
}
void
reset(pointer p = pointer()) {
if (_p) {
_deleter(_p);
}
_p = p;
}
pointer
release() {
auto tmp = _p;
_p = nullptr;
return tmp;
}
pointer
get() {
return _p;
}
const_pointer
get() const {
return _p;
}
std::add_lvalue_reference_t<element_type const>
operator*() const {
return *_p;
}
std::add_lvalue_reference_t<element_type>
operator*() {
return *_p;
}
const_pointer
operator->() const {
return _p;
}
pointer
operator->() {
return _p;
}
pointer *
operator&() const {
return &_p;
}
pointer *
operator&() {
return &_p;
}
deleter_type &
get_deleter() {
return _deleter;
}
const deleter_type &
get_deleter() const {
return _deleter;
}
explicit operator bool() const {
return _p != nullptr;
}
protected:
pointer _p;
deleter_type _deleter;
};
template <class T1, class D1, class T2, class D2>
bool
operator==(const uniq_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
return x.get() == y.get();
}
template <class T1, class D1, class T2, class D2>
bool
operator!=(const uniq_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
return x.get() != y.get();
}
template <class T1, class D1, class T2, class D2>
bool
operator==(const std::unique_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
return x.get() == y.get();
}
template <class T1, class D1, class T2, class D2>
bool
operator!=(const std::unique_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
return x.get() != y.get();
}
template <class T1, class D1, class T2, class D2>
bool
operator==(const uniq_ptr<T1, D1> &x, const std::unique_ptr<T1, D1> &y) {
return x.get() == y.get();
}
template <class T1, class D1, class T2, class D2>
bool
operator!=(const uniq_ptr<T1, D1> &x, const std::unique_ptr<T1, D1> &y) {
return x.get() != y.get();
}
template <class T, class D>
bool
operator==(const uniq_ptr<T, D> &x, std::nullptr_t) {
return !(bool) x;
}
template <class T, class D>
bool
operator!=(const uniq_ptr<T, D> &x, std::nullptr_t) {
return (bool) x;
}
template <class T, class D>
bool
operator==(std::nullptr_t, const uniq_ptr<T, D> &y) {
return !(bool) y;
}
template <class T, class D>
bool
operator!=(std::nullptr_t, const uniq_ptr<T, D> &y) {
return (bool) y;
}
template <class P>
using shared_t = std::shared_ptr<typename P::element_type>;
template <class P, class T>
shared_t<P>
make_shared(T *pointer) {
return shared_t<P>(reinterpret_cast<typename P::pointer>(pointer), typename P::deleter_type());
}
template <class T>
class wrap_ptr {
public:
using element_type = T;
using pointer = element_type *;
using const_pointer = element_type const *;
using reference = element_type &;
using const_reference = element_type const &;
wrap_ptr():
_own_ptr { false }, _p { nullptr } {}
wrap_ptr(pointer p):
_own_ptr { false }, _p { p } {}
wrap_ptr(std::unique_ptr<element_type> &&uniq_p):
_own_ptr { true }, _p { uniq_p.release() } {}
wrap_ptr(wrap_ptr &&other):
_own_ptr { other._own_ptr }, _p { other._p } {
other._own_ptr = false;
}
wrap_ptr &
operator=(wrap_ptr &&other) noexcept {
if (_own_ptr) {
delete _p;
}
_p = other._p;
_own_ptr = other._own_ptr;
other._own_ptr = false;
return *this;
}
template <class V>
wrap_ptr &
operator=(std::unique_ptr<V> &&uniq_ptr) {
static_assert(std::is_base_of_v<element_type, V>, "element_type must be base class of V");
_own_ptr = true;
_p = uniq_ptr.release();
return *this;
}
wrap_ptr &
operator=(pointer p) {
if (_own_ptr) {
delete _p;
}
_p = p;
_own_ptr = false;
return *this;
}
~wrap_ptr() {
if (_own_ptr) {
delete _p;
}
_own_ptr = false;
}
const_reference
operator*() const {
return *_p;
}
reference
operator*() {
return *_p;
}
const_pointer
operator->() const {
return _p;
}
pointer
operator->() {
return _p;
}
private:
bool _own_ptr;
pointer _p;
};
template <class T>
constexpr bool is_pointer_v =
instantiation_of_v<std::unique_ptr, T> ||
instantiation_of_v<std::shared_ptr, T> ||
instantiation_of_v<uniq_ptr, T> ||
std::is_pointer_v<T>;
template <class T, class V = void>
struct __false_v;
template <class T>
struct __false_v<T, std::enable_if_t<instantiation_of_v<std::optional, T>>> {
static constexpr std::nullopt_t value = std::nullopt;
};
template <class T>
struct __false_v<T, std::enable_if_t<is_pointer_v<T>>> {
static constexpr std::nullptr_t value = nullptr;
};
template <class T>
struct __false_v<T, std::enable_if_t<std::is_same_v<T, bool>>> {
static constexpr bool value = false;
};
template <class T>
static constexpr auto false_v = __false_v<T>::value;
template <class T>
using optional_t = either_t<
(std::is_same_v<T, bool> || is_pointer_v<T>),
T, std::optional<T>>;
template <class T>
class buffer_t {
public:
buffer_t():
_els { 0 } {};
buffer_t(buffer_t &&o) noexcept:
_els { o._els }, _buf { std::move(o._buf) } {
o._els = 0;
}
buffer_t(const buffer_t &o):
_els { o._els }, _buf { std::make_unique<T[]>(_els) } {
std::copy(o.begin(), o.end(), begin());
}
buffer_t &
operator=(buffer_t &&o) noexcept {
std::swap(_els, o._els);
std::swap(_buf, o._buf);
return *this;
};
explicit buffer_t(size_t elements):
_els { elements }, _buf { std::make_unique<T[]>(elements) } {}
explicit buffer_t(size_t elements, const T &t):
_els { elements }, _buf { std::make_unique<T[]>(elements) } {
std::fill_n(_buf.get(), elements, t);
}
T &
operator[](size_t el) {
return _buf[el];
}
const T &
operator[](size_t el) const {
return _buf[el];
}
size_t
size() const {
return _els;
}
void
fake_resize(std::size_t els) {
_els = els;
}
T *
begin() {
return _buf.get();
}
const T *
begin() const {
return _buf.get();
}
T *
end() {
return _buf.get() + _els;
}
const T *
end() const {
return _buf.get() + _els;
}
private:
size_t _els;
std::unique_ptr<T[]> _buf;
};
template <class T>
T
either(std::optional<T> &&l, T &&r) {
if (l) {
return std::move(*l);
}
return std::forward<T>(r);
}
template <class ReturnType, class... Args>
struct Function {
typedef ReturnType (*type)(Args...);
};
template <class T, class ReturnType, typename Function<ReturnType, T>::type function>
struct Destroy {
typedef T pointer;
void
operator()(pointer p) {
function(p);
}
};
template <class T, typename Function<void, T *>::type function>
using safe_ptr = uniq_ptr<T, Destroy<T *, void, function>>;
// You cannot specialize an alias
template <class T, class ReturnType, typename Function<ReturnType, T *>::type function>
using safe_ptr_v2 = uniq_ptr<T, Destroy<T *, ReturnType, function>>;
template <class T>
void
c_free(T *p) {
free(p);
}
template <class T, class ReturnType, ReturnType (**function)(T *)>
void
dynamic(T *p) {
(*function)(p);
}
template <class T, void (**function)(T *)>
using dyn_safe_ptr = safe_ptr<T, dynamic<T, void, function>>;
template <class T, class ReturnType, ReturnType (**function)(T *)>
using dyn_safe_ptr_v2 = safe_ptr<T, dynamic<T, ReturnType, function>>;
template <class T>
using c_ptr = safe_ptr<T, c_free<T>>;
template <class It>
std::string_view
view(It begin, It end) {
return std::string_view { (const char *) begin, (std::size_t)(end - begin) };
}
template <class T>
std::string_view
view(const T &data) {
return std::string_view((const char *) &data, sizeof(T));
}
namespace endian {
template <class T = void>
struct endianness {
enum : bool {
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
defined(__BIG_ENDIAN__) || \
defined(__ARMEB__) || \
defined(__THUMBEB__) || \
defined(__AARCH64EB__) || \
defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
little = false,
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
defined(__LITTLE_ENDIAN__) || \
defined(__ARMEL__) || \
defined(__THUMBEL__) || \
defined(__AARCH64EL__) || \
defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
defined(_WIN32)
// It's a little-endian target architecture
little = true,
#else
#error "Unknown Endianness"
#endif
big = !little
};
};
template <class T, class S = void>
struct endian_helper {};
template <class T>
struct endian_helper<T, std::enable_if_t<
!(instantiation_of_v<std::optional, T>)>> {
static inline T
big(T x) {
if constexpr (endianness<T>::little) {
uint8_t *data = reinterpret_cast<uint8_t *>(&x);
std::reverse(data, data + sizeof(x));
}
return x;
}
static inline T
little(T x) {
if constexpr (endianness<T>::big) {
uint8_t *data = reinterpret_cast<uint8_t *>(&x);
std::reverse(data, data + sizeof(x));
}
return x;
}
};
template <class T>
struct endian_helper<T, std::enable_if_t<
instantiation_of_v<std::optional, T>>> {
static inline T
little(T x) {
if (!x) return x;
if constexpr (endianness<T>::big) {
auto *data = reinterpret_cast<uint8_t *>(&*x);
std::reverse(data, data + sizeof(*x));
}
return x;
}
static inline T
big(T x) {
if (!x) return x;
if constexpr (endianness<T>::little) {
auto *data = reinterpret_cast<uint8_t *>(&*x);
std::reverse(data, data + sizeof(*x));
}
return x;
}
};
template <class T>
inline auto
little(T x) { return endian_helper<T>::little(x); }
template <class T>
inline auto
big(T x) { return endian_helper<T>::big(x); }
} // namespace endian
} // namespace util