diff --git a/include/opendht/infohash.h b/include/opendht/infohash.h
index 5e07ba1c44dc2f1a9d30ff98623fbce0907da86c..74186ed2e6309a2c97ac14a797c4e182aaf9bfda 100644
--- a/include/opendht/infohash.h
+++ b/include/opendht/infohash.h
@@ -278,6 +278,8 @@ public:
     OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
     OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, InfoHash& h);
 
+    const char* to_c_str() const;
+
     std::string toString() const;
 
     template <typename Packer>
diff --git a/src/infohash.cpp b/src/infohash.cpp
index 5070abe43fb14e761d21ff6e49d4bcf50a0292d0..7530ca1d4edd305e5eb88727327a90859edbf670 100644
--- a/src/infohash.cpp
+++ b/src/infohash.cpp
@@ -71,20 +71,40 @@ InfoHash::getRandom()
     return h;
 }
 
+struct HexMap : public std::array<std::array<char, 2>, 256> {
+    HexMap() {
+        for (size_t i=0; i<size(); i++) {
+            auto& e = (*this)[i];
+            e[0] = hex_digits[(i >> 4) & 0x0F];
+            e[1] = hex_digits[i & 0x0F];
+        }
+    }
+private:
+    static constexpr const char* hex_digits = "0123456789abcdef";
+};
+
+const char*
+InfoHash::to_c_str() const
+{
+    static const HexMap map;
+    thread_local std::array<char, HASH_LEN*2+1> buf;
+    for (size_t i=0; i<HASH_LEN; i++) {
+        auto b = buf.data()+i*2;
+        const auto& m = map[(*this)[i]];
+        *((uint16_t*)b) = *((uint16_t*)&m);
+    }
+    return buf.data();
+}
+
 std::string
 InfoHash::toString() const
 {
-    std::stringstream ss;
-    ss << *this;
-    return ss.str();
+    return std::string(to_c_str(), HASH_LEN*2);
 }
 
 std::ostream& operator<< (std::ostream& s, const InfoHash& h)
 {
-    s << std::hex;
-    for (unsigned i=0; i<HASH_LEN; i++)
-        s << std::setfill('0') << std::setw(2) << (unsigned)h[i];
-    s << std::dec;
+    s.write(h.to_c_str(), HASH_LEN*2);
     return s;
 }