/**
 * @file src/utility.h
 * @brief Declarations for utility functions.
 */
#pragma once

// standard includes
#include <algorithm>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <optional>
#include <ostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>

/**
 * @def KITTY_WHILE_LOOP(x, y, z)
 * @brief Execute an initializer followed by a while loop body without leaking helper names.
 */
#define KITTY_WHILE_LOOP(x, y, z) \
  { \
    x; \
    while (y) z \
  }

template<typename T>
struct argument_type;

/**
 * @brief Extracts the argument type from a single-argument function signature.
 */
template<typename T, typename U>
struct argument_type<T(U)> {
  /**
   * @brief Type extracted from the matched function signature.
   */
  typedef U type;
};

/**
 * @def KITTY_USING_MOVE_T(move_t, t, init_val, z)
 * @brief Define a move-only RAII wrapper with caller-provided initial value and destructor body.
 */
#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \
  /** \
   * @brief Move-only RAII wrapper generated by KITTY_USING_MOVE_T. \
   */ \
  class move_t { \
  public: \
    /** \
     * @brief Wrapped element type. \
     */ \
    using element_type = typename argument_type<void(t)>::type; \
\
    /** \
     * @brief Initialize the generated wrapper with the configured initial value. \
     */ \
    move_t(): \
        el {init_val} { \
    } \
    /** \
     * @brief Initialize the generated wrapper from forwarded element arguments. \
     * \
     * @param args Arguments forwarded to the wrapped element. \
     */ \
    template<class... Args> \
    move_t(Args &&...args): \
        el {std::forward<Args>(args)...} { \
    } \
    /** \
     * @brief Copy construction is disabled. \
     */ \
    move_t(const move_t &) = delete; \
\
    /** \
     * @brief Move-construct the wrapper and reset the source wrapper. \
     * \
     * @param other Wrapper to move from. \
     */ \
    move_t(move_t &&other) noexcept: \
        el {std::move(other.el)} { \
      other.el = element_type {init_val}; \
    } \
\
    /** \
     * @brief Copy assignment is disabled. \
     */ \
    move_t &operator=(const move_t &) = delete; \
\
    /** \
     * @brief Move-assign the wrapped element by swapping with another wrapper. \
     * \
     * @param other Wrapper to move from. \
     * @return This wrapper. \
     */ \
    move_t &operator=(move_t &&other) { \
      std::swap(el, other.el); \
      return *this; \
    } \
    /** \
     * @brief Access the wrapped element. \
     * \
     * @return Pointer to the wrapped element. \
     */ \
    element_type *operator->() { \
      return &el; \
    } \
    /** \
     * @brief Access the wrapped element. \
     * \
     * @return Pointer to the wrapped element. \
     */ \
    const element_type *operator->() const { \
      return &el; \
    } \
\
    /** \
     * @brief Release the wrapped element and reset the wrapper. \
     * \
     * @return Previously wrapped element after ownership is released. \
     */ \
    inline element_type release() { \
      element_type val = std::move(el); \
      el = element_type {init_val}; \
      return val; \
    } \
\
    /** \
     * @brief Destroy the wrapper and run the configured cleanup body. \
     */ \
    ~move_t() z \
\
      /** \
       * @brief Wrapped element value. \
       */ \
      element_type el; \
  }

/**
 * @def KITTY_DECL_CONSTR(x)
 * @brief Declare the standard move operations and out-of-line default constructor for a type.
 */
#define KITTY_DECL_CONSTR(x) \
  /** \
   * @brief Defaulted move constructor for the generated type. \
   */ \
  x(x &&) noexcept = default; \
  /** \
   * @brief Defaulted move assignment for the generated type. \
   * \
   * @return This instance. \
   */ \
  x &operator=(x &&) noexcept = default; \
  /** \
   * @brief Default constructor for the generated type. \
   */ \
  x();

/**
 * @def KITTY_DEFAULT_CONSTR_MOVE(x)
 * @brief Declare defaulted noexcept move construction and assignment for a type.
 */
#define KITTY_DEFAULT_CONSTR_MOVE(x) \
  /** \
   * @brief Defaulted move constructor for the generated type. \
   */ \
  x(x &&) noexcept = default; \
  /** \
   * @brief Defaulted move assignment for the generated type. \
   * \
   * @return This instance. \
   */ \
  x &operator=(x &&) noexcept = default;

/**
 * @def KITTY_DEFAULT_CONSTR_MOVE_THROW(x)
 * @brief Declare defaulted move construction and assignment that may throw.
 */
#define KITTY_DEFAULT_CONSTR_MOVE_THROW(x) \
  /** \
   * @brief Defaulted move constructor for the generated type. \
   */ \
  x(x &&) = default; \
  /** \
   * @brief Defaulted move assignment for the generated type. \
   * \
   * @return This instance. \
   */ \
  x &operator=(x &&) = default; \
  /** \
   * @brief Default constructor for the generated type. \
   */ \
  x() = default;

/**
 * @def KITTY_DEFAULT_CONSTR(x)
 * @brief Declare defaulted copy and move operations for a value type.
 */
#define KITTY_DEFAULT_CONSTR(x) \
  KITTY_DEFAULT_CONSTR_MOVE(x) \
  /** \
   * @brief Defaulted copy constructor for the generated type. \
   */ \
  x(const x &) noexcept = default; \
  /** \
   * @brief Defaulted copy assignment for the generated type. \
   * \
   * @return This instance. \
   */ \
  x &operator=(const x &) = default;

/**
 * @def TUPLE_2D(a, b, expr)
 * @brief Evaluate an expression returning a 2-tuple and bind both elements to local references.
 */
#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)

/**
 * @def TUPLE_2D_REF(a, b, expr)
 * @brief Bind both elements of an existing 2-tuple expression to local references.
 */
#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)

/**
 * @def TUPLE_3D(a, b, c, expr)
 * @brief Evaluate an expression returning a 3-tuple and bind all elements to local references.
 */
#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)

/**
 * @def TUPLE_3D_REF(a, b, c, expr)
 * @brief Bind all elements of an existing 3-tuple expression to local references.
 */
#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)

/**
 * @def TUPLE_EL(a, b, expr)
 * @brief Evaluate a tuple expression and bind one selected element to a local reference.
 */
#define TUPLE_EL(a, b, expr) \
  decltype(expr) a##_ = expr; \
  auto &a = std::get<b>(a##_)

/**
 * @def TUPLE_EL_REF(a, b, expr)
 * @brief Bind one selected element of an existing tuple expression to a local reference.
 */
#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;

  /**
   * @brief Type selector that chooses the first type when the condition is true.
   */
  template<class X, class Y>
  struct __either<true, X, Y> {
    /**
     * @brief Type extracted from the matched function signature.
     */
    using type = X;
  };

  /**
   * @brief Type selector that chooses the second type when the condition is false.
   */
  template<class X, class Y>
  struct __either<false, X, Y> {
    /**
     * @brief Type extracted from the matched function signature.
     */
    using type = Y;
  };

  /**
   * @brief Compile-time selector for one of two types.
   */
  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()...;
  };
  /**
   * @brief Combine multiple callables into one overload set for std::visit.
   */
  template<class... Ts>
  overloaded(Ts...) -> overloaded<Ts...>;

  /**
   * @brief Scope guard that runs a cleanup action unless it is disabled.
   */
  template<class T>
  class FailGuard {
  public:
    FailGuard() = delete;

    /**
     * @brief Construct a fail guard that owns the cleanup callback.
     *
     * @param f Callable executed by the helper.
     */
    FailGuard(T &&f) noexcept:
        _func {std::forward<T>(f)} {
    }

    /**
     * @brief Construct a fail guard that owns the cleanup callback.
     *
     * @param other Source object whose state is copied or moved into this object.
     */
    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();
      }
    }

    /**
     * @brief Disable the fail guard so destruction will not run the cleanup action.
     */
    void disable() {
      failure = false;
    }

    bool failure {true};  ///< Whether the fail guard should run its cleanup action.

  private:
    T _func;
  };

  /**
   * @brief Create a scope guard that runs unless it is disabled.
   *
   * @param f Callable executed by the helper.
   * @return Fail guard object that invokes the callable on scope exit.
   */
  template<class T>
  [[nodiscard]] auto fail_guard(T &&f) {
    return FailGuard<T> {std::forward<T>(f)};
  }

  /**
   * @brief Append the raw bytes of a trivially-copyable structure to a byte buffer.
   *
   * @param buf Destination buffer used for protocol serialization.
   * @param _struct Structure instance whose in-memory bytes should be appended.
   */
  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]);
    }
  }

  /**
   * @brief Formatter that exposes a value's bytes as hexadecimal text.
   */
  template<class T>
  class Hex {
  public:
    /**
     * @brief Integral type used for one formatted hex element.
     */
    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:
    /**
     * @brief Construct a hexadecimal byte view for the supplied value.
     *
     * @param elem Element value being serialized as bytes.
     * @param rev Whether bytes should be emitted in reverse order.
     */
    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];
        }
      }
    }

    /**
     * @brief Return an iterator to the first byte in the buffer view.
     *
     * @return Iterator to the first element.
     */
    char *begin() {
      return _hex;
    }

    /**
     * @brief Return an iterator one past the final byte in the buffer view.
     *
     * @return Iterator one past the last element.
     */
    char *end() {
      return _hex + sizeof(elem_type) * 2;
    }

    /**
     * @brief Return an iterator to the first byte in the buffer view.
     *
     * @return Iterator to the first element.
     */
    const char *begin() const {
      return _hex;
    }

    /**
     * @brief Return an iterator one past the final byte in the buffer view.
     *
     * @return Iterator one past the last element.
     */
    const char *end() const {
      return _hex + sizeof(elem_type) * 2;
    }

    /**
     * @brief Return a const iterator to the first byte in the view.
     *
     * @return Pointer to the first formatted hexadecimal character.
     */
    const char *cbegin() const {
      return _hex;
    }

    /**
     * @brief Return a const iterator one past the last byte in the view.
     *
     * @return Pointer one past the last formatted hexadecimal character.
     */
    const char *cend() const {
      return _hex + sizeof(elem_type) * 2;
    }

    /**
     * @brief Convert to string.
     *
     * @return Value converted to string.
     */
    std::string to_string() const {
      return {begin(), end()};
    }

    /**
     * @brief Convert to string view.
     *
     * @return Value converted to string view.
     */
    std::string_view to_string_view() const {
      return {begin(), sizeof(elem_type) * 2};
    }
  };

  /**
   * @brief Serialize an element as hexadecimal text.
   *
   * @param elem Element value being serialized as bytes.
   * @param rev Whether bytes should be emitted in reverse order.
   * @return Lightweight hexadecimal formatter for the input object.
   */
  template<class T>
  Hex<T> hex(const T &elem, bool rev = false) {
    return Hex<T>(elem, rev);
  }

  /**
   * @brief Format a value as hexadecimal text for logging.
   *
   * @param value Value whose bytes should be logged in hexadecimal.
   * @return Hexadecimal string representation of the input object.
   */
  template<typename T>
  std::string log_hex(const T &value) {
    return "0x" + Hex<T>(value, false).to_string();
  }

  /**
   * @brief Convert a value to a vector of hexadecimal bytes.
   *
   * @param begin Iterator or pointer marking the start of the input range.
   * @param end Iterator or pointer marking the end of the input range.
   * @param rev Whether bytes should be emitted in reverse order.
   * @return Hexadecimal string for the byte range.
   */
  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;
  }

  /**
   * @brief Parse hexadecimal text into a byte vector.
   *
   * @param c Character or context value being converted or released.
   * @param rev Whether bytes should be emitted in reverse order.
   * @return Hexadecimal string for the contiguous container.
   */
  template<class C>
  std::string hex_vec(C &&c, bool rev = false) {
    return hex_vec(std::begin(c), std::end(c), rev);
  }

  /**
   * @brief Convert from hex.
   *
   * @param hex Hexadecimal text to decode.
   * @param rev Whether bytes should be emitted in reverse order.
   * @return Value converted from hex.
   */
  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);
  }

  /**
   * @brief Convert from hex vec.
   *
   * @param hex Hexadecimal text to decode.
   * @param rev Whether bytes should be emitted in reverse order.
   * @return Value converted from hex vec.
   */
  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;
  }

  /**
   * @brief Hash functor that hashes the raw bytes of trivially-copyable values.
   */
  template<class T>
  class hash {
  public:
    /**
     * @brief Value type accepted by the hash functor.
     */
    using value_type = T;

    /**
     * @brief Hash a value by viewing its object representation as bytes.
     *
     * @param value Value whose raw bytes are hashed.
     * @return Hash value for the byte representation.
     */
    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)});
    }
  };

  /**
   * @brief Convert between enum values and their string names.
   *
   * @param val Value assigned to the synchronized object.
   * @return Const reference to the enum's underlying integral storage.
   */
  template<class T>
  auto enm(const T &val) -> const std::underlying_type_t<T> & {
    return *reinterpret_cast<const std::underlying_type_t<T> *>(&val);
  }

  /**
   * @brief Convert between enum values and their string names.
   *
   * @param val Value assigned to the synchronized object.
   * @return Mutable reference to the enum's underlying integral storage.
   */
  template<class T>
  auto enm(T &val) -> std::underlying_type_t<T> & {
    return *reinterpret_cast<std::underlying_type_t<T> *>(&val);
  }

  /**
   * @brief Convert from chars.
   *
   * @param begin Iterator or pointer marking the start of the input range.
   * @param end Iterator or pointer marking the end of the input range.
   * @return Value converted from chars.
   */
  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;
  }

  /**
   * @brief Convert from view.
   *
   * @param number Integer value to serialize or convert.
   * @return Value converted from view.
   */
  inline std::int64_t from_view(const std::string_view &number) {
    return from_chars(std::begin(number), std::end(number));
  }

  /**
   * @brief Tagged storage for one of two possible value types.
   */
  template<class X, class Y>
  class Either: public std::variant<std::monostate, X, Y> {
  public:
    using std::variant<std::monostate, X, Y>::variant;

    /**
     * @brief Check whether left.
     *
     * @return True when the comparison condition is satisfied.
     */
    constexpr bool has_left() const {
      return std::holds_alternative<X>(*this);
    }

    /**
     * @brief Check whether right.
     *
     * @return True when the comparison condition is satisfied.
     */
    constexpr bool has_right() const {
      return std::holds_alternative<Y>(*this);
    }

    /**
     * @brief Return the left-hand alternative held by the variant.
     *
     * @return Mutable reference to the left-hand alternative.
     */
    X &left() {
      return std::get<X>(*this);
    }

    /**
     * @brief Return the right-hand alternative held by the variant.
     *
     * @return Mutable reference to the right-hand alternative.
     */
    Y &right() {
      return std::get<Y>(*this);
    }

    /**
     * @brief Return the left-hand alternative held by the variant.
     *
     * @return Const reference to the left-hand alternative.
     */
    const X &left() const {
      return std::get<X>(*this);
    }

    /**
     * @brief Return the right-hand alternative held by the variant.
     *
     * @return Const reference to the right-hand alternative.
     */
    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
  /**
   * @brief Unique pointer wrapper with customizable pointer and deleter types.
   */
  template<typename T, typename D = std::default_delete<T>>
  class uniq_ptr {
  public:
    /**
     * @brief Object type managed by the unique pointer wrapper.
     */
    using element_type = T;
    /**
     * @brief Pointer type stored by the unique pointer wrapper.
     */
    using pointer = element_type *;
    /**
     * @brief Const pointer type exposed by the unique pointer wrapper.
     */
    using const_pointer = element_type const *;
    /**
     * @brief Callable type used to release the managed pointer.
     */
    using deleter_type = D;

    constexpr uniq_ptr() noexcept:
        _p {nullptr} {
    }

    /**
     * @brief Construct a unique ownership wrapper.
     */
    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;

    /**
     * @brief Construct a unique ownership wrapper.
     *
     * @param p Pointer passed to the deleter or conversion helper.
     */
    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");
    }

    /**
     * @brief Construct a unique ownership wrapper.
     *
     * @param uniq Unique pointer whose owned value is transferred.
     */
    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");
    }

    /**
     * @brief Construct a unique ownership wrapper.
     *
     * @param other Source object whose state is copied or moved into this object.
     */
    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");
    }

    /**
     * @brief Assign state from another instance while preserving ownership semantics.
     *
     * @param other Source object whose state is copied or moved into this object.
     * @return Reference returned by the operator overload.
     */
    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;
    }

    /**
     * @brief Assign state from another instance while preserving ownership semantics.
     *
     * @param uniq Unique pointer whose owned value is transferred.
     * @return Reference returned by the operator overload.
     */
    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();
    }

    /**
     * @brief Reset the object to its initial empty state.
     *
     * @param p Pointer passed to the deleter or conversion helper.
     */
    void reset(pointer p = pointer()) {
      if (_p) {
        _deleter(_p);
      }

      _p = p;
    }

    /**
     * @brief Release the COM or platform reference owned by the pointer.
     *
     * @return Reference count or status returned after releasing the object.
     */
    pointer release() {
      auto tmp = _p;
      _p = nullptr;
      return tmp;
    }

    /**
     * @brief Return the currently wrapped value or handle.
     *
     * @return Underlying native handle or object pointer.
     */
    pointer get() {
      return _p;
    }

    /**
     * @brief Return the currently wrapped value or handle.
     *
     * @return Underlying native handle or object pointer.
     */
    const_pointer get() const {
      return _p;
    }

    /**
     * @brief Dereference the managed pointer.
     *
     * @return Const reference to the pointed-to object.
     */
    std::add_lvalue_reference_t<element_type const> operator*() const {
      return *_p;
    }

    /**
     * @brief Dereference the managed pointer.
     *
     * @return Mutable reference to the pointed-to object.
     */
    std::add_lvalue_reference_t<element_type> operator*() {
      return *_p;
    }

    /**
     * @brief Access members of the managed pointer.
     *
     * @return Const pointer to the managed object.
     */
    const_pointer operator->() const {
      return _p;
    }

    /**
     * @brief Access members of the managed pointer.
     *
     * @return Mutable pointer to the managed object.
     */
    pointer operator->() {
      return _p;
    }

    /**
     * @brief Expose the stored pointer address for C APIs that write handles.
     *
     * @return Address of the stored pointer.
     */
    pointer *operator&() const {
      return &_p;
    }

    /**
     * @brief Expose the stored pointer address for C APIs that write handles.
     *
     * @return Address of the stored pointer.
     */
    pointer *operator&() {
      return &_p;
    }

    /**
     * @brief Return the deleter used when resetting the wrapped pointer.
     *
     * @return Mutable deleter stored by the wrapper.
     */
    deleter_type &get_deleter() {
      return _deleter;
    }

    /**
     * @brief Return the deleter used when resetting the wrapped pointer.
     *
     * @return Const deleter stored by the wrapper.
     */
    const deleter_type &get_deleter() const {
      return _deleter;
    }

    /**
     * @brief Check whether the wrapper currently owns a non-null pointer.
     */
    explicit operator bool() const {
      return _p != nullptr;
    }

  protected:
    pointer _p;  ///< Pointer currently owned by the wrapper.
    deleter_type _deleter;  ///< Callable used to release `_p`.
  };

  /**
   * @brief Compare two Sunshine unique pointers by their stored addresses.
   *
   * @param x Left-hand pointer or value being compared.
   * @param y Right-hand pointer or value being compared.
   * @return True when both wrappers store the same pointer address.
   */
  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();
  }

  /**
   * @brief Compare two Sunshine unique pointers by their stored addresses.
   *
   * @param x Left-hand pointer or value being compared.
   * @param y Right-hand pointer or value being compared.
   * @return True when the wrappers store different pointer addresses.
   */
  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();
  }

  /**
   * @brief Compare a standard unique pointer with a Sunshine unique pointer.
   *
   * @param x Left-hand pointer or value being compared.
   * @param y Right-hand pointer or value being compared.
   * @return True when both wrappers store the same pointer address.
   */
  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();
  }

  /**
   * @brief Compare a standard unique pointer with a Sunshine unique pointer.
   *
   * @param x Left-hand pointer or value being compared.
   * @param y Right-hand pointer or value being compared.
   * @return True when the wrappers store different pointer addresses.
   */
  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();
  }

  /**
   * @brief Compare a Sunshine unique pointer with a standard unique pointer.
   *
   * @param x Left-hand pointer or value being compared.
   * @param y Right-hand pointer or value being compared.
   * @return True when both wrappers store the same pointer address.
   */
  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();
  }

  /**
   * @brief Compare a Sunshine unique pointer with a standard unique pointer.
   *
   * @param x Left-hand pointer or value being compared.
   * @param y Right-hand pointer or value being compared.
   * @return True when the wrappers store different pointer addresses.
   */
  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();
  }

  /**
   * @brief Compare a Sunshine unique pointer with null.
   *
   * @param x Left-hand pointer or value being compared.
   * @return True when the wrapper does not own a pointer.
   */
  template<class T, class D>
  bool operator==(const uniq_ptr<T, D> &x, std::nullptr_t) {
    return !(bool) x;
  }

  /**
   * @brief Compare a Sunshine unique pointer with null.
   *
   * @param x Left-hand pointer or value being compared.
   * @return True when the wrapper owns a pointer.
   */
  template<class T, class D>
  bool operator!=(const uniq_ptr<T, D> &x, std::nullptr_t) {
    return (bool) x;
  }

  /**
   * @brief Compare null with a Sunshine unique pointer.
   *
   * @param y Right-hand pointer or value being compared.
   * @return True when the wrapper does not own a pointer.
   */
  template<class T, class D>
  bool operator==(std::nullptr_t, const uniq_ptr<T, D> &y) {
    return !(bool) y;
  }

  /**
   * @brief Compare null with a Sunshine unique pointer.
   *
   * @param y Right-hand pointer or value being compared.
   * @return True when the wrapper owns a pointer.
   */
  template<class T, class D>
  bool operator!=(std::nullptr_t, const uniq_ptr<T, D> &y) {
    return (bool) y;
  }

  /**
   * @brief Shared pointer type matching a safe pointer wrapper's element type.
   */
  template<class P>
  using shared_t = std::shared_ptr<typename P::element_type>;

  /**
   * @brief Create a shared object or message.
   *
   * @param pointer Raw pointer adopted by the safe pointer wrapper.
   * @return Constructed shared object.
   */
  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());
  }

  /**
   * @brief Pointer wrapper that may borrow or own the pointee.
   */
  template<class T>
  class wrap_ptr {
  public:
    /**
     * @brief Object type referenced by the pointer wrapper.
     */
    using element_type = T;
    /**
     * @brief Mutable pointer type exposed by the pointer wrapper.
     */
    using pointer = element_type *;
    /**
     * @brief Const pointer type exposed by the pointer wrapper.
     */
    using const_pointer = element_type const *;
    /**
     * @brief Mutable reference type exposed by the pointer wrapper.
     */
    using reference = element_type &;
    /**
     * @brief Const reference type exposed by the pointer wrapper.
     */
    using const_reference = element_type const &;

    wrap_ptr():
        _own_ptr {false},
        _p {nullptr} {
    }

    /**
     * @brief Construct an owning or non-owning wrapper around a raw pointer.
     *
     * @param p Pointer passed to the deleter or conversion helper.
     */
    wrap_ptr(pointer p):
        _own_ptr {false},
        _p {p} {
    }

    /**
     * @brief Construct an owning or non-owning wrapper around a raw pointer.
     *
     * @param uniq_p Uniq p.
     */
    wrap_ptr(std::unique_ptr<element_type> &&uniq_p):
        _own_ptr {true},
        _p {uniq_p.release()} {
    }

    /**
     * @brief Construct an owning or non-owning wrapper around a raw pointer.
     *
     * @param other Source object whose state is copied or moved into this object.
     */
    wrap_ptr(wrap_ptr &&other):
        _own_ptr {other._own_ptr},
        _p {other._p} {
      other._own_ptr = false;
    }

    /**
     * @brief Assign state from another instance while preserving ownership semantics.
     *
     * @param other Source object whose state is copied or moved into this object.
     * @return Reference returned by the operator overload.
     */
    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;
    }

    /**
     * @brief Assign state from another instance while preserving ownership semantics.
     *
     * @param uniq_ptr Unique pointer whose ownership is transferred to the wrapper.
     * @return Reference returned by the operator overload.
     */
    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;
    }

    /**
     * @brief Assign state from another instance while preserving ownership semantics.
     *
     * @param p Pointer passed to the deleter or conversion helper.
     * @return Reference returned by the operator overload.
     */
    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;
    }

    /**
     * @brief Dereference the wrapped pointer.
     *
     * @return Const reference to the pointed-to object.
     */
    const_reference operator*() const {
      return *_p;
    }

    /**
     * @brief Dereference the wrapped pointer.
     *
     * @return Mutable reference to the pointed-to object.
     */
    reference operator*() {
      return *_p;
    }

    /**
     * @brief Access members of the wrapped pointer.
     *
     * @return Const pointer to the wrapped object.
     */
    const_pointer operator->() const {
      return _p;
    }

    /**
     * @brief Access members of the wrapped pointer.
     *
     * @return Mutable pointer to the wrapped object.
     */
    pointer operator->() {
      return _p;
    }

  private:
    bool _own_ptr;
    pointer _p;
  };

  template<class T>
  /**
   * @brief Trait value indicating whether the template argument is a pointer.
   */
  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;

  /**
   * @brief Helper specialization for optional values.
   */
  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;  ///< Value.
  };

  /**
   * @brief Enables an overload only when the provided type is a pointer.
   */
  template<class T>
  struct __false_v<T, std::enable_if_t<is_pointer_v<T>>> {
    static constexpr std::nullptr_t value = nullptr;  ///< Value.
  };

  /**
   * @brief Type trait comparing a value with a boolean template argument.
   */
  template<class T>
  struct __false_v<T, std::enable_if_t<std::is_same_v<T, bool>>> {
    static constexpr bool value = false;  ///< Trait value for the false specialization.
  };

  template<class T>
  static constexpr auto false_v = __false_v<T>::value;

  /**
   * @brief Optional trait value used by endian serialization helpers.
   */
  template<class T>
  using optional_t = either_t<
    (std::is_same_v<T, bool> || is_pointer_v<T>),
    T,
    std::optional<T>>;

  /**
   * @brief Owning contiguous buffer with an explicit logical element count.
   */
  template<class T>
  class buffer_t {
  public:
    buffer_t():
        _els {0} {};

    /**
     * @brief Construct an owning contiguous buffer.
     *
     * @param o Source object used for comparison or assignment.
     */
    buffer_t(buffer_t &&o) noexcept:
        _els {o._els},
        _buf {std::move(o._buf)} {
      o._els = 0;
    }

    /**
     * @brief Construct an owning contiguous buffer.
     *
     * @param o Source object used for comparison or assignment.
     */
    buffer_t(const buffer_t &o):
        _els {o._els},
        _buf {std::make_unique<T[]>(_els)} {
      std::copy(o.begin(), o.end(), begin());
    }

    /**
     * @brief Assign state from another instance while preserving ownership semantics.
     *
     * @param o Source object used for comparison or assignment.
     * @return Reference returned by the operator overload.
     */
    buffer_t &operator=(buffer_t &&o) noexcept {
      std::swap(_els, o._els);
      std::swap(_buf, o._buf);

      return *this;
    };

    /**
     * @brief Construct an owning contiguous buffer.
     *
     * @param elements Elements copied into the buffer.
     */
    explicit buffer_t(size_t elements):
        _els {elements},
        _buf {std::make_unique<T[]>(elements)} {
    }

    /**
     * @brief Construct an owning contiguous buffer.
     *
     * @param elements Elements copied into the buffer.
     * @param t Initial value used to populate the GPU buffer.
     */
    explicit buffer_t(size_t elements, const T &t):
        _els {elements},
        _buf {std::make_unique<T[]>(elements)} {
      std::fill_n(_buf.get(), elements, t);
    }

    /**
     * @brief Access an element in the owning buffer.
     *
     * @param el Zero-based element index.
     * @return Mutable reference to the requested element.
     */
    T &operator[](size_t el) {
      return _buf[el];
    }

    /**
     * @brief Access an element in the owning buffer.
     *
     * @param el Zero-based element index.
     * @return Const reference to the requested element.
     */
    const T &operator[](size_t el) const {
      return _buf[el];
    }

    /**
     * @brief Return the serialized size of the current object.
     *
     * @return Number of elements currently stored.
     */
    size_t size() const {
      return _els;
    }

    /**
     * @brief Update the logical element count without reallocating storage.
     *
     * @param els Elements used to initialize the buffer.
     */
    void fake_resize(std::size_t els) {
      _els = els;
    }

    /**
     * @brief Return an iterator to the first byte in the buffer view.
     *
     * @return Iterator to the first element.
     */
    T *begin() {
      return _buf.get();
    }

    /**
     * @brief Return an iterator to the first byte in the buffer view.
     *
     * @return Iterator to the first element.
     */
    const T *begin() const {
      return _buf.get();
    }

    /**
     * @brief Return an iterator one past the final byte in the buffer view.
     *
     * @return Iterator one past the last element.
     */
    T *end() {
      return _buf.get() + _els;
    }

    /**
     * @brief Return an iterator one past the final byte in the buffer view.
     *
     * @return Iterator one past the last element.
     */
    const T *end() const {
      return _buf.get() + _els;
    }

  private:
    size_t _els;
    std::unique_ptr<T[]> _buf;
  };

  /**
   * @brief Build an either value from a left-hand value.
   *
   * @param l Left-hand value used to construct the either object.
   * @param r Fallback value returned when the optional left-hand value is empty.
   * @return Left-hand optional value when present, otherwise the fallback.
   */
  template<class T>
  T either(std::optional<T> &&l, T &&r) {
    if (l) {
      return std::move(*l);
    }

    return std::forward<T>(r);
  }

  /**
   * @brief Callable wrapper used by utility metaprogramming helpers.
   */
  template<class ReturnType, class... Args>
  struct Function {
    /**
     * @brief Type extracted from the matched function signature.
     */
    typedef ReturnType (*type)(Args...);
  };

  /**
   * @brief Deleter adapter that destroys the wrapped pointer.
   */
  template<class T, class ReturnType, typename Function<ReturnType, T>::type function>
  struct Destroy {
    /**
     * @brief Pointer type accepted by this deleter.
     */
    typedef T pointer;

    /**
     * @brief Invoke the configured destroy function on a pointer.
     *
     * @param p Pointer to release.
     */
    void operator()(pointer p) {
      function(p);
    }
  };

  /**
   * @brief Unique pointer using a compile-time C-style destroy function.
   */
  template<class T, typename Function<void, T *>::type function>
  using safe_ptr = uniq_ptr<T, Destroy<T *, void, function>>;

  // You cannot specialize an alias
  /**
   * @brief Safe pointer wrapper for APIs whose release function returns a status value.
   */
  template<class T, class ReturnType, typename Function<ReturnType, T *>::type function>
  using safe_ptr_v2 = uniq_ptr<T, Destroy<T *, ReturnType, function>>;

  /**
   * @brief Release memory allocated by C APIs with `free`.
   *
   * @param p Pointer passed to the deleter or conversion helper.
   */
  template<class T>
  void c_free(T *p) {
    free(p);
  }

  /**
   * @brief Create a non-owning dynamic buffer view over contiguous memory.
   *
   * @param p Pointer passed to the deleter or conversion helper.
   */
  template<class T, class ReturnType, ReturnType (**function)(T *)>
  void dynamic(T *p) {
    (*function)(p);
  }

  /**
   * @brief Unique pointer using a runtime-provided destroy function.
   */
  template<class T, void (**function)(T *)>
  using dyn_safe_ptr = safe_ptr<T, dynamic<T, void, function>>;

  /**
   * @brief Safe pointer wrapper for runtime-resolved release functions with status returns.
   */
  template<class T, class ReturnType, ReturnType (**function)(T *)>
  using dyn_safe_ptr_v2 = safe_ptr<T, dynamic<T, ReturnType, function>>;

  /**
   * @brief Safe pointer wrapper for memory released by `std::free`.
   */
  template<class T>
  using c_ptr = safe_ptr<T, c_free<T>>;

  /**
   * @brief Read the current value without removing it from the queue.
   *
   * @param begin Iterator or pointer marking the start of the input range.
   * @param end Iterator or pointer marking the end of the input range.
   * @return String view spanning the iterator range.
   */
  template<class It>
  std::string_view view(It begin, It end) {
    return std::string_view {(const char *) begin, (std::size_t) (end - begin)};
  }

  /**
   * @brief Read the current value without removing it from the queue.
   *
   * @param data Payload or state data to serialize, deserialize, or forward.
   * @return String view over the contiguous object's bytes.
   */
  template<class T>
  std::string_view view(const T &data) {
    return std::string_view((const char *) &data, sizeof(T));
  }

  /**
   * @brief Two-dimensional integer point.
   */
  struct point_t {
    double x;  ///< X.
    double y;  ///< Y.

    /**
     * @brief Operator.
     */
    friend std::ostream &operator<<(std::ostream &os, const point_t &p) {
      return (os << "Point(x: " << p.x << ", y: " << p.y << ")");
    }
  };

  namespace endian {
    /**
     * @brief Describes the byte order used when serializing values.
     */
    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)
        little = true,  ///< little-endian target architecture
#else
  #error "Unknown Endianness"
#endif
        big = !little  ///< big-endian target architecture
      };
    };

    template<class T, class S = void>
    struct endian_helper {};

    /**
     * @brief Helper specialization for optional values.
     */
    template<class T>
    struct endian_helper<T, std::enable_if_t<!(instantiation_of_v<std::optional, T>)>> {
      /**
       * @brief Convert a value to or from big-endian byte order.
       *
       * @param x Value to convert.
       * @return Value represented in big-endian byte order.
       */
      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;
      }

      /**
       * @brief Convert a value to or from little-endian byte order.
       *
       * @param x Value to convert.
       * @return Value represented in little-endian byte order.
       */
      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;
      }
    };

    /**
     * @brief Helper specialization for optional values.
     */
    template<class T>
    struct endian_helper<T, std::enable_if_t<instantiation_of_v<std::optional, T>>> {
      /**
       * @brief Convert a value to or from little-endian byte order.
       *
       * @param x Pointer to convert.
       * @return Pointer with the pointed-to value represented in little-endian byte order.
       */
      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;
      }

      /**
       * @brief Convert a value to or from big-endian byte order.
       *
       * @param x Pointer to convert.
       * @return Pointer with the pointed-to value represented in big-endian byte order.
       */
      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;
      }
    };

    /**
     * @brief Convert a value to or from little-endian byte order.
     *
     * @param x Value or pointer to convert.
     * @return Input represented in little-endian byte order.
     */
    template<class T>
    inline auto little(T x) {
      return endian_helper<T>::little(x);
    }

    /**
     * @brief Convert a value to or from big-endian byte order.
     *
     * @param x Value or pointer to convert.
     * @return Input represented in big-endian byte order.
     */
    template<class T>
    inline auto big(T x) {
      return endian_helper<T>::big(x);
    }
  }  // namespace endian
}  // namespace util
