# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kinkie@squid-cache.org-20130329175551-tzunfysprhdmiy71 # target_branch: ../trunk/ # testament_sha1: 8ce94b42527eacf5be85fe4a0a3447389053c8cf # timestamp: 2013-03-29 18:56:06 +0100 # base_revision_id: squid3@treenet.co.nz-20130329054819-\ # zhcm0d8aj5gzsyek # # 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-03-29 16:50:25 +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 _buf; + SBuf::size_type _pos; +}; + +#endif /* _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H */ === added file 'src/SBuf.cc' --- src/SBuf.cc 1970-01-01 00:00:00 +0000 +++ src/SBuf.cc 2013-03-29 16:50:25 +0000 @@ -0,0 +1,850 @@ +/* + * 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 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, + //times 1.2 for instance... + 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 < 0 || sz >= (int)store_->spaceSize()) + reserveSpace(sz*2); // TODO: tune heuristics + 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); + size_type sz = min(S.length(), length()); + if (n != npos) + sz = min(n, sz); + ++stats.compareSlow; + 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 +{ + if (length() < S.length()) { + ++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 = length(); + if (toexport > n) + toexport = n; + + 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) +{ + Must(pos >= 0); + Must(n == npos || n >= 0); + /* + * TODO: possible optimization: if the SBuf is at the tail of the + * MemBlob we could decrease the MemBlob tail-pointer so that a subsequent + * append will reuse the freed space. + */ + if (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 + // memrhr() 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 & +operator <<(std::ostream& os, const SBuf& S) +{ + return S.print(os); +} + +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-03-29 16:50:25 +0000 @@ -0,0 +1,197 @@ +/* + * 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 "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 + * + */ +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; + if (pos < 0 || pos >= length()) + return '\0'; + 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); +} === added file 'src/SBuf.h' --- src/SBuf.h 1970-01-01 00:00:00 +0000 +++ src/SBuf.h 2013-03-29 16:50:25 +0000 @@ -0,0 +1,566 @@ +/* + * 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 "Debug.h" +#include "MemBlob.h" +#include "SquidString.h" + +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STDARG_H +#include +#endif +#if HAVE_IOSFWD +#include +#endif + +/* squid string 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; + + /** equality check + */ + 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 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 to ensure that a following write will not trample + * a shared MemBlob. The returned pointer must not be stored, and will + * become invalid at the first call to a non-const method call + * on the SBuf. + * This call guarantees to never return NULL + * This call always forces a cow() + * \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 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 append-able backing store (on top of 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 + * minCapcity 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. + * \param pos start sub-stringing from this byte. If it is + * greater than the SBuf length, the SBuf is emptied and + * an empty SBuf is returned + * \param n maximum number of bytes of the resulting SBuf. + * SBuf::npos means "to end of SBuf". + * if 0 returns an empty SBuf. + */ + 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. + * \see trim + */ + 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 SBuf::npos, 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 SBuf::npos, 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 unspecified or npos, the whole SBuf is considered. + * If < 0, npos is 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 unspecified or npos, the whole SBuf is considered + * if < 0, then 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. Transitional. + */ + 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; + +}; + +/** + * Prints a SBuf to the supplied stream, allowing for chaining + */ +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-03-29 16:50:25 +0000 @@ -0,0 +1,72 @@ +/* + * 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" + +// Note: the SBuf is intentionally passed by copy rather than reference, +// to let refcounting act. +OutOfBoundsException::OutOfBoundsException(const SBuf &throwingBuf, + SBuf::size_type &pos, + const char *aFileName, int aLineNo) + :TextException(NULL, aFileName, aLineNo) +{ + _buf = throwingBuf; + _pos = pos; + SBuf explanatoryText("OutOfBoundsException"); + if (aLineNo != -1) + explanatoryText.appendf(" at line %d", aLineNo); + if (aFileName != 0) + 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-03-29 16:50:25 +0000 @@ -0,0 +1,141 @@ +/* + * 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. + * + */ +class SBufStreamBuf : public std::streambuf +{ +public: + /// initialize streambuf; use supplied SBuf as backing store + 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 + * - this is an optimisation method. + */ + 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 argument's SBuf. + * + * The supplied SBuf is not aliased: in order to retrieve the altered contents + * they must be 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-03-29 17:05:35 +0000 @@ -0,0 +1,678 @@ +#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); + } +} + +void +testSBuf::testSBufConstructDestructAfterMemInit() +{ + Mem::Init(); + testSBufConstructDestruct(); + +// XXX: or perhapse ... +// repeat all of the tests inside testSBufConstructDestructBeforeMemInit() +// with additional checks on Mem usage stats after each operation ?? +} + +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'); +// std::cout << chg << std::endl << empty_sbuf << std::endl ; +} + +// 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(1234); //out of bounds + //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); + char *rb=s2.rawSpace(strlen(fox2)+1); + strcat(rb,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); +} + +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); +} + +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); +} + +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-03-29 17:05:35 +0000 @@ -0,0 +1,86 @@ +#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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