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),
: 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;
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;
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;
}
}
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
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&
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) {
<< ",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();
++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
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;
}
}
SBuf
-SBuf::consume(SBuf::size_type n)
+SBuf::consume(size_type n)
{
Must (n == npos || n >= 0);
if (n == npos)
}
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;
}
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;
}
}
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;
}
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
}
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)
const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos);
if (i == NULL)
- return SBuf::npos;
+ return 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;
+ 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 <<
tmp = static_cast<char *>(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())) {
begin = tmp+1;
}
debugs(24, 8, "not found");
- return SBuf::npos;
+ return npos;
}
SBuf::size_type
++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;
++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<const char *>(i)-buf());
}
++stats.find;
if (startPos == npos)
- return SBuf::npos;
+ return npos;
if (startPos > length())
- return SBuf::npos;
+ return npos;
if (startPos < 0)
startPos = 0;
++cur;
}
debugs(24, 7, "not found");
- return SBuf::npos;
+ return npos;
}
/*
* \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__);
* 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)
++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;
+}
#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"
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
+ uint64_t alloc; ///<number of calls to SBuf constructors
+ uint64_t allocCopy; ///<number of calls to SBuf copy-constructor
+ uint64_t allocFromString; ///<number of copy-allocations from Strings
+ uint64_t allocFromCString; ///<number of copy-allocations from c-strings
+ uint64_t assignFast; ///<number of no-copy assignment operations
+ uint64_t clear; ///<number of clear operations
+ uint64_t append; ///<number of append operations
+ uint64_t toStream; ///<number of write operations to ostreams
+ uint64_t setChar; ///<number of calls to setAt
+ uint64_t getChar; ///<number of calls to at() and operator[]
+ uint64_t compareSlow; ///<number of comparison operations requiring data scan
+ uint64_t compareFast; ///<number of comparison operations not requiring data scan
+ uint64_t copyOut; ///<number of data-copies to other forms of buffers
+ uint64_t rawAccess; ///<number of accesses to raw contents
+ uint64_t chop; ///<number of chop operations
+ uint64_t trim; ///<number of trim operations
+ uint64_t find; ///<number of find operations
+ uint64_t scanf; ///<number of scanf operations
+ uint64_t caseChange; ///<number of toUpper and toLower operations
+ uint64_t cowFast; ///<number of cow operations not actually requiring a copy
+ uint64_t cowSlow; ///<number of cow operations requiring a copy
+ uint64_t live; ///<number of currently-allocated SBuf
///Dump statistics to an ostream.
std::ostream& dump(std::ostream &os) const;
class SBuf
{
public:
- typedef int32_t size_type;
+ typedef MemBlob::size_type size_type;
static const size_type npos = -1;
/// Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
* 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
+ * \param n how many bytes to import into the SBuf. If it is 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);
+ explicit SBuf(const char *S, size_type n = npos);
/** Constructor: import SquidString, copying contents.
*
*
* 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
+ * \param n how many bytes to import into the SBuf. If it is 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()
+ * \note to assign a std::string use the pattern:
+ * assign(stdstr.data(), stdstd.length())
*/
- SBuf& assign(const char *S, size_type pos = 0, size_type n = npos);
+ SBuf& assign(const char *S, 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.
+ * \note not \0-clean
*/
SBuf& operator =(const char *S) {return assign(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.
*
* \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
+ * \param n how many bytes to import into the SBuf. If it is npos
* or unspecified, imports to end-of-cstring
+ * \note to append a std::string use the pattern
+ * cstr_append(stdstr.data(), stdstd.length())
*/
- SBuf& append(const std::string &str, size_type pos = 0, size_type n = npos);
+ SBuf& append(const char * S, size_type Ssize = npos);
/** Assignment operation with printf(3)-style definition
* \note arguments may be evaluated more than once, be careful
/** 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
+ * \param n compare up to this many bytes. if npos (default), compare whole SBufs
* \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;
+ 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
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
*
* 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);
*
* 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);
/** 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);
/** 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);
* 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
/** 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;
* \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
*
* 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
*
* 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;
*/
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:
size_type len_; ///< number of our content bytes in shared store_
static SBufStats stats; ///< class-wide statistics
- const InstanceId<SBuf> id; ///< blob identifier
+ /// SBuf object identifier; does not change when contents do,
+ /// including during assignment
+ const InstanceId<SBuf> id;
/** obtain prototype store
*
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
char chars[1] = {static_cast<char>(aChar)};
if (aChar != traits_type::eof())
- theBuf.append(chars, 0, 1);
+ theBuf.append(chars, 1);
}
pbump(-pending); // Reset pptr().
std::streamsize pending(pptr() - pbase());
if (pending)
- theBuf.append(pbase(), 0, pending);
+ theBuf.append(pbase(), pending);
return 0;
}
*/
virtual std::streamsize xsputn(const char * chars, std::streamsize number) {
if (number)
- theBuf.append(chars, 0, number);
+ theBuf.append(chars, number);
return number;
}
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
// 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<size_t>(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 ?
}
// 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);
}
{
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
{
Mem::Init();
testSBufConstructDestruct();
-
}
void
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
{
//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
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));
}
// 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
// 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);
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
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<float>(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()
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()
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()
// 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"));
SBufFindTest test;
test.run();
}
+
+void testSBuf::testStdStringOps()
+{
+ const char *alphabet="abcdefghijklmnopqrstuvwxyz";
+ std::string astr(alphabet);
+ SBuf sb(alphabet);
+ CPPUNIT_ASSERT_EQUAL(astr,sb.toStdString());
+}
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:
void testSBufStream();
void testFindFirstOf();
void testAutoFind();
+ void testStdStringOps();
};
#endif