net/addr/ip: Add basic IP comparison and hash ops

Change-Id: I79ab9c225ff031da7df0c788078f2a8e5c486990
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/meson.build b/include/meson.build
index 8b8b1dd..6d02351 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -9,6 +9,7 @@
   'stdplus/hash.hpp',
   'stdplus/hash/array.hpp',
   'stdplus/hash/tuple.hpp',
+  'stdplus/net/addr/ip.hpp',
   'stdplus/numeric/endian.hpp',
   'stdplus/pinned.hpp',
   'stdplus/raw.hpp',
diff --git a/include/stdplus/net/addr/ip.hpp b/include/stdplus/net/addr/ip.hpp
new file mode 100644
index 0000000..71bb7f6
--- /dev/null
+++ b/include/stdplus/net/addr/ip.hpp
@@ -0,0 +1,90 @@
+#pragma once
+#include <netinet/in.h>
+
+#include <stdplus/hash.hpp>
+
+#include <algorithm>
+#include <variant>
+
+namespace stdplus
+{
+namespace detail
+{
+struct In4AddrInner
+{
+    union
+    {
+        in_addr a;
+        uint32_t s4_addr32;
+        uint8_t s4_addr[4];
+    };
+};
+static_assert(sizeof(In4AddrInner) == sizeof(in_addr));
+} // namespace detail
+
+struct In4Addr : detail::In4AddrInner
+{
+    constexpr In4Addr() noexcept : detail::In4AddrInner() {}
+    constexpr In4Addr(in_addr a) noexcept : detail::In4AddrInner({a}) {}
+    explicit constexpr In4Addr(std::initializer_list<uint8_t> a) noexcept :
+        detail::In4AddrInner()
+    {
+        std::copy(a.begin(), a.end(), s4_addr);
+    }
+
+    constexpr operator in_addr() const noexcept
+    {
+        return a;
+    }
+
+    constexpr bool operator==(in_addr rhs) const noexcept
+    {
+        return a.s_addr == rhs.s_addr;
+    }
+
+    constexpr bool operator==(In4Addr rhs) const noexcept
+    {
+        return a.s_addr == rhs.a.s_addr;
+    }
+};
+
+struct In6Addr : in6_addr
+{
+    constexpr In6Addr() noexcept : in6_addr() {}
+    constexpr In6Addr(in6_addr a) noexcept : in6_addr(a) {}
+    explicit constexpr In6Addr(std::initializer_list<uint8_t> a) noexcept :
+        in6_addr()
+    {
+        std::copy(a.begin(), a.end(), s6_addr);
+    }
+
+    constexpr bool operator==(in6_addr rhs) const noexcept
+    {
+        return std::equal(s6_addr32, s6_addr32 + 4, rhs.s6_addr32);
+    }
+
+    constexpr bool operator==(In6Addr rhs) const noexcept
+    {
+        return *this == static_cast<in6_addr&>(rhs);
+    }
+};
+
+} // namespace stdplus
+
+template <>
+struct std::hash<stdplus::In4Addr>
+{
+    constexpr std::size_t operator()(in_addr addr) const noexcept
+    {
+        return stdplus::hashMulti(addr.s_addr);
+    }
+};
+
+template <>
+struct std::hash<stdplus::In6Addr>
+{
+    constexpr std::size_t operator()(in6_addr addr) const noexcept
+    {
+        return stdplus::hashMulti(addr.s6_addr32);
+    }
+};