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();
}
{
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);
}
/* end of stubs */
// test string
-static char fox[]="The quick brown fox jumped over the lazy dog";
-static char fox1[]="The quick brown fox ";
-static char fox2[]="jumped over the lazy dog";
+static const char fox[] = "The quick brown fox jumped over the lazy dog";
+static const char fox1[] = "The quick brown fox ";
+static const char fox2[] = "jumped over the lazy dog";
// TEST: globals variables (default/empty and with contents) are
// created outside and before any unit tests and memory subsystem
buffer.reserve(requirements);
CPPUNIT_ASSERT(buffer.spaceSize() >= requirements.minSpace);
}
+
+ // TODO: Decide whether to encapsulate the (nearly identical) code of the
+ // gap-related test cases below into a function, obscuring each case logic a
+ // little, but facilitating new test cases (and removing code duplication).
+
+ { // reserveSpace() uses the trailing space before the front gap
+ SBuf buffer(fox);
+
+ // assure there is some trailing space
+ buffer.reserveSpace(1);
+ CPPUNIT_ASSERT(buffer.spaceSize() > 0);
+
+ // create a leading gap and (weak-)check that it was created
+ const auto gap = 1U; // the smallest gap may be the most challenging
+ CPPUNIT_ASSERT(gap < buffer.length());
+ const void *gapEnd = buffer.rawContent() + gap;
+ buffer.consume(gap);
+ CPPUNIT_ASSERT_EQUAL(gapEnd, static_cast<const void*>(buffer.rawContent()));
+
+ const auto before = SBuf::GetStats();
+ const auto beforeSpaceSize = buffer.spaceSize();
+ const void * const beforePosition = buffer.rawContent();
+ buffer.reserveSpace(beforeSpaceSize);
+ const auto after = SBuf::GetStats();
+ const void * const afterPosition = buffer.rawContent();
+ CPPUNIT_ASSERT_EQUAL(before.cowAvoided + 1, after.cowAvoided);
+ CPPUNIT_ASSERT_EQUAL(before.cowShift, after.cowShift);
+ CPPUNIT_ASSERT_EQUAL(before.cowJustAlloc, after.cowJustAlloc);
+ CPPUNIT_ASSERT_EQUAL(before.cowAllocCopy, after.cowAllocCopy);
+ CPPUNIT_ASSERT_EQUAL(beforeSpaceSize, buffer.spaceSize());
+ CPPUNIT_ASSERT_EQUAL(beforePosition, afterPosition);
+ CPPUNIT_ASSERT(strcmp(fox + gap, buffer.c_str()) == 0);
+ }
+
+ { // reserveSpace() uses the front gap when the trailing space is not enough
+ SBuf buffer(fox);
+
+ // assure there is some trailing space to keep the test case challenging
+ buffer.reserveSpace(1);
+ CPPUNIT_ASSERT(buffer.spaceSize() > 0);
+ const void * const initialStorage = buffer.rawContent();
+
+ // create a leading gap and (weak-)check that it was created
+ const auto gap = 1U; // the smallest gap may be the most challenging
+ CPPUNIT_ASSERT(gap < buffer.length());
+ const void *gapEnd = buffer.rawContent() + gap;
+ buffer.consume(gap);
+ CPPUNIT_ASSERT_EQUAL(gapEnd, static_cast<const void*>(buffer.rawContent()));
+
+ const auto before = SBuf::GetStats();
+ const auto beforeSpaceSize = buffer.spaceSize();
+ buffer.reserveSpace(beforeSpaceSize + gap); // force (entire) gap use
+ const auto after = SBuf::GetStats();
+ const void * const afterStorage = buffer.rawContent();
+ CPPUNIT_ASSERT_EQUAL(before.cowAvoided, after.cowAvoided);
+ CPPUNIT_ASSERT_EQUAL(before.cowShift + 1, after.cowShift);
+ CPPUNIT_ASSERT_EQUAL(before.cowJustAlloc, after.cowJustAlloc);
+ CPPUNIT_ASSERT_EQUAL(before.cowAllocCopy, after.cowAllocCopy);
+ CPPUNIT_ASSERT_EQUAL(initialStorage, afterStorage);
+ CPPUNIT_ASSERT(beforeSpaceSize + gap <= buffer.spaceSize());
+ CPPUNIT_ASSERT(strcmp(fox + gap, buffer.c_str()) == 0);
+ }
+
+ { // reserveSpace() uses the entire front gap when using the front gap
+ SBuf buffer(fox);
+
+ // assure there is some trailing space to keep the test case challenging
+ buffer.reserveSpace(1);
+ CPPUNIT_ASSERT(buffer.spaceSize() > 0);
+ const void * const initialStorage = buffer.rawContent();
+
+ // create a leading gap and (weak-)check that it was created
+ const auto gap = 2U; // the smallest extra gap may be the most challenging
+ CPPUNIT_ASSERT(gap < buffer.length());
+ const void *gapEnd = buffer.rawContent() + gap;
+ buffer.consume(gap);
+ CPPUNIT_ASSERT_EQUAL(gapEnd, static_cast<const void*>(buffer.rawContent()));
+
+ const auto before = SBuf::GetStats();
+ const auto beforeSpaceSize = buffer.spaceSize();
+ buffer.reserveSpace(beforeSpaceSize + 1); // force (minimal) gap use
+ const auto after = SBuf::GetStats();
+ const void * const afterStorage = buffer.rawContent();
+ CPPUNIT_ASSERT_EQUAL(before.cowAvoided, after.cowAvoided);
+ CPPUNIT_ASSERT_EQUAL(before.cowShift + 1, after.cowShift);
+ CPPUNIT_ASSERT_EQUAL(before.cowJustAlloc, after.cowJustAlloc);
+ CPPUNIT_ASSERT_EQUAL(before.cowAllocCopy, after.cowAllocCopy);
+ CPPUNIT_ASSERT_EQUAL(initialStorage, afterStorage);
+ CPPUNIT_ASSERT(beforeSpaceSize + gap <= buffer.spaceSize());
+ CPPUNIT_ASSERT(strcmp(fox + gap, buffer.c_str()) == 0);
+ }
}
void