Add toFullyQualifiedAppend() methods
authorNick Terrell <terrelln@fb.com>
Tue, 13 Jun 2017 06:12:42 +0000 (23:12 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Tue, 13 Jun 2017 06:22:28 +0000 (23:22 -0700)
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

folly/IPAddress.h
folly/IPAddressV4.cpp
folly/IPAddressV4.h
folly/IPAddressV6.cpp
folly/IPAddressV6.h
folly/detail/IPAddressSource.h
folly/test/IPAddressBenchmark.cpp
folly/test/IPAddressTest.cpp

index 20eddb9..bd6906c 100644 (file)
@@ -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()
index dfe4669..cd81d6d 100644 (file)
@@ -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(
index 0949694..2de04f1 100644 (file)
@@ -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; }
 
index 6f1eb59..038a68f 100644 (file)
@@ -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";
index 21e892a..2a3880b 100644 (file)
@@ -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
index 53ae7b6..7f08f84 100644 (file)
@@ -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));
 }
 }
 }
index bf3433c..3099aa7 100644 (file)
@@ -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[]) {
index 3dd64e9..7f1f5a9 100644 (file)
@@ -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) {