430 lines
11 KiB
C++
430 lines
11 KiB
C++
#pragma once
|
||
|
||
|
||
#include <type_traits>
|
||
#include <algorithm>
|
||
#include <stdexcept>
|
||
#include <cstddef>
|
||
#include <utility>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <uv.h>
|
||
|
||
|
||
namespace uvw {
|
||
|
||
|
||
namespace details {
|
||
|
||
|
||
enum class UVHandleType: std::underlying_type_t<uv_handle_type> {
|
||
UNKNOWN = UV_UNKNOWN_HANDLE,
|
||
ASYNC = UV_ASYNC,
|
||
CHECK = UV_CHECK,
|
||
FS_EVENT = UV_FS_EVENT,
|
||
FS_POLL = UV_FS_POLL,
|
||
HANDLE = UV_HANDLE,
|
||
IDLE = UV_IDLE,
|
||
PIPE = UV_NAMED_PIPE,
|
||
POLL = UV_POLL,
|
||
PREPARE = UV_PREPARE,
|
||
PROCESS = UV_PROCESS,
|
||
STREAM = UV_STREAM,
|
||
TCP = UV_TCP,
|
||
TIMER = UV_TIMER,
|
||
TTY = UV_TTY,
|
||
UDP = UV_UDP,
|
||
SIGNAL = UV_SIGNAL,
|
||
FILE = UV_FILE
|
||
};
|
||
|
||
|
||
template<typename T>
|
||
struct UVTypeWrapper {
|
||
using Type = T;
|
||
|
||
constexpr UVTypeWrapper(Type val): value{val} { }
|
||
constexpr operator Type() const noexcept { return value; }
|
||
|
||
private:
|
||
const Type value;
|
||
};
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Utility class to handle flags.
|
||
*
|
||
* This class can be used to handle flags of a same enumeration type.<br/>
|
||
* It is meant to be used as an argument for functions and member methods and
|
||
* as part of events.<br/>
|
||
* `Flags<E>` objects can be easily _or-ed_ and _and-ed_ with other instances of
|
||
* the same type or with instances of the type `E` (that is, the actual flag
|
||
* type), thus converted to the underlying type when needed.
|
||
*/
|
||
template<typename E>
|
||
class Flags final {
|
||
using InnerType = std::underlying_type_t<E>;
|
||
|
||
constexpr InnerType toInnerType(E flag) const noexcept { return static_cast<InnerType>(flag); }
|
||
|
||
public:
|
||
using Type = InnerType;
|
||
|
||
/**
|
||
* @brief Constructs a Flags object from a value of the enum `E`.
|
||
* @param flag An value of the enum `E`.
|
||
*/
|
||
constexpr Flags(E flag) noexcept: flags{toInnerType(flag)} { }
|
||
|
||
/**
|
||
* @brief Constructs a Flags object from an instance of the underlying type
|
||
* of the enum `E`.
|
||
* @param f An instance of the underlying type of the enum `E`.
|
||
*/
|
||
constexpr Flags(Type f): flags{f} { }
|
||
|
||
/**
|
||
* @brief Constructs an uninitialized Flags object.
|
||
*/
|
||
constexpr Flags(): flags{} { }
|
||
|
||
constexpr Flags(const Flags &f) noexcept: flags{f.flags} { }
|
||
constexpr Flags(Flags &&f) noexcept: flags{std::move(f.flags)} { }
|
||
|
||
~Flags() noexcept { static_assert(std::is_enum<E>::value, "!"); }
|
||
|
||
constexpr Flags& operator=(const Flags &f) noexcept {
|
||
flags = f.flags;
|
||
return *this;
|
||
}
|
||
|
||
constexpr Flags& operator=(Flags &&f) noexcept {
|
||
flags = std::move(f.flags);
|
||
return *this;
|
||
}
|
||
|
||
/**
|
||
* @brief Or operator.
|
||
* @param f A valid instance of Flags.
|
||
* @return This instance _or-ed_ with `f`.
|
||
*/
|
||
constexpr Flags operator|(const Flags &f) const noexcept { return Flags(flags | f.flags); }
|
||
|
||
/**
|
||
* @brief Or operator.
|
||
* @param flag A value of the enum `E`.
|
||
* @return This instance _or-ed_ with `flag`.
|
||
*/
|
||
constexpr Flags operator|(E flag) const noexcept { return Flags(flags | toInnerType(flag)); }
|
||
|
||
/**
|
||
* @brief And operator.
|
||
* @param f A valid instance of Flags.
|
||
* @return This instance _and-ed_ with `f`.
|
||
*/
|
||
constexpr Flags operator&(const Flags &f) const noexcept { return Flags(flags & f.flags); }
|
||
|
||
/**
|
||
* @brief And operator.
|
||
* @param flag A value of the enum `E`.
|
||
* @return This instance _and-ed_ with `flag`.
|
||
*/
|
||
constexpr Flags operator&(E flag) const noexcept { return Flags(flags & toInnerType(flag)); }
|
||
|
||
/**
|
||
* @brief Checks if this instance is initialized.
|
||
* @return False if it's uninitialized, true otherwise.
|
||
*/
|
||
explicit constexpr operator bool() const noexcept { return !(flags == InnerType{}); }
|
||
|
||
/**
|
||
* @brief Casts the instance to the underlying type of `E`.
|
||
* @return An integral representation of the contained flags.
|
||
*/
|
||
constexpr operator Type() const noexcept { return flags; }
|
||
|
||
private:
|
||
InnerType flags;
|
||
};
|
||
|
||
|
||
/**
|
||
* @brief Windows size representation.
|
||
*/
|
||
struct WinSize {
|
||
int width; /*!< The _width_ of the given window. */
|
||
int height; /*!< The _height_ of the given window. */
|
||
};
|
||
|
||
|
||
using HandleType = details::UVHandleType;
|
||
|
||
using FileHandle = details::UVTypeWrapper<uv_file>;
|
||
using OSSocketHandle = details::UVTypeWrapper<uv_os_sock_t>;
|
||
using OSFileDescriptor = details::UVTypeWrapper<uv_os_fd_t>;
|
||
|
||
using TimeSpec = uv_timespec_t;
|
||
using Stat = uv_stat_t;
|
||
using Uid = uv_uid_t;
|
||
using Gid = uv_gid_t;
|
||
|
||
|
||
/**
|
||
* @brief The IPv4 tag.
|
||
*
|
||
* To be used as template parameter to switch between IPv4 and IPv6.
|
||
*/
|
||
struct IPv4 { };
|
||
|
||
|
||
/**
|
||
* @brief The IPv6 tag.
|
||
*
|
||
* To be used as template parameter to switch between IPv4 and IPv6.
|
||
*/
|
||
struct IPv6 { };
|
||
|
||
|
||
/**
|
||
* @brief Address representation.
|
||
*/
|
||
struct Addr {
|
||
std::string ip; /*!< Either an IPv4 or an IPv6. */
|
||
unsigned int port; /*!< A valid service identifier. */
|
||
};
|
||
|
||
|
||
/**
|
||
* \brief Interface address.
|
||
*/
|
||
struct Interface {
|
||
std::string name; /*!< The name of the interface (as an example _eth0_). */
|
||
std::string physical; /*!< The physical address. */
|
||
bool internal; /*!< True if it is an internal interface (as an example _loopback_), false otherwise. */
|
||
Addr address; /*!< The address of the given interface. */
|
||
Addr netmask; /*!< The netmask of the given interface. */
|
||
};
|
||
|
||
|
||
namespace details {
|
||
|
||
|
||
template<typename>
|
||
struct IpTraits;
|
||
|
||
|
||
template<>
|
||
struct IpTraits<IPv4> {
|
||
using Type = sockaddr_in;
|
||
using AddrFuncType = int(*)(const char *, int, Type *);
|
||
using NameFuncType = int(*)(const Type *, char *, std::size_t);
|
||
static constexpr AddrFuncType addrFunc = &uv_ip4_addr;
|
||
static constexpr NameFuncType nameFunc = &uv_ip4_name;
|
||
static constexpr auto sinPort(const Type *addr) { return addr->sin_port; }
|
||
};
|
||
|
||
|
||
template<>
|
||
struct IpTraits<IPv6> {
|
||
using Type = sockaddr_in6;
|
||
using AddrFuncType = int(*)(const char *, int, Type *);
|
||
using NameFuncType = int(*)(const Type *, char *, std::size_t);
|
||
static constexpr AddrFuncType addrFunc = &uv_ip6_addr;
|
||
static constexpr NameFuncType nameFunc = &uv_ip6_name;
|
||
static constexpr auto sinPort(const Type *addr) { return addr->sin6_port; }
|
||
};
|
||
|
||
|
||
template<typename I, typename..., std::size_t N = 128>
|
||
Addr address(const typename details::IpTraits<I>::Type *aptr) noexcept {
|
||
Addr addr;
|
||
char name[N];
|
||
|
||
int err = details::IpTraits<I>::nameFunc(aptr, name, N);
|
||
|
||
if(0 == err) {
|
||
addr.port = ntohs(details::IpTraits<I>::sinPort(aptr));
|
||
addr.ip = std::string{name};
|
||
}
|
||
|
||
return addr;
|
||
}
|
||
|
||
|
||
template<typename I, typename F, typename H>
|
||
Addr address(F &&f, const H *handle) noexcept {
|
||
sockaddr_storage ssto;
|
||
int len = sizeof(ssto);
|
||
Addr addr{};
|
||
|
||
int err = std::forward<F>(f)(handle, reinterpret_cast<sockaddr *>(&ssto), &len);
|
||
|
||
if(0 == err) {
|
||
typename IpTraits<I>::Type *aptr = reinterpret_cast<typename IpTraits<I>::Type *>(&ssto);
|
||
addr = address<I>(aptr);
|
||
}
|
||
|
||
return addr;
|
||
}
|
||
|
||
|
||
template<typename F, typename H, typename..., std::size_t N = 128>
|
||
std::string path(F &&f, H *handle) noexcept {
|
||
std::size_t size = N;
|
||
char buf[size];
|
||
std::string str{};
|
||
auto err = std::forward<F>(f)(handle, buf, &size);
|
||
|
||
if(UV_ENOBUFS == err) {
|
||
std::unique_ptr<char[]> data{new char[size]};
|
||
err = std::forward<F>(f)(handle, data.get(), &size);
|
||
|
||
if(0 == err) {
|
||
str = data.get();
|
||
}
|
||
} else {
|
||
str.assign(buf, size);
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Miscellaneous utilities.
|
||
*
|
||
* Miscellaneous functions that don’t really belong to any other class.
|
||
*/
|
||
struct Utilities {
|
||
/**
|
||
* @brief Gets the type of the stream to be used with the given descriptor.
|
||
*
|
||
* Returns the type of stream that should be used with a given file
|
||
* descriptor.<br/>
|
||
* Usually this will be used during initialization to guess the type of the
|
||
* stdio streams.
|
||
*
|
||
* @param file A valid descriptor.
|
||
* @return One of the following types:
|
||
*
|
||
* * `HandleType::UNKNOWN`
|
||
* * `HandleType::PIPE`
|
||
* * `HandleType::TCP`
|
||
* * `HandleType::TTY`
|
||
* * `HandleType::UDP`
|
||
* * `HandleType::FILE`
|
||
*/
|
||
static HandleType guessHandle(FileHandle file) {
|
||
auto type = uv_guess_handle(file);
|
||
|
||
switch(type) {
|
||
case UV_ASYNC:
|
||
return HandleType::ASYNC;
|
||
case UV_CHECK:
|
||
return HandleType::CHECK;
|
||
case UV_FS_EVENT:
|
||
return HandleType::FS_EVENT;
|
||
case UV_FS_POLL:
|
||
return HandleType::FS_POLL;
|
||
case UV_HANDLE:
|
||
return HandleType::HANDLE;
|
||
case UV_IDLE:
|
||
return HandleType::IDLE;
|
||
case UV_NAMED_PIPE:
|
||
return HandleType::PIPE;
|
||
case UV_POLL:
|
||
return HandleType::POLL;
|
||
case UV_PREPARE:
|
||
return HandleType::PREPARE;
|
||
case UV_PROCESS:
|
||
return HandleType::PROCESS;
|
||
case UV_STREAM:
|
||
return HandleType::STREAM;
|
||
case UV_TCP:
|
||
return HandleType::TCP;
|
||
case UV_TIMER:
|
||
return HandleType::TIMER;
|
||
case UV_TTY:
|
||
return HandleType::TTY;
|
||
case UV_UDP:
|
||
return HandleType::UDP;
|
||
case UV_SIGNAL:
|
||
return HandleType::SIGNAL;
|
||
case UV_FILE:
|
||
return HandleType::FILE;
|
||
default:
|
||
return HandleType::UNKNOWN;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Gets a set of descriptors of all the available interfaces.
|
||
*
|
||
* This function can be used to query the underlying system and get a set of
|
||
* descriptors of all the available interfaces, either internal or not.
|
||
*
|
||
* @return A set of descriptors of all the available interfaces.
|
||
*/
|
||
static std::vector<Interface> interfaces() noexcept {
|
||
std::vector<Interface> interfaces;
|
||
|
||
uv_interface_address_t *ifaces;
|
||
int count;
|
||
|
||
uv_interface_addresses(&ifaces, &count);
|
||
|
||
std::for_each(ifaces, ifaces+count, [&interfaces](const auto &iface) {
|
||
Interface interface;
|
||
|
||
interface.name = iface.name;
|
||
interface.physical = iface.phys_addr;
|
||
interface.internal = iface.is_internal;
|
||
|
||
if(iface.address.address4.sin_family == AF_INET) {
|
||
interface.address = details::address<IPv4>(&iface.address.address4);
|
||
interface.netmask = details::address<IPv4>(&iface.netmask.netmask4);
|
||
} else if(iface.address.address4.sin_family == AF_INET6) {
|
||
interface.address = details::address<IPv6>(&iface.address.address6);
|
||
interface.netmask = details::address<IPv6>(&iface.netmask.netmask6);
|
||
}
|
||
|
||
interfaces.push_back(std::move(interface));
|
||
});
|
||
|
||
uv_free_interface_addresses(ifaces, count);
|
||
|
||
return interfaces;
|
||
}
|
||
};
|
||
|
||
|
||
/**
|
||
* TODO
|
||
*
|
||
* * uv_replace_allocator
|
||
* * uv_uptime
|
||
* * uv_getrusage
|
||
* * uv_cpu_info
|
||
* * uv_free_cpu_info
|
||
* * uv_loadavg
|
||
* * uv_exepath
|
||
* * uv_cwd
|
||
* * uv_chdir
|
||
* * uv_os_homedir
|
||
* * uv_os_tmpdir
|
||
* * uv_os_get_passwd
|
||
* * uv_os_free_passwd
|
||
* * uv_get_total_memory
|
||
* * uv_hrtime
|
||
*/
|
||
|
||
|
||
}
|