util: add printHex() overload that takes a uint64_t

And AsHex helper class.

Change-Id: If6fb6edea258ab281b5ea9cc30deffd2d8994dc5
Refs: #3006
diff --git a/src/encoding/nfd-constants.cpp b/src/encoding/nfd-constants.cpp
index c1c8060..2b9f8ff 100644
--- a/src/encoding/nfd-constants.cpp
+++ b/src/encoding/nfd-constants.cpp
@@ -20,9 +20,10 @@
  */
 
 #include "nfd-constants.hpp"
+#include "util/string-helper.hpp"
 
-#include <iostream>
 #include <map>
+#include <ostream>
 
 namespace ndn {
 namespace nfd {
@@ -137,20 +138,17 @@
     std::string token;
     std::tie(bit, token) = pair;
 
-    if ((routeFlags & bit) == 0) {
-      continue;
+    if ((routeFlags & bit) != 0) {
+      printToken(token);
+      routeFlags = static_cast<RouteFlags>(routeFlags & ~bit);
     }
-
-    printToken(token);
-    routeFlags = static_cast<RouteFlags>(routeFlags & ~bit);
   }
 
-  if (routeFlags != 0) {
-    printToken("0x");
-    std::ios_base::fmtflags oldFmt = os.flags();
-    os << std::hex << std::nouppercase
-       << static_cast<uint64_t>(routeFlags);
-    os.flags(oldFmt);
+  if (routeFlags != ROUTE_FLAGS_NONE) {
+    if (!isFirst) {
+      os << '|';
+    }
+    os << AsHex{routeFlags};
   }
 
   return os;
diff --git a/src/mgmt/nfd/control-parameters.cpp b/src/mgmt/nfd/control-parameters.cpp
index 30f8fe9..4958749 100644
--- a/src/mgmt/nfd/control-parameters.cpp
+++ b/src/mgmt/nfd/control-parameters.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -23,6 +23,7 @@
 #include "encoding/tlv-nfd.hpp"
 #include "encoding/block-helpers.hpp"
 #include "util/concepts.hpp"
+#include "util/string-helper.hpp"
 
 namespace ndn {
 namespace nfd {
@@ -304,15 +305,11 @@
   }
 
   if (parameters.hasFlags()) {
-    std::ios_base::fmtflags osFlags = os.flags();
-    os << "Flags: " << std::showbase << std::hex << parameters.getFlags() << ", ";
-    os.flags(osFlags);
+    os << "Flags: " << AsHex{parameters.getFlags()} << ", ";
   }
 
   if (parameters.hasMask()) {
-    std::ios_base::fmtflags osFlags = os.flags();
-    os << "Mask: " << std::showbase << std::hex << parameters.getMask() << ", ";
-    os.flags(osFlags);
+    os << "Mask: " << AsHex{parameters.getMask()} << ", ";
   }
 
   if (parameters.hasStrategy()) {
diff --git a/src/mgmt/nfd/face-event-notification.cpp b/src/mgmt/nfd/face-event-notification.cpp
index 505026b..888fe61 100644
--- a/src/mgmt/nfd/face-event-notification.cpp
+++ b/src/mgmt/nfd/face-event-notification.cpp
@@ -24,8 +24,7 @@
 #include "encoding/encoding-buffer.hpp"
 #include "encoding/tlv-nfd.hpp"
 #include "util/concepts.hpp"
-
-#include <iomanip>
+#include "util/string-helper.hpp"
 
 namespace ndn {
 namespace nfd {
@@ -197,13 +196,8 @@
      << "          LocalUri: " << notification.getLocalUri() << ",\n"
      << "          FaceScope: " << notification.getFaceScope() << ",\n"
      << "          FacePersistency: " << notification.getFacePersistency() << ",\n"
-     << "          LinkType: " << notification.getLinkType() << ",\n";
-
-  auto osFlags = os.flags();
-  // std::showbase doesn't work with number 0
-  os << "          Flags: 0x" << std::noshowbase << std::noshowpos << std::nouppercase
-     << std::hex << notification.getFlags() << "\n";
-  os.flags(osFlags);
+     << "          LinkType: " << notification.getLinkType() << ",\n"
+     << "          Flags: " << AsHex{notification.getFlags()} << "\n";
 
   return os << "          )";
 }
diff --git a/src/mgmt/nfd/face-status.cpp b/src/mgmt/nfd/face-status.cpp
index ea0d546..df0521f 100644
--- a/src/mgmt/nfd/face-status.cpp
+++ b/src/mgmt/nfd/face-status.cpp
@@ -24,8 +24,7 @@
 #include "encoding/encoding-buffer.hpp"
 #include "encoding/tlv-nfd.hpp"
 #include "util/concepts.hpp"
-
-#include <iomanip>
+#include "util/string-helper.hpp"
 
 namespace ndn {
 namespace nfd {
@@ -366,15 +365,9 @@
 
   os << "     FaceScope: " << status.getFaceScope() << ",\n"
      << "     FacePersistency: " << status.getFacePersistency() << ",\n"
-     << "     LinkType: " << status.getLinkType() << ",\n";
-
-  auto osFlags = os.flags();
-  // std::showbase doesn't work with number 0
-  os << "     Flags: 0x" << std::noshowbase << std::noshowpos << std::nouppercase
-     << std::hex << status.getFlags() << ",\n";
-  os.flags(osFlags);
-
-  os << "     Counters: {Interests: {in: " << status.getNInInterests() << ", "
+     << "     LinkType: " << status.getLinkType() << ",\n"
+     << "     Flags: " << AsHex{status.getFlags()} << ",\n"
+     << "     Counters: {Interests: {in: " << status.getNInInterests() << ", "
      << "out: " << status.getNOutInterests() << "},\n"
      << "                Data: {in: " << status.getNInDatas() << ", "
      << "out: " << status.getNOutDatas() << "},\n"
diff --git a/src/mgmt/nfd/rib-entry.cpp b/src/mgmt/nfd/rib-entry.cpp
index 17b53d6..c7ec47a 100644
--- a/src/mgmt/nfd/rib-entry.cpp
+++ b/src/mgmt/nfd/rib-entry.cpp
@@ -24,6 +24,7 @@
 #include "encoding/encoding-buffer.hpp"
 #include "encoding/tlv-nfd.hpp"
 #include "util/concepts.hpp"
+#include "util/string-helper.hpp"
 
 #include <boost/range/adaptor/reversed.hpp>
 
@@ -206,13 +207,8 @@
   os << "Route("
      << "FaceId: " << route.getFaceId() << ", "
      << "Origin: " << route.getOrigin() << ", "
-     << "Cost: " << route.getCost() << ", ";
-
-  auto osFlags = os.flags();
-  // std::showbase doesn't work with number 0
-  os << "Flags: 0x" << std::noshowbase << std::noshowpos << std::nouppercase
-     << std::hex << route.getFlags() << ", ";
-  os.flags(osFlags);
+     << "Cost: " << route.getCost() << ", "
+     << "Flags: " << AsHex{route.getFlags()} << ", ";
 
   if (route.hasExpirationPeriod()) {
     os << "ExpirationPeriod: " << route.getExpirationPeriod();
diff --git a/src/name-component.cpp b/src/name-component.cpp
index 13005dd..746f69a 100644
--- a/src/name-component.cpp
+++ b/src/name-component.cpp
@@ -104,7 +104,7 @@
       trimmedString.erase(0, getSha256DigestUriPrefix().size());
       return fromImplicitSha256Digest(fromHex(trimmedString));
     }
-    catch (StringHelperError& e) {
+    catch (const StringHelperError&) {
       BOOST_THROW_EXCEPTION(Error("Cannot convert to a ImplicitSha256DigestComponent (invalid hex "
                                   "encoding)"));
     }
@@ -154,7 +154,7 @@
     }
     else {
       // In case we need to escape, set to upper case hex and save the previous flags.
-      std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
+      auto savedFlags = result.flags(std::ios::hex | std::ios::uppercase);
 
       for (size_t i = 0; i < valueSize; ++i) {
         uint8_t x = value[i];
@@ -172,7 +172,7 @@
       }
 
       // Restore.
-      result.flags(saveFlags);
+      result.flags(savedFlags);
     }
   }
 }
diff --git a/src/util/string-helper.cpp b/src/util/string-helper.cpp
index 3d4ea3f..7665afc 100644
--- a/src/util/string-helper.cpp
+++ b/src/util/string-helper.cpp
@@ -31,6 +31,24 @@
 
 namespace ndn {
 
+std::ostream&
+operator<<(std::ostream& os, const AsHex& hex)
+{
+  printHex(os, hex.m_value, os.flags() & std::ostream::uppercase);
+  return os;
+}
+
+void
+printHex(std::ostream& os, uint64_t num, bool wantUpperCase)
+{
+  auto osFlags = os.flags();
+  // std::showbase doesn't work with number 0
+  os << "0x" << std::noshowbase << std::noshowpos
+     << (wantUpperCase ? std::uppercase : std::nouppercase)
+     << std::hex << num;
+  os.flags(osFlags);
+}
+
 void
 printHex(std::ostream& os, const uint8_t* buffer, size_t length, bool wantUpperCase)
 {
diff --git a/src/util/string-helper.hpp b/src/util/string-helper.hpp
index 3f1a303..403c25e 100644
--- a/src/util/string-helper.hpp
+++ b/src/util/string-helper.hpp
@@ -39,17 +39,60 @@
 };
 
 /**
- * @brief Output the hex representation of the bytes in array to the output stream @p os
+ * @brief Helper class to convert a number to hexadecimal
+ *        format, for use with stream insertion operators
+ *
+ * Example usage:
+ *
+ * @code
+ * std::cout << AsHex{42};                   // outputs "0x2a"
+ * std::cout << std::uppercase << AsHex{42}; // outputs "0x2A"
+ * @endcode
+ */
+class AsHex
+{
+public:
+  constexpr explicit
+  AsHex(uint64_t val) noexcept
+    : m_value(val)
+  {
+  }
+
+private:
+  uint64_t m_value;
+
+  friend std::ostream& operator<<(std::ostream&, const AsHex&);
+};
+
+std::ostream&
+operator<<(std::ostream& os, const AsHex& hex);
+
+/**
+ * @brief Output the hex representation of @p num to the output stream @p os
  *
  * @param os Output stream
- * @param buffer The array of bytes
+ * @param num Number to print in hexadecimal format
+ * @param wantUpperCase if true, print uppercase hex chars; the default is to use lowercase
+ *
+ * The output string is a continuous sequence of hex characters without any whitespace separators.
+ */
+void
+printHex(std::ostream& os, uint64_t num, bool wantUpperCase = false);
+
+/**
+ * @brief Output the hex representation of the bytes in @p buffer to the output stream @p os
+ *
+ * @param os Output stream
+ * @param buffer Pointer to an array of bytes
  * @param length Size of the array
- * @param wantUpperCase if true (default) output use uppercase for hex values
+ * @param wantUpperCase if true (the default) print uppercase hex chars
  *
  * Examples:
  *
- *     printHex(std::cout, "Hello, World!") outputs "48656C6C6F2C20776F726C6421"
- *     printHex(std::cout, "Hello, World!", false) outputs "48656c6c6f2c20776f726c6421"
+ * @code
+ * printHex(std::cout, "Hello, World!");        // outputs "48656C6C6F2C20776F726C6421"
+ * printHex(std::cout, "Hello, World!", false); // outputs "48656c6c6f2c20776f726c6421"
+ * @endcode
  *
  * Each octet is always represented as two hex characters ("00" for octet==0).
  *
@@ -59,26 +102,28 @@
 printHex(std::ostream& os, const uint8_t* buffer, size_t length, bool wantUpperCase = true);
 
 /**
- * @brief Output the hex representation of the bytes in the @p buffer to the output stream @p os
+ * @brief Output the hex representation of the bytes in @p buffer to the output stream @p os
  *
  * @param os Output stream
- * @param buffer The array of bytes
- * @param wantUpperCase if true (default) output use uppercase for hex values
+ * @param buffer Buffer of bytes to print in hexadecimal format
+ * @param wantUpperCase if true (the default) print uppercase hex chars
  */
 void
 printHex(std::ostream& os, const Buffer& buffer, bool wantUpperCase = true);
 
 /**
- * @brief Return the hex representation of the bytes in array
+ * @brief Return a string containing the hex representation of the bytes in @p buffer
  *
- * @param buffer The array of bytes
+ * @param buffer Pointer to an array of bytes
  * @param length Size of the array
- * @param wantUpperCase if true (default) output use uppercase for hex values
+ * @param wantUpperCase if true (the default) use uppercase hex chars
  *
  * Examples:
  *
- *     toHex("Hello, World!") == "48656C6C6F2C20776F726C6421"
- *     toHex("Hello, World!", false) == "48656c6c6f2c20776f726c6421"
+ * @code
+ * toHex("Hello, World!") == "48656C6C6F2C20776F726C6421"
+ * toHex("Hello, World!", false) == "48656c6c6f2c20776f726c6421"
+ * @endcode
  *
  * Each octet is always represented as two hex characters ("00" for octet==0).
  *
@@ -88,10 +133,10 @@
 toHex(const uint8_t* buffer, size_t length, bool wantUpperCase = true);
 
 /**
- * @brief Return the hex representation of the bytes in the @p buffer to the output stream @p os
+ * @brief Return a string containing the hex representation of the bytes in @p buffer
  *
- * @param buffer The array of bytes
- * @param wantUpperCase if true (default) output use uppercase for hex values
+ * @param buffer Buffer of bytes to convert to hexadecimal format
+ * @param wantUpperCase if true (the default) use uppercase hex chars
  */
 std::string
 toHex(const Buffer& buffer, bool wantUpperCase = true);
@@ -119,8 +164,10 @@
  *
  * Examples:
  *
- *     unescape("hello%20world") == "hello world"
- *     unescape("hello%20world%FooBar") == "hello world%FooBar"
+ * @code
+ * unescape("hello%20world") == "hello world"
+ * unescape("hello%20world%FooBar") == "hello world%FooBar"
+ * @endcode
  */
 std::string
 unescape(const std::string& str);
diff --git a/tests/unit-tests/name.t.cpp b/tests/unit-tests/name.t.cpp
index a49a9c4..9840196 100644
--- a/tests/unit-tests/name.t.cpp
+++ b/tests/unit-tests/name.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -67,28 +67,19 @@
 BOOST_AUTO_TEST_CASE(Encode)
 {
   Name name("/local/ndn/prefix");
+  const Block& wire = name.wireEncode();
 
-  const Block &wire = name.wireEncode();
-
-  // for (Buffer::const_iterator i = wire.begin();
-  //      i != wire.end();
-  //      ++i)
-  //   {
-  //     std::ios::fmtflags saveFlags = std::cout.flags(std::ios::hex);
-
-  //     if (i != wire.begin())
-  //       std::cout << ", ";
-  //     std::cout << "0x" << static_cast<uint32_t>(*i);
-
-  //     std::cout.flags(saveFlags);
-  //   }
+  // for (auto i = wire.begin(); i != wire.end(); ++i) {
+  //   if (i != wire.begin())
+  //     std::cout << ", ";
+  //   printHex(std::cout, *i);
+  // }
   // std::cout << std::endl;
 
-  BOOST_CHECK_EQUAL_COLLECTIONS(TestName, TestName+sizeof(TestName),
+  BOOST_CHECK_EQUAL_COLLECTIONS(TestName, TestName + sizeof(TestName),
                                 wire.begin(), wire.end());
 }
 
-
 BOOST_AUTO_TEST_CASE(Decode)
 {
   Block block(TestName, sizeof(TestName));
diff --git a/tests/unit-tests/util/string-helper.t.cpp b/tests/unit-tests/util/string-helper.t.cpp
index c5cc218..64144f8 100644
--- a/tests/unit-tests/util/string-helper.t.cpp
+++ b/tests/unit-tests/util/string-helper.t.cpp
@@ -31,6 +31,47 @@
 BOOST_AUTO_TEST_SUITE(Util)
 BOOST_AUTO_TEST_SUITE(TestStringHelper)
 
+BOOST_AUTO_TEST_CASE(PrintHex)
+{
+  boost::test_tools::output_test_stream os;
+
+  printHex(os, 0);
+  BOOST_CHECK(os.is_equal("0x0"));
+
+  printHex(os, 42);
+  BOOST_CHECK(os.is_equal("0x2a"));
+
+  printHex(os, 2748, true);
+  BOOST_CHECK(os.is_equal("0xABC"));
+
+  printHex(os, static_cast<uint64_t>(-1));
+  BOOST_CHECK(os.is_equal("0xffffffffffffffff"));
+
+  printHex(os, ~0U, true);
+  BOOST_CHECK(os.is_equal("0xFFFFFFFF"));
+
+  printHex(os, ~0ULL, true);
+  BOOST_CHECK(os.is_equal("0xFFFFFFFFFFFFFFFF"));
+}
+
+BOOST_AUTO_TEST_CASE(AsHex)
+{
+  using ndn::AsHex;
+  boost::test_tools::output_test_stream os;
+
+  os << AsHex{0};
+  BOOST_CHECK(os.is_equal("0x0"));
+
+  os << AsHex{42};
+  BOOST_CHECK(os.is_equal("0x2a"));
+
+  os << std::uppercase << AsHex{~0U};
+  BOOST_CHECK(os.is_equal("0xFFFFFFFF"));
+
+  os << std::nouppercase << AsHex{~0U};
+  BOOST_CHECK(os.is_equal("0xffffffff"));
+}
+
 BOOST_AUTO_TEST_CASE(ToHex)
 {
   std::string test = "Hello, world!";