# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kinkie@squid-cache.org-20130401173418-j7o0k3fyhlo20rpt # target_branch: ../trunk/ # testament_sha1: 256f7493215cdb0f5101135db8af33df5ec14f87 # timestamp: 2013-04-01 19:42:15 +0200 # base_revision_id: squid3@treenet.co.nz-20130401095422-\ # 17v6spp1fcsw563o # # Begin patch === modified file 'src/Makefile.am' --- src/Makefile.am 2013-03-18 04:55:51 +0000 +++ src/Makefile.am 2013-03-29 17:55:51 +0000 @@ -24,7 +24,13 @@ SBUF_SOURCE= \ base/InstanceId.h \ MemBlob.h \ - MemBlob.cc + MemBlob.cc \ + OutOfBoundsException.h \ + SBuf.h \ + SBuf.cc \ + SBuf.cci \ + SBufExceptions.h \ + SBufExceptions.cc LOADABLE_MODULES_SOURCES = \ LoadableModule.h \ @@ -1088,6 +1094,7 @@ tests/testStore \ tests/testString \ tests/testURL \ + tests/testSBuf \ tests/testConfigParser \ tests/testStatHist \ $(STORE_TESTS) @@ -3787,6 +3794,42 @@ $(REPL_OBJS) \ $(SQUID_CPPUNIT_LA) +tests_testSBuf_SOURCES= \ + tests/testSBuf.h \ + tests/testSBuf.cc \ + tests/testMain.cc \ + tests/SBufFindTest.h \ + tests/SBufFindTest.cc \ + $(SBUF_SOURCE) \ + SBufStream.h \ + time.cc \ + mem.cc \ + tests/stub_debug.cc \ + tests/stub_fatal.cc \ + tests/stub_HelperChildConfig.cc \ + tests/stub_cache_cf.cc \ + tests/stub_cache_manager.cc \ + tests/stub_store.cc \ + tests/stub_store_stats.cc \ + tests/stub_tools.cc \ + SquidString.h \ + String.cc \ + wordlist.cc \ + MemBuf.cc +nodist_tests_testSBuf_SOURCES=$(TESTSOURCES) +tests_testSBuf_LDFLAGS = $(LIBADD_DL) +tests_testSBuf_LDADD=\ + $(SQUID_CPPUNIT_LIBS) \ + $(SQUID_CPPUNIT_LA) \ + $(COMPAT_LIB) \ + libsquid.la \ + ip/libip.la \ + mgr/libmgr.la \ + base/libbase.la \ + $(top_builddir)/lib/libmiscutil.la \ + $(COMMON_LIBS) +tests_testSBuf_DEPENDENCIES= $(SQUID_CPPUNIT_LA) + tests_testConfigParser_SOURCES = \ ClientInfo.h \ Mem.h \ === modified file 'src/MemBlob.cc' --- src/MemBlob.cc 2012-11-15 22:12:03 +0000 +++ src/MemBlob.cc 2013-03-29 16:50:25 +0000 @@ -46,6 +46,17 @@ MemBlobStats::MemBlobStats(): alloc(0), live(0), append(0), liveBytes(0) {} +MemBlobStats& +MemBlobStats::operator += (const MemBlobStats& s) +{ + alloc+=s.alloc; + live+=s.live; + append+=s.append; + liveBytes+=s.liveBytes; + + return *this; +} + std::ostream& MemBlobStats::dump(std::ostream &os) const { === modified file 'src/MemBlob.h' --- src/MemBlob.h 2012-10-29 04:59:58 +0000 +++ src/MemBlob.h 2013-03-29 16:50:25 +0000 @@ -46,6 +46,8 @@ /// dumps class-wide statistics std::ostream& dump(std::ostream& os) const; + MemBlobStats& operator += (const MemBlobStats&); + public: uint64_t alloc; ///< number of MemBlob instances created so far uint64_t live; ///< number of MemBlob instances currently alive === added file 'src/OutOfBoundsException.h' --- src/OutOfBoundsException.h 1970-01-01 00:00:00 +0000 +++ src/OutOfBoundsException.h 2013-04-01 17:31:11 +0000 @@ -0,0 +1,22 @@ +#ifndef _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H +#define _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H + +#include "base/TextException.h" +#include "SBuf.h" + +/** + * Exception raised when the user is going out of bounds when accessing + * a char within the SBuf + */ +class OutOfBoundsException : public TextException +{ +public: + OutOfBoundsException(const SBuf &buf, SBuf::size_type &pos, const char *aFileName = 0, int aLineNo = -1); + virtual ~OutOfBoundsException() throw(); + +protected: + SBuf theThrowingBuf; + SBuf::size_type accessedPosition; +}; + +#endif /* _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H */ === added file 'src/SBuf.cc' --- src/SBuf.cc 1970-01-01 00:00:00 +0000 +++ src/SBuf.cc 2013-04-01 17:31:11 +0000 @@ -0,0 +1,851 @@ +/* + * SBuf.cc (C) 2008 Francesco Chemolli + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "squid.h" +#include "base/RefCount.h" +#include "Debug.h" +#include "OutOfBoundsException.h" +#include "SBuf.h" +#include "SBufExceptions.h" +#include "util.h" + +#if HAVE_STRING_H +#include +#endif + +#if HAVE_SSTREAM +#include +#endif + +#if HAVE_IOSTREAM +#include +#endif + +#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; + +SBufStats::SBufStats() + : alloc(0), allocCopy(0), allocFromString(0), allocFromCString(0), + assignFast(0), clear(0), append(0), toStream(0), setChar(0), + getChar(0), compareSlow(0), compareFast(0), copyOut(0), + rawAccess(0), chop(0), trim(0), find(0), scanf(0), + caseChange(0), cowFast(0), cowSlow(0), live(0) +{} + +SBufStats& +SBufStats::operator +=(const SBufStats& ss) +{ + alloc += ss.alloc; + allocCopy += ss.allocCopy; + allocFromString += ss.allocFromString; + allocFromCString += ss.allocFromCString; + assignFast += ss.assignFast; + clear += ss.clear; + append += ss.append; + toStream += ss.toStream; + setChar += ss.setChar; + getChar += ss.getChar; + compareSlow += ss.compareSlow; + compareFast += ss.compareFast; + copyOut += ss.copyOut; + rawAccess += ss.rawAccess; + chop += ss.chop; + trim += ss.trim; + find += ss.find; + scanf += ss.scanf; + caseChange += ss.caseChange; + cowFast += ss.cowFast; + cowSlow += ss.cowSlow; + live += ss.live; + + return *this; +} + +SBuf::SBuf() + : store_(GetStorePrototype()), off_(0), len_(0) +{ + debugs(24, 8, id << " created"); + ++stats.alloc; + ++stats.live; +} + +SBuf::SBuf(const SBuf &S) + : store_(S.store_), off_(S.off_), len_(S.len_) +{ + debugs(24, 8, id << " created from id " << S.id); + ++stats.alloc; + ++stats.allocCopy; + ++stats.live; +} + +SBuf::SBuf(const String &S) + : store_(GetStorePrototype()), off_(0), len_(0) +{ + debugs(24, 8, id << " created from string"); + assign(S.rawBuf(), 0, S.size()); + ++stats.alloc; + ++stats.allocFromString; + ++stats.live; +} + +SBuf::SBuf(const std::string &s) + : store_(GetStorePrototype()), off_(0), len_(0) +{ + debugs(24, 8, id << " created from string"); + assign(s.data(),0,s.size()); + ++stats.alloc; + ++stats.allocFromString; + ++stats.live; +} + +SBuf::SBuf(const char *S, size_type pos, size_type n) + : store_(GetStorePrototype()), off_(0), len_(0) +{ + append(S,pos,n); //bounds checked in append() + ++stats.alloc; + ++stats.allocFromCString; + ++stats.live; +} + +SBuf::~SBuf() +{ + debugs(24, 8, id << " destructed"); + --stats.live; +} + +SBuf& +SBuf::assign(const SBuf &S) +{ + debugs(24, 7, "assigning " << id << " from " << S.id); + if (&S == this) //assignment to self. Noop. + return *this; + ++stats.assignFast; + store_ = S.store_; + off_ = S.off_; + len_ = S.len_; + return *this; +} + +SBuf& +SBuf::assign(const char *S, size_type pos, size_type n) +{ + debugs(24, 6, id << " from c-string, pos=" << pos << "n=" << n << ")"); + clear(); + return append(S, pos, n); //bounds checked in append() +} + +void +SBuf::reserveCapacity(size_type minCapacity) +{ + Must(0 <= minCapacity && minCapacity <= maxSize); + reserveSpace(minCapacity-length()); +} + +void +SBuf::reserveSpace(size_type minSpace) +{ + Must(0 <= minSpace && minSpace <= maxSize); + debugs(24, 7, "reserving " << minSpace << " for " << id); + // we're not concerned about RefCounts here, + // the store knows the last-used portion. If + // it's available, we're effectively claiming ownership + // of it. If it's not, we need to go away (realloc) + if (store_->canAppend(off_+len_, minSpace)) { + debugs(24, 7, "not growing"); + return; + } + // 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()? + reAlloc(estimateCapacity(minSpace+length())); +} + +void +SBuf::clear() +{ +#if 0 + //enabling this code path, the store will be freed and reinitialized + store_ = GetStorePrototype(); //uncomment to actually free storage upon clear() +#else + //enabling this code path, we try to release the store without deallocating it. + // will be lazily reallocated if needed. + if (store_->LockCount() == 1) + store_->clear(); +#endif + len_ = 0; + off_ = 0; + ++stats.clear; +} + +SBuf& +SBuf::append(const SBuf &S) +{ + return append(S.buf(), 0, S.length()); +} + +SBuf& +SBuf::append(const char * S, size_type pos, size_type n) +{ + Must(pos == npos || pos >= 0); + Must(n == npos || n >= 0); + + if (S == NULL) + return *this; + if (n == npos) + n = strlen(S)-pos; + + debugs(24, 7, "from c-string to id " << id); + + reserveSpace(n); //called method also checks n <= maxSize() + const char *actual_start = S+pos; + store_->append(actual_start, n); + len_ += n; + ++stats.append; + return *this; +} + +SBuf& +SBuf::append(const std::string &str, SBuf::size_type pos, SBuf::size_type n) +{ + return append(str.data(), pos, n); //bounds checked in append() +} + +SBuf& +SBuf::assign(const std::string &str, size_type pos, size_type n) +{ + clear(); + return append(str, pos, n); //bounds checked in append() +} + +SBuf& +SBuf::Printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + clear(); + vappendf(fmt, args); + va_end(args); + return *this; +} + +SBuf& +SBuf::appendf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vappendf(fmt, args); + va_end(args); + return *this; +} + +SBuf& +SBuf::vappendf(const char *fmt, va_list vargs) +{ +#ifdef VA_COPY + va_list ap; +#endif + int sz = 0; + + Must(fmt != NULL); + + //we can assume that we'll need to append at least strlen(fmt) bytes, + reserveSpace(strlen(fmt)*2); + + while (length() <= maxSize) { +#ifdef VA_COPY + /* Fix of bug 753r. The value of vargs is undefined + * after vsnprintf() returns. Make a copy of vargs + * in case we loop around and call vsnprintf() again. + */ + VA_COPY(ap, vargs); + sz = vsnprintf(bufEnd(), store_->spaceSize(), fmt, ap); + va_end(ap); +#else /* VA_COPY */ + sz = vsnprintf(bufEnd(), store_->spaceSize(), fmt, vargs); +#endif /* VA_COPY*/ + /* check for possible overflow */ + /* snprintf on Linux returns -1 on overflows */ + /* snprintf on FreeBSD returns at least free_space on overflows */ + + if (sz >= static_cast(store_->spaceSize())) + reserveSpace(sz*2); // TODO: tune heuristics + else if (sz < 0) // output error in vsnprintf + throw TextException("output error in vsnprintf",__FILE__, __LINE__); + else + break; + } + + len_ += sz; + // TODO: this does NOT belong here, but to class-init or autoconf + /* on Linux and FreeBSD, '\0' is not counted in return value */ + /* on XXX it might be counted */ + /* check that '\0' is appended and not counted */ + + if (operator[](len_-1) == '\0') { + --sz; + --len_; + } + + store_->size += sz; + ++stats.append; + + return *this; +} + +std::ostream& +SBuf::print(std::ostream &os) const +{ + os.write(buf(), length()); + ++stats.toStream; + return os; +} + +std::ostream& +SBuf::dump(std::ostream &os) const +{ + os << id + << ": "; + store_->dump(os); + os << ",offset:" << off_ + << ",len:" << len_ + << ") : '"; + print(os); + os << std::endl; + return os; +} + +void +SBuf::setAt(SBuf::size_type pos, char toset) +{ + checkAccessBounds(pos); + cow(); + store_->mem[off_+pos] = toset; + ++stats.setChar; +} + +int +SBuf::compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n) const +{ + Must(n == npos || n >= 0); + ++stats.compareSlow; + size_type sz = min(S.length(), length()); + if (n != npos) + sz = min(n, sz); + int rv; + if (isCaseSensitive == caseSensitive) + rv = strncmp(buf(), S.buf(), sz); + else + rv = strncasecmp(buf(), S.buf(), sz); + if (rv != 0) + return rv; + //if max-length was specified, it was hit, and the tail is ignored. + if (n != npos) + return 0; + //first sz bytes equal. longest string "wins" + return commonCompareChecksPost(S); +} + +bool +SBuf::startsWith(const SBuf &S, SBufCaseSensitive isCaseSensitive) const +{ + debugs(24, 8, id << " startsWith " << S.id << ", caseSensitive: " << + isCaseSensitive); + if (length() < S.length()) { + debugs(24, 8, "no, too short"); + ++stats.compareFast; + return false; + } + return (compare(S, isCaseSensitive, S.length()) == 0); +} + +bool +SBuf::operator ==(const SBuf & S) const +{ + debugs(24, 8, id << " == " << S.id); + if (length() != S.length()) { + debugs(24, 8, "no, different lengths"); + ++stats.compareFast; + return false; //shortcut: must be equal length + } + if (store_ == S.store_ && off_ == S.off_) { + debugs(24, 8, "yes, same length and backing store"); + ++stats.compareFast; + return true; //shortcut: same store, offset and length + } + ++stats.compareSlow; + const bool rv = (0 == strncmp(buf(), S.buf(), length())); + debugs(24, 8, "returning " << rv); + return rv; +} + +bool +SBuf::operator !=(const SBuf & S) const +{ + return !(*this == S); +} + +SBuf +SBuf::consume(SBuf::size_type n) +{ + Must (n == npos || n >= 0); + if (n == npos) + n = length(); + else + n = min(n, length()); + SBuf rv(substr(0, n)); + chop(n); + return rv; +} + +const +SBufStats& SBuf::GetStats() +{ + return stats; +} + +SBuf::size_type +SBuf::copy(char *dest, SBuf::size_type n) const +{ + Must(n >= 0); + + SBuf::size_type toexport = min(n,length()); + memcpy(dest, buf(), toexport); + ++stats.copyOut; + return toexport; +} + +const char* +SBuf::rawContent() const +{ + ++stats.rawAccess; + return buf(); +} + +char * +SBuf::rawSpace(size_type minSize) +{ + cow(minSize+length()); + ++stats.rawAccess; + return bufEnd(); +} + +void +SBuf::forceSize(SBuf::size_type newSize) +{ + Must(store_->LockCount() == 1); + 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 */ + *rawSpace(1) = '\0'; + ++store_->size; + ++stats.setChar; + return buf(); +} + +SBuf& +SBuf::chop(SBuf::size_type pos, SBuf::size_type n) +{ + if (pos != npos && pos < 0) + pos = 0; + if (n != npos && n < 0) + n = npos; + if (pos == npos || pos > length() || n == 0) { + clear(); + return *this; + } + if (n == npos || (pos+n) > length()) + n = length()-pos; + ++stats.chop; + off_ += pos; + len_ = n; + return *this; +} + +SBuf& +SBuf::trim(const SBuf &toRemove, bool atBeginning, bool atEnd) +{ + ++stats.trim; + if (atEnd) { + const char *p = bufEnd()-1; + while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != NULL) { + //current end-of-buf is in the searched set + --len_; + --p; + } + } + if (atBeginning) { + const char *p = buf(); + while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != NULL) { + --len_; + ++off_; + ++p; + } + } + if (isEmpty()) + clear(); + return *this; +} + +SBuf +SBuf::substr(SBuf::size_type pos, SBuf::size_type n) const +{ + SBuf rv(*this); + rv.chop(pos, n); //stats handled by callee + return rv; +} + +SBuf::size_type +SBuf::find(char c, SBuf::size_type startPos) const +{ + ++stats.find; + + if (startPos == SBuf::npos) + return SBuf::npos; + + // std::string returns npos if needle is outside hay + if (startPos >= length()) + return SBuf::npos; + + // ignore invalid startPos + if (startPos < 0) + startPos = 0; + + const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos); + + if (i == NULL) + return SBuf::npos; + + return (static_cast(i)-buf()); +} + +SBuf::size_type +SBuf::find(const SBuf &needle, size_type startPos) const +{ + // if needle length is 1 use the char search. Stats updated there + if (needle.length() == 1) + return find(needle[0], startPos); + + // if needle length is 1, the stats were + ++stats.find; + + if (startPos == SBuf::npos) + return SBuf::npos; + + // std::string allows needle to overhang hay but not start outside + if (startPos > length()) + return SBuf::npos; + + // ignore invalid startPos + if (startPos < 0) + startPos = 0; + + // for empty needle std::string returns startPos + if (needle.length() == 0) + return startPos; + + char *begin = buf()+startPos; + char *lastPossible = buf()+length()-needle.length()+1; + char needleBegin = needle[0]; + + debugs(24, 7, "looking for " << needle << "starting at " << startPos << + " in id " << id); + while (begin < lastPossible) { + char *tmp; + debugs(24, 8, " begin=" << (void *) begin << + ", lastPossible=" << (void*) lastPossible ); + tmp = static_cast(memchr(begin, needleBegin, lastPossible-begin)); + if (tmp == NULL) { + debugs(24, 8 , "First byte not found"); + return SBuf::npos; + } + // lastPossible guarrantees no out-of-bounds with memcmp() + if (0 == memcmp(needle.buf(), tmp, needle.length())) { + debugs(24, 8, "Found at " << (tmp-buf())); + return (tmp-buf()); + } + begin = tmp+1; + } + debugs(24, 8, "not found"); + return SBuf::npos; +} + +SBuf::size_type +SBuf::rfind(const SBuf &needle, SBuf::size_type endPos) const +{ + // when the needle is 1 char, use the 1-char rfind() + if (needle.length() == 1) + return rfind(needle[0], endPos); + + ++stats.find; + + // on npos input std::string scans from the end of hay + if (endPos == SBuf::npos || endPos > length()) + endPos=length(); + + // on empty hay std::string returns npos + if (length() < needle.length()) + return SBuf::npos; + + if (endPos < 0) + return SBuf::npos; + + // on empty needle std::string returns the position the search starts + if (needle.length() == 0) + return endPos; + +/* std::string permits needle to overhang endPos + if (endPos <= needle.length()) + return npos; +*/ + + char *bufBegin = buf(); + char *cur = bufBegin+endPos; + char needleBegin = needle[0]; + while (cur >= bufBegin) { + if (*cur == needleBegin) { + if (0 == memcmp(needle.buf(), cur, needle.length())) { + // found + return (cur-buf()); + } + } + --cur; + } + return npos; +} + +SBuf::size_type +SBuf::rfind(char c, SBuf::size_type endPos) const +{ + ++stats.find; + + // on empty hay std::string returns size of hay + if (length() < 1) + return SBuf::npos; + + // on npos input std::string compares last octet of hay + if (endPos == SBuf::npos || endPos >= length()) { + endPos=length(); + } else if (endPos < 0 ) { + return SBuf::npos; + } else { + // NP: off-by-one weirdness: + // endPos is an offset ... 0-based + // length() is a count ... 1-based + // memrchr() requires a 1-based count of space to scan. + ++endPos; + } + + const void *i = memrchr(buf(), (int)c, (size_type)endPos); + + if (i == NULL) + return SBuf::npos; + + return (static_cast(i)-buf()); +} + +SBuf::size_type +SBuf::find_first_of(const SBuf &set, size_type startPos) const +{ + // if set is 1 char big, use the char search. Stats updated there + if (set.length() == 1) + return find(set[0], startPos); + + ++stats.find; + + if (startPos == npos) + return SBuf::npos; + + if (startPos > length()) + return SBuf::npos; + + if (startPos < 0) + startPos = 0; + + if (set.length() == 0) + return npos; + + debugs(24, 7, "any of '" << set << "' " << " in id " << id); + char *cur = buf()+startPos, *end = bufEnd(); + while (cur < end) { + if (memchr(set.buf(), *cur, set.length())) + return (cur-buf()); + ++cur; + } + debugs(24, 7, "not found"); + return SBuf::npos; +} + +/* + * TODO: borrow a sscanf implementation from Linux or similar? + * we'd really need a vsnscanf(3)... ? As an alternative, a + * light-regexp-like domain-specific syntax might be an idea. + */ +int +SBuf::scanf(const char *format, ...) +{ + va_list arg; + int rv; + ++stats.scanf; + va_start(arg, format); + rv = vsscanf(c_str(), format, arg); + va_end(arg); + return rv; +} + +std::ostream & +SBufStats::dump(std::ostream& os) const +{ + MemBlobStats ststats = MemBlob::GetStats(); + os << + "SBuf stats:\nnumber of allocations: " << alloc << + "\ncopy-allocations: " << allocCopy << + "\ncopy-allocations from SquidString: " << allocFromString << + "\ncopy-allocations from C String: " << allocFromCString << + "\nlive references: " << live << + "\nno-copy assignments: " << assignFast << + "\nclearing operations: " << clear << + "\nappend operations: " << append << + "\ndump-to-ostream: " << toStream << + "\nset-char: " << setChar << + "\nget-char: " << getChar << + "\ncomparisons with data-scan: " << compareSlow << + "\ncomparisons not requiring data-scan: " << compareFast << + "\ncopy-out ops: " << copyOut << + "\nraw access to memory: " << rawAccess << + "\nchop operations: " << chop << + "\ntrim operations: " << trim << + "\nfind: " << find << + "\nscanf: " << scanf << + "\ncase-change ops: " << caseChange << + "\nCOW not actually requiring a copy: " << cowFast << + "\nCOW: " << cowSlow << + "\naverage store share factor: " << + (ststats.live != 0 ? static_cast(live)/ststats.live : 0) << + std::endl; + return os; +} + +SBuf +SBuf::toLower() const +{ + debugs(24, 8, "\"" << *this << "\""); + SBuf rv(*this); + for (size_type j = 0; j < length(); ++j) { + const int c = (*this)[j]; + if (isupper(c)) + rv.setAt(j, tolower(c)); //will cow() if needed + } + debugs(24, 8, "result: \"" << *this << "\""); + ++stats.caseChange; + return rv; +} + +SBuf +SBuf::toUpper() const +{ + debugs(24, 8, "\"" << *this << "\""); + SBuf rv(*this); + for (size_type j = 0; j < length(); ++j) { + const int c = (*this)[j]; + if (islower(c)) + rv.setAt(j, toupper(c)); //will cow() if needed + } + debugs(24, 8, "result: \"" << *this << "\""); + ++stats.caseChange; + return rv; +} + +/** + * checks whether the requested 'pos' is within the bounds of the SBuf + * \throw OutOfBoundsException if access is out of bounds + */ +void +SBuf::checkAccessBounds(SBuf::size_type pos) const +{ + if (pos < 0) + throw OutOfBoundsException(*this, pos, __FILE__, __LINE__); + if (pos >= length()) + throw OutOfBoundsException(*this, pos, __FILE__, __LINE__); +} + +String +SBuf::toString() const +{ + String rv; + rv.limitInit(buf(), length()); + ++stats.copyOut; + return rv; +} + +/** 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. + */ +void +SBuf::reAlloc(SBuf::size_type newsize) +{ + debugs(24, DBG_DATA, "new size: " << newsize); + if (newsize > maxSize) + throw SBufTooBigException(__FILE__, __LINE__); + MemBlob::Pointer newbuf = new MemBlob(newsize); + if (length() > 0) + newbuf->append(buf(), length()); + store_ = newbuf; + off_ = 0; + ++stats.cowSlow; + debugs(24, 7, "new store capacity: " << store_->capacity); +} + +#if !_USE_INLINE_ +#include "SBuf.cci" +#endif + === added file 'src/SBuf.cci' --- src/SBuf.cci 1970-01-01 00:00:00 +0000 +++ src/SBuf.cci 2013-04-01 17:31:11 +0000 @@ -0,0 +1,204 @@ +/* + * SBuf.cc (C) 2008 Francesco Chemolli + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "base/RefCount.h" +#include "Debug.h" +#include "OutOfBoundsException.h" +#include "SBufExceptions.h" + +#if HAVE_CLIMITS +#include +#elif HAVE_LIMITS_H +#include +#endif + +SBuf& +SBuf::operator =(const SBuf & S) +{ + return assign(S); +} + +SBuf& +SBuf::operator =(const char *S) +{ + return assign(S); +} + +bool +SBuf::operator <(const SBuf &S) const +{ + return (compare(S) < 0); +} + +bool +SBuf::operator >(const SBuf &S) const +{ + return (compare(S) > 0); +} + +bool +SBuf::operator <=(const SBuf &S) const +{ + return (compare(S) <= 0); +} + +bool +SBuf::operator >=(const SBuf &S) const +{ + return (compare(S) >= 0); +} + +SBuf::size_type +SBuf::length() const +{ + return len_; +} + +int +SBuf::plength() const +{ + if (length() > INT_MAX) + throw SBufTooBigException(__FILE__, __LINE__); + return (int)length(); +} + +/** + * obtains a char* to the beginning of this SBuf in memory. + * \note the obtained string is NOT null-terminated. + */ +char * +SBuf::buf() const +{ + return store_->mem+off_; +} + +/** returns the pointer to the first char after this SBuf end + * + * No checks are made that the space returned is safe, checking that is + * up to the caller. + */ +char * +SBuf::bufEnd() const +{ + return store_->mem+off_+len_; +} + +/** + * copy-on-write: make sure that we are the only holder of the backing store. + * If not, reallocate. If a new size is specified, and it is greater than the + * current length, the backing store will be extended as needed + * \retval false no grow was needed + * \retval true had to copy + */ +bool +SBuf::cow(SBuf::size_type newsize) +{ + debugs(24, DBG_DATA, "new size:" << newsize); + if (newsize == npos || newsize < length()) + newsize = length(); + + if (store_->LockCount() == 1 && newsize == length()) { + debugs(24, DBG_DATA, "no cow needed"); + ++stats.cowFast; + return false; + } + reAlloc(newsize); + return true; +} + +/** + * Try to guesstimate how big a MemBlob to allocate. + * The result is guarranteed to be to be at least the desired + * size. + */ +const SBuf::size_type +SBuf::estimateCapacity(SBuf::size_type desired) const +{ + return 2*desired; +} + +/** + * To be called after having determined that the buffers are equal up to the + * length of the shortest one. + * If the buffers' length is the same, then they're equal. Otherwise, the + * longest one is deemed to be greater than the other. + * This matches the behavior of strcmp(1) and strcasecmp(1) + */ +int +SBuf::commonCompareChecksPost(const SBuf &S) const +{ + if (length() == S.length()) //I'll be damned..they're REALLY the same.. + return 0; + if (length() > S.length()) + return 1; + return -1; +} + +/** obtain prototype store + * + * Just-created SBufs all share to the same MemBlob. + * This call instantiates and returns it. + */ +MemBlob::Pointer +SBuf::GetStorePrototype() +{ + static MemBlob::Pointer InitialStore = NULL; + if (InitialStore == NULL) { + static char lowPrototype[] = ""; + InitialStore = new MemBlob(lowPrototype, 0); + } + return InitialStore; +} + +const char +SBuf::operator [](SBuf::size_type pos) const +{ + ++stats.getChar; + return store_->mem[off_+pos]; +} + +const char +SBuf::at(SBuf::size_type pos) const +{ + checkAccessBounds(pos); + return operator[](pos); +} + +bool +SBuf::isEmpty() const +{ + return (len_ == 0); +} + +std::ostream & +operator <<(std::ostream& os, const SBuf& S) +{ + return S.print(os); +} === added file 'src/SBuf.h' --- src/SBuf.h 1970-01-01 00:00:00 +0000 +++ src/SBuf.h 2013-04-01 17:31:11 +0000 @@ -0,0 +1,565 @@ +/* + * SBuf.h (C) 2008 Francesco Chemolli + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#ifndef SQUID_SBUF_H +#define SQUID_SBUF_H + +#include "base/InstanceId.h" +#include "MemBlob.h" +#include "SquidString.h" + +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STDARG_H +#include +#endif +#if HAVE_IOSFWD +#include +#endif + +/* SBuf placeholder for printf */ +#ifndef SQUIDSBUFPH +#define SQUIDSBUFPH "%.*s" +#define SQUIDSBUFPRINT(s) (s).plength(),(s).rawContent() +#endif /* SQUIDSBUFPH */ + +typedef enum { + caseSensitive, + caseInsensitive +} SBufCaseSensitive; + +/** + * Container for various SBuf class-wide statistics. + * + * The stats are not completely accurate; they're mostly meant to + * understand whether Squid is leaking resources + * and whether SBuf is paying off the expected gains. + */ +class SBufStats +{ +public: + u_int64_t alloc; ///0 argument of the call is greater than called SBuf + * \retval <0 argument of the call is smaller than called SBuf + * \retval 0 argument of the call has the same contents of called SBuf + */ + int compare(const SBuf &S, SBufCaseSensitive isCaseSensitive = caseSensitive, size_type n = npos) const; + + /** check whether the entire supplied argument is a prefix of the SBuf. + * \param S the prefix to match against + * \param isCaseSensitive one of caseSensitive or caseInsensitive + * \retval true argument is a prefix of the SBuf + */ + bool startsWith(const SBuf &S, SBufCaseSensitive isCaseSensitive = caseSensitive) const; + + bool operator ==(const SBuf & S) const; + bool operator !=(const SBuf & S) const; + _SQUID_INLINE_ bool operator <(const SBuf &S) const; + _SQUID_INLINE_ bool operator >(const SBuf &S) const; + _SQUID_INLINE_ bool operator <=(const SBuf &S) const; + _SQUID_INLINE_ bool operator >=(const SBuf &S) const; + + /** Consume bytes at the head of the SBuf + * + * Consume N chars at SBuf head, or to SBuf's end, + * whichever is shorter. If more bytes are consumed than available, + * the SBuf is emptied + * \param n how many bytes to remove; could be zero. + * SBuf::npos (or no argument) means 'to the end of SBuf' + * \return a new SBuf containing the consumed bytes. + */ + SBuf consume(size_type n = npos); + + /// gets global statistic informations + static const SBufStats& GetStats(); + + /** Copy SBuf contents into user-supplied C buffer. + * + * Export a copy of the SBuf's contents into the user-supplied + * buffer, up to the user-supplied-length. No zero-termination is performed + * \return num the number of actually-copied chars. + */ + size_type copy(char *dest, size_type n) const; + + /** exports a pointer to the SBuf internal storage. + * \warning ACCESSING RAW STORAGE IS DANGEROUS! + * + * Returns a ead-only pointer to SBuf's content. No terminating null + * character is appended (use c_str() for that). + * The returned value points to an internal location whose contents + * are guaranteed to remain unchanged only until the next call + * to a non-constant member function of the SBuf object. Such a + * call may be implicit (e.g., when SBuf is destroyed + * upon leaving the current context). + * This is a very UNSAFE way of accessing the data. + * This call never returns NULL. + * \see c_str + * \note the memory management system guarantees that the exported region + * of memory will remain valid if the caller keeps holding + * a valid reference to the SBuf object and does not write or append to + * it. For example: + * \code + * SBuf foo("some string"); + * const char *bar = foo.rawContent(); + * doSomething(bar); //safe + * foo.append(" other string"); + * doSomething(bar); //unsafe + * \endcode + */ + const char* rawContent() const; + + /** Exports a writable pointer to the SBuf internal storage. + * \warning Use with EXTREME caution, this is a dangerous operation. + * + * Returns a pointer to the first unused byte in the SBuf's storage, + * to be used for writing. If minsize is specified, it is guaranteed + * that at least minsize bytes will be available for writing. Otherwise + * it is guaranteed that at least as much storage as is currently + * available will be available for the call. A COW will be performed + * if necessary so that the returned pointer can be written to without + * unwanted side-effects. + * The returned pointer must not be stored, and will + * be invalidated by the first call to a non-const method call + * on the SBuf. + * This call guarantees to never return NULL + * \throw SBufTooBigException if the user tries to allocate too big a SBuf + */ + char *rawSpace(size_type minSize = npos); + + /** Force a SBuf's size + * \warning use with EXTREME caution, this is a dangerous operation + * + * Adapt the SBuf internal state after external interference + * such as writing into it via rawSpace. + * \throw TextException if we + */ + void forceSize(size_type newSize); + + /** exports a null-terminated reference to the SBuf internal storage. + * \warning ACCESSING RAW STORAGE IS DANGEROUS! DO NOT EVER USE + * THE RETURNED POINTER FOR WRITING + * + * The returned value points to an internal location whose contents + * are guaranteed to remain unchanged only until the next call + * to a non-constant member function of the SBuf object. Such a + * call may be implicit (e.g., when SBuf is destroyed + * upon leaving the current context). + * This is a very UNSAFE way of accessing the data. + * This call never returns NULL. + * \see rawContent + * \note the memory management system guarantees that the exported region + * of memory will remain valid will remain valid only if the + * caller keeps holding a valid reference to the SBuf object and + * does not write or append to it + */ + const char* c_str(); + + /// Returns the number of bytes stored in SBuf. + _SQUID_INLINE_ size_type length() const; + + /** Get the length of the SBuf, as a signed integer + * + * Compatibility function for printf(3) which requires a signed int + * \throw SBufTooBigException if the SBuf is too big for a signed integer + */ + _SQUID_INLINE_ int plength() const; + + /** Check whether the SBuf is empty + * + * \return true if length() == 0 + */ + _SQUID_INLINE_ bool isEmpty() const; + + /** Request to extend the SBuf's free store space. + * + * After the reserveSpace request, the SBuf is guaranteed to have at + * least minSpace bytes of unused backing store + * following the currently used portion + * \throw SBufTooBigException if the user tries to allocate too big a SBuf + */ + void reserveSpace(size_type minSpace); + + /** Request to resize the SBuf's store + * + * After this method is called, the SBuf is guaranteed to have at least + * minCapacity bytes of total space, including the currently-used portion + * \throw SBufTooBigException if the user tries to allocate too big a SBuf + */ + void reserveCapacity(size_type minCapacity); + + /** slicing method + * + * Removes SBuf prefix and suffix, leaving a sequence of 'n' + * bytes starting from position 'pos', first byte is at pos 0. + * It is an in-place-modifying version of substr. + * \param pos start sub-stringing from this byte. If it is + * npos or it is greater than the SBuf length, the SBuf is cleared and + * an empty SBuf is returned. If it is <0, it is ignored + * \param n maximum number of bytes of the resulting SBuf. + * SBuf::npos means "to end of SBuf". + * if it is 0, the SBuf is cleared and an empty SBuf is returned. + * if it is < 0, it is ignored (same as supplying npos) + * if it overflows the end of the SBuf, it is capped to the end of SBuf + * \see substr, trim + */ + SBuf& chop(size_type pos, size_type n = npos); + + /** Remove characters in the toremove set at the beginning, end or both + * + * \param toremove characters to be removed. Stops chomping at the first + * found char not in the set + * \param atBeginning if true (default), strips at the beginning of the SBuf + * \param atEnd if true (default), strips at the end of the SBuf + */ + SBuf& trim(const SBuf &toRemove, bool atBeginning = true, bool atEnd = true); + + /** Extract a part of the current SBuf. + * + * Return a fresh a fresh copy of a portion the current SBuf, which is + * left untouched. The same parameter convetions apply as for chop. + * \see trim, chop + */ + SBuf substr(size_type pos, size_type n = npos) const; + + /** Find first occurrence of character in SBuf + * + * Returns the index in the SBuf of the first occurrence of char c. + * \return SBuf::npos if the char was not found + * \param startPos if specified, ignore any occurrences before that position + * if startPos is npos or greater than length() npos is always returned + * if startPos is < 0, it is ignored + */ + size_type find(char c, size_type startPos = 0) const; + + /** Find first occurrence of SBuf in SBuf. + * + * Returns the index in the SBuf of the first occurrence of the + * sequence contained in the str argument. + * \param startPos if specified, ignore any occurrences before that position + * if startPos is npos or greater than length() npos is always returned + * if startPos is < 0, it is ignored + * \return SBuf::npos if the SBuf was not found + */ + size_type find(const SBuf & str, size_type startPos = 0) const; + + /** Find last occurrence of character in SBuf + * + * Returns the index in the SBuf of the last occurrence of char c. + * \return SBuf::npos if the char was not found + * \param endPos if specified, ignore any occurrences after that position. + * if npos or greater than length(), the whole SBuf is considered + * if < 0, npos is always returned + */ + size_type rfind(char c, size_type endPos = npos) const; + + /** Find last occurrence of SBuf in SBuf + * + * Returns the index in the SBuf of the last occurrence of the + * sequence contained in the str argument. + * \return SBuf::npos if the sequence was not found + * \param endPos if specified, ignore any occurrences after that position + * if npos or greater than length(), the whole SBuf is considered + * if < 0, npos is always returned + */ + size_type rfind(const SBuf &str, size_type endPos = npos) const; + + /** Find first occurrence of character of set in SBuf + * + * Finds the first occurrence of ANY of the characters in the supplied set in + * the SBuf. + * \return SBuf::npos if no character in the set could be found + * \param startPos if specified, ignore any occurrences before that position + * if SBuf::npos, then npos is always returned + * if <0, it is ignored. + */ + size_type find_first_of(const SBuf &set, size_type startPos = 0) const; + + /** sscanf-alike + * + * sscanf re-implementation. Non-const, and not \0-clean. + * \return same as sscanf + * \see man sscanf(3) + */ + int scanf(const char *format, ...); + + /** Lower-case SBuf + * + * Returns a lower-cased COPY of the SBuf + * \see man tolower(3) + */ + SBuf toLower() const; + + /** Upper-case SBuf + * + * Returns an upper-cased COPY of the SBuf + * \see man toupper(3) + */ + SBuf toUpper() const; + + /** String export function + * converts the SBuf to a legacy String, by copy. + * \deprecated + */ + String toString() const; + + // TODO: possibly implement erase() similar to std::string's erase + // TODO: possibly implement a replace() call +private: + + MemBlob::Pointer store_; ///< memory block, possibly shared with other SBufs + size_type off_; ///< our content start offset from the beginning of shared store_ + size_type len_; ///< number of our content bytes in shared store_ + static SBufStats stats; ///< class-wide statistics + + const InstanceId id; ///< blob identifier + + _SQUID_INLINE_ static MemBlob::Pointer GetStorePrototype(); + + _SQUID_INLINE_ char * buf() const; + _SQUID_INLINE_ char * bufEnd() const; + _SQUID_INLINE_ const size_type estimateCapacity(size_type desired) const; + void reAlloc(size_type newsize); + + _SQUID_INLINE_ bool cow(size_type minsize = npos); + + void checkAccessBounds(size_type pos) const; + _SQUID_INLINE_ int commonCompareChecksPre(const SBuf &S) const; + _SQUID_INLINE_ int commonCompareChecksPost(const SBuf &S) const; + +}; + +/// ostream output operator +_SQUID_INLINE_ std::ostream& operator <<(std::ostream &os, const SBuf &S); + +#if _USE_INLINE_ +#include "SBuf.cci" +#endif + +#endif /* SQUID_SBUF_H */ === added file 'src/SBufExceptions.cc' --- src/SBufExceptions.cc 1970-01-01 00:00:00 +0000 +++ src/SBufExceptions.cc 2013-04-01 17:31:11 +0000 @@ -0,0 +1,70 @@ +/* + * SBufExceptions.cc (C) 2008 Francesco Chemolli + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "squid.h" +#include "OutOfBoundsException.h" +#include "SBuf.h" +#include "SBufExceptions.h" + +OutOfBoundsException::OutOfBoundsException(const SBuf &throwingBuf, + SBuf::size_type &pos, + const char *aFileName, int aLineNo) + : TextException(NULL, aFileName, aLineNo), + theThrowingBuf(throwingBuf), + accessedPosition(pos) +{ + SBuf explanatoryText("OutOfBoundsException"); + if (aLineNo != -1) + explanatoryText.appendf(" at line %d", aLineNo); + if (aFileName != NULL) + explanatoryText.appendf(" in file %s", aFileName); + explanatoryText.appendf(" while accessing position %d in a SBuf long %d", + pos, throwingBuf.length()); + // we can safely alias c_str as both are local to the object + // and will not further manipulated. + message = xstrndup(explanatoryText.c_str(),explanatoryText.length()); +} + +OutOfBoundsException::~OutOfBoundsException() throw() +{ } + +NullSBufException::NullSBufException(const char *aFilename, int aLineNo) + : TextException("Trying to access a null SBuf", aFilename, aLineNo) +{ } + +InvalidParamException::InvalidParamException(const char *aFilename, int aLineNo) + : TextException("Invalid parameter", aFilename, aLineNo) +{ } + +SBufTooBigException::SBufTooBigException(const char *aFilename, int aLineNo) + : TextException("Trying to create an oversize SBuf", aFilename, aLineNo) +{ } + +/* */ === added file 'src/SBufExceptions.h' --- src/SBufExceptions.h 1970-01-01 00:00:00 +0000 +++ src/SBufExceptions.h 2013-03-29 16:50:25 +0000 @@ -0,0 +1,65 @@ +/* + * SBufExceptions.h (C) 2008 Francesco Chemolli + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#ifndef SQUID_SBUFEXCEPTIONS_H +#define SQUID_SBUFEXCEPTIONS_H + +#include "base/TextException.h" + +/** + * Exception raised when the user is trying to operate on a Null SBuf + * \todo move to an Exceptions.h? + */ +class NullSBufException : public TextException +{ +public: + explicit NullSBufException(const char *aFilename = 0, int aLineNo = -1); +}; + +/** + * Exception raised when call parameters are not valid + * \todo move to an Exceptions.h? + */ +class InvalidParamException : public TextException +{ +public: + explicit InvalidParamException(const char *aFilename = 0, int aLineNo = -1); +}; + +/** + * Exception raised when an attempt to resize a SBuf would cause it to reserve too big + */ +class SBufTooBigException : public TextException +{ +public: + explicit SBufTooBigException(const char *aFilename = 0, int aLineNo = -1); +}; + +#endif /* SQUID_SBUFEXCEPTIONS_H */ === added file 'src/SBufStream.h' --- src/SBufStream.h 1970-01-01 00:00:00 +0000 +++ src/SBufStream.h 2013-04-01 17:31:11 +0000 @@ -0,0 +1,142 @@ +/* + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_SBUFSTREAM_H +#define SQUID_SBUFSTREAM_H + +#include "SBuf.h" + +#if HAVE_OSTREAM +#include +#endif + +/** 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(aChar)}; + + if (aChar != traits_type::eof()) + theBuf.append(chars, 0, 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(), 0, 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, 0, number); + + return number; + } + +private: + SBuf theBuf; + SBufStreamBuf(); // no default constructor +}; + +/** Stream interface to write to a SBuf. + * + * Data is appended using standard operator << semantics, and extracted + * using the buf() method, in analogy with std::strstream . + */ +class SBufStream : public std::ostream +{ +public: + /** Create a SBufStream preinitialized with the contents of a SBuf + * + * 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) + } + + /// 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) + } + + /// Retrieve a copy of the current stream status + SBuf buf() { + return theBuffer.getBuf(); + } + + /// Clear the stream's backing store + SBufStream& clearBuf() { + theBuffer.clearBuf(); + return *this; + } + +private: + SBufStreamBuf theBuffer; +}; + +#endif /* SQUID_SBUFSTREAM_H */ === added file 'src/tests/SBufFindTest.cc' --- src/tests/SBufFindTest.cc 1970-01-01 00:00:00 +0000 +++ src/tests/SBufFindTest.cc 2013-03-29 16:50:25 +0000 @@ -0,0 +1,431 @@ +#include "squid.h" +#include "SBufFindTest.h" +#include +#include +#include + + +/* TODO: The whole SBufFindTest class is currently implemented as a single + CppUnit test case (because we do not want to register and report every one + of the thousands of generated test cases). Is there a better way to + integrate with CppUnit? + */ + + +SBufFindTest::SBufFindTest(): + caseLimit(std::numeric_limits::max()), + errorLimit(std::numeric_limits::max()), + randomSeed(1), + hushSimilar(true), + maxHayLength(40), + thePos(0), + thePlacement(placeEof), + theStringPos(0), + theBareNeedlePos(0), + caseCount(0), + errorCount(0), + reportCount(0) +{ +} + +void +SBufFindTest::run() +{ + srandom(randomSeed); + + for (SBuf::size_type hayLen = 0; hayLen <= maxHayLength; nextLen(hayLen, maxHayLength)) { + const SBuf cleanHay = RandomSBuf(hayLen); + + const SBuf::size_type maxNeedleLen = hayLen + 10; + for (SBuf::size_type needleLen = 0; needleLen <= maxNeedleLen; nextLen(needleLen, maxNeedleLen)) { + theSBufNeedle = RandomSBuf(needleLen); + + for (int i = 0; i < placeEof; i++) { + thePlacement = Placement(i); + placeNeedle(cleanHay); + + const SBuf::size_type maxArg = + max(theSBufHay.length(), theSBufNeedle.length()) + 10; + for (thePos = 0; thePos <= maxArg; nextLen(thePos, maxArg)) + testAllMethods(); + + // the special npos value is not tested as the behavior is + // different from std::string (where the behavior is undefined) + // It is ad-hoc tested in testSBuf instead + //thePos = SBuf::npos; + //testAllMethods(); + } + } + } + + if (errorCount > 0) { + std::cerr << "Generated SBuf test cases: " << caseCount << std::endl; + std::cerr << "\tfailed cases: " << errorCount << std::endl; + std::cerr << "\treported cases: " << reportCount << std::endl; + std::cerr << "Asserting because some cases failed..." << std::endl; + CPPUNIT_ASSERT(!SBufFindTest::errorCount); + } +} + +/// tests SBuf::find(string needle) +void +SBufFindTest::testFindDefs() { + theFindString = theBareNeedlePos = theStringHay.find(theStringNeedle); + theFindSBuf = theSBufHay.find(theSBufNeedle); + checkResults("find"); +} + +/// tests SBuf::rfind(string needle) +void +SBufFindTest::testRFindDefs() { + theFindString = theBareNeedlePos = theStringHay.rfind(theStringNeedle); + theFindSBuf = theSBufHay.rfind(theSBufNeedle); + checkResults("rfind"); +} + +/// tests SBuf::find(string needle, pos) +void +SBufFindTest::testFind() { + theFindString = theStringHay.find(theStringNeedle, thePos); + theBareNeedlePos = theStringHay.find(theStringNeedle); + theFindSBuf = theSBufHay.find(theSBufNeedle, thePos); + checkResults("find"); +} + +/// tests SBuf::find_first_of(string needle, pos) +void +SBufFindTest::testFindFirstOf() { + theFindString = theStringHay.find_first_of(theStringNeedle, thePos); + theBareNeedlePos = theStringHay.find_first_of(theStringNeedle); + theFindSBuf = theSBufHay.find_first_of(theSBufNeedle, thePos); + checkResults("find_first_of"); +} + + +/// tests SBuf::rfind(string needle, pos) +void +SBufFindTest::testRFind() { + theFindString = theStringHay.rfind(theStringNeedle, thePos); + theBareNeedlePos = theStringHay.rfind(theStringNeedle); + theFindSBuf = theSBufHay.rfind(theSBufNeedle, thePos); + checkResults("rfind"); +} + +/// tests SBuf::find(char needle) +void +SBufFindTest::testFindCharDefs() { + const char c = theStringNeedle[0]; + theFindString = theBareNeedlePos = theStringHay.find(c); + theFindSBuf = theSBufHay.find(c); + checkResults("find"); +} + +/// tests SBuf::find(char needle, pos) +void +SBufFindTest::testFindChar() { + const char c = theStringNeedle[0]; + theFindString = theStringHay.find(c, thePos); + theBareNeedlePos = theStringHay.find(c); + theFindSBuf = theSBufHay.find(c, thePos); + checkResults("find"); +} + +/// tests SBuf::rfind(char needle) +void +SBufFindTest::testRFindCharDefs() { + const char c = theStringNeedle[0]; + theFindString = theBareNeedlePos = theStringHay.rfind(c); + theFindSBuf = theSBufHay.rfind(c); + checkResults("rfind"); +} + +/// tests SBuf::rfind(char needle, pos) +void +SBufFindTest::testRFindChar() { + const char c = theStringNeedle[0]; + theFindString = theStringHay.rfind(c, thePos); + theBareNeedlePos = theStringHay.rfind(c); + theFindSBuf = theSBufHay.rfind(c, thePos); + checkResults("rfind"); +} + +/// whether the last SBuf and std::string find() results are the same +bool +SBufFindTest::resultsMatch() const { + // this method is needed because SBuf and std::string use different + // size_types (and npos values); comparing the result values directly + // would lead to bugs + + if (theFindString == std::string::npos && theFindSBuf == SBuf::npos) + return true; // both npos + + if (theFindSBuf < 0) // should not happen, treat as error + return false; + + // now safe to cast a non-negative SBuf result + return theFindString == static_cast(theFindSBuf); +} + +/// called at the end of test case to update state, detect and report failures +void +SBufFindTest::checkResults(const char *method) { + ++caseCount; + if (!resultsMatch()) + handleFailure(method); +} + +/// helper function to convert "printable" Type to std::string +template +inline std::string +AnyToString(const Type &value) +{ + std::stringstream sbuf; + sbuf << value; + return sbuf.str(); +} + +/// helper function to convert SBuf position to a human-friendly string +inline std::string +PosToString(const SBuf::size_type pos) +{ + return pos == SBuf::npos ? std::string("npos") : AnyToString(pos); +} + +/// helper function to convert std::string position to a human-friendly string +inline std::string +PosToString(const std::string::size_type pos) +{ + return pos == std::string::npos ? std::string("npos") : AnyToString(pos); +} + +/// tests each supported SBuf::*find() method using generated hay, needle, pos +void +SBufFindTest::testAllMethods() { + theStringHay = std::string(theSBufHay.rawContent(), theSBufHay.length()); + theStringNeedle = std::string(theSBufNeedle.rawContent(), theSBufNeedle.length()); + theBareNeedlePos = std::string::npos; + const std::string reportPos = PosToString(thePos); + + // always test string search + { + theReportQuote = '"'; + theReportNeedle = theStringNeedle; + + theReportPos = ""; + testFindDefs(); + testRFindDefs(); + + theReportPos = reportPos; + testFind(); + testRFind(); + testFindFirstOf(); + } + + // if possible, test char search + if (!theStringNeedle.empty()) { + theReportQuote = '\''; + theReportNeedle = theStringNeedle[0]; + + theReportPos = ""; + testFindCharDefs(); + testRFindCharDefs(); + + theReportPos = reportPos; + testFindChar(); + testRFindChar(); + } +} + +/// helper function to format a length-based key (part of case category string) +inline std::string +lengthKey(const std::string &str) +{ + if (str.length() == 0) + return "0"; + if (str.length() == 1) + return "1"; + return "N"; +} + +/// formats position key (part of the case category string) +std::string +SBufFindTest::posKey() const +{ + // the search position does not matter if needle is not in hay + if (theBareNeedlePos == std::string::npos) + return std::string(); + + if (thePos == SBuf::npos) + return ",npos"; + + if (thePos < 0) + return ",posN"; // negative + + // we know Pos is not negative or special; avoid signed/unsigned warnings + const std::string::size_type pos = + static_cast(thePos); + + if (pos < theBareNeedlePos) + return ",posL"; // to the Left of the needle + if (pos == theBareNeedlePos) + return ",posB"; // Beginning of the needle + if (pos < theBareNeedlePos + theStringNeedle.length()) + return ",posM"; // in the Middle of the needle + if (pos == theBareNeedlePos + theStringNeedle.length()) + return ",posE"; // at the End of the needle + if (pos < theStringHay.length()) + return ",posR"; // to the Right of the needle + return ",posP"; // past the hay +} + +/// formats placement key (part of the case category string) +std::string +SBufFindTest::placementKey() const +{ + // Ignore thePlacement because theBareNeedlePos covers it better: we may + // try to place the needle somewhere, but hay limits the actual placement. + + // the placent does not matter if needle is not in hay + if (theBareNeedlePos == std::string::npos) + return std::string(); + + if (theBareNeedlePos == 0) + return "@B"; // at the beggining of the hay string + if (theBareNeedlePos == theStringHay.length()-theStringNeedle.length()) + return "@E"; // at the end of the hay string + return "@M"; // in the "middle" of the hay string +} + +/// called when a test case fails; counts and possibly reports the failure +void +SBufFindTest::handleFailure(const char *method) { + // line break after "........." printed for previous tests + if (!errorCount) + std::cerr << std::endl; + + ++errorCount; + + if (errorCount > errorLimit) { + std::cerr << "Will stop generating SBuf test cases because the " << + "number of failed ones is over the limit: " << errorCount << + " (after " << caseCount << " test cases)" << std::endl; + CPPUNIT_ASSERT(errorCount <= errorLimit); + /* NOTREACHED */ + } + + // format test case category; category allows us to hush failure reports + // for already seen categories with failed cases (to reduce output noise) + std::string category = "hay" + lengthKey(theStringHay) + + "." + method + '('; + if (theReportQuote == '"') + category += "needle" + lengthKey(theStringNeedle); + else + category += "char"; + category += placementKey(); + category += posKey(); + category += ')'; + + if (hushSimilar) { + if (failedCats.find(category) != failedCats.end()) + return; // do not report another similar test case failure + failedCats.insert(category); + } + + std::string reportPos = theReportPos; + if (!reportPos.empty()) + reportPos = ", " + reportPos; + + std::cerr << "case" << caseCount << ": " << + "SBuf(\"" << theStringHay << "\")." << method << + "(" << theReportQuote << theReportNeedle << theReportQuote << + reportPos << ") returns " << PosToString(theFindSBuf) << + " instead of " << PosToString(theFindString) << + std::endl << + " std::string(\"" << theStringHay << "\")." << method << + "(" << theReportQuote << theReportNeedle << theReportQuote << + reportPos << ") returns " << PosToString(theFindString) << + std::endl << + " category: " << category << std::endl; + + ++reportCount; +} + +/// generates a random string of the specified length +SBuf +SBufFindTest::RandomSBuf(const int length) { + static const char characters[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklomnpqrstuvwxyz"; + // sizeof() counts the terminating zero at the end of characters + // TODO: add \0 character (needs reporting adjustments to print it as \0) + static const size_t charCount = sizeof(characters)-1; + + char buf[length]; + for (int i = 0; i < length; ++i) { + const unsigned int pos = random() % charCount; + assert(pos < sizeof(characters)); + assert(characters[pos] > 32); + buf[i] = characters[random() % charCount]; + } + + return SBuf(buf, 0, length); +} + +/// increments len to quickly cover [0, max] range, slowing down in risky areas +/// jumps to max+1 if caseLimit is reached +void +SBufFindTest::nextLen(int &len, const int max) { + assert(len <= max); + + if (caseCount >= caseLimit) + len = max+1; // avoid future test cases + else if (len <= 10) + ++len; // move slowly at the beginning of the [0,max] range + else if (len >= max - 10) + ++len; // move slowly at the end of the [0,max] range + else { + // move fast in the middle of the [0,max] range + len += len/10 + 1; + + // but do not overshoot the interesting area at the end of the range + if (len > max - 10) + len = max - 10; + } +} + +/// Places the needle into the hay using cleanHay as a starting point. +void +SBufFindTest::placeNeedle(const SBuf &cleanHay) { + // For simplicity, we do not overwrite clean hay characters but use them as + // needle suffix and/or prefix. Should not matter since hay length varies? + + // TODO: support two needles per hay (explicitly) + // TODO: better handle cases where clean hay already contains needle + switch (thePlacement) + { + case placeBeginning: + theSBufHay.assign(theSBufNeedle).append(cleanHay); + break; + + case placeMiddle: + { + const SBuf firstHalf = cleanHay.substr(0, cleanHay.length()/2); + const SBuf secondHalf = cleanHay.substr(cleanHay.length()/2); + theSBufHay.assign(firstHalf).append(theSBufNeedle).append(secondHalf); + break; + } + + case placeEnd: + theSBufHay.assign(cleanHay).append(theSBufNeedle); + break; + + case placeNowhere: + theSBufHay.assign(cleanHay); + break; + + case placeEof: + assert(false); // should not happen + break; + } +} === added file 'src/tests/SBufFindTest.h' --- src/tests/SBufFindTest.h 1970-01-01 00:00:00 +0000 +++ src/tests/SBufFindTest.h 2013-03-29 16:50:25 +0000 @@ -0,0 +1,86 @@ +#ifndef SQUID_SRC_TEST_SBUFFINDTEST_H +#define SQUID_SRC_TEST_SBUFFINDTEST_H + +#include "SBuf.h" + +#if HAVE_STRING +#include +#endif +#include + +/// Generates and executes a [configurable] large number of SBuf::*find() +/// test cases using random strings. Reports detected failures. +class SBufFindTest +{ +public: + SBufFindTest(); + + void run(); ///< generates and executes cases using configuration params + + /* test configuration parameters; can be optionally set before run() */ + int caseLimit; ///< approximate caseCount limit + int errorLimit; ///< errorCount limit + unsigned int randomSeed; ///< pseudo-random sequence choice + /// whether to report only one failed test case per "category" + bool hushSimilar; + /// approximate maximum generated hay string length + SBuf::size_type maxHayLength; + + /// Supported algorithms for placing needle in the hay. + typedef enum { placeBeginning, placeMiddle, placeEnd, placeNowhere, + placeEof } Placement; // placeLast marker must terminate +protected: + + static SBuf RandomSBuf(const int length); + void nextLen(int &len, const int max); + void placeNeedle(const SBuf &cleanHay); + + void testAllMethods(); + void testFindDefs(); + void testFind(); + void testRFindDefs(); + void testRFind(); + void testFindCharDefs(); + void testFindChar(); + void testRFindCharDefs(); + void testRFindChar(); + void testFindFirstOf(); + + std::string posKey() const; + std::string placementKey() const; + + bool resultsMatch() const; + void checkResults(const char *method); + void handleFailure(const char *method); + +private: + /* test case parameters */ + SBuf theSBufHay; ///< the string to be searched + SBuf theSBufNeedle; ///< the string to be found + SBuf::size_type thePos; ///< search position limit + Placement thePlacement; ///< where in the hay the needle is placed + std::string::size_type theStringPos; ///< thePos converted to std::string::size_type + std::string theStringHay; ///< theHay converted to std::string + std::string theStringNeedle; ///< theNeedle converted to std::string + + /// needle pos w/o thePos restrictions; used for case categorization + std::string::size_type theBareNeedlePos; + + /* test case results */ + std::string::size_type theFindString; + SBuf::size_type theFindSBuf; + std::string theReportFunc; + std::string theReportNeedle; + std::string theReportPos; + char theReportQuote; + + /* test progress indicators */ + int caseCount; ///< cases executed so far + int errorCount; ///< total number of failed test cases so far + int reportCount; ///< total number of test cases reported so far + std::set failedCats; ///< reported failed categories +}; + +typedef SBufFindTest::Placement Placement; + +#endif === added file 'src/tests/testSBuf.cc' --- src/tests/testSBuf.cc 1970-01-01 00:00:00 +0000 +++ src/tests/testSBuf.cc 2013-04-01 17:31:11 +0000 @@ -0,0 +1,760 @@ +#include "squid.h" +#include "Mem.h" +#include "SBuf.h" +#include "SBufStream.h" +#include "SquidString.h" +#include "testSBuf.h" +#include "SBufFindTest.h" + +#include +#include + +CPPUNIT_TEST_SUITE_REGISTRATION( testSBuf ); + +/* let this test link sanely */ +#include "event.h" +#include "MemObject.h" +void +eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata) +{} +int64_t +MemObject::endOffset() const +{ return 0; } +/* 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"; + +// TEST: globals variables (default/empty and with contents) are +// created outside and before any unit tests and memory subsystem +// initialization. Check for correct constructor operation. +SBuf empty_sbuf; +SBuf literal("The quick brown fox jumped over the lazy dog"); + +void +testSBuf::testSBufConstructDestruct() +{ + /* NOTE: Do not initialize memory here because we need + * to test correct operation before and after Mem::Init + */ + + // XXX: partial demo below of how to do constructor unit-test. use scope to ensure each test + // is working on local-scope variables constructed fresh for the test, and destructed when + // scope exists. use nested scopes to test destructor affects on copied data (MemBlob etc) + + // TEST: default constructor (implicit destructor non-crash test) + // test accessors on empty SBuf. + { + SBuf s1; + CPPUNIT_ASSERT_EQUAL(s1.length(),0); + CPPUNIT_ASSERT_EQUAL(s1,SBuf("")); + CPPUNIT_ASSERT_EQUAL(s1,empty_sbuf); + CPPUNIT_ASSERT(0==strcmp("",s1.c_str())); + } + + // TEST: copy-construct NULL string (implicit destructor non-crash test) + { + SBuf s1(NULL); + CPPUNIT_ASSERT_EQUAL(s1.length(),0); + CPPUNIT_ASSERT_EQUAL(s1,SBuf("")); + CPPUNIT_ASSERT_EQUAL(s1,empty_sbuf); + CPPUNIT_ASSERT(0==strcmp("",s1.c_str())); + } + + // TEST: copy-construct empty string (implicit destructor non-crash test) + { + SBuf s1(""); + CPPUNIT_ASSERT_EQUAL(s1.length(),0); + CPPUNIT_ASSERT_EQUAL(s1,SBuf("")); + CPPUNIT_ASSERT_EQUAL(s1,empty_sbuf); + CPPUNIT_ASSERT(0==strcmp("",s1.c_str())); + } + + // TEST: copy-construct from a SBuf + { + SBuf s1(empty_sbuf); + CPPUNIT_ASSERT_EQUAL(s1.length(),0); + CPPUNIT_ASSERT_EQUAL(s1,SBuf("")); + CPPUNIT_ASSERT_EQUAL(s1,empty_sbuf); + CPPUNIT_ASSERT(0==strcmp("",s1.c_str())); + + SBuf s5(literal); + CPPUNIT_ASSERT_EQUAL(s5,literal); + SBuf s6(fox); + CPPUNIT_ASSERT_EQUAL(s6,literal); + // XXX: other state checks. expected result of calling any state accessor on s4 ? + } + + // TEST: check that COW doesn't happen upon copy-construction + { + SBuf s1(empty_sbuf), s2(s1); + CPPUNIT_ASSERT_EQUAL(s1.rawContent(), s2.rawContent()); + SBuf s3(literal), s4(literal); + CPPUNIT_ASSERT_EQUAL(s3.rawContent(), s4.rawContent()); + } + + // TEST: sub-string copy + { + SBuf s1=SBuf(fox,4), s2(fox); + SBuf s3=s2.substr(4,s2.length()); //n is out-of-bounds + CPPUNIT_ASSERT_EQUAL(s1,s3); + SBuf s4=SBuf(fox,0,4); + s3=s2.substr(0,4); + CPPUNIT_ASSERT_EQUAL(s4,s3); + } + + // TEST: go via SquidString adapters. + { + String str(fox); + SBuf s1(str); + CPPUNIT_ASSERT_EQUAL(s1,literal); + } + + // TEST: go via std::string adapter. + { + std::string str(fox); + SBuf s1(str); + CPPUNIT_ASSERT_EQUAL(s1,literal); + } + + +} + +void +testSBuf::testSBufConstructDestructAfterMemInit() +{ + Mem::Init(); + testSBufConstructDestruct(); + +} + +void +testSBuf::testEqualityTest() +{ + SBuf s1(fox),s2(fox); + CPPUNIT_ASSERT_EQUAL(s1,s1); //self-equality + CPPUNIT_ASSERT_EQUAL(s1,s2); //same contents + s2.assign("The quick brown fox jumped over the lazy doe"); + CPPUNIT_ASSERT(!(s1 == s2)); //same length, different contents + s2.assign("foo"); + CPPUNIT_ASSERT(!(s1 == s2)); //different length and contents + CPPUNIT_ASSERT(s1 != s2); //while we're ready, let's test inequality + s2.clear(); + CPPUNIT_ASSERT(!(s1 == s2)); //null and not-null + CPPUNIT_ASSERT(s1 != s2); //while we're ready, let's test inequality + s1.clear(); + CPPUNIT_ASSERT_EQUAL(s1,s2); //null and null +} + +void +testSBuf::testAppendSBuf() +{ + SBuf s1(fox1),s2(fox2); + s1.append(s2); + CPPUNIT_ASSERT_EQUAL(s1,literal); +} + +void +testSBuf::testPrintf() +{ + SBuf s1,s2; + s1.Printf("%s:%d:%03.3f","fox",10,12345.67); + s2.assign("fox:10:12345.670"); + CPPUNIT_ASSERT_EQUAL(s1,s2); +} + +void +testSBuf::testAppendCString() +{ + SBuf s1(fox1); + s1.append(fox2); + CPPUNIT_ASSERT_EQUAL(s1,literal); +} + +void +testSBuf::testAppendStdString() +{ + SBuf s1(fox1); + std::string str(fox2); + s1.append(str); + CPPUNIT_ASSERT_EQUAL(s1,literal); +} + +void +testSBuf::testAppendf() +{ + SBuf s1,s2; + s1.appendf("%s:%d:%03.2f",fox,1234,1234.56); + s2.assign("The quick brown fox jumped over the lazy dog:1234:1234.56"); + CPPUNIT_ASSERT_EQUAL(s1,s2); +} + +void +testSBuf::testDumpStats() +{ + SBuf::GetStats().dump(std::cout); + MemBlob::GetStats().dump(std::cout); + std::cout << "sizeof(SBuf): " << sizeof(SBuf) << std::endl; + std::cout << "sizeof(MemBlob): " << sizeof(MemBlob) << std::endl; +} + +void +testSBuf::testSubscriptOp() +{ + SBuf chg(literal); + CPPUNIT_ASSERT_EQUAL(chg[5],'u'); + chg.setAt(5,'e'); + CPPUNIT_ASSERT_EQUAL(literal[5],'u'); + CPPUNIT_ASSERT_EQUAL(chg[5],'e'); +} + +// note: can't use cppunit's CPPUNIT_TEST_EXCEPTION because TextException asserts, and +// so the test can't be properly completed. +void +testSBuf::testSubscriptOpFail() +{ + char c; + c=literal.at(literal.length()); //out of bounds by 1 + //notreached + std::cout << c << std::endl; +} + +static int sign(int v) +{ + if (v < 0) + return -1; + if (v>0) + return 1; + return 0; +} + +void +testSBuf::testComparisons() +{ + //same length + SBuf s1("foo"),s2("foe"); + CPPUNIT_ASSERT(s1.compare(s2)>0); + CPPUNIT_ASSERT(s1.compare(s2,caseInsensitive,2)==0); + CPPUNIT_ASSERT(s1 > s2); + CPPUNIT_ASSERT(s2 < s1); + CPPUNIT_ASSERT_EQUAL(sign(s1.compare(s2)),sign(strcmp(s1.c_str(),s2.c_str()))); + //different lengths + s1.assign("foo"); + s2.assign("foof"); + CPPUNIT_ASSERT(s1.compare(s2)<0); + CPPUNIT_ASSERT_EQUAL(sign(s1.compare(s2)),sign(strcmp(s1.c_str(),s2.c_str()))); + CPPUNIT_ASSERT(s1 < s2); +} + +void +testSBuf::testConsume() +{ + SBuf s1(literal),s2,s3; + s2=s1.consume(4); + s3.assign("The "); + CPPUNIT_ASSERT_EQUAL(s2,s3); + s3.assign("quick brown fox jumped over the lazy dog"); + CPPUNIT_ASSERT_EQUAL(s1,s3); + s1.consume(40); + CPPUNIT_ASSERT_EQUAL(s1,SBuf()); +} + +void +testSBuf::testRawContent() +{ + SBuf s1(literal); + SBuf s2(s1); + s2.append("foo"); + const char *foo; + foo = s1.rawContent(); + CPPUNIT_ASSERT(strncmp(fox,foo,s1.length())==0); + foo = s1.c_str(); + CPPUNIT_ASSERT(!strcmp(fox,foo)); +} + +void +testSBuf::testRawSpace() +{ + SBuf s1(literal); + SBuf s2(fox1); + SBuf::size_type sz=s2.length(); + char *rb=s2.rawSpace(strlen(fox2)+1); + strcpy(rb,fox2); + s2.forceSize(sz+strlen(fox2)); + CPPUNIT_ASSERT_EQUAL(s1,s2); +} + +void +testSBuf::testChop() +{ + SBuf s1(literal),s2; + s1.chop(4,5); + s2.assign("quick"); + CPPUNIT_ASSERT_EQUAL(s1,s2); + s1=literal; + s2.clear(); + s1.chop(5,0); + CPPUNIT_ASSERT_EQUAL(s1,s2); + const char *alphabet="abcdefghijklmnopqrstuvwxyz"; + SBuf a(alphabet); + std::string s(alphabet); // TODO + { //regular chopping + SBuf b(a); + b.chop(3,3); + SBuf ref("def"); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // chop at end + SBuf b(a); + b.chop(b.length()-3); + SBuf ref("xyz"); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // chop at beginning + SBuf b(a); + b.chop(0,3); + SBuf ref("abc"); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // chop to zero length + SBuf b(a); + b.chop(5,0); + SBuf ref(""); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // chop beyond end (at npos) + SBuf b(a); + b.chop(SBuf::npos,4); + SBuf ref(""); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // chop beyond end + SBuf b(a); + b.chop(b.length()+2,4); + SBuf ref(""); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // null-chop + SBuf b(a); + b.chop(0,b.length()); + SBuf ref(a); + CPPUNIT_ASSERT_EQUAL(ref,b); + } + { // overflow chopped area + SBuf b(a); + b.chop(b.length()-3,b.length()); + SBuf ref("xyz"); + CPPUNIT_ASSERT_EQUAL(ref,b); + } +} + +void +testSBuf::testChomp() +{ + SBuf s1("complete string"); + SBuf s2(s1); + s2.trim(SBuf(" ,")); + CPPUNIT_ASSERT_EQUAL(s1,s2); + s2.assign(" complete string ,"); + s2.trim(SBuf(" ,")); + CPPUNIT_ASSERT_EQUAL(s1,s2); + s1.assign(", complete string ,"); + s2=s1; + s2.trim(SBuf(" ")); + CPPUNIT_ASSERT_EQUAL(s1,s2); +} + +// inspired to SBufFindTest; to be expanded. +class SBufSubstrAutoTest +{ + SBuf fullString, sb; + std::string fullReference, str; + public: + void performEqualityTest() + { + SBuf ref(str); + CPPUNIT_ASSERT_EQUAL(ref,sb); + } + SBufSubstrAutoTest() : fullString(fox), fullReference(fox) + { + for (int offset=fullString.length()-1; offset >= 0; --offset ) { + for (int length=fullString.length()-1-offset; length >= 0; --length) { + sb=fullString.substr(offset,length); + str=fullReference.substr(offset,length); + performEqualityTest(); + } + } + } +}; + +void +testSBuf::testSubstr() +{ + SBuf s1(literal),s2,s3; + s2=s1.substr(4,5); + s3.assign("quick"); + CPPUNIT_ASSERT_EQUAL(s2,s3); + s1.chop(4,5); + CPPUNIT_ASSERT_EQUAL(s1,s2); + SBufSubstrAutoTest sat; // work done in the constructor +} + +void +testSBuf::testFindChar() +{ + const char *alphabet="abcdefghijklmnopqrstuvwxyz"; + SBuf s1(alphabet); + SBuf::size_type idx; + SBuf::size_type nposResult=SBuf::npos; + + // FORWARD SEARCH + // needle in haystack + idx=s1.find('d'); + CPPUNIT_ASSERT(idx == 3); + CPPUNIT_ASSERT(s1[idx]=='d'); + + // needle not present in haystack + idx=s1.find(' '); //fails + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // search in portion + idx=s1.find('e',3); + CPPUNIT_ASSERT_EQUAL(4,idx); + + // char not in searched portion + idx=s1.find('e',5); + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // invalid start position + idx=s1.find('d',SBuf::npos); + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // invalid start position + idx=s1.find('d', -5); + CPPUNIT_ASSERT_EQUAL(3, idx); + + // search outside of haystack + idx=s1.find('d',s1.length()+1); + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // REVERSE SEARCH + // needle in haystack + idx=s1.rfind('d'); + CPPUNIT_ASSERT_EQUAL(3, idx); + CPPUNIT_ASSERT_EQUAL('d', s1[idx]); + + // needle not present in haystack + idx=s1.rfind(' '); //fails + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // search in portion + idx=s1.rfind('e',5); + CPPUNIT_ASSERT_EQUAL(4,idx); + + // char not in searched portion + idx=s1.rfind('e',3); + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // invalid start position + idx=s1.rfind('d', -5); + CPPUNIT_ASSERT_EQUAL(nposResult,idx); + + // overlong haystack specification + idx=s1.rfind('d',s1.length()+1); + CPPUNIT_ASSERT_EQUAL(3,idx); +} + +void +testSBuf::testFindSBuf() +{ + const char *alphabet="abcdefghijklmnopqrstuvwxyz"; + SBuf haystack(alphabet); + SBuf::size_type idx; + SBuf::size_type nposResult=SBuf::npos; + + // FORWARD search + // needle in haystack + idx = haystack.find(SBuf("def")); + CPPUNIT_ASSERT_EQUAL(3,idx); + + idx = haystack.find(SBuf("xyz")); + CPPUNIT_ASSERT_EQUAL(23,idx); + + // needle not in haystack, no initial char match + idx = haystack.find(SBuf(" eq")); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // needle not in haystack, initial sequence match + idx = haystack.find(SBuf("deg")); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // needle past end of haystack + idx = haystack.find(SBuf("xyz1")); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // search in portion: needle not in searched part + idx = haystack.find(SBuf("def"),7); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // search in portion: overhang + idx = haystack.find(SBuf("def"),4); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // invalid start position + idx = haystack.find(SBuf("def"),SBuf::npos); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // invalid start position: negative + idx = haystack.find(SBuf("def"),-5); + CPPUNIT_ASSERT_EQUAL(3, idx); + + // needle bigger than haystack + idx = SBuf("def").find(haystack); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // search in a double-matching haystack + { + SBuf h2=haystack; + h2.append(haystack); + + idx = h2.find(SBuf("def")); + CPPUNIT_ASSERT_EQUAL(3,idx); + + idx = h2.find(SBuf("xyzab")); + CPPUNIT_ASSERT_EQUAL(23,idx); + } + + + // REVERSE search + // needle in haystack + idx = haystack.rfind(SBuf("def")); + CPPUNIT_ASSERT_EQUAL(3,idx); + + idx = haystack.rfind(SBuf("xyz")); + CPPUNIT_ASSERT_EQUAL(23,idx); + + // needle not in haystack, no initial char match + idx = haystack.rfind(SBuf(" eq")); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // needle not in haystack, initial sequence match + idx = haystack.rfind(SBuf("deg")); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // needle past end of haystack + idx = haystack.rfind(SBuf("xyz1")); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // search in portion: needle in searched part + idx = haystack.rfind(SBuf("def"),7); + CPPUNIT_ASSERT_EQUAL(3, idx); + + // search in portion: needle not in searched part + idx = haystack.rfind(SBuf("mno"),3); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // search in portion: overhang + idx = haystack.rfind(SBuf("def"),4); + CPPUNIT_ASSERT_EQUAL(3, idx); + + // npos start position + idx = haystack.rfind(SBuf("def"),SBuf::npos); + CPPUNIT_ASSERT_EQUAL(3, idx); + + // invalid start position: negative + idx = haystack.rfind(SBuf("def"),-5); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // needle bigger than haystack + idx = SBuf("def").rfind(haystack); + CPPUNIT_ASSERT_EQUAL(nposResult, idx); + + // search in a double-matching haystack + { + SBuf h2=haystack; + h2.append(haystack); + + idx = h2.rfind(SBuf("def")); + CPPUNIT_ASSERT_EQUAL(29,idx); + + idx = h2.find(SBuf("xyzab")); + CPPUNIT_ASSERT_EQUAL(23,idx); + } +} + +void +testSBuf::testRFindChar() +{ + SBuf s1(literal); + SBuf::size_type idx; + idx=s1.rfind(' '); + CPPUNIT_ASSERT_EQUAL(40,idx); + CPPUNIT_ASSERT_EQUAL(' ',s1[idx]); +} + +void +testSBuf::testRFindSBuf() +{ + SBuf haystack(literal),afox("fox"); + SBuf goobar("goobar"); + SBuf::size_type idx; + + // corner case: search for a zero-length SBuf + idx=haystack.rfind(SBuf("")); + CPPUNIT_ASSERT_EQUAL(haystack.length(),idx); + + // corner case: search for a needle longer than the haystack + idx=afox.rfind(SBuf(" ")); + CPPUNIT_ASSERT(idx==SBuf::npos); + + idx=haystack.rfind(SBuf("fox")); + CPPUNIT_ASSERT_EQUAL(16,idx); + + // needle not found, no match for first char + idx=goobar.rfind(SBuf("foo")); + CPPUNIT_ASSERT(idx==SBuf::npos); + + // needle not found, match for first char but no match for SBuf + idx=haystack.rfind(SBuf("foe")); + CPPUNIT_ASSERT(idx==SBuf::npos); + + SBuf g("g"); //match at the last char + idx=haystack.rfind(g); + CPPUNIT_ASSERT_EQUAL(43,idx); + CPPUNIT_ASSERT_EQUAL('g',haystack[idx]); + + idx=haystack.rfind(SBuf("The")); + CPPUNIT_ASSERT_EQUAL(0,idx); + + haystack.append("The"); + idx=haystack.rfind(SBuf("The")); + CPPUNIT_ASSERT_EQUAL(44,idx); + + //partial match + haystack="The quick brown fox"; + SBuf needle("foxy lady"); + idx=haystack.rfind(needle); + CPPUNIT_ASSERT(idx==SBuf::npos); +} + +void +testSBuf::testSBufLength() +{ + SBuf s(fox); + CPPUNIT_ASSERT((size_t)s.length()==strlen(fox)); +} + +void +testSBuf::testScanf() +{ + SBuf s1; + char s[128]; + int i; + float f; + int rv; + s1.assign("string , 123 , 123.50"); + rv=s1.scanf("%s , %d , %f",s,&i,&f); + CPPUNIT_ASSERT(3 == rv); + CPPUNIT_ASSERT(0 == strcmp(s,"string")); + CPPUNIT_ASSERT(i == 123); + CPPUNIT_ASSERT(f == 123.5); +} + +void testSBuf::testCopy() +{ + char buf[40]; //shorter than literal() + SBuf s(fox1),s2; + CPPUNIT_ASSERT(s.copy(buf,40)==s.length()); + CPPUNIT_ASSERT(strncmp(s.rawContent(),buf,s.length())==0); + s=literal; + CPPUNIT_ASSERT(s.copy(buf,40)==40); + s2.assign(buf,0,40); + s.chop(0,40); + CPPUNIT_ASSERT(s==s2); +} + +void testSBuf::testStringOps() +{ + SBuf sng(literal), + ref("the quick brown fox jumped over the lazy dog"); + sng=sng.toLower(); + CPPUNIT_ASSERT_EQUAL(ref,sng); + sng=literal; + CPPUNIT_ASSERT(0==sng.compare(ref,caseInsensitive)); +} + +void testSBuf::testGrow() +{ + SBuf t; + t.assign("foo"); + const char *ref=t.rawContent(); + t.reserveCapacity(10240); + const char *match=t.rawContent(); + CPPUNIT_ASSERT(match!=ref); + ref=match; + t.append(literal).append(literal).append(literal).append(literal).append(literal); + t.append(t).append(t).append(t).append(t).append(t); + CPPUNIT_ASSERT(match==ref); +} + +void testSBuf::testStartsWith() +{ + static SBuf casebuf("THE QUICK"); + CPPUNIT_ASSERT(literal.startsWith(SBuf(fox1))); + CPPUNIT_ASSERT(!SBuf("The quick brown").startsWith(SBuf(fox1))); //too short + CPPUNIT_ASSERT(!literal.startsWith(SBuf(fox2))); //wrong contents + + CPPUNIT_ASSERT(literal.startsWith(casebuf,caseInsensitive)); + casebuf=SBuf(fox1).toUpper(); + CPPUNIT_ASSERT(literal.startsWith(casebuf,caseInsensitive)); + CPPUNIT_ASSERT(literal.startsWith(SBuf(fox1),caseInsensitive)); + casebuf = "tha quick"; + CPPUNIT_ASSERT(!literal.startsWith(casebuf,caseInsensitive)); +} + +void testSBuf::testSBufStream() +{ + SBuf b("const.string, int 10 and a float 10.5"); + SBufStream ss; + ss << "const.string, int " << 10 << " and a float " << 10.5; + SBuf o=ss.buf(); + CPPUNIT_ASSERT_EQUAL(b,o); + ss.clearBuf(); + o=ss.buf(); + CPPUNIT_ASSERT_EQUAL(SBuf(),o); + SBuf f1(fox1); + SBufStream ss2(f1); + ss2 << fox2; + CPPUNIT_ASSERT_EQUAL(ss2.buf(),literal); + CPPUNIT_ASSERT_EQUAL(f1,SBuf(fox1)); +} + +void testSBuf::testFindFirstOf() +{ + SBuf haystack(literal); + SBuf::size_type idx; + + // not found + idx=haystack.find_first_of(SBuf("ADHRWYP")); + CPPUNIT_ASSERT(idx==SBuf::npos); + + // found at beginning + idx=haystack.find_first_of(SBuf("THANDF")); + CPPUNIT_ASSERT_EQUAL(0,idx); + + //found at end of haystack + idx=haystack.find_first_of(SBuf("QWERYVg")); + CPPUNIT_ASSERT_EQUAL(haystack.length()-1,idx); + + //found in the middle of haystack + idx=haystack.find_first_of(SBuf("QWERqYV")); + CPPUNIT_ASSERT_EQUAL(4,idx); +} + +void testSBuf::testAutoFind() +{ + SBufFindTest test; + test.run(); +} === added file 'src/tests/testSBuf.h' --- src/tests/testSBuf.h 1970-01-01 00:00:00 +0000 +++ src/tests/testSBuf.h 2013-04-01 17:31:11 +0000 @@ -0,0 +1,84 @@ +#ifndef SQUID_SRC_TEST_TESTSBUF_H +#define SQUID_SRC_TEST_TESTSBUF_H + +#include + +#include "OutOfBoundsException.h" + +/* + * test the SBuf functionalities + */ + +class testSBuf : public CPPUNIT_NS::TestFixture +{ + CPPUNIT_TEST_SUITE( testSBuf ); + CPPUNIT_TEST( testSBufConstructDestruct ); + CPPUNIT_TEST( testSBufConstructDestructAfterMemInit ); + CPPUNIT_TEST( testSBufLength ); + CPPUNIT_TEST( testEqualityTest ); + CPPUNIT_TEST( testStartsWith ); + CPPUNIT_TEST( testAppendSBuf ); + CPPUNIT_TEST( testAppendCString ); + CPPUNIT_TEST( testAppendStdString ); + CPPUNIT_TEST( testAppendf ); + CPPUNIT_TEST( testSubscriptOp ); + CPPUNIT_TEST_EXCEPTION( testSubscriptOpFail , OutOfBoundsException ); + CPPUNIT_TEST( testComparisons ); + CPPUNIT_TEST( testConsume ); + CPPUNIT_TEST( testRawContent ); + CPPUNIT_TEST( testRawSpace ); + CPPUNIT_TEST( testChop ); + CPPUNIT_TEST( testChomp ); + CPPUNIT_TEST( testSubstr ); + CPPUNIT_TEST( testFindChar ); + CPPUNIT_TEST( testFindSBuf ); + CPPUNIT_TEST( testRFindChar ); + CPPUNIT_TEST( testRFindSBuf ); + CPPUNIT_TEST( testFindFirstOf ); + CPPUNIT_TEST( testPrintf ); + CPPUNIT_TEST( testScanf ); + CPPUNIT_TEST( testCopy ); + CPPUNIT_TEST( testStringOps ); + CPPUNIT_TEST( testGrow ); + CPPUNIT_TEST( testSBufStream ); + CPPUNIT_TEST( testAutoFind ); +// CPPUNIT_TEST( testDumpStats ); //fake test, to print alloc stats + CPPUNIT_TEST_SUITE_END(); +protected: + void commonInit(); + void testSBufConstructDestruct(); + void testSBufConstructDestructAfterMemInit(); + void testEqualityTest(); + void testAppendSBuf(); + void testAppendCString(); + void testAppendStdString(); + void testAppendf(); + void testPrintf(); + void testScanf(); + void testSubscriptOp(); + void testSubscriptOpFail(); + void testDumpStats(); + void testComparisons(); + void testConsume(); + void testRawContent(); + void testRawSpace(); + void testChop(); + void testChomp(); + void testSubstr(); + void testTailCopy(); + void testSBufLength(); + void testFindChar(); + void testFindSBuf(); + void testRFindChar(); + void testRFindSBuf(); + void testSearchFail(); + void testCopy(); + void testStringOps(); + void testGrow(); + void testStartsWith(); + void testSBufStream(); + void testFindFirstOf(); + void testAutoFind(); +}; + +#endif # Begin bundle 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