result->append(addr.str());
}
-bool IPAddress::validate(StringPiece ip) {
+bool IPAddress::validate(StringPiece ip) noexcept {
return IPAddressV4::validate(ip) || IPAddressV6::validate(ip);
}
}
}
+Expected<IPAddress, IPAddressFormatError> IPAddress::tryFromBinary(
+ ByteRange bytes) noexcept {
+ // Check IPv6 first since it's our main protocol.
+ if (bytes.size() == 16) {
+ return IPAddressV6::tryFromBinary(bytes);
+ } else if (bytes.size() == 4) {
+ return IPAddressV4::tryFromBinary(bytes);
+ } else {
+ return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY);
+ }
+}
+
// public static
IPAddress IPAddress::fromLong(uint32_t src) {
return IPAddress(IPAddressV4::fromLong(src));
IPAddress::IPAddress() : addr_(), family_(AF_UNSPEC) {}
// public string constructor
-IPAddress::IPAddress(StringPiece addr) : addr_(), family_(AF_UNSPEC) {
- string ip = addr.str(); // inet_pton() needs NUL-terminated string
- auto throwFormatException = [&](const string& msg) {
- throw IPAddressFormatException(sformat("Invalid IP '{}': {}", ip, msg));
- };
-
- if (ip.size() < 2) {
- throwFormatException("address too short");
- }
- if (ip.front() == '[' && ip.back() == ']') {
- ip = ip.substr(1, ip.size() - 2);
+IPAddress::IPAddress(StringPiece str) : addr_(), family_(AF_UNSPEC) {
+ auto maybeIp = tryFromString(str);
+ if (maybeIp.hasError()) {
+ throw IPAddressFormatException(
+ to<std::string>("Invalid IP address '", str, "'"));
}
+ *this = std::move(maybeIp.value());
+}
+Expected<IPAddress, IPAddressFormatError> IPAddress::tryFromString(
+ StringPiece str) noexcept {
// need to check for V4 address second, since IPv4-mapped IPv6 addresses may
// contain a period
- if (ip.find(':') != string::npos) {
- struct addrinfo* result;
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET6;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_NUMERICHOST;
- if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) {
- struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr;
- addr_ = IPAddressV46(IPAddressV6(*ipAddr));
- family_ = AF_INET6;
- freeaddrinfo(result);
- } else {
- throwFormatException("getsockaddr failed for V6 address");
- }
- } else if (ip.find('.') != string::npos) {
- in_addr ipAddr;
- if (inet_pton(AF_INET, ip.c_str(), &ipAddr) != 1) {
- throwFormatException("inet_pton failed for V4 address");
- }
- addr_ = IPAddressV46(IPAddressV4(ipAddr));
- family_ = AF_INET;
+ if (str.find(':') != string::npos) {
+ return IPAddressV6::tryFromString(str);
+ } else if (str.find('.') != string::npos) {
+ return IPAddressV4::tryFromString(str);
} else {
- throwFormatException("invalid address format");
+ return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY);
}
}
}
// public ipv4 constructor
-IPAddress::IPAddress(const IPAddressV4 ipV4Addr)
+IPAddress::IPAddress(const IPAddressV4 ipV4Addr) noexcept
: addr_(ipV4Addr), family_(AF_INET) {}
// public ipv4 constructor
-IPAddress::IPAddress(const in_addr ipV4Addr)
+IPAddress::IPAddress(const in_addr ipV4Addr) noexcept
: addr_(IPAddressV4(ipV4Addr)), family_(AF_INET) {}
// public ipv6 constructor
-IPAddress::IPAddress(const IPAddressV6& ipV6Addr)
+IPAddress::IPAddress(const IPAddressV6& ipV6Addr) noexcept
: addr_(ipV6Addr), family_(AF_INET6) {}
// public ipv6 constructor
-IPAddress::IPAddress(const in6_addr& ipV6Addr)
+IPAddress::IPAddress(const in6_addr& ipV6Addr) noexcept
: addr_(IPAddressV6(ipV6Addr)), family_(AF_INET6) {}
// Assign from V4 address
-IPAddress& IPAddress::operator=(const IPAddressV4& ipv4_addr) {
+IPAddress& IPAddress::operator=(const IPAddressV4& ipv4_addr) noexcept {
addr_ = IPAddressV46(ipv4_addr);
family_ = AF_INET;
return *this;
}
// Assign from V6 address
-IPAddress& IPAddress::operator=(const IPAddressV6& ipv6_addr) {
+IPAddress& IPAddress::operator=(const IPAddressV6& ipv6_addr) noexcept {
addr_ = IPAddressV46(ipv6_addr);
family_ = AF_INET6;
return *this;
public:
// returns true iff the input string can be parsed as an ip-address
- static bool validate(StringPiece ip);
+ static bool validate(StringPiece ip) noexcept;
// return the V4 representation of the address, converting it from V6 to V4 if
// needed. Note that this will throw an IPAddressFormatException if the V6
*/
static IPAddress fromBinary(ByteRange bytes);
+ /**
+ * Non-throwing version of fromBinary().
+ * On failure returns IPAddressFormatError.
+ */
+ static Expected<IPAddress, IPAddressFormatError> tryFromBinary(
+ ByteRange bytes) noexcept;
+
+ /**
+ * Tries to create a new IPAddress instance from provided string and
+ * returns it on success. Returns IPAddressFormatError on failure.
+ */
+ static Expected<IPAddress, IPAddressFormatError> tryFromString(
+ StringPiece str) noexcept;
+
/**
* Create an IPAddress from a 32bit long (network byte order).
* @throws IPAddressFormatException
explicit IPAddress(const sockaddr* addr);
// Create an IPAddress from a V4 address
- /* implicit */ IPAddress(const IPAddressV4 ipV4Addr);
- /* implicit */ IPAddress(const in_addr addr);
+ /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept;
+ /* implicit */ IPAddress(const in_addr addr) noexcept;
// Create an IPAddress from a V6 address
- /* implicit */ IPAddress(const IPAddressV6& ipV6Addr);
- /* implicit */ IPAddress(const in6_addr& addr);
+ /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept;
+ /* implicit */ IPAddress(const in6_addr& addr) noexcept;
// Assign from V4 address
- IPAddress& operator=(const IPAddressV4& ipV4Addr);
+ IPAddress& operator=(const IPAddressV4& ipV4Addr) noexcept;
// Assign from V6 address
- IPAddress& operator=(const IPAddressV6& ipV6Addr);
+ IPAddress& operator=(const IPAddressV6& ipV6Addr) noexcept;
/**
* Converts an IPAddress to an IPAddressV4 instance.
* @note This is not some handy convenience wrapper to convert an IPv4 address
* to a mapped IPv6 address. If you want that use
* IPAddress::createIPv6(addr)
- * @throws IPAddressFormatException is not a V4 instance
+ * @throws InvalidAddressFamilyException is not a V4 instance
*/
const IPAddressV4& asV4() const {
if (UNLIKELY(!isV4())) {
IPAddressV4 ipV4Addr;
IPAddressV6 ipV6Addr;
// default constructor
- IPAddressV46() {
+ IPAddressV46() noexcept {
std::memset(this, 0, sizeof(IPAddressV46));
}
- explicit IPAddressV46(const IPAddressV4& addr) : ipV4Addr(addr) {}
- explicit IPAddressV46(const IPAddressV6& addr) : ipV6Addr(addr) {}
+ explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {}
+ explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {}
} IPAddressV46;
IPAddressV46 addr_;
sa_family_t family_;
namespace folly {
+/**
+ * Error codes for non-throwing interface of IPAddress family of functions.
+ */
+enum class IPAddressFormatError { INVALID_IP, UNSUPPORTED_ADDR_FAMILY };
+
/**
* Exception for invalid IP addresses.
*/
result->append(addr.str());
}
-bool IPAddressV4::validate(StringPiece ip) {
- constexpr size_t kStrMaxLen = INET_ADDRSTRLEN;
- std::array<char, kStrMaxLen + 1> ip_cstr;
- const size_t len = std::min(ip.size(), kStrMaxLen);
- std::memcpy(ip_cstr.data(), ip.data(), len);
- ip_cstr[len] = 0;
- struct in_addr addr;
- return 1 == inet_pton(AF_INET, ip_cstr.data(), &addr);
+bool IPAddressV4::validate(StringPiece ip) noexcept {
+ return tryFromString(ip).hasValue();
}
// public static
IPAddressV4::IPAddressV4() {}
// ByteArray4 constructor
-IPAddressV4::IPAddressV4(const ByteArray4& src) : addr_(src) {}
+IPAddressV4::IPAddressV4(const ByteArray4& src) noexcept : addr_(src) {}
// public string constructor
IPAddressV4::IPAddressV4(StringPiece addr) : addr_() {
- auto ip = addr.str();
- if (inet_pton(AF_INET, ip.c_str(), &addr_.inAddr_) != 1) {
- throw IPAddressFormatException(sformat("Invalid IPv4 address '{}'", addr));
+ auto maybeIp = tryFromString(addr);
+ if (maybeIp.hasError()) {
+ throw IPAddressFormatException(
+ to<std::string>("Invalid IPv4 address '", addr, "'"));
+ }
+ *this = std::move(maybeIp.value());
+}
+
+Expected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromString(
+ StringPiece str) noexcept {
+ struct in_addr inAddr;
+ if (inet_pton(AF_INET, str.str().c_str(), &inAddr) != 1) {
+ return makeUnexpected(IPAddressFormatError::INVALID_IP);
}
+ return IPAddressV4(inAddr);
}
// in_addr constructor
-IPAddressV4::IPAddressV4(const in_addr src) : addr_(src) {}
+IPAddressV4::IPAddressV4(const in_addr src) noexcept : addr_(src) {}
-// public
-void IPAddressV4::setFromBinary(ByteRange bytes) {
- if (bytes.size() != 4) {
- throw IPAddressFormatException(sformat(
- "Invalid IPv4 binary data: length must be 4 bytes, got {}",
+IPAddressV4 IPAddressV4::fromBinary(ByteRange bytes) {
+ auto maybeIp = tryFromBinary(bytes);
+ if (maybeIp.hasError()) {
+ throw IPAddressFormatException(to<std::string>(
+ "Invalid IPv4 binary data: length must be 4 bytes, got ",
bytes.size()));
}
+ return maybeIp.value();
+}
+
+Expected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromBinary(
+ ByteRange bytes) noexcept {
+ IPAddressV4 addr;
+ auto setResult = addr.trySetFromBinary(bytes);
+ if (setResult.hasError()) {
+ return makeUnexpected(std::move(setResult.error()));
+ }
+ return addr;
+}
+
+Expected<Unit, IPAddressFormatError> IPAddressV4::trySetFromBinary(
+ ByteRange bytes) noexcept {
+ if (bytes.size() != 4) {
+ return makeUnexpected(IPAddressFormatError::INVALID_IP);
+ }
memcpy(&addr_.inAddr_.s_addr, bytes.data(), sizeof(in_addr));
+ return folly::unit;
}
// static
// reverse 1.0.168.192 -> 192.168.0.1
return IPAddressV4(join(".", pieces.rbegin(), pieces.rend()));
}
-
-// public
IPAddressV6 IPAddressV4::createIPv6() const {
ByteArray16 ba{};
ba[10] = 0xff;
#include <functional>
#include <iosfwd>
+#include <folly/Expected.h>
#include <folly/FBString.h>
+#include <folly/IPAddressException.h>
#include <folly/Range.h>
#include <folly/detail/IPAddress.h>
#include <folly/hash/Hash.h>
4 /*words*/ * 3 /*max chars per word*/ + 3 /*separators*/;
// returns true iff the input string can be parsed as an ipv4-address
- static bool validate(StringPiece ip);
+ static bool validate(StringPiece ip) noexcept;
// create an IPAddressV4 instance from a uint32_t (network byte order)
static IPAddressV4 fromLong(uint32_t src);
* Create a new IPAddress instance from the provided binary data.
* @throws IPAddressFormatException if the input length is not 4 bytes.
*/
- static IPAddressV4 fromBinary(ByteRange bytes) {
- IPAddressV4 addr;
- addr.setFromBinary(bytes);
- return addr;
- }
+ static IPAddressV4 fromBinary(ByteRange bytes);
+
+ /**
+ * Non-throwing version of fromBinary().
+ * On failure returns IPAddressFormatError.
+ */
+ static Expected<IPAddressV4, IPAddressFormatError> tryFromBinary(
+ ByteRange bytes) noexcept;
+
+ /**
+ * Tries to create a new IPAddressV4 instance from provided string and
+ * returns it on success. Returns IPAddressFormatError on failure.
+ */
+ static Expected<IPAddressV4, IPAddressFormatError> tryFromString(
+ StringPiece str) noexcept;
/**
* Returns the address as a Range.
explicit IPAddressV4(StringPiece ip);
// ByteArray4 constructor
- explicit IPAddressV4(const ByteArray4& src);
+ explicit IPAddressV4(const ByteArray4& src) noexcept;
// in_addr constructor
- explicit IPAddressV4(const in_addr src);
+ explicit IPAddressV4(const in_addr src) noexcept;
// Return the V6 mapped representation of the address.
IPAddressV6 createIPv6() const;
/**
* Set the current IPAddressV4 object to have the address specified by bytes.
- * @throws IPAddressFormatException if bytes.size() is not 4.
+ * Returns IPAddressFormatError if bytes.size() is not 4.
*/
- void setFromBinary(ByteRange bytes);
+ Expected<Unit, IPAddressFormatError> trySetFromBinary(
+ ByteRange bytes) noexcept;
};
// boost::hash uses hash_value() so this allows boost::hash to work
result->append(addr.str());
}
-bool IPAddressV6::validate(StringPiece ip) {
- if (ip.size() > 0 && ip.front() == '[' && ip.back() == ']') {
- ip = ip.subpiece(1, ip.size() - 2);
- }
-
- constexpr size_t kStrMaxLen = INET6_ADDRSTRLEN;
- std::array<char, kStrMaxLen + 1> ip_cstr;
- const size_t len = std::min(ip.size(), kStrMaxLen);
- std::memcpy(ip_cstr.data(), ip.data(), len);
- ip_cstr[len] = 0;
- struct in6_addr addr;
- return 1 == inet_pton(AF_INET6, ip_cstr.data(), &addr);
+bool IPAddressV6::validate(StringPiece ip) noexcept {
+ return tryFromString(ip).hasValue();
}
// public default constructor
// public string constructor
IPAddressV6::IPAddressV6(StringPiece addr) {
- auto ip = addr.str();
+ auto maybeIp = tryFromString(addr);
+ if (maybeIp.hasError()) {
+ throw IPAddressFormatException(
+ to<std::string>("Invalid IPv6 address '", addr, "'"));
+ }
+ *this = std::move(maybeIp.value());
+}
+
+Expected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromString(
+ StringPiece str) noexcept {
+ auto ip = str.str();
// Allow addresses surrounded in brackets
if (ip.size() < 2) {
- throw IPAddressFormatException(
- sformat("Invalid IPv6 address '{}': address too short", ip));
+ return makeUnexpected(IPAddressFormatError::INVALID_IP);
}
if (ip.front() == '[' && ip.back() == ']') {
ip = ip.substr(1, ip.size() - 2);
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
- if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) {
- struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr;
- addr_.in6Addr_ = ipAddr->sin6_addr;
- scope_ = uint16_t(ipAddr->sin6_scope_id);
- freeaddrinfo(result);
- } else {
- throw IPAddressFormatException(sformat("Invalid IPv6 address '{}'", ip));
+ if (::getaddrinfo(ip.c_str(), nullptr, &hints, &result) == 0) {
+ SCOPE_EXIT {
+ ::freeaddrinfo(result);
+ };
+ const struct sockaddr_in6* sa =
+ reinterpret_cast<struct sockaddr_in6*>(result->ai_addr);
+ return IPAddressV6(*sa);
}
+ return makeUnexpected(IPAddressFormatError::INVALID_IP);
}
// in6_addr constructor
-IPAddressV6::IPAddressV6(const in6_addr& src) : addr_(src) {}
+IPAddressV6::IPAddressV6(const in6_addr& src) noexcept : addr_(src) {}
// sockaddr_in6 constructor
-IPAddressV6::IPAddressV6(const sockaddr_in6& src)
+IPAddressV6::IPAddressV6(const sockaddr_in6& src) noexcept
: addr_(src.sin6_addr), scope_(uint16_t(src.sin6_scope_id)) {}
// ByteArray16 constructor
-IPAddressV6::IPAddressV6(const ByteArray16& src) : addr_(src) {}
+IPAddressV6::IPAddressV6(const ByteArray16& src) noexcept : addr_(src) {}
// link-local constructor
IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) : addr_(mac) {}
return Optional<MacAddress>(MacAddress::fromBinary(range(bytes)));
}
-void IPAddressV6::setFromBinary(ByteRange bytes) {
- if (bytes.size() != 16) {
- throw IPAddressFormatException(sformat(
- "Invalid IPv6 binary data: length must be 16 bytes, got {}",
+IPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) {
+ auto maybeIp = tryFromBinary(bytes);
+ if (maybeIp.hasError()) {
+ throw IPAddressFormatException(to<std::string>(
+ "Invalid IPv6 binary data: length must be 16 bytes, got ",
bytes.size()));
}
+ return maybeIp.value();
+}
+
+Expected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromBinary(
+ ByteRange bytes) noexcept {
+ IPAddressV6 addr;
+ auto setResult = addr.trySetFromBinary(bytes);
+ if (setResult.hasError()) {
+ return makeUnexpected(std::move(setResult.error()));
+ }
+ return addr;
+}
+
+Expected<Unit, IPAddressFormatError> IPAddressV6::trySetFromBinary(
+ ByteRange bytes) noexcept {
+ if (bytes.size() != 16) {
+ return makeUnexpected(IPAddressFormatError::INVALID_IP);
+ }
memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr));
scope_ = 0;
+ return unit;
}
// static
#include <map>
#include <stdexcept>
+#include <folly/Expected.h>
#include <folly/FBString.h>
+#include <folly/IPAddressException.h>
#include <folly/Optional.h>
#include <folly/Range.h>
#include <folly/detail/IPAddress.h>
8 /*words*/ * 4 /*hex chars per word*/ + 7 /*separators*/;
// returns true iff the input string can be parsed as an ipv6-address
- static bool validate(StringPiece ip);
+ static bool validate(StringPiece ip) noexcept;
/**
* Create a new IPAddress instance from the provided binary data.
* @throws IPAddressFormatException if the input length is not 16 bytes.
*/
- static IPAddressV6 fromBinary(ByteRange bytes) {
- IPAddressV6 addr;
- addr.setFromBinary(bytes);
- return addr;
- }
+ static IPAddressV6 fromBinary(ByteRange bytes);
+
+ /**
+ * Non-throwing version of fromBinary().
+ * On failure returns IPAddressFormatError.
+ */
+ static Expected<IPAddressV6, IPAddressFormatError> tryFromBinary(
+ ByteRange bytes) noexcept;
+
+ /**
+ * Tries to create a new IPAddressV6 instance from provided string and
+ * returns it on success. Returns IPAddressFormatError on failure.
+ */
+ static Expected<IPAddressV6, IPAddressFormatError> tryFromString(
+ StringPiece str) noexcept;
/**
* Create a new IPAddress instance from the ip6.arpa representation.
explicit IPAddressV6(StringPiece ip);
// ByteArray16 constructor
- explicit IPAddressV6(const ByteArray16& src);
+ explicit IPAddressV6(const ByteArray16& src) noexcept;
// in6_addr constructor
- explicit IPAddressV6(const in6_addr& src);
+ explicit IPAddressV6(const in6_addr& src) noexcept;
// sockaddr_in6 constructor
- explicit IPAddressV6(const sockaddr_in6& src);
+ explicit IPAddressV6(const sockaddr_in6& src) noexcept;
/**
* Create a link-local IPAddressV6 from the specified ethernet MAC address.
/**
* Set the current IPAddressV6 object to have the address specified by bytes.
- * @throws IPAddressFormatException if bytes.size() is not 16.
+ * Returns IPAddressFormatError if bytes.size() is not 16.
*/
- void setFromBinary(ByteRange bytes);
+ Expected<Unit, IPAddressFormatError> trySetFromBinary(
+ ByteRange bytes) noexcept;
};
// boost::hash uses hash_value() so this allows boost::hash to work
}
}
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(ipv6_ctor_valid, iters) {
+ while (iters--) {
+ try {
+ IPAddressV6 ip("2803:6082:18e0:2c49:1a23:9ee0:5c87:9800");
+ doNotOptimizeAway(ip);
+ } catch (const IPAddressFormatException& ex) {
+ doNotOptimizeAway(ex);
+ }
+ }
+}
+
+BENCHMARK_RELATIVE(ipv6_ctor_invalid, iters) {
+ while (iters--) {
+ try {
+ IPAddressV6 ip("2803:6082:18e0:2c49:1a23:9ee0:5c87:980r");
+ doNotOptimizeAway(ip);
+ } catch (const IPAddressFormatException& ex) {
+ doNotOptimizeAway(ex);
+ }
+ }
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(ipv6_try_from_string_valid, iters) {
+ while (iters--) {
+ auto maybeIp =
+ IPAddressV6::tryFromString("2803:6082:18e0:2c49:1a23:9ee0:5c87:9800");
+ CHECK(maybeIp.hasValue());
+ doNotOptimizeAway(maybeIp);
+ doNotOptimizeAway(maybeIp.value());
+ }
+}
+
+BENCHMARK_RELATIVE(ipv6_try_from_string_invalid, iters) {
+ while (iters--) {
+ auto maybeIp =
+ IPAddressV6::tryFromString("2803:6082:18e0:2c49:1a23:9ee0:5c87:980r");
+ CHECK(maybeIp.hasError());
+ doNotOptimizeAway(maybeIp);
+ doNotOptimizeAway(maybeIp.error());
+ }
+}
+
// Benchmark results on Intel Xeon CPU E5-2660 @ 2.20GHz
// ============================================================================
// folly/test/IPAddressBenchmark.cpp relative time/iter iters/s
// ----------------------------------------------------------------------------
// ipv6_to_fully_qualified_port 150.76ns 6.63M
// ipv6_append_to_fully_qualified_port 178.73% 84.35ns 11.86M
+// ----------------------------------------------------------------------------
+// ipv6_ctor_valid 379.97ns 2.63M
+// ipv6_ctor_invalid 10.38% 3.66us 273.22K
+// ----------------------------------------------------------------------------
+// ipv6_try_from_string_valid 375.34ns 2.66M
+// ipv6_try_from_string_invalid 111.93% 335.34ns 2.98M
// ============================================================================
int main(int argc, char* argv[]) {
struct IPAddressByteAccessorTest : TestWithParam<AddressData> {};
struct IPAddressBitAccessorTest : TestWithParam<AddressData> {};
+struct StringTestParam {
+ std::string in;
+ folly::Optional<std::string> out;
+ folly::Optional<std::string> out4;
+ folly::Optional<std::string> out6;
+};
+
+struct TryFromStringTest : TestWithParam<StringTestParam> {
+ static std::vector<StringTestParam> ipInOutProvider() {
+ const std::string lo6{"::1"};
+ const std::string lo6brackets{"[::1]"};
+ const std::string ip6{"1234::abcd"};
+ const std::string invalid6{"[::aaaR]"};
+
+ const std::string lo4{"127.0.0.1"};
+ const std::string ip4{"192.168.0.1"};
+ const std::string invalid4{"127.0.0.256"};
+
+ const static std::vector<StringTestParam> ret = {
+ {lo6, lo6, none, lo6},
+ {lo6brackets, lo6, none, lo6},
+ {ip6, ip6, none, ip6},
+ {invalid6, none, none, none},
+ {lo4, lo4, lo4, none},
+ {ip4, ip4, ip4, none},
+ {invalid4, none, none, none},
+ };
+
+ return ret;
+ }
+};
+
// tests code example
TEST(IPAddress, CodeExample) {
EXPECT_EQ(4, sizeof(IPAddressV4));
}
}
+TEST_P(TryFromStringTest, IPAddress) {
+ auto param = GetParam();
+ auto maybeIp = IPAddress::tryFromString(param.in);
+ if (param.out) {
+ EXPECT_TRUE(maybeIp.hasValue());
+ EXPECT_EQ(param.out, maybeIp.value().str());
+ } else {
+ EXPECT_TRUE(maybeIp.hasError());
+ EXPECT_TRUE(
+ IPAddressFormatError::INVALID_IP == maybeIp.error() ||
+ IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY == maybeIp.error());
+ }
+}
+
+TEST_P(TryFromStringTest, IPAddressV4) {
+ auto param = GetParam();
+ auto maybeIp = IPAddressV4::tryFromString(param.in);
+ if (param.out4) {
+ EXPECT_TRUE(maybeIp.hasValue());
+ EXPECT_EQ(param.out4, maybeIp.value().str());
+ } else {
+ EXPECT_TRUE(maybeIp.hasError());
+ EXPECT_EQ(IPAddressFormatError::INVALID_IP, maybeIp.error());
+ }
+}
+
+TEST_P(TryFromStringTest, IPAddressV6) {
+ auto param = GetParam();
+ auto maybeIp = IPAddressV6::tryFromString(param.in);
+ if (param.out6) {
+ EXPECT_TRUE(maybeIp.hasValue());
+ EXPECT_EQ(param.out6, maybeIp.value().str());
+ } else {
+ EXPECT_TRUE(maybeIp.hasError());
+ EXPECT_EQ(IPAddressFormatError::INVALID_IP, maybeIp.error());
+ }
+}
+
TEST(IPAddress, ToString) {
// Test with IPAddressV4
IPAddressV4 addr_10_0_0_1("10.0.0.1");
<< "should have thrown an IPAddressFormatException";
}
-// Test that invalid binary values throw an exception
+// Test that invalid binary values throw or return an exception
TEST_P(IPAddressCtorBinaryTest, InvalidBinary) {
auto bin = GetParam();
- EXPECT_THROW(
- IPAddress::fromBinary(ByteRange(&bin[0], bin.size())),
- IPAddressFormatException);
+ auto byteRange = ByteRange(&bin[0], bin.size());
+ // Throwing versions.
+ EXPECT_THROW(IPAddress::fromBinary(byteRange), IPAddressFormatException);
+ EXPECT_THROW(IPAddressV4::fromBinary(byteRange), IPAddressFormatException);
+ EXPECT_THROW(IPAddressV6::fromBinary(byteRange), IPAddressFormatException);
+ // Non-throwing versions.
+ EXPECT_TRUE(IPAddress::tryFromBinary(byteRange).hasError());
+ EXPECT_TRUE(IPAddressV4::tryFromBinary(byteRange).hasError());
+ EXPECT_TRUE(IPAddressV6::tryFromBinary(byteRange).hasError());
}
TEST(IPAddressSource, ToHex) {
addr2 = IPAddressV4::fromBinary(bytes);
EXPECT_EQ(fromStr, addr2);
+ auto maybeAddr3 = IPAddressV4::tryFromBinary(bytes);
+ EXPECT_TRUE(maybeAddr3.hasValue());
+ EXPECT_EQ(fromStr, maybeAddr3.value());
+
IPAddress genericAddr = IPAddress::fromBinary(bytes);
ASSERT_TRUE(genericAddr.isV4());
EXPECT_EQ(fromStr, genericAddr.asV4());
addr2 = IPAddressV6::fromBinary(bytes);
EXPECT_EQ(fromStr, addr2);
+ auto maybeAddr3 = IPAddressV6::tryFromBinary(bytes);
+ EXPECT_TRUE(maybeAddr3.hasValue());
+ EXPECT_EQ(fromStr, maybeAddr3.value());
+
IPAddress genericAddr = IPAddress::fromBinary(bytes);
ASSERT_TRUE(genericAddr.isV6());
EXPECT_EQ(fromStr, genericAddr.asV6());
IPAddress,
IPAddressBitAccessorTest,
::testing::ValuesIn(validAddressProvider));
+INSTANTIATE_TEST_CASE_P(
+ IPAddress,
+ TryFromStringTest,
+ ::testing::ValuesIn(TryFromStringTest::ipInOutProvider()));
TEST(IPAddressV4, fetchMask) {
struct X : private IPAddressV4 {