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
{
/// 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
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * SBuf.cc (C) 2008 Francesco Chemolli <kinkie@squid-cache.org>
+ *
+ * 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 <string.h>
+#endif
+
+#if HAVE_SSTREAM
+#include <sstream>
+#endif
+
+#if HAVE_IOSTREAM
+#include <iostream>
+#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<const char *>(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<char *>(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<const char *>(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<float>(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
+
--- /dev/null
+/*
+ * SBuf.cc (C) 2008 Francesco Chemolli <kinkie@squid-cache.org>
+ *
+ * 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 <climits>
+#elif HAVE_LIMITS_H
+#include <limits.h>
+#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);
+}
--- /dev/null
+/*
+ * SBuf.h (C) 2008 Francesco Chemolli <kinkie@squid-cache.org>
+ *
+ * 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 <unistd.h>
+#endif
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#if HAVE_IOSFWD
+#include <iosfwd>
+#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; ///<number of calls to SBuf constructors
+ u_int64_t allocCopy; ///<number of calls to SBuf copy-constructor
+ u_int64_t allocFromString; ///<number of copy-allocations from Strings
+ u_int64_t allocFromCString; ///<number of copy-allocations from c-strings
+ u_int64_t assignFast; ///<number of no-copy assignment operations
+ u_int64_t clear; ///<number of clear operations
+ u_int64_t append; ///<number of append operations
+ u_int64_t toStream; ///<number of write operations to ostreams
+ u_int64_t setChar; ///<number of calls to setAt
+ u_int64_t getChar; ///<number of calls to at() and operator[]
+ u_int64_t compareSlow; ///<number of comparison operations requiring data scan
+ u_int64_t compareFast; ///<number of comparison operations not requiring data scan
+ u_int64_t copyOut; ///<number of data-copies to other forms of buffers
+ u_int64_t rawAccess; ///<number of accesses to raw contents
+ u_int64_t chop; ///<number of chop operations
+ u_int64_t trim; ///<number of trim operations
+ u_int64_t find; ///<number of find operations
+ u_int64_t scanf; ///<number of scanf operations
+ u_int64_t caseChange; ///<number of toUpper and toLower operations
+ u_int64_t cowFast; ///<number of cow operations not actually requiring a copy
+ u_int64_t cowSlow; ///<number of cow operations requiring a copy
+ u_int64_t live; ///<number of currently-allocated SBuf
+
+ /**
+ * Dump statistics to an ostream.
+ */
+ std::ostream& dump(std::ostream &os) const;
+ SBufStats();
+
+ SBufStats& operator +=(const SBufStats&);
+};
+
+/**
+ * A String or Buffer.
+ * Features: refcounted backing store, cheap copy and sub-stringing
+ * operations, copy-on-write to isolate change operations to each instance.
+ * Where possible, we're trying to mimic std::string's interface.
+ */
+class SBuf
+{
+public:
+ typedef int32_t size_type;
+ static const size_type npos = -1;
+
+ /// Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
+ static const size_type maxSize = 0xfffffff;
+
+ /// create an empty (zero-size) SBuf
+ SBuf();
+ SBuf(const SBuf &S);
+
+ /** Constructor: import c-style string
+ *
+ * Create a new SBuf containing a COPY of the contents of the
+ * c-string
+ * \param S the c string to be copied
+ * \param pos how many bytes to skip at the beginning of the c-string
+ * \param n how many bytes to import into the SBuf. If it is SBuf::npos
+ * or unspecified, imports to end-of-cstring
+ * \note it is the caller's responsibility not to go out of bounds
+ * \note bounds is 0 <= pos < length()
+ */
+ explicit SBuf(const char *S, size_type pos = 0, size_type n = npos);
+
+ /** Constructor: import SquidString, copying contents.
+ *
+ * This method will be removed once SquidString has gone.
+ */
+ SBuf(const String &S);
+
+ ~SBuf();
+ /** Explicit assignment.
+ *
+ * Current SBuf will share backing store with the assigned one.
+ */
+ SBuf& assign(const SBuf &S);
+ /** Assignment operator.
+ *
+ * Current SBuf will share backing store with the assigned one.
+ */
+ _SQUID_INLINE_ SBuf& operator =(const SBuf & S);
+
+ /** Import a c-string into a SBuf, copying the data.
+ *
+ * It is the caller's duty to free the imported string, if needed.
+ * \param S the c string to be copied
+ * \param pos how many bytes to skip at the beginning of the c-string.
+ * \param n how many bytes to import into the SBuf. If it is SBuf::npos
+ * or unspecified, imports to end-of-cstring
+ * \note it is the caller's responsibility not to go out of bounds
+ * \note bounds is 0 <= pos < length()
+ */
+ SBuf& assign(const char *S, size_type pos = 0, size_type n = npos);
+
+ /** Assignment operator. Copy a NULL-terminated c-style string into a SBuf.
+ *
+ * Copy a c-style string into a SBuf. Shortcut for SBuf.assign(S)
+ * It is the caller's duty to free the imported string, if needed.
+ */
+ _SQUID_INLINE_ SBuf& operator =(const char *S);
+
+ /** Import a std::string into a SBuf. Contents are copied.
+ *
+ * \param pos skip this many bytes at the beginning of string.
+ * 0 is beginning-of-string
+ * \param n how many bytes to copy. Default is SBuf::npos, end-of-string.
+ */
+ SBuf& assign(const std::string &s, size_type pos = 0, size_type n = npos);
+
+ /** reset the SBuf as if it was just created.
+ *
+ * Resets the SBuf to empty, memory is freed lazily.
+ */
+ void clear();
+
+ /** Append operation
+ *
+ * Append the supplied SBuf to the current one; extend storage as needed.
+ */
+ SBuf& append(const SBuf & S);
+
+ /** Append operation for C-style strings.
+ *
+ * Append the supplied c-string to the SBuf; extend storage
+ * as needed.
+ *
+ * \param S the c string to be copied. Can be NULL.
+ * \param pos how many bytes to skip at the beginning of the c-string
+ * \param n how many bytes to import into the SBuf. If it is SBuf::npos
+ * or unspecified, imports to end-of-cstring
+ */
+ SBuf& append(const char * S, size_type pos = 0, size_type n = npos);
+
+ /** Append operation for std::string
+ *
+ * Append the supplied std::string to the SBuf; extend storage as needed.
+ *
+ * \param string the std::string to be copied.
+ * \param pos how many bytes to skip at the beginning of the c-string
+ * \param n how many bytes to import into the SBuf. If it is SBuf::npos
+ * or unspecified, imports to end-of-cstring
+ */
+ SBuf& append(const std::string &str, size_type pos = 0, size_type n = npos);
+
+ /** Assignment operation with printf(3)-style definition
+ * \note arguments may be evaluated more than once, be careful
+ * of side-effects
+ */
+ SBuf& Printf(const char *fmt, ...);
+
+ /** Append operation with printf-style arguments
+ * \note arguments may be evaluated more than once, be careful
+ * of side-effects
+ */
+ SBuf& appendf(const char *fmt, ...);
+ /** Append operation, with vsprintf(3)-style arguments.
+ * \note arguments may be evaluated more than once, be careful
+ * of side-effects
+ */
+ SBuf& vappendf(const char *fmt, va_list vargs);
+
+ /** print a SBuf.
+ */
+ std::ostream& print(std::ostream &os) const;
+
+ /** print the sbuf, debug information and stats
+ *
+ * Debug function, dumps to a stream informations on the current SBuf,
+ * including low-level details and statistics.
+ */
+ std::ostream& dump(std::ostream &os) const;
+
+ /** random-access read to any char within the SBuf
+ *
+ * does not check access bounds. If you need that, use at()
+ */
+ _SQUID_INLINE_ const char operator [](size_type pos) const;
+
+ /** random-access read to any char within the SBuf.
+ *
+ * \throw OutOfBoundsException when access is out of bounds
+ * \note bounds is 0 <= pos < length()
+ */
+ _SQUID_INLINE_ const char at(size_type pos) const;
+
+ /** direct-access set a byte at a specified operation.
+ *
+ * \param pos the position to be overwritten
+ * \param toset the value to be written
+ * \throw OutOfBoundsException when pos is of bounds
+ * \note bounds is 0 <= pos < length()
+ * \note performs a copy-on-write if needed.
+ */
+ void setAt(size_type pos, char toset);
+
+ /** compare to other SBuf, str(case)cmp-style
+ *
+ * \param isCaseSensitive one of caseSensitive or caseInsensitive
+ * \param n compare up to this many bytes. if npos (default), to end-of-string
+ * \retval >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 <i>n</i>
+ * bytes starting from position <i>pos</i> 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<SBuf> 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 */
--- /dev/null
+/*
+ * SBufExceptions.cc (C) 2008 Francesco Chemolli <kinkie@squid-cache.org>
+ *
+ * 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)
+{ }
+
+/* */
--- /dev/null
+/*
+ * SBufExceptions.h (C) 2008 Francesco Chemolli <kinkie@squid-cache.org>
+ *
+ * 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 */
--- /dev/null
+/*
+ * 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 <ostream>
+#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<char>(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 */
--- /dev/null
+#include "squid.h"
+#include "SBufFindTest.h"
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/Message.h>
+#include <limits>
+
+
+/* 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<int>::max()),
+ errorLimit(std::numeric_limits<int>::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<std::string::size_type>(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<typename Type>
+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<std::string::size_type>(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;
+ }
+}
--- /dev/null
+#ifndef SQUID_SRC_TEST_SBUFFINDTEST_H
+#define SQUID_SRC_TEST_SBUFFINDTEST_H
+
+#include "SBuf.h"
+
+#if HAVE_STRING
+#include <string>
+#endif
+#include <set>
+
+/// 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<std::string> failedCats; ///< reported failed categories
+};
+
+typedef SBufFindTest::Placement Placement;
+
+#endif
--- /dev/null
+#include "squid.h"
+#include "Mem.h"
+#include "SBuf.h"
+#include "SBufList.h"
+#include "SBufStream.h"
+#include "SBufTokenizer.h"
+#include "SBufUtil.h"
+#include "SquidString.h"
+#include "testSBuf.h"
+#include "SBufFindTest.h"
+
+#include <iostream>
+#include <stdexcept>
+
+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);
+}
+
+// XXX: please split each class into a separate unit-test .cc / even if they share a binary.
+// rule-of-thumb guideline for layering is 'one class Foo -> one Foo.cc, one Foo.cc -> one testFoo.cc'
+
+static int sbuf_tokens_number=9;
+static SBuf tokens[]={
+ SBuf("The",0,3), SBuf("quick",0,5), SBuf("brown",0,5), SBuf("fox",0,3),
+ SBuf("jumped",0,6), SBuf("over",0,4), SBuf("the",0,3), SBuf("lazy",0,4),
+ SBuf("dog",0,3)
+};
+
+void testSBuf::testSBufTokenizer()
+{
+ int j=0;
+ SBuf s;
+ for (SBufTokenizer st(literal,SBuf(" ",0,1)); !st.atEnd(); st.next()) {
+ s=st.token();
+ CPPUNIT_ASSERT(s==tokens[j]);
+ j++;
+ }
+ CPPUNIT_ASSERT(j==9);
+}
+
+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::testSBufList()
+{
+ SBufList foo;
+ for (int j=0; j<sbuf_tokens_number; ++j)
+ foo.add(tokens[j]);
+ CPPUNIT_ASSERT(foo.isMember(SBuf("fox")));
+ CPPUNIT_ASSERT(foo.isMember(SBuf("Fox"),caseInsensitive));
+ CPPUNIT_ASSERT(!foo.isMember(SBuf("garble")));
+ CPPUNIT_ASSERT(foo.isPrefix(SBuf("qui")));
+ CPPUNIT_ASSERT(foo.isPrefix(SBuf("qUi"),caseInsensitive));
+ CPPUNIT_ASSERT(!foo.isPrefix(SBuf("qUa"),caseInsensitive));
+}
+
+void testSBuf::testBaseName()
+{
+ SBuf totest("/foo/bar/gazonk");
+ CPPUNIT_ASSERT_EQUAL(BaseName(totest),SBuf("gazonk"));
+ CPPUNIT_ASSERT_EQUAL(BaseName(totest,'.'),totest);
+}
+
+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();
+}
--- /dev/null
+#ifndef SQUID_SRC_TEST_TESTSBUF_H
+#define SQUID_SRC_TEST_TESTSBUF_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#if 0 // guideline violation: only #include into .h when used by the .h
+#include "SBuf.h"
+#include "SBufExceptions.h"
+#include "SBufList.h"
+#include "SBufUtil.h"
+#endif
+#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( testSBufTokenizer );
+ CPPUNIT_TEST( testStringOps );
+ CPPUNIT_TEST( testGrow );
+ CPPUNIT_TEST( testSBufList );
+ CPPUNIT_TEST( testBaseName );
+ 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 testSBufTokenizer();
+ void testStringOps();
+ void testGrow();
+ void testStartsWith();
+ void testSBufList();
+ void testBaseName();
+ void testSBufStream();
+ void testFindFirstOf();
+ void testAutoFind();
+};
+
+#endif