]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/sbuf/SBuf.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / sbuf / SBuf.cc
index 663477fc5b80ca5fa5c59d0fb26dee839b843b01..2b5d5a92e475fbda4ae89ce23221b1091c593629 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -11,8 +11,6 @@
 #include "base/RefCount.h"
 #include "Debug.h"
 #include "sbuf/DetailedStats.h"
-#include "sbuf/Exceptions.h"
-#include "sbuf/OutOfBoundsException.h"
 #include "sbuf/SBuf.h"
 #include "util.h"
 
 #include <iostream>
 #include <sstream>
 
-#ifdef VA_COPY
-#undef VA_COPY
-#endif
-#if defined HAVE_VA_COPY
-#define VA_COPY va_copy
-#elif defined HAVE___VA_COPY
-#define VA_COPY __va_copy
-#endif
-
 InstanceIdDefinitions(SBuf, "SBuf");
 
 SBufStats SBuf::stats;
 const SBuf::size_type SBuf::npos;
 const SBuf::size_type SBuf::maxSize;
 
-SBuf::SBuf()
-    : store_(GetStorePrototype()), off_(0), len_(0)
+SBuf::SBuf() : store_(GetStorePrototype())
 {
     debugs(24, 8, id << " created");
     ++stats.alloc;
@@ -53,8 +41,7 @@ SBuf::SBuf(const SBuf &S)
     ++stats.live;
 }
 
-SBuf::SBuf(const std::string &s)
-    : store_(GetStorePrototype()), off_(0), len_(0)
+SBuf::SBuf(const std::string &s) : store_(GetStorePrototype())
 {
     debugs(24, 8, id << " created from std::string");
     lowAppend(s.data(),s.length());
@@ -62,8 +49,7 @@ SBuf::SBuf(const std::string &s)
     ++stats.live;
 }
 
-SBuf::SBuf(const char *S, size_type n)
-    : store_(GetStorePrototype()), off_(0), len_(0)
+SBuf::SBuf(const char *S, size_type n) : store_(GetStorePrototype())
 {
     append(S,n);
     ++stats.alloc;
@@ -71,8 +57,7 @@ SBuf::SBuf(const char *S, size_type n)
     ++stats.live;
 }
 
-SBuf::SBuf(const char *S)
-    : store_(GetStorePrototype()), off_(0), len_(0)
+SBuf::SBuf(const char *S) : store_(GetStorePrototype())
 {
     append(S,npos);
     ++stats.alloc;
@@ -139,13 +124,35 @@ SBuf::reserve(const SBufReservationRequirements &req)
     if (!mustRealloc && len_ >= req.maxCapacity)
         return spaceSize(); // but we cannot reallocate
 
-    const size_type newSpace = std::min(req.idealSpace, maxSize - len_);
+    const size_type desiredSpace = std::max(req.minSpace, req.idealSpace);
+    const size_type newSpace = std::min(desiredSpace, maxSize - len_);
     reserveCapacity(std::min(len_ + newSpace, req.maxCapacity));
     debugs(24, 7, id << " now: " << off_ << '+' << len_ << '+' << spaceSize() <<
            '=' << store_->capacity);
     return spaceSize(); // reallocated and probably reserved enough space
 }
 
+char *
+SBuf::rawAppendStart(size_type anticipatedSize)
+{
+    char *space = rawSpace(anticipatedSize);
+    debugs(24, 8, id << " start appending up to " << anticipatedSize << " bytes");
+    return space;
+}
+
+void
+SBuf::rawAppendFinish(const char *start, size_type actualSize)
+{
+    Must(bufEnd() == start);
+    Must(store_->canAppend(off_ + len_, actualSize));
+    debugs(24, 8, id << " finish appending " << actualSize << " bytes");
+
+    size_type newSize = length() + actualSize;
+    Must3(newSize <= min(maxSize, store_->capacity-off_), "raw append fits", Here());
+    len_ = newSize;
+    store_->size = off_ + newSize;
+}
+
 char *
 SBuf::rawSpace(size_type minSpace)
 {
@@ -160,9 +167,6 @@ SBuf::rawSpace(size_type minSpace)
         debugs(24, 7, id << " not growing");
         return bufEnd();
     }
-    // TODO: we may try to memmove before realloc'ing in order to avoid
-    //   one allocation operation, if we're the sole owners of a MemBlob.
-    //   Maybe some heuristic on off_ and length()?
     cow(minSpace+length());
     return bufEnd();
 }
@@ -217,7 +221,7 @@ SBuf&
 SBuf::Printf(const char *fmt, ...)
 {
     // with printf() the fmt or an arg might be a dangerous char*
-    // NP: cant rely on vappendf() Locker because of clear()
+    // NP: can't rely on vappendf() Locker because of clear()
     const Locker blobKeeper(this, buf());
 
     va_list args;
@@ -250,14 +254,11 @@ SBuf::vappendf(const char *fmt, va_list vargs)
     size_type requiredSpaceEstimate = strlen(fmt)*2;
 
     char *space = rawSpace(requiredSpaceEstimate);
-#ifdef VA_COPY
     va_list ap;
-    VA_COPY(ap, vargs);
+    va_copy(ap, vargs);
     sz = vsnprintf(space, spaceSize(), fmt, ap);
     va_end(ap);
-#else
-    sz = vsnprintf(space, spaceSize(), fmt, vargs);
-#endif
+    Must3(sz >= 0, "vsnprintf() succeeds", Here());
 
     /* check for possible overflow */
     /* snprintf on Linux returns -1 on output errors, or the size
@@ -269,14 +270,9 @@ SBuf::vappendf(const char *fmt, va_list vargs)
         requiredSpaceEstimate = sz*2; // TODO: tune heuristics
         space = rawSpace(requiredSpaceEstimate);
         sz = vsnprintf(space, spaceSize(), fmt, vargs);
-        if (sz < 0) // output error in vsnprintf
-            throw TextException("output error in second-go vsnprintf",__FILE__,
-                                __LINE__);
+        Must3(sz >= 0, "vsnprintf() succeeds (with increased buffer space)", Here());
     }
 
-    if (sz < 0) // output error in either vsnprintf
-        throw TextException("output error in vsnprintf",__FILE__, __LINE__);
-
     // data was appended, update internal state
     len_ += sz;
 
@@ -523,25 +519,13 @@ SBuf::rawContent() const
     return buf();
 }
 
-void
-SBuf::forceSize(size_type newSize)
-{
-    debugs(24, 8, id << " force " << (newSize > length() ? "grow" : "shrink") << " to length=" << newSize);
-
-    Must(store_->LockCount() == 1);
-    if (newSize > min(maxSize,store_->capacity-off_))
-        throw SBufTooBigException(__FILE__,__LINE__);
-    len_ = newSize;
-    store_->size = newSize;
-}
-
 const char*
 SBuf::c_str()
 {
     ++stats.rawAccess;
     /* null-terminate the current buffer, by hand-appending a \0 at its tail but
      * without increasing its length. May COW, the side-effect is to guarantee that
-     * the MemBlob's tail is availabe for us to use */
+     * the MemBlob's tail is available for us to use */
     *rawSpace(1) = '\0';
     ++store_->size;
     ++stats.setChar;
@@ -661,10 +645,10 @@ SBuf::find(const SBuf &needle, size_type startPos) const
                ", lastPossible=" << (void*) lastPossible );
         tmp = static_cast<char *>(memchr(start, needleBegin, lastPossible-start));
         if (tmp == NULL) {
-            debugs(24, 8 , "First byte not found");
+            debugs(24, 8, "First byte not found");
             return npos;
         }
-        // lastPossible guarrantees no out-of-bounds with memcmp()
+        // lastPossible guarantees no out-of-bounds with memcmp()
         if (0 == memcmp(needle.buf(), tmp, needle.length())) {
             debugs(24, 8, "Found at " << (tmp-buf()));
             return (tmp-buf());
@@ -856,37 +840,29 @@ SBuf::toUpper()
     ++stats.caseChange;
 }
 
-/**
- * checks whether the requested 'pos' is within the bounds of the SBuf
- * \throw OutOfBoundsException if access is out of bounds
- */
-void
-SBuf::checkAccessBounds(size_type pos) const
-{
-    if (pos >= length())
-        throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
-}
-
 /** re-allocate the backing store of the SBuf.
  *
  * If there are contents in the SBuf, they will be copied over.
  * NO verifications are made on the size parameters, it's up to the caller to
  * make sure that the new size is big enough to hold the copied contents.
  * The re-allocated storage MAY be bigger than the requested size due to size-chunking
- * algorithms in MemBlock, it is guarranteed NOT to be smaller.
+ * algorithms in MemBlock, it is guaranteed NOT to be smaller.
  */
 void
 SBuf::reAlloc(size_type newsize)
 {
     debugs(24, 8, id << " new size: " << newsize);
-    if (newsize > maxSize)
-        throw SBufTooBigException(__FILE__, __LINE__);
+    Must(newsize <= maxSize);
+    // TODO: Consider realloc(3)ing in some cases instead.
     MemBlob::Pointer newbuf = new MemBlob(newsize);
-    if (length() > 0)
+    if (length() > 0) {
         newbuf->append(buf(), length());
+        ++stats.cowAllocCopy;
+    } else {
+        ++stats.cowJustAlloc;
+    }
     store_ = newbuf;
     off_ = 0;
-    ++stats.cowSlow;
     debugs(24, 7, id << " new store capacity: " << store_->capacity);
 }
 
@@ -912,10 +888,27 @@ SBuf::cow(SBuf::size_type newsize)
     if (newsize == npos || newsize < length())
         newsize = length();
 
-    if (store_->LockCount() == 1 && newsize == length()) {
-        debugs(24, 8, id << " no cow needed");
-        ++stats.cowFast;
-        return;
+    if (store_->LockCount() == 1) {
+        // MemBlob::size reflects past owners. Refresh to maximize spaceSize().
+        store_->syncSize(off_ + length());
+
+        const auto availableSpace = spaceSize();
+        const auto neededSpace = newsize - length();
+        if (neededSpace <= availableSpace) {
+            debugs(24, 8, id << " no cow needed; have " << availableSpace);
+            ++stats.cowAvoided;
+            return;
+        }
+        // consume idle leading space if doing so avoids reallocation
+        // this case is typical for fill-consume-fill-consume-... I/O buffers
+        if (neededSpace <= availableSpace + off_) {
+            debugs(24, 8, id << " no cow after shifting " << off_ << " to get " << (availableSpace + off_));
+            store_->consume(off_);
+            off_ = 0;
+            ++stats.cowShift;
+            assert(neededSpace <= spaceSize());
+            return;
+        }
     }
     reAlloc(newsize);
 }