From: Francesco Chemolli Date: Fri, 26 Jul 2013 09:20:09 +0000 (+0200) Subject: Tied more closely SBuf and MemBlob size_type X-Git-Tag: SQUID_3_5_0_1~612^2~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7cbd3256f41bbf80deed8ae17fd9efae98ff2cf7;p=thirdparty%2Fsquid.git Tied more closely SBuf and MemBlob size_type Better compliance with standards Improved SBuf documentation Improved adherence to std::string method signatures Improved \0-cleanliness Removed some methods taking std::string arguments to avoid double data copies, documented alternate patterns Implemented cmp and caseCmp shortcuts Cleaned-up some methods' implementations after Alex Rousskov's suggestions Improved unit tests' cleanliness by making more effective use of cppunit macros Implemented more unit tests, better coverage for \0-cleanliness --- diff --git a/src/SBuf.cc b/src/SBuf.cc index 42520b4fe0..ae47f36eeb 100644 --- a/src/SBuf.cc +++ b/src/SBuf.cc @@ -60,6 +60,8 @@ InstanceIdDefinitions(SBuf, "SBuf"); SBufStats SBuf::stats; +const SBuf::size_type SBuf::npos; +const SBuf::size_type SBuf::maxSize; SBufStats::SBufStats() : alloc(0), allocCopy(0), allocFromString(0), allocFromCString(0), @@ -119,7 +121,7 @@ SBuf::SBuf(const String &S) : store_(GetStorePrototype()), off_(0), len_(0) { debugs(24, 8, id << " created from string"); - assign(S.rawBuf(), 0, S.size()); + assign(S.rawBuf(), S.size()); ++stats.alloc; ++stats.allocFromString; ++stats.live; @@ -128,17 +130,17 @@ SBuf::SBuf(const String &S) SBuf::SBuf(const std::string &s) : store_(GetStorePrototype()), off_(0), len_(0) { - debugs(24, 8, id << " created from string"); - assign(s.data(),0,s.size()); + debugs(24, 8, id << " created from std::string"); + lowAppend(s.data(),s.length()); ++stats.alloc; ++stats.allocFromString; ++stats.live; } -SBuf::SBuf(const char *S, size_type pos, size_type n) +SBuf::SBuf(const char *S, size_type n) : store_(GetStorePrototype()), off_(0), len_(0) { - append(S,pos,n); //bounds checked in append() + append(S,n); ++stats.alloc; ++stats.allocFromCString; ++stats.live; @@ -153,11 +155,7 @@ SBuf::~SBuf() MemBlob::Pointer SBuf::GetStorePrototype() { - static MemBlob::Pointer InitialStore = NULL; - if (InitialStore == NULL) { - static char lowPrototype[] = ""; - InitialStore = new MemBlob(lowPrototype, 0); - } + static MemBlob::Pointer InitialStore = new MemBlob(0); return InitialStore; } @@ -175,11 +173,11 @@ SBuf::assign(const SBuf &S) } SBuf& -SBuf::assign(const char *S, size_type pos, size_type n) +SBuf::assign(const char *S, size_type n) { - debugs(24, 6, id << " from c-string, pos=" << pos << "n=" << n << ")"); + debugs(24, 6, id << " from c-string, n=" << n << ")"); clear(); - return append(S, pos, n); //bounds checked in append() + return append(S, n); //bounds checked in append() } void @@ -228,41 +226,20 @@ SBuf::clear() SBuf& SBuf::append(const SBuf &S) { - return append(S.buf(), 0, S.length()); + return lowAppend(S.buf(), S.length()); } -SBuf& -SBuf::append(const char * S, size_type pos, size_type n) +SBuf & +SBuf::append(const char * S, size_type Ssize) { - Must(pos == npos || pos >= 0); - Must(n == npos || n >= 0); + Must (Ssize == npos || Ssize >= 0); if (S == NULL) return *this; - if (n == npos) - n = strlen(S)-pos; - + if (Ssize == npos) + Ssize = strlen(S); 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() + return lowAppend(S, Ssize); } SBuf& @@ -296,7 +273,7 @@ SBuf::vappendf(const char *fmt, va_list vargs) Must(fmt != NULL); - //we can assume that we'll need to append at least strlen(fmt) bytes, + //reserve twice the format-string size, it's a likely heuristic reserveSpace(strlen(fmt)*2); while (length() <= maxSize) { @@ -358,12 +335,12 @@ SBuf::dump(std::ostream &os) const << ",len:" << len_ << ") : '"; print(os); - os << std::endl; + os << '\'' << std::endl; return os; } void -SBuf::setAt(SBuf::size_type pos, char toset) +SBuf::setAt(size_type pos, char toset) { checkAccessBounds(pos); cow(); @@ -371,26 +348,45 @@ SBuf::setAt(SBuf::size_type pos, char toset) ++stats.setChar; } +static int +memcasecmp(const char *b1, const char *b2, SBuf::size_type len) +{ + int rv=0; + while (len > 0) { + rv = tolower(*b1)-tolower(*b2); + if (rv != 0) + return rv; + ++b1; + ++b2; + --len; + } + return rv; +} + int SBuf::compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n) const { Must(n == npos || n >= 0); + if (n != npos) { + if (n > length()) + return compare(S.substr(0,n),isCaseSensitive); + return substr(0,n).compare(S.substr(0,n),isCaseSensitive); + } + size_type byteCompareLen = min(S.length(), length()); ++stats.compareSlow; - size_type sz = min(S.length(), length()); - if (n != npos) - sz = min(n, sz); - int rv; - if (isCaseSensitive == caseSensitive) - rv = strncmp(buf(), S.buf(), sz); - else - rv = strncasecmp(buf(), S.buf(), sz); + int rv = 0; + if (isCaseSensitive == caseSensitive) { + rv = memcmp(buf(), S.buf(), byteCompareLen); + } else { + rv = memcasecmp(buf(), S.buf(), byteCompareLen); + } if (rv != 0) return rv; - //if max-length was specified, it was hit, and the tail is ignored. - if (n != npos) + if (length() == S.length()) return 0; - //first sz bytes equal. longest string "wins" - return commonCompareChecksPost(S); + if (length() > S.length()) + return 1; + return -1; } bool @@ -421,7 +417,7 @@ SBuf::operator ==(const SBuf & S) const return true; //shortcut: same store, offset and length } ++stats.compareSlow; - const bool rv = (0 == strncmp(buf(), S.buf(), length())); + const bool rv = (0 == memcmp(buf(), S.buf(), length())); debugs(24, 8, "returning " << rv); return rv; } @@ -433,7 +429,7 @@ SBuf::operator !=(const SBuf & S) const } SBuf -SBuf::consume(SBuf::size_type n) +SBuf::consume(size_type n) { Must (n == npos || n >= 0); if (n == npos) @@ -452,11 +448,11 @@ SBufStats& SBuf::GetStats() } SBuf::size_type -SBuf::copy(char *dest, SBuf::size_type n) const +SBuf::copy(char *dest, size_type n) const { Must(n >= 0); - SBuf::size_type toexport = min(n,length()); + size_type toexport = min(n,length()); memcpy(dest, buf(), toexport); ++stats.copyOut; return toexport; @@ -478,9 +474,11 @@ SBuf::rawSpace(size_type minSize) } void -SBuf::forceSize(SBuf::size_type newSize) +SBuf::forceSize(size_type newSize) { Must(store_->LockCount() == 1); + if (newSize > min(maxSize,store_->capacity-off_)) + throw SBufTooBigException(__FILE__,__LINE__); len_ = newSize; store_->size = newSize; } @@ -499,7 +497,7 @@ SBuf::c_str() } SBuf& -SBuf::chop(SBuf::size_type pos, SBuf::size_type n) +SBuf::chop(size_type pos, size_type n) { if (pos != npos && pos < 0) pos = 0; @@ -543,7 +541,7 @@ SBuf::trim(const SBuf &toRemove, bool atBeginning, bool atEnd) } SBuf -SBuf::substr(SBuf::size_type pos, SBuf::size_type n) const +SBuf::substr(size_type pos, size_type n) const { SBuf rv(*this); rv.chop(pos, n); //stats handled by callee @@ -551,16 +549,18 @@ SBuf::substr(SBuf::size_type pos, SBuf::size_type n) const } SBuf::size_type -SBuf::find(char c, SBuf::size_type startPos) const +SBuf::find(char c, size_type startPos) const { ++stats.find; - if (startPos == SBuf::npos) - return SBuf::npos; + // for npos with char sd::string returns npos + // this differs from how std::string handles 1-length string + if (startPos == npos) + return npos; // std::string returns npos if needle is outside hay if (startPos >= length()) - return SBuf::npos; + return npos; // ignore invalid startPos if (startPos < 0) @@ -569,7 +569,7 @@ SBuf::find(char c, SBuf::size_type startPos) const const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos); if (i == NULL) - return SBuf::npos; + return npos; return (static_cast(i)-buf()); } @@ -577,34 +577,35 @@ SBuf::find(char c, SBuf::size_type startPos) const 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; + if (startPos != npos && startPos > length()) { + ++stats.find; + return npos; + } // for empty needle std::string returns startPos - if (needle.length() == 0) + if (needle.length() == 0) { + ++stats.find; return startPos; + } + + // for npos with char* std::string scans entire hay + // this differs from how std::string handles single char from npos + if (startPos == npos) + return npos; + + // if needle length is 1 use the char search + if (needle.length() == 1) + return find(needle[0], startPos); + + ++stats.find; 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); + " in id " << id); while (begin < lastPossible) { char *tmp; debugs(24, 8, " begin=" << (void *) begin << @@ -612,7 +613,7 @@ SBuf::find(const SBuf &needle, size_type startPos) const tmp = static_cast(memchr(begin, needleBegin, lastPossible-begin)); if (tmp == NULL) { debugs(24, 8 , "First byte not found"); - return SBuf::npos; + return npos; } // lastPossible guarrantees no out-of-bounds with memcmp() if (0 == memcmp(needle.buf(), tmp, needle.length())) { @@ -622,7 +623,7 @@ SBuf::find(const SBuf &needle, size_type startPos) const begin = tmp+1; } debugs(24, 8, "not found"); - return SBuf::npos; + return npos; } SBuf::size_type @@ -635,24 +636,21 @@ SBuf::rfind(const SBuf &needle, SBuf::size_type endPos) const ++stats.find; // on npos input std::string scans from the end of hay - if (endPos == SBuf::npos || endPos > length()) + if (endPos == 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; + return 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; - */ +/* std::string permits needle to overhang endPos + if (endPos <= needle.length()) + return npos; +*/ char *bufBegin = buf(); char *cur = bufBegin+endPos; @@ -675,26 +673,26 @@ 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; + if (length() == 0) + return 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; + if (endPos == npos || endPos >= length()) { + endPos = length(); + } else if (endPos < 0) { + return npos; } else { // NP: off-by-one weirdness: // endPos is an offset ... 0-based // length() is a count ... 1-based - // memrchr() requires a 1-based count of space to scan. + // 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 npos; return (static_cast(i)-buf()); } @@ -709,10 +707,10 @@ SBuf::find_first_of(const SBuf &set, size_type startPos) const ++stats.find; if (startPos == npos) - return SBuf::npos; + return npos; if (startPos > length()) - return SBuf::npos; + return npos; if (startPos < 0) startPos = 0; @@ -728,7 +726,7 @@ SBuf::find_first_of(const SBuf &set, size_type startPos) const ++cur; } debugs(24, 7, "not found"); - return SBuf::npos; + return npos; } /* @@ -816,7 +814,7 @@ SBuf::toUpper() const * \throw OutOfBoundsException if access is out of bounds */ void -SBuf::checkAccessBounds(SBuf::size_type pos) const +SBuf::checkAccessBounds(size_type pos) const { if (pos < 0) throw OutOfBoundsException(*this, pos, __FILE__, __LINE__); @@ -842,7 +840,7 @@ SBuf::toString() const * algorithms in MemBlock, it is guarranteed NOT to be smaller. */ void -SBuf::reAlloc(SBuf::size_type newsize) +SBuf::reAlloc(size_type newsize) { debugs(24, DBG_DATA, "new size: " << newsize); if (newsize > maxSize) @@ -855,3 +853,36 @@ SBuf::reAlloc(SBuf::size_type newsize) ++stats.cowSlow; debugs(24, 7, "new store capacity: " << store_->capacity); } + +SBuf& +SBuf::lowAppend(const char * memArea, size_type areaSize) +{ + reserveSpace(areaSize); //called method also checks n <= maxSize() + store_->append(memArea, areaSize); + len_ += areaSize; + ++stats.append; + return *this; +} + +/** + * 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; +} diff --git a/src/SBuf.h b/src/SBuf.h index 678725d000..60708d3bba 100644 --- a/src/SBuf.h +++ b/src/SBuf.h @@ -32,8 +32,6 @@ #define SQUID_SBUF_H #include "base/InstanceId.h" -// Debug.h only needed for for SBuf::cow() debug statements. -#include "Debug.h" #include "MemBlob.h" #include "SBufExceptions.h" #include "SquidString.h" @@ -74,28 +72,28 @@ typedef enum { 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; + int compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n = npos) const; + + /// shorthand version for compare + inline int cmp(const SBuf &S, size_type n = npos) const { + return compare(S,caseSensitive,n); + } + + /// shorthand version for case-insensitive comparison + inline int caseCmp(const SBuf &S, size_type n = npos) const { + return compare(S,caseInsensitive,n); + } /** check whether the entire supplied argument is a prefix of the SBuf. * \param S the prefix to match against @@ -291,10 +282,10 @@ public: bool operator ==(const SBuf & S) const; bool operator !=(const SBuf & S) const; - bool operator <(const SBuf &S) const {return (compare(S) < 0);} - bool operator >(const SBuf &S) const {return (compare(S) > 0);} - bool operator <=(const SBuf &S) const {return (compare(S) <= 0);} - bool operator >=(const SBuf &S) const {return (compare(S) >= 0);} + bool operator <(const SBuf &S) const {return (cmp(S) < 0);} + bool operator >(const SBuf &S) const {return (cmp(S) > 0);} + bool operator <=(const SBuf &S) const {return (cmp(S) <= 0);} + bool operator >=(const SBuf &S) const {return (cmp(S) >= 0);} /** Consume bytes at the head of the SBuf * @@ -302,7 +293,7 @@ public: * 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' + * npos (or no argument) means 'to the end of SBuf' * \return a new SBuf containing the consumed bytes. */ SBuf consume(size_type n = npos); @@ -368,7 +359,8 @@ public: * * Adapt the SBuf internal state after external interference * such as writing into it via rawSpace. - * \throw TextException if we + * \throw TextException if SBuf doesn't have exclusive ownership of store + * \throw SBufTooBigException if new size is bigger than available store space */ void forceSize(size_type newSize); @@ -414,8 +406,9 @@ public: /** Request to extend the SBuf's free store space. * * After the reserveSpace request, the SBuf is guaranteed to have at - * least minSpace bytes of unused backing store - * following the currently used portion + * least minSpace bytes of unused backing store following the currently + * used portion until the next append operation to any of the SBufs + * sharing the backing MemBlob * \throw SBufTooBigException if the user tries to allocate too big a SBuf */ void reserveSpace(size_type minSpace); @@ -423,7 +416,8 @@ public: /** Request to resize the SBuf's store * * After this method is called, the SBuf is guaranteed to have at least - * minCapacity bytes of total space, including the currently-used portion + * minCapacity bytes of total buffer size, including the currently-used + * portion * \throw SBufTooBigException if the user tries to allocate too big a SBuf */ void reserveCapacity(size_type minCapacity); @@ -437,7 +431,7 @@ public: * npos or it is greater than the SBuf length, the SBuf is cleared and * an empty SBuf is returned. If it is <0, it is ignored * \param n maximum number of bytes of the resulting SBuf. - * SBuf::npos means "to end of SBuf". + * npos means "to end of SBuf". * if it is 0, the SBuf is cleared and an empty SBuf is returned. * if it is < 0, it is ignored (same as supplying npos) * if it overflows the end of the SBuf, it is capped to the end of SBuf @@ -465,10 +459,10 @@ public: /** 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 + * \return npos if the char was not found * \param startPos if specified, ignore any occurrences before that position * if startPos is npos or greater than length() npos is always returned - * if startPos is < 0, it is ignored + * if startPos is less than zero, it is ignored */ size_type find(char c, size_type startPos = 0) const; @@ -479,14 +473,14 @@ public: * \param startPos if specified, ignore any occurrences before that position * if startPos is npos or greater than length() npos is always returned * if startPos is < 0, it is ignored - * \return SBuf::npos if the SBuf was not found + * \return 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 + * \return npos if the char was not found * \param endPos if specified, ignore any occurrences after that position. * if npos or greater than length(), the whole SBuf is considered * if < 0, npos is always returned @@ -497,7 +491,7 @@ public: * * 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 + * \return npos if the sequence was not found * \param endPos if specified, ignore any occurrences after that position * if npos or greater than length(), the whole SBuf is considered * if < 0, npos is always returned @@ -508,9 +502,9 @@ public: * * 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 + * \return 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 npos, then npos is always returned * if <0, it is ignored. */ size_type find_first_of(const SBuf &set, size_type startPos = 0) const; @@ -543,6 +537,9 @@ public: */ String toString() const; + /// std::string export function + std::string toStdString() const { return std::string(buf(),length()); } + // TODO: possibly implement erase() similar to std::string's erase // TODO: possibly implement a replace() call private: @@ -552,7 +549,9 @@ private: size_type len_; ///< number of our content bytes in shared store_ static SBufStats stats; ///< class-wide statistics - const InstanceId id; ///< blob identifier + /// SBuf object identifier; does not change when contents do, + /// including during assignment + const InstanceId id; /** obtain prototype store * @@ -582,44 +581,18 @@ private: void reAlloc(size_type newsize); - /** - * 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 cow(size_type minsize = npos) { - debugs(24, DBG_DATA, "new size (minimum):" << minsize); - if (minsize == npos || minsize < length()) - minsize = length(); - - if (store_->LockCount() == 1 && minsize == length()) { - debugs(24, DBG_DATA, "no cow needed"); - ++stats.cowFast; - return false; - } - reAlloc(minsize); - return true; - } + bool cow(size_type minsize = npos); void checkAccessBounds(size_type pos) const; - /** - * 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 commonCompareChecksPost(const SBuf &S) const { - if (length() == S.length()) - return 0; - if (length() > S.length()) - return 1; - return -1; - } - + /** Low-level append operation + * + * Takes as input a contiguous area of memory and appends its contents + * to the SBuf, taking care of memory management. Does no bounds checking + * on the supplied memory buffer, it is the duty of the caller to ensure + * that the supplied area is valid. + */ + SBuf& lowAppend(const char * memArea, size_type areaSize); }; /// ostream output operator diff --git a/src/SBufStream.h b/src/SBufStream.h index 6378912764..63331f130e 100644 --- a/src/SBufStream.h +++ b/src/SBufStream.h @@ -68,7 +68,7 @@ protected: char chars[1] = {static_cast(aChar)}; if (aChar != traits_type::eof()) - theBuf.append(chars, 0, 1); + theBuf.append(chars, 1); } pbump(-pending); // Reset pptr(). @@ -80,7 +80,7 @@ protected: std::streamsize pending(pptr() - pbase()); if (pending) - theBuf.append(pbase(), 0, pending); + theBuf.append(pbase(), pending); return 0; } @@ -90,7 +90,7 @@ protected: */ virtual std::streamsize xsputn(const char * chars, std::streamsize number) { if (number) - theBuf.append(chars, 0, number); + theBuf.append(chars, number); return number; } diff --git a/src/tests/SBufFindTest.cc b/src/tests/SBufFindTest.cc index 84e8ffe4ec..cd3d2079fc 100644 --- a/src/tests/SBufFindTest.cc +++ b/src/tests/SBufFindTest.cc @@ -380,7 +380,7 @@ SBufFindTest::RandomSBuf(const int length) buf[i] = characters[random() % charCount]; } - return SBuf(buf, 0, length); + return SBuf(buf, length); } /// increments len to quickly cover [0, max] range, slowing down in risky areas diff --git a/src/tests/testSBuf.cc b/src/tests/testSBuf.cc index a70534c68a..41cc334a3b 100644 --- a/src/tests/testSBuf.cc +++ b/src/tests/testSBuf.cc @@ -48,49 +48,42 @@ testSBuf::testSBufConstructDestruct() // 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())); + CPPUNIT_ASSERT_EQUAL(0,s1.length()); + CPPUNIT_ASSERT_EQUAL(SBuf(""),s1); + CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1); + CPPUNIT_ASSERT_EQUAL(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())); + CPPUNIT_ASSERT_EQUAL(0,s1.length()); + CPPUNIT_ASSERT_EQUAL(SBuf(""),s1); + CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1); + CPPUNIT_ASSERT_EQUAL(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 char* - { - SBuf s1(fox); - CPPUNIT_ASSERT_EQUAL(static_cast(s1.length()),strlen(fox)); - CPPUNIT_ASSERT(0==strcmp(fox,s1.c_str())); + CPPUNIT_ASSERT_EQUAL(0,s1.length()); + CPPUNIT_ASSERT_EQUAL(SBuf(""),s1); + CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1); + CPPUNIT_ASSERT_EQUAL(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())); + CPPUNIT_ASSERT_EQUAL(0,s1.length()); + CPPUNIT_ASSERT_EQUAL(SBuf(""),s1); + CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1); + CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1.c_str())); SBuf s5(literal); - CPPUNIT_ASSERT_EQUAL(s5,literal); + CPPUNIT_ASSERT_EQUAL(literal,s5); SBuf s6(fox); - CPPUNIT_ASSERT_EQUAL(s6,literal); + CPPUNIT_ASSERT_EQUAL(literal,s6); // XXX: other state checks. expected result of calling any state accessor on s4 ? } @@ -104,10 +97,10 @@ testSBuf::testSBufConstructDestruct() // TEST: sub-string copy { - SBuf s1=SBuf(fox,4), s2(fox); + 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); + SBuf s4=SBuf(fox,4); s3=s2.substr(0,4); CPPUNIT_ASSERT_EQUAL(s4,s3); } @@ -116,16 +109,15 @@ testSBuf::testSBufConstructDestruct() { String str(fox); SBuf s1(str); - CPPUNIT_ASSERT_EQUAL(s1,literal); + CPPUNIT_ASSERT_EQUAL(literal,s1); } // TEST: go via std::string adapter. { std::string str(fox); SBuf s1(str); - CPPUNIT_ASSERT_EQUAL(s1,literal); + CPPUNIT_ASSERT_EQUAL(literal,s1); } - } void @@ -133,7 +125,6 @@ testSBuf::testSBufConstructDestructAfterMemInit() { Mem::Init(); testSBufConstructDestruct(); - } void @@ -182,10 +173,26 @@ testSBuf::testAppendCString() void testSBuf::testAppendStdString() { - SBuf s1(fox1); - std::string str(fox2); - s1.append(str); - CPPUNIT_ASSERT_EQUAL(s1,literal); + const char *alphabet="abcdefghijklmnopqrstuvwxyz"; + { + SBuf alpha(alphabet), s; + s.append(alphabet,5).append(alphabet+5); + CPPUNIT_ASSERT_EQUAL(alpha,s); + } + { + SBuf s; + std::string control; + s.append(alphabet,5).append("\0",1).append(alphabet+6,SBuf::npos); + control.append(alphabet,5).append(1,'\0').append(alphabet,6,std::string::npos); + SBuf scontrol(control); // we need this to test the equality. sigh. + CPPUNIT_ASSERT_EQUAL(scontrol,s); + } + { + const char *alphazero="abcdefghijk\0mnopqrstuvwxyz"; + SBuf s(alphazero,26); + std::string str(alphazero,26); + CPPUNIT_ASSERT_EQUAL(0,memcmp(str.data(),s.rawContent(),26)); + } } void @@ -241,17 +248,35 @@ 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.cmp(s2)>0); + CPPUNIT_ASSERT(s1.caseCmp(s2)>0); + CPPUNIT_ASSERT(s2.cmp(s1)<0); + CPPUNIT_ASSERT_EQUAL(0,s1.cmp(s2,2)); + CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2)); CPPUNIT_ASSERT(s1 > s2); CPPUNIT_ASSERT(s2 < s1); - CPPUNIT_ASSERT_EQUAL(sign(s1.compare(s2)),sign(strcmp(s1.c_str(),s2.c_str()))); + CPPUNIT_ASSERT_EQUAL(sign(s1.cmp(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.cmp(s2)<0); + CPPUNIT_ASSERT_EQUAL(sign(s1.cmp(s2)),sign(strcmp(s1.c_str(),s2.c_str()))); CPPUNIT_ASSERT(s1 < s2); + // specifying the max-length and overhanging size + CPPUNIT_ASSERT_EQUAL(1,SBuf("foolong").caseCmp(SBuf("foo"), 5)); + // case-insensive comaprison + s1 = "foo"; + s2 = "fOo"; + CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2)); + CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2)); + // \0-clenliness test + s1.assign("f\0oo",4); + s2.assign("f\0Oo",4); + CPPUNIT_ASSERT_EQUAL(1,s1.cmp(s2)); + CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2)); + CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,3)); + CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2)); + CPPUNIT_ASSERT_EQUAL(0,s1.cmp(s2,2)); } void @@ -275,7 +300,7 @@ testSBuf::testRawContent() s2.append("foo"); const char *foo; foo = s1.rawContent(); - CPPUNIT_ASSERT(strncmp(fox,foo,s1.length())==0); + CPPUNIT_ASSERT_EQUAL(0,strncmp(fox,foo,s1.length())); foo = s1.c_str(); CPPUNIT_ASSERT(!strcmp(fox,foo)); } @@ -416,8 +441,8 @@ testSBuf::testFindChar() // FORWARD SEARCH // needle in haystack idx=s1.find('d'); - CPPUNIT_ASSERT(idx == 3); - CPPUNIT_ASSERT(s1[idx]=='d'); + CPPUNIT_ASSERT_EQUAL(3,idx); + CPPUNIT_ASSERT_EQUAL('d',s1[idx]); // needle not present in haystack idx=s1.find(' '); //fails @@ -610,18 +635,18 @@ testSBuf::testRFindSBuf() // corner case: search for a needle longer than the haystack idx=afox.rfind(SBuf(" ")); - CPPUNIT_ASSERT(idx==SBuf::npos); + CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx); 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); + CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx); // needle not found, match for first char but no match for SBuf idx=haystack.rfind(SBuf("foe")); - CPPUNIT_ASSERT(idx==SBuf::npos); + CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx); SBuf g("g"); //match at the last char idx=haystack.rfind(g); @@ -639,14 +664,14 @@ testSBuf::testRFindSBuf() haystack="The quick brown fox"; SBuf needle("foxy lady"); idx=haystack.rfind(needle); - CPPUNIT_ASSERT(idx==SBuf::npos); + CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx); } void testSBuf::testSBufLength() { SBuf s(fox); - CPPUNIT_ASSERT((size_t)s.length()==strlen(fox)); + CPPUNIT_ASSERT_EQUAL(strlen(fox),(size_t)s.length()); } void @@ -659,33 +684,36 @@ testSBuf::testScanf() 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); + CPPUNIT_ASSERT_EQUAL(3,rv); + CPPUNIT_ASSERT_EQUAL(0,strcmp(s,"string")); + CPPUNIT_ASSERT_EQUAL(123,i); + CPPUNIT_ASSERT_EQUAL(static_cast(123.5),f); } 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); + CPPUNIT_ASSERT_EQUAL(s.length(),s.copy(buf,40)); + CPPUNIT_ASSERT_EQUAL(0,strncmp(s.rawContent(),buf,s.length())); s=literal; - CPPUNIT_ASSERT(s.copy(buf,40)==40); - s2.assign(buf,0,40); + CPPUNIT_ASSERT_EQUAL(40,s.copy(buf,40)); + s2.assign(buf,40); s.chop(0,40); - CPPUNIT_ASSERT(s==s2); + CPPUNIT_ASSERT_EQUAL(s2,s); } void testSBuf::testStringOps() { - SBuf sng(literal), + SBuf sng(literal.toLower()), 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)); + CPPUNIT_ASSERT_EQUAL(0,sng.compare(ref,caseInsensitive)); + // max-size comparison + CPPUNIT_ASSERT_EQUAL(0,ref.compare(SBuf("THE"),caseInsensitive,3)); + CPPUNIT_ASSERT_EQUAL(1,ref.compare(SBuf("THE"),caseInsensitive,6)); + CPPUNIT_ASSERT_EQUAL(0,SBuf("the").compare(SBuf("THE"),caseInsensitive,6)); } void testSBuf::testGrow() @@ -699,7 +727,7 @@ void testSBuf::testGrow() 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); + CPPUNIT_ASSERT_EQUAL(ref,match); } void testSBuf::testStartsWith() @@ -707,14 +735,15 @@ 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(SBuf(fox2))); //different contents + // case-insensitive checks 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)); + CPPUNIT_ASSERT_EQUAL(false,literal.startsWith(casebuf,caseInsensitive)); } void testSBuf::testSBufStream() @@ -741,7 +770,7 @@ void testSBuf::testFindFirstOf() // not found idx=haystack.find_first_of(SBuf("ADHRWYP")); - CPPUNIT_ASSERT(idx==SBuf::npos); + CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx); // found at beginning idx=haystack.find_first_of(SBuf("THANDF")); @@ -761,3 +790,11 @@ void testSBuf::testAutoFind() SBufFindTest test; test.run(); } + +void testSBuf::testStdStringOps() +{ + const char *alphabet="abcdefghijklmnopqrstuvwxyz"; + std::string astr(alphabet); + SBuf sb(alphabet); + CPPUNIT_ASSERT_EQUAL(astr,sb.toStdString()); +} diff --git a/src/tests/testSBuf.h b/src/tests/testSBuf.h index 241889f219..373eeef118 100644 --- a/src/tests/testSBuf.h +++ b/src/tests/testSBuf.h @@ -42,6 +42,7 @@ class testSBuf : public CPPUNIT_NS::TestFixture CPPUNIT_TEST( testGrow ); CPPUNIT_TEST( testSBufStream ); CPPUNIT_TEST( testAutoFind ); + CPPUNIT_TEST( testStdStringOps ); // CPPUNIT_TEST( testDumpStats ); //fake test, to print alloc stats CPPUNIT_TEST_SUITE_END(); protected: @@ -79,6 +80,7 @@ protected: void testSBufStream(); void testFindFirstOf(); void testAutoFind(); + void testStdStringOps(); }; #endif