net/addr/sock: Add classes for generating sockaddrs

This adds classes to generate IP socket information that can be
consumed by `connect()` and `bind()`.

Change-Id: I84c8fc3794d1113877f0f967291317152a0669d5
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/meson.build b/include/meson.build
index c541513..f9eff41 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -14,6 +14,7 @@
   'stdplus/hash/tuple.hpp',
   'stdplus/net/addr/ether.hpp',
   'stdplus/net/addr/ip.hpp',
+  'stdplus/net/addr/sock.hpp',
   'stdplus/net/addr/subnet.hpp',
   'stdplus/numeric/endian.hpp',
   'stdplus/numeric/str.hpp',
diff --git a/include/stdplus/net/addr/sock.hpp b/include/stdplus/net/addr/sock.hpp
new file mode 100644
index 0000000..9a0238d
--- /dev/null
+++ b/include/stdplus/net/addr/sock.hpp
@@ -0,0 +1,164 @@
+#pragma once
+#include <netinet/in.h>
+
+#include <stdplus/net/addr/ip.hpp>
+#include <stdplus/numeric/endian.hpp>
+#include <stdplus/variant.hpp>
+
+#include <array>
+#include <cstdint>
+
+namespace stdplus
+{
+
+struct SockAddrBuf : sockaddr
+{
+    static inline constexpr std::size_t maxLen =
+        std::max({sizeof(sockaddr_in), sizeof(sockaddr_in6)}) -
+        sizeof(sockaddr);
+    std::array<std::uint8_t, maxLen> buf;
+    std::uint8_t len;
+    static_assert(maxLen <= std::numeric_limits<decltype(len)>::max());
+};
+
+template <typename T>
+struct IsSockAddr : std::false_type
+{};
+
+template <typename T>
+concept SockAddr = IsSockAddr<T>::value;
+
+struct Sock4Addr
+{
+    In4Addr addr;
+    std::uint16_t port;
+
+    constexpr bool operator==(Sock4Addr rhs) const noexcept
+    {
+        return addr == rhs.addr && port == rhs.port;
+    }
+
+    constexpr sockaddr_in sockaddr() const noexcept
+    {
+        return {.sin_family = AF_INET,
+                .sin_port = stdplus::hton(port),
+                .sin_addr = addr,
+                .sin_zero = {}};
+    }
+    constexpr operator sockaddr_in() const noexcept
+    {
+        return sockaddr();
+    }
+
+    static constexpr std::size_t sockaddrLen() noexcept
+    {
+        return sizeof(sockaddr_in);
+    }
+
+    constexpr SockAddrBuf buf() const noexcept
+    {
+        SockAddrBuf ret;
+        reinterpret_cast<sockaddr_in&>(ret) = sockaddr();
+        ret.len = sockaddrLen();
+        return ret;
+    }
+    constexpr operator SockAddrBuf() const noexcept
+    {
+        return buf();
+    }
+};
+
+template <>
+struct IsSockAddr<Sock4Addr> : std::true_type
+{};
+
+struct Sock6Addr
+{
+    In6Addr addr;
+    std::uint16_t port;
+    std::uint32_t scope;
+
+    constexpr bool operator==(Sock6Addr rhs) const noexcept
+    {
+        return addr == rhs.addr && port == rhs.port && scope == rhs.scope;
+    }
+
+    constexpr sockaddr_in6 sockaddr() const noexcept
+    {
+        return {.sin6_family = AF_INET6,
+                .sin6_port = stdplus::hton(port),
+                .sin6_flowinfo = 0,
+                .sin6_addr = addr,
+                .sin6_scope_id = scope};
+    }
+    constexpr operator sockaddr_in6() const noexcept
+    {
+        return sockaddr();
+    }
+
+    static constexpr std::size_t sockaddrLen() noexcept
+    {
+        return sizeof(sockaddr_in6);
+    }
+
+    constexpr SockAddrBuf buf() const noexcept
+    {
+        SockAddrBuf ret;
+        reinterpret_cast<sockaddr_in6&>(ret) = sockaddr();
+        ret.len = sockaddrLen();
+        return ret;
+    }
+    constexpr operator SockAddrBuf() const noexcept
+    {
+        return buf();
+    }
+};
+
+template <>
+struct IsSockAddr<Sock6Addr> : std::true_type
+{};
+
+namespace detail
+{
+
+using SockAnyAddrV = std::variant<Sock4Addr, Sock6Addr>;
+
+} // namespace detail
+
+struct SockAnyAddr : detail::SockAnyAddrV
+{
+    constexpr SockAnyAddr(auto&&... a) noexcept :
+        detail::SockAnyAddrV(std::forward<decltype(a)>(a)...)
+    {}
+
+    template <SockAddr T>
+    constexpr bool operator==(const T& rhs) const noexcept
+    {
+        return variantEqFuzzy(*this, rhs);
+    }
+
+    constexpr SockAddrBuf sockaddr() const noexcept
+    {
+        return std::visit([](const auto& t) { return t.buf(); }, *this);
+    }
+
+    constexpr std::size_t sockaddrLen() const noexcept
+    {
+        return std::visit([](const auto& t) { return t.sockaddrLen(); }, *this);
+    }
+
+    constexpr SockAddrBuf buf() const noexcept
+    {
+        return sockaddr();
+    }
+    constexpr operator SockAddrBuf() const noexcept
+    {
+        return sockaddr();
+    }
+};
+
+template <>
+struct IsSockAddr<SockAnyAddr> : std::true_type
+{};
+
+} // namespace stdplus