]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Maintenance: De-duplicate PackableStreamBuf and SBufStreamBuf (#959)
authorAlex Rousskov <rousskov@measurement-factory.com>
Thu, 13 Jan 2022 22:38:24 +0000 (22:38 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Wed, 19 Jan 2022 20:53:39 +0000 (20:53 +0000)
SBufStreamBuf was almost identical to PackableStreamBuf. Both classes
are pass-through write-only streambufs that lack their own put area. Now
their common code lives in AppendingStreamBuf.

No functionality changes intended.

The removed std::streambuf method descriptions were imprecise and some
were misleading. STL documentation describes this standard/parent API.

While also similar, PackableStream and SBufStream have important
differences in API and implementation. For example, unlike
PackableStream, SBufStream owns the sink buffer and does not modify the
buffer it was created with, providing a safe content extraction method
instead. The two classes cannot be merged without changing their users,
and it may be impossible to justify SBufStream users exposure to the
dangers of forgetting to sync() the streambuf before accessing the sink.

src/base/PackableStream.h
src/sbuf/Stream.h

index 28e9e1506b49694c978e86615e2e0848034875b8..080768c55bea0e491eeac0d617c31b54717c1ec2 100644 (file)
 
 #include <ostream>
 
-/**
- * Provides a streambuf interface for writing to Packable objects.
- * Typical use is via a PackableStream rather than direct manipulation
- */
-class PackableStreamBuf : public std::streambuf
+// TODO: Move to src/base/AppendingStreamBuf.h
+/// write-only std::streambuf that append()s all writes to a given Buffer
+template <class Buffer>
+class AppendingStreamBuf : public std::streambuf
 {
 public:
-    explicit PackableStreamBuf(Packable &p) : buf_(p) { buf_.buffer(); }
-    virtual ~PackableStreamBuf() = default;
+    explicit AppendingStreamBuf(Buffer &p): buf_(p) { postInit(); }
+    virtual ~AppendingStreamBuf() = default;
 
 protected:
-    /** flush the current buffer and the character that is overflowing
-     * to the Packable.
-     */
+    /* std::streambuf API */
+
     virtual int_type overflow(int_type aChar = traits_type::eof()) override {
         std::streamsize pending(pptr() - pbase());
 
@@ -42,28 +40,38 @@ protected:
         return aChar;
     }
 
-    /** push the buffer to the Packable */
     virtual int sync() override {
         std::streamsize pending(pptr() - pbase());
         lowAppend(pbase(), pending);
-        buf_.flush();
+        postSync();
         return 0;
     }
 
-    /** write multiple characters to the Packable
-     * - this is an optimisation method.
-     */
     virtual std::streamsize xsputn(const char * chars, std::streamsize number) override {
         lowAppend(chars, number);
         return number;
     }
 
 private:
+    /// for specializations that must customize the last construction step
+    void postInit() {}
+
+    /// for specializations that must customize the last sync() step
+    void postSync() {}
+
     void lowAppend(const char *s, const std::streamsize n) {buf_.append(s,n);}
 
-    Packable &buf_;
+    Buffer &buf_; ///< the associated character sequence (a.k.a. the sink)
 };
 
+/**
+ * Provides a streambuf interface for writing to Packable objects.
+ * Typical use is via a PackableStream rather than direct manipulation
+ */
+using PackableStreamBuf = AppendingStreamBuf<Packable>;
+template <> inline void PackableStreamBuf::postInit() { buf_.buffer(); }
+template <> inline void PackableStreamBuf::postSync() { buf_.flush(); }
+
 class PackableStream : public std::ostream
 {
 public:
index d910d480afeb50f987c653a3e1be61bbfd3bcf71..5359ba94f3e7e062e2596dad538f5aab57345fa7 100644 (file)
 #define SQUID_SBUFSTREAM_H
 
 #include "sbuf/SBuf.h"
-
-#include <ostream>
-
-/** streambuf class for a SBuf-backed stream interface.
- *
- * Auxiliary class to be able to leverage an ostream generating SBuf's
- * analogous to std::ostrstream.
- */
-class SBufStreamBuf : public std::streambuf
-{
-public:
-    /// initialize streambuf; use supplied SBuf as backing store
-    explicit SBufStreamBuf(SBuf aBuf) : theBuf(aBuf) {}
-
-    /// get a copy of the stream's contents
-    SBuf getBuf() {
-        return theBuf;
-    }
-
-    /// clear the stream's store
-    void clearBuf() {
-        theBuf.clear();
-    }
-
-protected:
-    virtual int_type overflow(int_type aChar = traits_type::eof()) {
-        std::streamsize pending(pptr() - pbase());
-
-        if (pending && sync())
-            return traits_type::eof();
-
-        if (aChar != traits_type::eof()) {
-            char chars[1] = {static_cast<char>(aChar)};
-
-            if (aChar != traits_type::eof())
-                theBuf.append(chars, 1);
-        }
-
-        pbump(-pending);  // Reset pptr().
-        return aChar;
-    }
-
-    /// push the streambuf to the backing SBuf
-    virtual int sync() {
-        std::streamsize pending(pptr() - pbase());
-
-        if (pending)
-            theBuf.append(pbase(), pending);
-
-        return 0;
-    }
-
-    /** write multiple characters to the store entry
-     * \note this is an optimisation consistent with std::streambuf API
-     */
-    virtual std::streamsize xsputn(const char * chars, std::streamsize number) {
-        if (number)
-            theBuf.append(chars, number);
-
-        return number;
-    }
-
-private:
-    SBuf theBuf;
-    SBufStreamBuf(); // no default constructor
-};
+#include "base/PackableStream.h"
 
 /** Stream interface to write to a SBuf.
  *
@@ -90,32 +25,36 @@ public:
      * The supplied SBuf copied: in order to retrieve the written-to contents
      * they must be later fetched using the buf() class method.
      */
-    SBufStream(SBuf aBuf): std::ostream(0), theBuffer(aBuf) {
-        rdbuf(&theBuffer); // set the buffer to now-initialized theBuffer
-        clear(); //clear badbit set by calling init(0)
+    SBufStream(const SBuf &aBuf):
+        std::ostream(nullptr), // initialize the parent; no stream buffer yet
+        sink_(aBuf),
+        streamBuffer_(sink_) // initialize the stream buffer
+    {
+        rdbuf(&streamBuffer_); // supply the now-initialized stream buffer
+        clear(); // clear the badbit that a nullptr stream buffer has triggered
     }
 
     /// Create an empty SBufStream
-    SBufStream(): std::ostream(0), theBuffer(SBuf()) {
-        rdbuf(&theBuffer); // set the buffer to now-initialized theBuffer
-        clear(); //clear badbit set by calling init(0)
-    }
+    SBufStream(): SBufStream(SBuf()) {}
 
-    /// Retrieve a copy of the current stream status
+    /// bytes written so far
     SBuf buf() {
         flush();
-        return theBuffer.getBuf();
+        return sink_;
     }
 
     /// Clear the stream's backing store
     SBufStream& clearBuf() {
         flush();
-        theBuffer.clearBuf();
+        sink_.clear();
         return *this;
     }
 
 private:
-    SBufStreamBuf theBuffer;
+    /// buffer for (flushed) bytes written to the stream
+    SBuf sink_;
+    /// writes raw (post-formatting) bytes to the sink_
+    AppendingStreamBuf<SBuf> streamBuffer_;
 };
 
 /// slowly stream-prints all arguments into a freshly allocated SBuf