Commit a34214c4 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Support various operations on empty IPAddress and SocketAddress

Summary: [Folly] Support various operations on empty `IPAddress` and `SocketAddress`, i.e. default-constructed instances, such as `operator==` and `hash`. V.s. throwing exceptions.

Reviewed By: simpkins

Differential Revision: D15750100

fbshipit-source-id: 89853635f6aab3c295f6fca4e66e6435b0839357
parent 3fe63e48
......@@ -373,6 +373,9 @@ uint8_t IPAddress::getNthMSByte(size_t byteIndex) const {
// public
bool operator==(const IPAddress& addr1, const IPAddress& addr2) {
if (addr1.empty() || addr2.empty()) {
return addr1.empty() == addr2.empty();
}
if (addr1.family() == addr2.family()) {
if (addr1.isV6()) {
return (addr1.asV6() == addr2.asV6());
......@@ -403,6 +406,9 @@ bool operator==(const IPAddress& addr1, const IPAddress& addr2) {
}
bool operator<(const IPAddress& addr1, const IPAddress& addr2) {
if (addr1.empty() || addr2.empty()) {
return addr1.empty() < addr2.empty();
}
if (addr1.family() == addr2.family()) {
if (addr1.isV6()) {
return (addr1.asV6() < addr2.asV6());
......
......@@ -29,6 +29,7 @@
#include <folly/IPAddressV6.h>
#include <folly/Range.h>
#include <folly/detail/IPAddress.h>
#include <folly/lang/Exception.h>
namespace folly {
......@@ -70,7 +71,68 @@ class IPAddress {
private:
template <typename F>
auto pick(F f) const {
return isV4() ? f(asV4()) : f(asV6());
return isV4() ? f(asV4()) : isV6() ? f(asV6()) : f(asNone());
}
class IPAddressNone {
public:
bool isZero() const {
return true;
}
size_t bitCount() const {
return 0;
}
std::string toJson() const {
return "{family:'AF_UNSPEC', addr:'', hash:0}";
}
std::size_t hash() const {
return std::hash<uint64_t>{}(0);
}
bool isLoopback() const {
throw_exception<InvalidAddressFamilyException>("empty address");
}
bool isLinkLocal() const {
throw_exception<InvalidAddressFamilyException>("empty address");
}
bool isLinkLocalBroadcast() const {
throw_exception<InvalidAddressFamilyException>("empty address");
}
bool isNonroutable() const {
throw_exception<InvalidAddressFamilyException>("empty address");
}
bool isPrivate() const {
throw_exception<InvalidAddressFamilyException>("empty address");
}
bool isMulticast() const {
throw_exception<InvalidAddressFamilyException>("empty address");
}
IPAddress mask(uint8_t numBits) const {
(void)numBits;
return IPAddress();
}
std::string str() const {
return "";
}
std::string toFullyQualified() const {
return "";
}
void toFullyQualifiedAppend(std::string& out) const {
(void)out;
return;
}
uint8_t version() const {
return 0;
}
const unsigned char* bytes() const {
return nullptr;
}
};
IPAddressNone const& asNone() const {
if (!empty()) {
throw_exception<InvalidAddressFamilyException>("not empty");
}
return addr_.ipNoneAddr;
}
public:
......@@ -427,7 +489,7 @@ class IPAddress {
return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); });
}
// Address version (4 or 6)
// Address version (0 if empty, or 4 or 6 if nonempty)
uint8_t version() const {
return pick([&](auto& _) { return _.version(); });
}
......@@ -444,14 +506,10 @@ class IPAddress {
[[noreturn]] void asV6Throw() const;
typedef union IPAddressV46 {
std::aligned_storage<
constexpr_max(sizeof(IPAddressV4), sizeof(IPAddressV6)),
constexpr_max(alignof(IPAddressV4), alignof(IPAddressV6))>::type
storage;
IPAddressNone ipNoneAddr;
IPAddressV4 ipV4Addr;
IPAddressV6 ipV6Addr;
// default constructor
IPAddressV46() noexcept : storage() {}
IPAddressV46() noexcept : ipNoneAddr() {}
explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {}
explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {}
} IPAddressV46;
......
......@@ -537,10 +537,11 @@ bool SocketAddress::operator==(const SocketAddress& other) const {
case AF_INET:
case AF_INET6:
return (other.storage_.addr == storage_.addr) && (other.port_ == port_);
case AF_UNSPEC:
return other.storage_.addr.empty();
default:
throw std::invalid_argument(
"SocketAddress: unsupported address family "
"for comparison");
throw_exception<std::invalid_argument>(
"SocketAddress: unsupported address family for comparison");
}
}
......@@ -589,10 +590,12 @@ size_t SocketAddress::hash() const {
assert(external_);
break;
case AF_UNSPEC:
assert(storage_.addr.empty());
boost::hash_combine(seed, storage_.addr.hash());
break;
default:
throw std::invalid_argument(
"SocketAddress: unsupported address family "
"for hashing");
throw_exception<std::invalid_argument>(
"SocketAddress: unsupported address family for comparison");
}
return seed;
......
......@@ -419,6 +419,11 @@ TEST(IPAddress, CtorDefault) {
EXPECT_EQ(IPAddressV4("0.0.0.0"), v4);
IPAddressV6 v6;
EXPECT_EQ(IPAddressV6("::0"), v6);
IPAddress v0;
EXPECT_EQ(IPAddress(), v0);
EXPECT_NE(v0, v4);
EXPECT_NE(v0, v6);
EXPECT_NE(v4, v6);
}
TEST(IPAddressV4, validate) {
......@@ -543,6 +548,14 @@ TEST(IPAddress, CtorSockaddr) {
EXPECT_THROW(IPAddress((sockaddr*)&addr), IPAddressFormatException);
}
// test none address
{
IPAddress ipAddr;
EXPECT_TRUE(ipAddr.empty());
EXPECT_FALSE(ipAddr.isV4());
EXPECT_FALSE(ipAddr.isV6());
EXPECT_EQ("", ipAddr.str());
}
}
TEST(IPAddress, ToSockaddrStorage) {
......
......@@ -197,6 +197,11 @@ TEST(SocketAddress, SetFromStrings) {
}
TEST(SocketAddress, EqualityAndHash) {
SocketAddress empty1;
SocketAddress empty2;
EXPECT_EQ(empty1, empty2);
EXPECT_EQ(empty1.hash(), empty2.hash());
// IPv4
SocketAddress local1("127.0.0.1", 1234);
EXPECT_EQ(local1, local1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment