Commit 5203b6ba authored by Nick Terrell's avatar Nick Terrell Committed by Facebook Github Bot

Add toFullyQualifiedAppend() methods

Summary: A common use case for `IPAddress::toFullyQualified()` is to print a `<ip>:<port>` string. It is faster to reserve enough space for both beforehand than create 2 strings.

Reviewed By: yfeldblum

Differential Revision: D5224454

fbshipit-source-id: 4536f89a9d51d39dd9fd970c753ecb8ecced5d22
parent 9883bc37
......@@ -387,6 +387,12 @@ class IPAddress {
: asV6().toFullyQualified();
}
/// Same as toFullyQualified but append to an output string.
void toFullyQualifiedAppend(std::string& out) const {
return isV4() ? asV4().toFullyQualifiedAppend(out)
: asV6().toFullyQualifiedAppend(out);
}
// Address version (4 or 6)
uint8_t version() const {
return isV4() ? asV4().version()
......
......@@ -241,6 +241,11 @@ string IPAddressV4::str() const {
return detail::fastIpv4ToString(addr_.inAddr_);
}
// public
void IPAddressV4::toFullyQualifiedAppend(std::string& out) const {
detail::fastIpv4AppendToString(addr_.inAddr_, out);
}
// public
string IPAddressV4::toInverseArpaName() const {
return sformat(
......
......@@ -54,6 +54,10 @@ typedef std::array<uint8_t, 4> ByteArray4;
*/
class IPAddressV4 {
public:
// Max size of std::string returned by toFullyQualified.
static constexpr size_t kMaxToFullyQualifiedSize =
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);
......@@ -212,6 +216,9 @@ class IPAddressV4 {
// @see IPAddress#toFullyQualified
std::string toFullyQualified() const { return str(); }
// @see IPAddress#toFullyQualifiedAppend
void toFullyQualifiedAppend(std::string& out) const;
// @see IPAddress#version
uint8_t version() const { return 4; }
......
......@@ -425,6 +425,11 @@ string IPAddressV6::toFullyQualified() const {
return detail::fastIpv6ToString(addr_.in6Addr_);
}
// public
void IPAddressV6::toFullyQualifiedAppend(std::string& out) const {
detail::fastIpv6AppendToString(addr_.in6Addr_, out);
}
// public
string IPAddressV6::toInverseArpaName() const {
constexpr folly::StringPiece lut = "0123456789abcdef";
......
......@@ -281,6 +281,9 @@ class IPAddressV6 {
// @see IPAddress#toFullyQualified
std::string toFullyQualified() const;
// @see IPAddress#toFullyQualifiedAppend
void toFullyQualifiedAppend(std::string& out) const;
std::string toInverseArpaName() const;
// @see IPAddress#str
......
......@@ -218,9 +218,8 @@ inline void writeIntegerString(IntegralType val, char** buffer) {
*buffer = buf;
}
inline std::string fastIpv4ToString(const in_addr& inAddr) {
inline size_t fastIpV4ToBufferUnsafe(const in_addr& inAddr, char* str) {
const uint8_t* octets = reinterpret_cast<const uint8_t*>(&inAddr.s_addr);
char str[sizeof("255.255.255.255")];
char* buf = str;
writeIntegerString<uint8_t, 3>(octets[0], &buf);
......@@ -231,16 +230,25 @@ inline std::string fastIpv4ToString(const in_addr& inAddr) {
*(buf++) = '.';
writeIntegerString<uint8_t, 3>(octets[3], &buf);
return std::string(str, size_t(buf - str));
return buf - str;
}
inline std::string fastIpv6ToString(const in6_addr& in6Addr) {
inline std::string fastIpv4ToString(const in_addr& inAddr) {
char str[sizeof("255.255.255.255")];
return std::string(str, fastIpV4ToBufferUnsafe(inAddr, str));
}
inline void fastIpv4AppendToString(const in_addr& inAddr, std::string& out) {
char str[sizeof("255.255.255.255")];
out.append(str, fastIpV4ToBufferUnsafe(inAddr, str));
}
inline size_t fastIpv6ToBufferUnsafe(const in6_addr& in6Addr, char* str) {
#ifdef _MSC_VER
const uint16_t* bytes = reinterpret_cast<const uint16_t*>(&in6Addr.u.Word);
#else
const uint16_t* bytes = reinterpret_cast<const uint16_t*>(&in6Addr.s6_addr16);
#endif
char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")];
char* buf = str;
for (int i = 0; i < 8; ++i) {
......@@ -255,7 +263,17 @@ inline std::string fastIpv6ToString(const in6_addr& in6Addr) {
}
}
return std::string(str, size_t(buf - str));
return buf - str;
}
inline std::string fastIpv6ToString(const in6_addr& in6Addr) {
char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")];
return std::string(str, fastIpv6ToBufferUnsafe(in6Addr, str));
}
inline void fastIpv6AppendToString(const in6_addr& in6Addr, std::string& out) {
char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")];
out.append(str, fastIpv6ToBufferUnsafe(in6Addr, str));
}
}
}
......@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <folly/Conv.h>
#include <folly/IPAddress.h>
#include <glog/logging.h>
......@@ -46,6 +47,30 @@ BENCHMARK_RELATIVE(ipv4_to_fully_qualified, iters) {
BENCHMARK_DRAW_LINE()
BENCHMARK(ipv4_to_fully_qualified_port, iters) {
IPAddressV4 ip("255.255.255.255");
while (iters--) {
string outputString = to<std::string>(ip.toFullyQualified(), ':', 65535);
folly::doNotOptimizeAway(outputString);
folly::doNotOptimizeAway(outputString.data());
}
}
BENCHMARK_RELATIVE(ipv4_append_to_fully_qualified_port, iters) {
IPAddressV4 ip("255.255.255.255");
while (iters--) {
string outputString;
outputString.reserve(IPAddressV4::kMaxToFullyQualifiedSize + 1 + 5);
ip.toFullyQualifiedAppend(outputString);
outputString += ':';
folly::toAppend(65535, &outputString);
folly::doNotOptimizeAway(outputString);
folly::doNotOptimizeAway(outputString.data());
}
}
BENCHMARK_DRAW_LINE()
BENCHMARK(ipv6_to_string_inet_ntop, iters) {
IPAddressV6 ipv6Addr("F1E0:0ACE:FB94:7ADF:22E8:6DE6:9672:3725");
in6_addr ip = ipv6Addr.toAddr();
......@@ -69,15 +94,45 @@ BENCHMARK_RELATIVE(ipv6_to_fully_qualified, iters) {
}
}
BENCHMARK_DRAW_LINE()
BENCHMARK(ipv6_to_fully_qualified_port, iters) {
IPAddressV6 ip("F1E0:0ACE:FB94:7ADF:22E8:6DE6:9672:3725");
while (iters--) {
string outputString = to<std::string>(ip.toFullyQualified(), ':', 65535);
folly::doNotOptimizeAway(outputString);
folly::doNotOptimizeAway(outputString.data());
}
}
BENCHMARK_RELATIVE(ipv6_append_to_fully_qualified_port, iters) {
IPAddressV6 ip("F1E0:0ACE:FB94:7ADF:22E8:6DE6:9672:3725");
while (iters--) {
string outputString;
outputString.reserve(folly::IPAddressV6::kToFullyQualifiedSize + 1 + 5);
ip.toFullyQualifiedAppend(outputString);
outputString += ':';
folly::toAppend(65535, &outputString);
folly::doNotOptimizeAway(outputString);
folly::doNotOptimizeAway(outputString.data());
}
}
// Benchmark results on Intel Xeon CPU E5-2660 @ 2.20GHz
// ============================================================================
// folly/test/IPAddressBenchmark.cpp relative time/iter iters/s
// ============================================================================
// ipv4_to_string_inet_ntop 237.87ns 4.20M
// ipv4_to_fully_qualified 362.31% 65.65ns 15.23M
// ipv4_to_string_inet_ntop 227.13ns 4.40M
// ipv4_to_fully_qualified 1418.95% 16.01ns 62.47M
// ----------------------------------------------------------------------------
// ipv4_to_fully_qualified_port 77.51ns 12.90M
// ipv4_append_to_fully_qualified_port 133.72% 57.96ns 17.25M
// ----------------------------------------------------------------------------
// ipv6_to_string_inet_ntop 750.53ns 1.33M
// ipv6_to_fully_qualified 608.68% 123.30ns 8.11M
// ----------------------------------------------------------------------------
// ipv6_to_string_inet_ntop 768.60ns 1.30M
// ipv6_to_fully_qualified 821.81% 93.53ns 10.69M
// ipv6_to_fully_qualified_port 150.76ns 6.63M
// ipv6_append_to_fully_qualified_port 178.73% 84.35ns 11.86M
// ============================================================================
int main(int argc, char *argv[]) {
......
......@@ -461,11 +461,28 @@ TEST(IPAddress, ToFullyQualifiedLocal) {
EXPECT_EQ("0000:0000:0000:0000:0000:0000:0000:0001", ip.toFullyQualified())
<< ip;
}
TEST(IPAddress, ToFullyQualifiedSize) {
TEST(IPAddress, ToFullyQualifiedAppendV6) {
IPAddress ip("2620:0:1cfe:face:b00c::3");
std::string result;
ip.toFullyQualifiedAppend(result);
EXPECT_EQ("2620:0000:1cfe:face:b00c:0000:0000:0003", result) << ip;
}
TEST(IPAddress, ToFullyQualifiedAppendV4) {
IPAddress ip("127.0.0.1");
std::string result;
ip.toFullyQualifiedAppend(result);
EXPECT_EQ("127.0.0.1", result) << ip;
}
TEST(IPAddress, ToFullyQualifiedSizeV6) {
auto actual = IPAddressV6::kToFullyQualifiedSize;
auto expected = IPAddress("::").toFullyQualified().size();
EXPECT_EQ(expected, actual);
}
TEST(IPAddress, MaxToFullyQualifiedSizeV4) {
auto actual = IPAddressV4::kMaxToFullyQualifiedSize;
auto expected = IPAddress("255.255.255.255").toFullyQualified().size();
EXPECT_EQ(expected, actual);
}
// test v4-v6 mapped addresses
TEST_P(IPAddressMappedTest, MappedEqual) {
......
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