/*
- * Copyright (C) 1996-2018 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.
#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;
debugs(24, 8, id << " finish appending " << actualSize << " bytes");
size_type newSize = length() + actualSize;
- Must2(newSize <= min(maxSize,store_->capacity-off_), "raw append overflow");
+ Must3(newSize <= min(maxSize, store_->capacity-off_), "raw append fits", Here());
len_ = newSize;
store_->size = off_ + newSize;
}
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();
}
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;
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
- Must2(sz >= 0, "vsnprintf() output error");
+ Must3(sz >= 0, "vsnprintf() succeeds", Here());
/* check for possible overflow */
/* snprintf on Linux returns -1 on output errors, or the size
requiredSpaceEstimate = sz*2; // TODO: tune heuristics
space = rawSpace(requiredSpaceEstimate);
sz = vsnprintf(space, spaceSize(), fmt, vargs);
- Must2(sz >= 0, "vsnprintf() output error despite increased buffer space");
+ Must3(sz >= 0, "vsnprintf() succeeds (with increased buffer space)", Here());
}
// data was appended, update internal state
++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;
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());
* 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);
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);
}
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);
}