]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Improve and broadly use asHex() (#1578)
authorFrancesco Chemolli <5175948+kinkie@users.noreply.github.com>
Tue, 12 Dec 2023 23:25:53 +0000 (23:25 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Wed, 13 Dec 2023 09:07:22 +0000 (09:07 +0000)
Replaced all raw std::hex uses for integers outside of IoManip code.

Also fixed std::ostream flags restoration in AsHex printing code.

Also fixed or polished a few level-2+ debugs() statements.

Also added AsHex unit tests.

28 files changed:
src/Makefile.am
src/acl/ConnMark.cc
src/acl/TimeData.cc
src/acl/external/LDAP_group/ext_ldap_group_acl.cc
src/base/IoManip.h
src/base/RandomUuid.cc
src/comm/ModDevPoll.cc
src/comm/ModEpoll.cc
src/dns_internal.cc
src/error/ExceptionErrorDetail.h
src/eui/Eui48.cc
src/fs/rock/RockSwapDir.cc
src/fs/ufs/RebuildState.cc
src/fs/ufs/UFSStoreState.cc
src/fs/ufs/UFSStrategy.cc
src/fs/ufs/UFSSwapDir.cc
src/ip/QosConfig.cc
src/ipc/StoreMap.cc
src/ipcache.cc
src/security/ErrorDetail.cc
src/security/Handshake.cc
src/security/ServerOptions.cc
src/ssl/bio.cc
src/ssl/gadgets.cc
src/store.cc
src/store/Disks.cc
src/store_swapout.cc
src/tests/testIoManip.cc [new file with mode: 0644]

index 224ac18c8436dd048b4f13af59eaa9ad190f7c43..8921c4f1430cfe05ee7df400f907cb74fed33e3f 100644 (file)
@@ -2757,3 +2757,18 @@ tests_testEventLoop_LDADD = \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_testEventLoop_LDFLAGS = $(LIBADD_DL)
+
+check_PROGRAMS += tests/testIoManip
+tests_testIoManip_SOURCES = \
+       tests/testIoManip.cc
+nodist_tests_testIoManip_SOURCES = \
+       tests/stub_SBuf.cc \
+       tests/stub_debug.cc \
+       tests/stub_libmem.cc \
+       tests/stub_libtime.cc
+tests_testIoManip_LDADD = \
+       base/libbase.la \
+       $(LIBCPPUNIT_LIBS) \
+       $(COMPAT_LIB) \
+       $(XTRA_LIBS)
+tests_testIoManip_LDFLAGS = $(LIBADD_DL)
index 50ffd25a303fc9bdd3f93e51e3edabd110e46150..41291f427e00145c7e5415ff7dd0694acefaa2fa 100644 (file)
@@ -50,10 +50,10 @@ Acl::ConnMark::match(ACLChecklist *cl)
 
         for (const auto &m : marks) {
             if (m.matches(connmark)) {
-                debugs(28, 5, "found " << m << " matching " << asHex(connmark));
+                debugs(28, 5, "found " << m << " matching 0x" << asHex(connmark));
                 return 1;
             }
-            debugs(28, 7, "skipped " << m << " mismatching " << asHex(connmark));
+            debugs(28, 7, "skipped " << m << " mismatching 0x" << asHex(connmark));
         }
     } else {
         debugs(28, 7, "fails: no client connection");
index 3c48934546e48cd8ac40c98f2bba52494ea4627a..1852937091407b1b421a5372c56219e4ab9616e9 100644 (file)
@@ -11,6 +11,7 @@
 #include "squid.h"
 #include "acl/Checklist.h"
 #include "acl/TimeData.h"
+#include "base/IoManip.h"
 #include "cache_cf.h"
 #include "ConfigParser.h"
 #include "debug/Stream.h"
@@ -53,7 +54,7 @@ ACLTimeData::match(time_t when)
     while (data) {
         debugs(28, 3, "aclMatchTime: checking " << t  << " in " <<
                data->start  << "-" << data->stop  << ", weekbits=" <<
-               std::hex << data->weekbits);
+               asHex(data->weekbits));
 
         if (t >= data->start && t <= data->stop && (data->weekbits & (1 << tm.tm_wday)))
             return 1;
index 435a49c38791b6adf5a963ddb6999aa1bbfe46cc..b5899bf1fdc2a32b84fea046a091208208cc3902 100644 (file)
@@ -38,6 +38,7 @@
  * OpenSSL libraries linked into openldap. See http://www.openssl.org/
  */
 #include "squid.h"
+#include "base/IoManip.h"
 #include "helper/protocol_defines.h"
 #include "rfc1738.h"
 #include "util.h"
@@ -634,7 +635,7 @@ ldap_escape_value(const std::string &src)
         case '(':
         case ')':
         case '\\':
-            str << '\\' << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(c);
+            str << '\\' << asHex(c).minDigits(2);
             break;
         default:
             str << c;
index b34a313e7db0e87d66326f19b9211a9150d67ccf..49312afb8f943d25e0958f1302f05701e5e7e37a 100644 (file)
 
 #include <iostream>
 #include <iomanip>
+#include <optional>
+
+/// \section Custom manipulator tuning methods
+///
+/// Our convenience manipulator/wrapper classes often have methods that tune
+/// their "printing" effects (e.g., AsHex::minDigits()). STL streams also have
+/// manipulators that tune how subsequent operator "<<" parameters are printed
+/// (e.g., std::setw()). The calling code can also print various decorations
+/// (i.e. prefixes and suffixes). The following principles are useful when
+/// deciding what manipulator methods to add and how to implement them:
+///
+/// \li Add a manipulator method if callers would otherwise have to restore
+/// stream format after calling the manipulator. For example, AsHex::toUpper()
+/// frees callers from doing `std::uppercase << asHex(n) << std::nouppercase`.
+///
+/// \li Add a manipulator method if callers would otherwise have to use
+/// conditionals to get the same effect. For example, AsList::prefixedBy() frees
+/// callers from doing `(c.empty() ? "" : "/") << asList(c)`.
+///
+/// \li Add a manipulator method if callers would otherwise have to repeat a
+/// combination of actions to get the right effect. For example,
+/// AsList::minDigits() prevents duplication of the following caller code:
+/// `std::setfill('0') << std::setw(8) << asHex(n)`.
+///
+/// \li Avoid adding a manipulator method that can be _fully_ replaced with a
+/// _single_ caller item. For example, do not add AsX::foo() if callers can do
+/// `bar << asX(y)` or `asX(y) << bar` and get exactly the same effect.
+///
+/// \li Manipulators should honor existing stream formatting to the extent
+/// possible (e.g., AsHex honors std::uppercase by default).
 
 /// Safely prints an object pointed to by the given pointer: [label]<object>
 /// Prints nothing at all if the pointer is nil.
@@ -75,13 +105,37 @@ operator <<(std::ostream &os, const RawPointerT<Pointer> &pd)
     return os;
 }
 
-/// std::ostream manipulator to print integers as hex numbers prefixed by 0x
+/// std::ostream manipulator to print integers and alike as hex numbers.
+/// Normally used through the asHex() convenience function.
 template <class Integer>
 class AsHex
 {
 public:
+    // Without this assertion, asHex(pointer) and AsHex(3.14) compile, but their
+    // caller is likely confused about the actual argument type and expects
+    // different output. Enum values are not integral types but arguably do not
+    // cause similar problems.
+    static_assert(std::is_integral<Integer>::value || std::is_enum<Integer>::value);
+
     explicit AsHex(const Integer n): io_manip(n) {}
+
+    /// Sets the minimum number of digits to print. If the integer has fewer
+    /// digits than the given width, then we also print leading zero(s).
+    /// Otherwise, this method has no effect.
+    auto &minDigits(const size_t w) { forcePadding = w; return *this; }
+
+    /// Print hex digits in upper (or, with a false parameter value, lower) case.
+    auto &upperCase(const bool u = true) { forceCase = u; return *this; }
+
     Integer io_manip; ///< the integer to print
+
+    /// \copydoc minDigits()
+    /// The default is to use stream's field width and stream's fill character.
+    std::optional<size_t> forcePadding;
+
+    /// \copydoc upperCase()
+    /// The default is to use stream's std::uppercase flag.
+    std::optional<bool> forceCase;
 };
 
 template <class Integer>
@@ -89,8 +143,24 @@ inline std::ostream &
 operator <<(std::ostream &os, const AsHex<Integer> number)
 {
     const auto oldFlags = os.flags();
-    os << std::hex << std::showbase << number.io_manip;
-    os.setf(oldFlags);
+    const auto oldFill = os.fill();
+
+    if (number.forceCase)
+        os << (*number.forceCase ? std::uppercase : std::nouppercase);
+
+    if (number.forcePadding) {
+        os.width(*number.forcePadding);
+        os.fill('0');
+    }
+
+    // When Integer is smaller than int, the unary plus converts the stored
+    // value into an equivalent integer because C++ "arithmetic operators do not
+    // accept types smaller than int as arguments, and integral promotions are
+    // automatically applied". For larger integer types, plus is a no-op.
+    os << std::hex << +number.io_manip;
+
+    os.fill(oldFill);
+    os.flags(oldFlags);
     return os;
 }
 
index 70344916286e75cfbe13126b3b045711bce90a93..bf21c9922ebc7dba63170d3830171324ca032758 100644 (file)
@@ -91,22 +91,15 @@ RandomUuid::serialize() const
 void
 RandomUuid::print(std::ostream &os) const
 {
-    const auto savedFlags = os.flags();
-    const auto savedFill = os.fill('0');
-
-    os << std::hex;
-
     os <<
-       std::setw(8) << timeLow  << '-' <<
-       std::setw(4) << timeMid << '-' <<
-       std::setw(4) << timeHiAndVersion << '-' <<
-       std::setw(2) << +clockSeqHiAndReserved << std::setw(2) << +clockSeqLow << '-';
+       asHex(timeLow).minDigits(8)  << '-' <<
+       asHex(timeMid).minDigits(4) << '-' <<
+       asHex(timeHiAndVersion).minDigits(4) << '-' <<
+       asHex(clockSeqHiAndReserved).minDigits(2) <<
+       asHex(clockSeqLow).minDigits(2) << '-';
 
     for (size_t i = 0; i < sizeof(node); ++i)
-        os << std::setw(2) << +node[i];
-
-    os.fill(savedFill);
-    os.flags(savedFlags);
+        os << asHex(node[i]).minDigits(2);
 }
 
 bool
index f21852618324d92d9c3788d676c3a402490ef3c9..dd996a58f6b0a9c0e8dab3f42f140d1bae3942e1 100644 (file)
@@ -28,6 +28,7 @@
 
 #if USE_DEVPOLL
 
+#include "base/IoManip.h"
 #include "comm/Loops.h"
 #include "fd.h"
 #include "fde.h"
@@ -347,8 +348,8 @@ Comm::DoSelect(int msec)
             5,
             DEBUG_DEVPOLL ? 0 : 8,
             "got FD " << fd
-            << ",events=" << std::hex << do_poll.dp_fds[i].revents
-            << ",monitoring=" << devpoll_state[fd].state
+            << ",events=" << asHex(do_poll.dp_fds[i].revents)
+            << ",monitoring=" << asHex(devpoll_state[fd].state)
             << ",F->read_handler=" << F->read_handler
             << ",F->write_handler=" << F->write_handler
         );
index 2b9ac4bac61c716ce0a54f069c336ae09c6f8e98..23b479b6bbd26ca5fb678f8c996a5abda998c49d 100644 (file)
@@ -33,6 +33,7 @@
 #if USE_EPOLL
 
 #include "base/CodeContext.h"
+#include "base/IoManip.h"
 #include "comm/Loops.h"
 #include "fde.h"
 #include "globals.h"
@@ -253,7 +254,7 @@ Comm::DoSelect(int msec)
         F = &fd_table[fd];
         CodeContext::Reset(F->codeContext);
         debugs(5, DEBUG_EPOLL ? 0 : 8, "got FD " << fd << " events=" <<
-               std::hex << cevents->events << " monitoring=" << F->epoll_state <<
+               asHex(cevents->events) << " monitoring=" << asHex(F->epoll_state) <<
                " F->read_handler=" << F->read_handler << " F->write_handler=" << F->write_handler);
 
         // TODO: add EPOLLPRI??
index e684e1433bdf11390fe6a05591060fa3dbfb8ed1..fb09f8cc277404a7201298e8042ba9bb721d145f 100644 (file)
@@ -11,6 +11,7 @@
 #include "squid.h"
 #include "base/CodeContext.h"
 #include "base/InstanceId.h"
+#include "base/IoManip.h"
 #include "base/Random.h"
 #include "base/RunnersRegistry.h"
 #include "comm.h"
@@ -1169,7 +1170,7 @@ idnsGrokReply(const char *buf, size_t sz, int /*from_ns*/)
         return;
     }
 
-    debugs(78, 3, "idnsGrokReply: QID 0x" << std::hex <<   message->id << ", " << std::dec << n << " answers");
+    debugs(78, 3, "idnsGrokReply: QID 0x" << asHex(message->id) << ", " << n << " answers");
 
     idns_query *q = idnsFindQuery(message->id);
 
@@ -1423,8 +1424,7 @@ idnsCheckQueue(void *)
         }
 
         debugs(78, 3, "idnsCheckQueue: ID " << q->xact_id <<
-               " QID 0x"  << std::hex << std::setfill('0')  <<
-               std::setw(4) << q->query_id << ": timeout" );
+               " QID 0x" << asHex(q->query_id).minDigits(4) << ": timeout");
 
         dlinkDelete(&q->lru, &lru_list);
         q->pending = 0;
@@ -1433,8 +1433,8 @@ idnsCheckQueue(void *)
             idnsSendQuery(q);
         } else {
             debugs(78, 2, "idnsCheckQueue: ID " << q->xact_id <<
-                   " QID 0x" << std::hex << q->query_id <<
-                   " : giving up after " << std::dec << q->nsends << " tries and " <<
+                   " QID 0x" << asHex(q->query_id) <<
+                   ": giving up after " << q->nsends << " tries and " <<
                    std::setw(5)<< std::setprecision(2) << tvSubDsec(q->start_t, current_time) << " seconds");
 
             if (q->rcode != 0)
@@ -1727,7 +1727,7 @@ idnsSendSlaveAAAAQuery(idns_query *master)
     q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, Config.dns.packet_max);
 
     debugs(78, 3, "buf is " << q->sz << " bytes for " << q->name <<
-           ", id = 0x" << std::hex << q->query_id);
+           ", id = 0x" << asHex(q->query_id));
     if (!q->sz) {
         delete q;
         return;
@@ -1791,7 +1791,7 @@ idnsALookup(const char *name, IDNSCB * callback, void *data)
     }
 
     debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
-           ", id = 0x" << std::hex << q->query_id);
+           ", id = 0x" << asHex(q->query_id));
 
     idnsCheckMDNS(q);
     idnsStartQuery(q, callback, data);
@@ -1834,7 +1834,7 @@ idnsPTRLookup(const Ip::Address &addr, IDNSCB * callback, void *data)
     }
 
     debugs(78, 3, "idnsPTRLookup: buf is " << q->sz << " bytes for " << ip <<
-           ", id = 0x" << std::hex << q->query_id);
+           ", id = 0x" << asHex(q->query_id));
 
     q->permit_mdns = Config.onoff.dns_mdns;
     idnsStartQuery(q, callback, data);
index f64a5845af1465d168b2d3ff6da53d77a64eda04..eb75c9f26398254e9b17db2a9789ffd24b0e95d4 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef _SQUID_SRC_ERROR_EXCEPTIONERRORDETAIL_H
 #define _SQUID_SRC_ERROR_EXCEPTIONERRORDETAIL_H
 
+#include "base/IoManip.h"
 #include "error/Detail.h"
 #include "sbuf/SBuf.h"
 #include "sbuf/Stream.h"
@@ -27,11 +28,11 @@ public:
 
     /* ErrorDetail API */
     SBuf brief() const override {
-        return ToSBuf("exception=", std::hex, exceptionId);
+        return ToSBuf("exception=", asHex(exceptionId));
     }
 
     SBuf verbose(const HttpRequestPointer &) const override {
-        return ToSBuf("Exception (ID=", std::hex, exceptionId, ')');
+        return ToSBuf("Exception (ID=", asHex(exceptionId), ')');
     }
 
 private:
index b2ab2fda5d0df7c2c159eb554bbc52765d451fad..30b7808a165b2dc235d6bfbe58a030e681d5d57f 100644 (file)
@@ -12,6 +12,7 @@
 
 #if USE_SQUID_EUI
 
+#include "base/IoManip.h"
 #include "debug/Stream.h"
 #include "eui/Eui48.h"
 #include "globals.h"
@@ -93,6 +94,30 @@ struct arpreq {
  *       Solaris code by R. Gancarz <radekg@solaris.elektrownia-lagisza.com.pl>
  */
 
+/// I/O manipulator to print EUI48 addresses
+template <typename HardwareAddress>
+class AsEui48 {
+public:
+    /// caller is responsible for the passed address storage lifetime
+    explicit AsEui48(const HardwareAddress * const a): hardwareAddress(a) {}
+    const HardwareAddress * const hardwareAddress;
+};
+
+template <typename HardwareAddress>
+static std::ostream &
+operator <<(std::ostream &os, const AsEui48<HardwareAddress> &manipulator)
+{
+    const auto &ha = *manipulator.hardwareAddress;
+    os <<
+       asHex(ha.sa_data[0] & 0xff).minDigits(2) << ':' <<
+       asHex(ha.sa_data[1] & 0xff).minDigits(2) << ':' <<
+       asHex(ha.sa_data[2] & 0xff).minDigits(2) << ':' <<
+       asHex(ha.sa_data[3] & 0xff).minDigits(2) << ':' <<
+       asHex(ha.sa_data[4] & 0xff).minDigits(2) << ':' <<
+       asHex(ha.sa_data[5] & 0xff).minDigits(2);
+    return os;
+}
+
 bool
 Eui::Eui48::decode(const char *asc)
 {
@@ -187,13 +212,7 @@ Eui::Eui48::lookup(const Ip::Address &c)
             return false;
         }
 
-        debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
+        debugs(28, 4, "id=" << static_cast<void*>(this) << " got address " << AsEui48(&arpReq.arp_ha));
 
         set(arpReq.arp_ha.sa_data, 6);
         return true;
@@ -265,14 +284,8 @@ Eui::Eui48::lookup(const Ip::Address &c)
             continue;
         }
 
-        debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff)  << " on "<<
-               std::setfill(' ') << ifr->ifr_name);
+        debugs(28, 4, "id=" << static_cast<void*>(this) << " got address " << AsEui48(&arpReq.arp_ha) <<
+               " on " << ifr->ifr_name);
 
         set(arpReq.arp_ha.sa_data, 6);
 
@@ -323,13 +336,7 @@ Eui::Eui48::lookup(const Ip::Address &c)
             return false;
         }
 
-        debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff)  << ":" <<
-               std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
+        debugs(28, 4, "Got address " << AsEui48(&arpReq.arp_ha));
 
         set(arpReq.arp_ha.sa_data, 6);
         return true;
@@ -429,13 +436,7 @@ Eui::Eui48::lookup(const Ip::Address &c)
         return false;
     }
 
-    debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
+    debugs(28, 4, "Got address " << AsEui48(&arpReq.arp_ha));
 
     set(arpReq.arp_ha.sa_data, 6);
     return true;
@@ -494,13 +495,7 @@ Eui::Eui48::lookup(const Ip::Address &c)
         return false;
     }
 
-    debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff)  << ":" <<
-           std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
+    debugs(28, 4, "Got address " << AsEui48(&arpReq.arp_ha));
 
     set(arpReq.arp_ha.sa_data, 6);
     return true;
index ad376aad26b8aee5903c3a787b641beca179b681..c9403e50fabae2d877be07aa31535c76a44ba394 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 47    Store Directory Routines */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "cache_cf.h"
 #include "CollapsedForwarding.h"
 #include "ConfigOption.h"
@@ -638,8 +639,7 @@ Rock::SwapDir::createStoreIO(StoreEntry &e, StoreIOState::STIOCB * const cbIo, v
     sio->writeableAnchor_ = slot;
 
     debugs(47,5, "dir " << index << " created new filen " <<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) <<
-           sio->swap_filen << std::dec << " starting at " <<
+           asHex(sio->swap_filen).upperCase().minDigits(8) << " starting at " <<
            diskOffset(sio->swap_filen));
 
     sio->file(theFile);
@@ -667,8 +667,7 @@ Rock::SwapDir::createUpdateIO(const Ipc::StoreMapUpdate &update, StoreIOState::S
     sio->writeableAnchor_ = update.fresh.anchor;
 
     debugs(47,5, "dir " << index << " updating filen " <<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) <<
-           sio->swap_filen << std::dec << " starting at " <<
+           asHex(sio->swap_filen).upperCase().minDigits(8) << " starting at " <<
            diskOffset(sio->swap_filen));
 
     sio->file(theFile);
@@ -788,8 +787,7 @@ Rock::SwapDir::openStoreIO(StoreEntry &e, StoreIOState::STIOCB * const cbIo, voi
     sio->file(theFile);
 
     debugs(47,5, "dir " << index << " has old filen: " <<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) <<
-           sio->swap_filen);
+           asHex(sio->swap_filen).upperCase().minDigits(8));
 
     // When StoreEntry::swap_filen for e was set by our anchorEntry(), e had a
     // public key, but it could have gone private since then (while keeping the
index 5801a18b9121e4adabc72ff8c10c52408b003569..3551d53a58fb9b075a209eb42afcf154cdcdc7bf 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 47    Store Directory Routines */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "fs_io.h"
 #include "globals.h"
 #include "RebuildState.h"
@@ -297,9 +298,8 @@ Fs::Ufs::RebuildState::rebuildFromSwapLog()
     swapData.swap_filen &= 0x00FFFFFF;
 
     debugs(47, 3, swap_log_op_str[(int) swapData.op]  << " " <<
-           storeKeyText(swapData.key)  << " "<< std::setfill('0') <<
-           std::hex << std::uppercase << std::setw(8) <<
-           swapData.swap_filen);
+           storeKeyText(swapData.key) << " " <<
+           asHex(swapData.swap_filen).upperCase().minDigits(8));
 
     if (swapData.op == SWAP_LOG_ADD) {
         (void) 0;
@@ -356,9 +356,9 @@ Fs::Ufs::RebuildState::getNextFile(sfileno * filn_p, int *)
     int fd = -1;
     int dirs_opened = 0;
     debugs(47, 3, "flag=" << flags.init  << ", " <<
-           sd->index  << ": /"<< std::setfill('0') << std::hex <<
-           std::uppercase << std::setw(2) << curlvl1  << "/" << std::setw(2) <<
-           curlvl2);
+           sd->index << ": /" <<
+           asHex(curlvl1).upperCase().minDigits(2) << "/" <<
+           asHex(curlvl2).upperCase().minDigits(2));
 
     if (done)
         return -2;
@@ -410,11 +410,10 @@ Fs::Ufs::RebuildState::getNextFile(sfileno * filn_p, int *)
             }
 
             if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) {
-                debugs(47, 3, std::setfill('0') <<
-                       std::hex << std::uppercase << std::setw(8) << fn  <<
-                       " does not belong in " << std::dec << sd->index  << "/" <<
-                       curlvl1  << "/" << curlvl2);
-
+                debugs(47, 3, asHex(fn).upperCase().minDigits(8) <<
+                       " does not belong in " << sd->index  << "/" <<
+                       asHex(curlvl1).upperCase().minDigits(2) << "/" <<
+                       asHex(curlvl2).upperCase().minDigits(2));
                 continue;
             }
 
index 01f3eac8654d2ef34e6f5d0db6282dc08286e5d4..e6d6ec8d7062197f65a7f3415a832a26f28b3b20 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 79    Storage Manager UFS Interface */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "DiskIO/DiskFile.h"
 #include "DiskIO/DiskIOStrategy.h"
 #include "DiskIO/ReadRequest.h"
@@ -27,10 +28,9 @@ Fs::Ufs::UFSStoreState::ioCompletedNotification()
 {
     if (opening) {
         opening = false;
-        debugs(79, 3, "UFSStoreState::ioCompletedNotification: dirno " <<
-               swap_dirn  << ", fileno "<< std::setfill('0') << std::hex <<
-               std::setw(8) << swap_filen  << " status "<< std::setfill(' ') <<
-               std::dec << theFile->error());
+        debugs(79, 3, "opening: dirno " << swap_dirn <<
+               ", fileno " << asHex(swap_filen).minDigits(8) <<
+               " status " << theFile->error());
 
         assert (FILE_MODE(mode) == O_RDONLY);
         openDone();
@@ -40,10 +40,9 @@ Fs::Ufs::UFSStoreState::ioCompletedNotification()
 
     if (creating) {
         creating = false;
-        debugs(79, 3, "UFSStoreState::ioCompletedNotification: dirno " <<
-               swap_dirn  << ", fileno "<< std::setfill('0') << std::hex <<
-               std::setw(8) << swap_filen  << " status "<< std::setfill(' ') <<
-               std::dec << theFile->error());
+        debugs(79, 3, "creating: dirno " << swap_dirn <<
+               ", fileno " << asHex(swap_filen).minDigits(8) <<
+               " status " << theFile->error());
 
         openDone();
 
@@ -51,9 +50,9 @@ Fs::Ufs::UFSStoreState::ioCompletedNotification()
     }
 
     assert (!(closing ||opening));
-    debugs(79, 3, "diskd::ioCompleted: dirno " << swap_dirn  << ", fileno "<<
-           std::setfill('0') << std::hex << std::setw(8) << swap_filen  <<
-           " status "<< std::setfill(' ') << std::dec << theFile->error());
+    debugs(79, 3, "error: dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).minDigits(8) <<
+           " status " << theFile->error());
 
     /* Ok, notification past open means an error has occurred */
     assert (theFile->error());
@@ -89,10 +88,9 @@ void
 Fs::Ufs::UFSStoreState::closeCompleted()
 {
     assert (closing);
-    debugs(79, 3, "UFSStoreState::closeCompleted: dirno " << swap_dirn  <<
-           ", fileno "<< std::setfill('0') << std::hex << std::setw(8) <<
-           swap_filen  << " status "<< std::setfill(' ') << std::dec <<
-           theFile->error());
+    debugs(79, 3, "dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).minDigits(8) <<
+           " status " << theFile->error());
 
     if (theFile->error()) {
         debugs(79,3, "theFile->error() ret " << theFile->error());
@@ -116,8 +114,9 @@ Fs::Ufs::UFSStoreState::closeCompleted()
 void
 Fs::Ufs::UFSStoreState::close(int)
 {
-    debugs(79, 3, "UFSStoreState::close: dirno " << swap_dirn  << ", fileno "<<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen);
+    // TODO: De-duplicate position printing Fs::Ufs code and fix upperCase() inconsistency.
+    debugs(79, 3, "dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).upperCase().minDigits(8));
     tryClosing(); // UFS does not distinguish different closure types
 }
 
@@ -139,8 +138,8 @@ Fs::Ufs::UFSStoreState::read_(char *buf, size_t size, off_t aOffset, STRCB * aCa
 
     read.callback = aCallback;
     read.callback_data = cbdataReference(aCallbackData);
-    debugs(79, 3, "UFSStoreState::read_: dirno " << swap_dirn  << ", fileno "<<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen);
+    debugs(79, 3, "dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).minDigits(8));
     offset_ = aOffset;
     read_buf = buf;
     reading = true;
@@ -158,8 +157,8 @@ Fs::Ufs::UFSStoreState::read_(char *buf, size_t size, off_t aOffset, STRCB * aCa
 bool
 Fs::Ufs::UFSStoreState::write(char const *buf, size_t size, off_t aOffset, FREE * free_func)
 {
-    debugs(79, 3, "UFSStoreState::write: dirn " << swap_dirn  << ", fileno "<<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen);
+    debugs(79, 3, "dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).minDigits(8));
 
     if (theFile->error()) {
         debugs(79, DBG_IMPORTANT, "ERROR: avoid write on theFile with error");
@@ -231,9 +230,9 @@ Fs::Ufs::UFSStoreState::readCompleted(const char *buf, int len, int, RefCount<Re
 {
     assert (result.getRaw());
     reading = false;
-    debugs(79, 3, "UFSStoreState::readCompleted: dirno " << swap_dirn  <<
-           ", fileno "<< std::setfill('0') << std::hex << std::setw(8) <<
-           swap_filen  << " len "<< std::setfill(' ') << std::dec << len);
+    debugs(79, 3, "dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).minDigits(8) <<
+           " len " << len);
 
     if (len > 0)
         offset_ += len;
@@ -272,8 +271,8 @@ Fs::Ufs::UFSStoreState::readCompleted(const char *buf, int len, int, RefCount<Re
 void
 Fs::Ufs::UFSStoreState::writeCompleted(int, size_t len, RefCount<WriteRequest>)
 {
-    debugs(79, 3, "dirno " << swap_dirn << ", fileno " <<
-           std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen <<
+    debugs(79, 3, "dirno " << swap_dirn <<
+           ", fileno " << asHex(swap_filen).upperCase().minDigits(8) <<
            ", len " << len);
     /*
      * DPW 2006-05-24
index 5e1a30a5864b00279cd863534132b3ba11d8be52..e0b4a0f4938e05edb906f2b17b6db3391ae16f9b 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "squid.h"
 
+#include "base/IoManip.h"
 #include "DiskIO/DiskIOStrategy.h"
 #include "UFSStoreState.h"
 #include "UFSStrategy.h"
@@ -58,8 +59,7 @@ Fs::Ufs::UFSStrategy::open(SwapDir * const SD, StoreEntry * const e,
                            StoreIOState::STIOCB * aCallback, void *callback_data)
 {
     assert (((UFSSwapDir *)SD)->IO == this);
-    debugs(79, 3, "fileno "<< std::setfill('0') << std::hex
-           << std::uppercase << std::setw(8) << e->swap_filen);
+    debugs(79, 3, "fileno " << asHex(e->swap_filen).upperCase().minDigits(8));
 
     /* to consider: make createstate a private UFSStrategy call */
     StoreIOState::Pointer sio = createState (SD, e, aCallback, callback_data);
@@ -96,8 +96,7 @@ Fs::Ufs::UFSStrategy::create(SwapDir * const SD, StoreEntry * const e,
     assert (((UFSSwapDir *)SD)->IO == this);
     /* Allocate a number */
     sfileno filn = ((UFSSwapDir *)SD)->mapBitAllocate();
-    debugs(79, 3, "fileno "<< std::setfill('0') <<
-           std::hex << std::uppercase << std::setw(8) << filn);
+    debugs(79, 3, "fileno " << asHex(filn).upperCase().minDigits(8));
 
     /* Shouldn't we handle a 'bitmap full' error here? */
 
index d4e744d31ab7583f947f678d96d6d0ba87b149d2..8dd64598718a30ab19d740d1b6fa6b9fe0fe4dce 100644 (file)
@@ -11,6 +11,7 @@
 #define CLEAN_BUF_SZ 16384
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "base/Random.h"
 #include "cache_cf.h"
 #include "ConfigOption.h"
@@ -339,8 +340,8 @@ Fs::Ufs::UFSSwapDir::~UFSSwapDir()
 void
 Fs::Ufs::UFSSwapDir::dumpEntry(StoreEntry &e) const
 {
-    debugs(47, DBG_CRITICAL, "FILENO "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen);
-    debugs(47, DBG_CRITICAL, "PATH " << fullPath(e.swap_filen, nullptr)   );
+    debugs(47, DBG_CRITICAL, "FILENO " << asHex(e.swap_filen).upperCase().minDigits(8) <<
+           ", PATH " << fullPath(e.swap_filen, nullptr));
     e.dump(0);
 }
 
@@ -796,8 +797,7 @@ Fs::Ufs::UFSSwapDir::addDiskRestore(const cache_key * key,
                                     int)
 {
     StoreEntry *e = nullptr;
-    debugs(47, 5, storeKeyText(key)  <<
-           ", fileno="<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << file_number);
+    debugs(47, 5, storeKeyText(key) << ", fileno=" << asHex(file_number).upperCase().minDigits(8));
     /* if you call this you'd better be sure file_number is not
      * already in use! */
     e = new StoreEntry();
@@ -1161,8 +1161,7 @@ Fs::Ufs::UFSSwapDir::validFileno(sfileno filn, int flag) const
 void
 Fs::Ufs::UFSSwapDir::unlinkFile(sfileno f)
 {
-    debugs(79, 3, "unlinking fileno " <<  std::setfill('0') <<
-           std::hex << std::uppercase << std::setw(8) << f << " '" <<
+    debugs(79, 3, "unlinking fileno " << asHex(f).upperCase().minDigits(8) << " '" <<
            fullPath(f,nullptr) << "'");
     /* commonUfsDirMapBitReset(this, f); */
     IO->unlinkFile(fullPath(f,nullptr));
@@ -1375,7 +1374,7 @@ Fs::Ufs::UFSSwapDir::DirClean(int swap_index)
         k = 10;
 
     for (n = 0; n < k; ++n) {
-        debugs(36, 3, "Cleaning file "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << files[n]);
+        debugs(36, 3, "Cleaning file " << asHex(files[n]).upperCase().minDigits(8));
         SBuf p2(p1);
         p2.appendf("/%08X", files[n]);
         safeunlink(p2.c_str(), 0);
index 2e4b2564744ad6be338b5d2ff19e6f50eda4afb3..c9c75b67954698cad92de64f9157cff61b73c43a 100644 (file)
@@ -97,7 +97,7 @@ getNfmarkCallback(enum nf_conntrack_msg_type, struct nf_conntrack *ct, void *con
 {
     auto *mark = static_cast<nfmark_t *>(connmark);
     *mark = nfct_get_attr_u32(ct, ATTR_MARK);
-    debugs(17, 3, asHex(*mark));
+    debugs(17, 3, "mark=0x" << asHex(*mark));
     return NFCT_CB_CONTINUE;
 }
 
index cd18ce2c00edd08f8b721bc6cd6423d78d46ea1b..6be0694dcd8bfa19e2b7d502656805ce8535e16b 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 54    Interprocess Communication */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "ipc/StoreMap.h"
 #include "sbuf/SBuf.h"
 #include "SquidConfig.h"
@@ -856,7 +857,7 @@ Ipc::StoreMap::validateHit(const sfileno fileno)
            "    expires=" << anchor.basics.expires << "\n" <<
            "    lastmod=" << anchor.basics.lastmod << "\n" <<
            "    refcount=" << anchor.basics.refcount << "\n" <<
-           "    flags=0x" << std::hex << anchor.basics.flags << std::dec << "\n" <<
+           "    flags=0x" << asHex(anchor.basics.flags) << "\n" <<
            "    start=" << anchor.start << "\n" <<
            "    splicingPoint=" << anchor.splicingPoint << "\n" <<
            "    lock=" << anchor.lock << "\n" <<
index 18cd5c346170123c4c0173f01e6381d22b0812c9..41290fdaee54b1ced8a3c98e47cda47daf55c61a 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 14    IP Cache */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "CacheManager.h"
 #include "cbdata.h"
 #include "debug/Messages.h"
@@ -729,7 +730,7 @@ ipcache_gethostbyname(const char *name, int flags)
 {
     ipcache_entry *i = nullptr;
     assert(name);
-    debugs(14, 3, "ipcache_gethostbyname: '" << name  << "', flags=" << std::hex << flags);
+    debugs(14, 3, "'" << name  << "', flags=" << asHex(flags));
     ++IpcacheStats.requests;
     i = ipcache_get(name);
 
index 162981525688ba4afa8071d23aaf11134be26369..2884abb4928876b1c8476a99e83b432cd043eec6 100644 (file)
@@ -453,7 +453,7 @@ Security::ErrorDetail::ErrorDetail(const ErrorCode err, const int aSysErrorNo):
 #if USE_OPENSSL
     /// Extract and remember errors stored internally by the TLS library.
     if ((lib_error_no = ERR_get_error())) {
-        debugs(83, 7, "got " << asHex(lib_error_no));
+        debugs(83, 7, "got 0x" << asHex(lib_error_no));
         // more errors may be stacked
         // TODO: Save/detail all stacked errors by always flushing stale ones.
         ForgetErrors();
@@ -507,7 +507,7 @@ Security::ErrorDetail::brief() const
 #if USE_OPENSSL
         // TODO: Log ERR_error_string_n() instead, despite length, whitespace?
         // Example: `error:1408F09C:SSL routines:ssl3_get_record:http request`.
-        os << "+TLS_LIB_ERR=" << std::hex << std::uppercase << lib_error_no << std::nouppercase << std::dec;
+        os << "+TLS_LIB_ERR=" << asHex(lib_error_no).upperCase();
 #elif USE_GNUTLS
         os << '+' << gnutls_strerror_name(lib_error_no);
 #endif
index 1b7f095d8c9c55adc2cd1d94480d77a23629a1b5..264479ffc43f412f1d11760df750456d47f1f181 100644 (file)
@@ -127,9 +127,9 @@ ParseProtocolVersionBase(Parser::BinaryTokenizer &tk, const char *contextLabel,
     /* handle unsupported versions */
 
     const uint16_t vRaw = (vMajor << 8) | vMinor;
-    debugs(83, 7, "unsupported: " << asHex(vRaw));
+    debugs(83, 7, "unsupported: 0x" << asHex(vRaw));
     if (beStrict)
-        throw TextException(ToSBuf("unsupported TLS version: ", asHex(vRaw)), Here());
+        throw TextException(ToSBuf("unsupported TLS version: 0x", asHex(vRaw)), Here());
     // else hide unsupported version details from the caller behind PROTO_NONE
     return AnyP::ProtocolVersion();
 }
index 8d9a8f45838bcee2845f0f6b758cfc9140aa30a0..2118058eddb4251e9903e1ab22ae78651acc46d0 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "squid.h"
 #include "anyp/PortCfg.h"
+#include "base/IoManip.h"
 #include "base/Packable.h"
 #include "cache_cf.h"
 #include "error/SysErrorDetail.h"
@@ -380,7 +381,7 @@ Security::ServerOptions::loadDhParams()
     int codes;
     if (DH_check(dhp, &codes) == 0) {
         if (codes) {
-            debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhParamsFile << "' (" << std::hex << codes << ")");
+            debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhParamsFile << "' (" << asHex(codes) << ")");
             DH_free(dhp);
             dhp = nullptr;
         }
index 0791b8b6c87928039807cf452e838155750d3ce5..3f045ed0edf86be505c7118e5a66fc5ce5c128ee 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 83    SSL accelerator support */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "ssl/support.h"
 
 /* support.cc says this is needed */
@@ -160,7 +161,7 @@ Ssl::Bio::stateChanged(const SSL *ssl, int where, int)
     // else if (where & SSL_CB_HANDSHAKE_DONE)
     //    debugs(83, 9, "SSL connection established");
 
-    debugs(83, 7, "FD " << fd_ << " now: 0x" << std::hex << where << std::dec << ' ' <<
+    debugs(83, 7, "FD " << fd_ << " now: 0x" << asHex(where) << ' ' <<
            SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")");
 }
 
index 3ebe1666de26849ee933c1d329b29501b9621423..0d6c6f183820399fd77bd58e0be3350326e6bfbc 100644 (file)
@@ -35,7 +35,7 @@ Ssl::ReportAndForgetErrors(std::ostream &os)
 {
     unsigned int reported = 0; // efficiently marks ForgetErrors() call boundary
     while (const auto errorToForget = ERR_get_error())
-        os << Debug::Extra << "OpenSSL-saved error #" << (++reported) << ": " << asHex(errorToForget);
+        os << Debug::Extra << "OpenSSL-saved error #" << (++reported) << ": 0x" << asHex(errorToForget);
     return os;
 }
 
index 9083f22308548037cca86144b249ce1ac2403dfe..97d2e87c610a9912da0444a3499f28185b83237a 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "squid.h"
 #include "base/AsyncCbdataCalls.h"
+#include "base/IoManip.h"
 #include "base/PackableStream.h"
 #include "base/TextException.h"
 #include "CacheDigest.h"
@@ -1927,8 +1928,7 @@ StoreEntry::attachToDisk(const sdirno dirn, const sfileno fno, const swap_status
 {
     debugs(88, 3, "attaching entry with key " << getMD5Text() << " : " <<
            swapStatusStr[status] << " " << dirn << " " <<
-           std::hex << std::setw(8) << std::setfill('0') <<
-           std::uppercase << fno);
+           asHex(fno).upperCase().minDigits(8));
     checkDisk();
     swap_dirn = dirn;
     swap_filen = fno;
index 1fa4756ca8b24ec8922d1e394e795227d6737c65..b25ad253e5806dcb5cdcf93058390b321b3ba579 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 47    Store Directory Routines */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "cache_cf.h"
 #include "ConfigParser.h"
 #include "debug/Messages.h"
@@ -853,7 +854,7 @@ storeDirSwapLog(const StoreEntry * e, int op)
            swap_log_op_str[op] << " " <<
            e->getMD5Text() << " " <<
            e->swap_dirn << " " <<
-           std::hex << std::uppercase << std::setfill('0') << std::setw(8) << e->swap_filen);
+           asHex(e->swap_filen).upperCase().minDigits(8));
 
     e->disk().logEntry(*e, op);
 }
index 5c61e7238a8cb9c086fbc7e3b5cf2cb450013e53..d65c5f50d652945e7864702b9726b761da2bcf99 100644 (file)
@@ -9,6 +9,7 @@
 /* DEBUG: section 20    Storage Manager Swapout Functions */
 
 #include "squid.h"
+#include "base/IoManip.h"
 #include "cbdata.h"
 #include "CollapsedForwarding.h"
 #include "globals.h"
@@ -44,8 +45,7 @@ storeSwapOutStart(StoreEntry * e)
      * metadata there is to store
      */
     debugs(20, 5, "storeSwapOutStart: Begin SwapOut '" << e->url() << "' to dirno " <<
-           e->swap_dirn << ", fileno " << std::hex << std::setw(8) << std::setfill('0') <<
-           std::uppercase << e->swap_filen);
+           e->swap_dirn << ", fileno " << asHex(e->swap_filen).upperCase().minDigits(8));
     /* If we start swapping out objects with OutOfBand Metadata,
      * then this code needs changing
      */
@@ -279,8 +279,8 @@ storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self)
     // if object_size is still unknown, the entry was probably aborted
     if (errflag || e->objectLen() < 0) {
         debugs(20, 2, "storeSwapOutFileClosed: dirno " << e->swap_dirn << ", swapfile " <<
-               std::hex << std::setw(8) << std::setfill('0') << std::uppercase <<
-               e->swap_filen << ", errflag=" << errflag);
+               asHex(e->swap_filen).upperCase().minDigits(8) <<
+               ", errflag=" << errflag);
 
         if (errflag == DISK_NO_SPACE_LEFT) {
             /* TODO: this should be handle by the link from store IO to
@@ -298,8 +298,7 @@ storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self)
     } else {
         /* swapping complete */
         debugs(20, 3, "storeSwapOutFileClosed: SwapOut complete: '" << e->url() << "' to " <<
-               e->swap_dirn  << ", " << std::hex << std::setw(8) << std::setfill('0') <<
-               std::uppercase << e->swap_filen);
+               e->swap_dirn  << ", " << asHex(e->swap_filen).upperCase().minDigits(8));
         debugs(20, 5, "swap_file_sz = " <<
                e->objectLen() << " + " << mem->swap_hdr_sz);
 
diff --git a/src/tests/testIoManip.cc b/src/tests/testIoManip.cc
new file mode 100644 (file)
index 0000000..22abc60
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "base/IoManip.h"
+#include "compat/cppunit.h"
+#include "unitTestMain.h"
+
+#include <cstdint>
+#include <limits>
+#include <sstream>
+
+class TestIoManip: public CPPUNIT_NS::TestFixture
+{
+    CPPUNIT_TEST_SUITE(TestIoManip);
+    CPPUNIT_TEST(testAsHex);
+    CPPUNIT_TEST_SUITE_END();
+
+protected:
+    void testAsHex();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( TestIoManip );
+
+/// resets the given stream, including any formatting
+static void
+resetStream(std::ostringstream &dirty)
+{
+    std::ostringstream clean;
+    dirty.swap(clean);
+}
+
+/// returns the result of printing the given manipulator
+template <typename Integer>
+static std::string
+toStdString(const AsHex<Integer> &manipulator)
+{
+    std::ostringstream os;
+    os << manipulator;
+    return os.str();
+}
+
+void
+TestIoManip::testAsHex()
+{
+    // basic values
+    CPPUNIT_ASSERT_EQUAL(std::string("0"), toStdString(asHex(0)));
+    CPPUNIT_ASSERT_EQUAL(std::string("123abc"), toStdString(asHex(0x123abc)));
+
+    // large values
+    CPPUNIT_ASSERT_EQUAL(std::string("7fffffffffffffff"), toStdString(asHex(std::numeric_limits<int64_t>::max())));
+    CPPUNIT_ASSERT_EQUAL(std::string("ffffffffffffffff"), toStdString(asHex(std::numeric_limits<uint64_t>::max())));
+
+    // negative values
+    // C++ defines printing with std::hex in terms of calling std::printf() with
+    // %x (or %X) conversion specifier; printf(%x) interprets its value argument
+    // as an unsigned integer, making it impossible for std::hex to print
+    // negative values as negative hex integers. AsHex has the same limitation.
+    CPPUNIT_ASSERT_EQUAL(std::string("80000000"), toStdString(asHex(std::numeric_limits<int32_t>::min())));
+
+    // integer and integer-like types that std::ostream formats specially by default
+    CPPUNIT_ASSERT_EQUAL(std::string("0"), toStdString(asHex(false)));
+    CPPUNIT_ASSERT_EQUAL(std::string("1"), toStdString(asHex(true)));
+    CPPUNIT_ASSERT_EQUAL(std::string("5a"), toStdString(asHex('Z')));
+    CPPUNIT_ASSERT_EQUAL(std::string("77"), toStdString(asHex(int8_t(0x77))));
+    CPPUNIT_ASSERT_EQUAL(std::string("ff"), toStdString(asHex(uint8_t(0xFF))));
+
+    // other interesting integer-like types we may want to print
+    enum { enumValue = 0xABCD };
+    CPPUNIT_ASSERT_EQUAL(std::string("abcd"), toStdString(asHex(enumValue)));
+    struct { uint8_t bitField:2; } s;
+    s.bitField = 3; // TODO: Convert to default initializer after switching to C++20.
+    CPPUNIT_ASSERT_EQUAL(std::string("3"), toStdString(asHex(s.bitField)));
+
+    // padding with zeros works
+    CPPUNIT_ASSERT_EQUAL(std::string("1"), toStdString(asHex(1).minDigits(1)));
+    CPPUNIT_ASSERT_EQUAL(std::string("01"), toStdString(asHex(1).minDigits(2)));
+    CPPUNIT_ASSERT_EQUAL(std::string("001"), toStdString(asHex(1).minDigits(3)));
+
+    // padding with zeros works even for zero values
+    CPPUNIT_ASSERT_EQUAL(std::string("0000"), toStdString(asHex(0).minDigits(4)));
+
+    // minDigits() does not truncate
+    CPPUNIT_ASSERT_EQUAL(std::string("1"), toStdString(asHex(0x1).minDigits(0)));
+    CPPUNIT_ASSERT_EQUAL(std::string("12"), toStdString(asHex(0x12).minDigits(1)));
+    CPPUNIT_ASSERT_EQUAL(std::string("123"), toStdString(asHex(0x123).minDigits(2)));
+
+    // upperCase() forces uppercase
+    CPPUNIT_ASSERT_EQUAL(std::string("A"), toStdString(asHex(0xA).upperCase()));
+    CPPUNIT_ASSERT_EQUAL(std::string("1A2B"), toStdString(asHex(0x1a2b).upperCase(true)));
+
+    std::ostringstream ss;
+
+    // upperCase(false) forces lowercase
+    ss << std::uppercase << asHex(0xABC).upperCase(false);
+    CPPUNIT_ASSERT_EQUAL(std::string("abc"), ss.str());
+    resetStream(ss);
+
+    // a combination of formatting options
+    CPPUNIT_ASSERT_EQUAL(std::string("01A"), toStdString(asHex(0x1A).upperCase().minDigits(3)));
+
+    // Test the effects of stream formatting flags on AsHex printing and the
+    // effects of AsHex printing on stream formatting flags.
+
+    // upperCase() effects are not leaked into the stream
+    ss << asHex(0xa0).upperCase() << asHex(0xa0);
+    CPPUNIT_ASSERT_EQUAL(std::string("A0a0"), ss.str());
+    resetStream(ss);
+
+    // original std::showbase is honored
+    ss << std::showbase << asHex(1);
+    CPPUNIT_ASSERT_EQUAL(std::string("0x1"), ss.str());
+    resetStream(ss);
+
+    // original std::uppercase is honored
+    ss << std::uppercase << std::hex << 0xA << asHex(0xB) << 0xC;
+    CPPUNIT_ASSERT_EQUAL(std::string("ABC"), ss.str());
+    resetStream(ss);
+
+    // original std::uppercase is preserved
+    ss << std::uppercase << std::hex << 0xA << asHex(0xB).upperCase(false) << 0xC;
+    CPPUNIT_ASSERT_EQUAL(std::string("AbC"), ss.str());
+    resetStream(ss);
+
+    // original std::oct is preserved
+    ss << std::oct << 9 << asHex(0xA) << 11;
+    CPPUNIT_ASSERT_EQUAL(std::string("11a13"), ss.str());
+    resetStream(ss);
+
+    // original std::setw() is honored
+    ss << std::setw(4) << asHex(0x1);
+    CPPUNIT_ASSERT_EQUAL(std::string("   1"), ss.str());
+    resetStream(ss);
+
+    // original std::setw() is consumed (by the printed number)
+    ss << std::setw(4) << asHex(0x1) << 2;
+    CPPUNIT_ASSERT_EQUAL(std::string("   12"), ss.str());
+    resetStream(ss);
+
+    // original std::setfill() is honored
+    ss << std::setfill('.') << std::setw(4) << asHex(0x2);
+    CPPUNIT_ASSERT_EQUAL(std::string("...2"), ss.str());
+    resetStream(ss);
+
+    // original std::setfill() is preserved
+    ss << std::setfill('.') << asHex(0x3).minDigits(2) << std::setw(4) << 4;
+    CPPUNIT_ASSERT_EQUAL(std::string("03...4"), ss.str());
+    resetStream(ss);
+}
+
+int
+main(int argc, char *argv[])
+{
+    return TestProgram().run(argc, argv);
+}
+