virtual ~OutOfBoundsException() throw();
protected:
- SBuf _buf;
- SBuf::size_type _pos;
+ SBuf theThrowingBuf;
+ SBuf::size_type accessedPosition;
};
#endif /* _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H */
++stats.live;
}
+SBuf::SBuf(const std::string &s)
+ : store_(GetStorePrototype()), off_(0), len_(0)
+{
+ debugs(24, 8, id << " created from string");
+ assign(s.data(),0,s.size());
+ ++stats.alloc;
+ ++stats.allocFromString;
+ ++stats.live;
+}
+
SBuf::SBuf(const char *S, size_type pos, size_type n)
: store_(GetStorePrototype()), off_(0), len_(0)
{
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) {
/* snprintf on Linux returns -1 on overflows */
/* snprintf on FreeBSD returns at least free_space on overflows */
- if (sz < 0 || sz >= (int)store_->spaceSize())
+ if (sz >= static_cast<int>(store_->spaceSize()))
reserveSpace(sz*2); // TODO: tune heuristics
+ else if (sz < 0) // output error in vsnprintf
+ throw TextException("output error in vsnprintf",__FILE__, __LINE__);
else
break;
}
/* on XXX it might be counted */
/* check that '\0' is appended and not counted */
- if (operator[](len_-1) == 0) {
+ if (operator[](len_-1) == '\0') {
--sz;
--len_;
}
SBuf::compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n) const
{
Must(n == npos || n >= 0);
+ ++stats.compareSlow;
size_type sz = min(S.length(), length());
if (n != npos)
sz = min(n, sz);
- ++stats.compareSlow;
int rv;
if (isCaseSensitive == caseSensitive)
rv = strncmp(buf(), S.buf(), sz);
bool
SBuf::startsWith(const SBuf &S, SBufCaseSensitive isCaseSensitive) const
{
+ debugs(24, 8, id << " startsWith " << S.id << ", caseSensitive: " <<
+ isCaseSensitive);
if (length() < S.length()) {
+ debugs(24, 8, "no, too short");
++stats.compareFast;
return false;
}
{
Must(n >= 0);
- SBuf::size_type toexport = length();
- if (toexport > n)
- toexport = n;
-
+ SBuf::size_type toexport = min(n,length());
memcpy(dest, buf(), toexport);
-
++stats.copyOut;
return toexport;
}
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) {
+ if (pos != npos && pos < 0)
+ pos = 0;
+ if (n != npos && n < 0)
+ n = npos;
+ if (pos == npos || pos > length() || n == 0) {
clear();
return *this;
}
// 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.
+ // memrchr() requires a 1-based count of space to scan.
++endPos;
}
return rv;
}
-std::ostream &
-operator <<(std::ostream& os, const SBuf& S)
-{
- return S.print(os);
-}
-
std::ostream &
SBufStats::dump(std::ostream& os) const
{
{
if (pos < 0)
throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
- if (pos > length())
+ if (pos >= length())
throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
}
return rv;
}
-/*
- * re-allocate the backing store of the SBuf.
+/** 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.
*/
#include "base/RefCount.h"
+#include "Debug.h"
#include "OutOfBoundsException.h"
#include "SBufExceptions.h"
/** returns the pointer to the first char after this SBuf end
*
+ * No checks are made that the space returned is safe, checking that is
+ * up to the caller.
*/
char *
SBuf::bufEnd() const
SBuf::operator [](SBuf::size_type pos) const
{
++stats.getChar;
- if (pos < 0 || pos >= length())
- return '\0';
return store_->mem[off_+pos];
}
{
return (len_ == 0);
}
+
+std::ostream &
+operator <<(std::ostream& os, const SBuf& S)
+{
+ return S.print(os);
+}
#define SQUID_SBUF_H
#include "base/InstanceId.h"
-#include "Debug.h"
#include "MemBlob.h"
#include "SquidString.h"
#include <iosfwd>
#endif
-/* squid string placeholder (for printf) */
+/* SBuf placeholder for printf */
#ifndef SQUIDSBUFPH
#define SQUIDSBUFPH "%.*s"
#define SQUIDSBUFPRINT(s) (s).plength(),(s).rawContent()
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.
- */
+ ///Dump statistics to an ostream.
std::ostream& dump(std::ostream &os) const;
SBufStats();
*
* This method will be removed once SquidString has gone.
*/
- SBuf(const String &S);
+ explicit SBuf(const String &S);
+
+ /// Constructor: import std::string. Contents are copied.
+ explicit SBuf(const std::string &s);
~SBuf();
/** Explicit assignment.
* 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.
- */
+ /// print the SBuf contents to the supplied ostream
std::ostream& print(std::ostream &os) const;
- /** print the sbuf, debug information and stats
+ /** print SBuf contents and debug information about the SBuf to an ostream
*
* Debug function, dumps to a stream informations on the current SBuf,
* including low-level details and statistics.
*/
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;
*/
SBuf consume(size_type n = npos);
- /** gets global statistic informations
- *
- */
+ /// gets global statistic informations
static const SBufStats& GetStats();
/** Copy SBuf contents into user-supplied C buffer.
/** 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).
+ * Returns a ead-only pointer to SBuf's content. No terminating null
+ * character is appended (use c_str() for that).
* The returned value points to an internal location whose contents
* are guaranteed to remain unchanged only until the next call
* to a non-constant member function of the SBuf object. Such a
* 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
+ * if necessary so that the returned pointer can be written to without
+ * unwanted side-effects.
+ * The returned pointer must not be stored, and will
+ * be invalidated by the first call to a non-const method call
* on the SBuf.
* This call guarantees to never return NULL
- * This call always forces a cow()
* \throw SBufTooBigException if the user tries to allocate too big a SBuf
*/
char *rawSpace(size_type minSize = npos);
* 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
+ * of memory will remain valid will remain valid only if the
+ * caller keeps holding a valid reference to the SBuf object and
+ * does not write or append to it
*/
const char* c_str();
- /** Returns the number of bytes stored in SBuf.
- */
+ /// Returns the number of bytes stored in SBuf.
_SQUID_INLINE_ size_type length() const;
/** Get the length of the SBuf, as a signed integer
/** 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).
+ * least minSpace bytes of unused backing store
+ * following the currently used portion
* \throw SBufTooBigException if the user tries to allocate too big a SBuf
*/
void reserveSpace(size_type minSpace);
/** Request to resize the SBuf's store
*
* After this method is called, the SBuf is guaranteed to have at least
- * minCapcity bytes of total space, including the currently-used portion
+ * minCapacity bytes of total space, including the currently-used portion
* \throw SBufTooBigException if the user tries to allocate too big a SBuf
*/
void reserveCapacity(size_type minCapacity);
/** slicing method
*
- * Removes SBuf prefix and suffix, leaving a sequence of <i>n</i>
- * bytes starting from position <i>pos</i> first byte is at pos 0.
+ * Removes SBuf prefix and suffix, leaving a sequence of 'n'
+ * bytes starting from position 'pos', first byte is at pos 0.
+ * It is an in-place-modifying version of substr.
* \param pos start sub-stringing from this byte. If it is
- * greater than the SBuf length, the SBuf is emptied and
- * an empty SBuf is returned
+ * npos or it is greater than the SBuf length, the SBuf is cleared and
+ * an empty SBuf is returned. If it is <0, it is ignored
* \param n maximum number of bytes of the resulting SBuf.
* SBuf::npos means "to end of SBuf".
- * if 0 returns an empty SBuf.
+ * if it is 0, the SBuf is cleared and an empty SBuf is returned.
+ * if it is < 0, it is ignored (same as supplying npos)
+ * if it overflows the end of the SBuf, it is capped to the end of SBuf
+ * \see substr, trim
*/
SBuf& chop(size_type pos, size_type n = npos);
/** 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
+ * Return a fresh a fresh copy of a portion the current SBuf, which is
+ * left untouched. The same parameter convetions apply as for chop.
+ * \see trim, chop
*/
SBuf substr(size_type pos, size_type n = npos) const;
* 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 npos or greater than length() npos is always returned
* if startPos is < 0, it is ignored
*/
size_type find(char c, size_type startPos = 0) const;
* 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 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
*/
* 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
+ * if npos or greater than length(), the whole SBuf is considered
+ * if < 0, npos is always returned
*/
size_type rfind(char c, size_type endPos = npos) const;
* 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
+ * if npos or greater than length(), the whole SBuf is considered
+ * if < 0, npos is always returned
*/
size_type rfind(const SBuf &str, size_type endPos = npos) const;
SBuf toUpper() const;
/** String export function
- * converts the SBuf to a legacy String, by copy. Transitional.
+ * converts the SBuf to a legacy String, by copy.
+ * \deprecated
*/
String toString() const;
- /// TODO: possibly implement erase() similar to std::string's erase
- /// TODO: possibly implement a replace() call
+ // 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
};
-/**
- * Prints a SBuf to the supplied stream, allowing for chaining
- */
-std::ostream& operator <<(std::ostream &os, const SBuf &S);
+/// ostream output operator
+_SQUID_INLINE_ std::ostream& operator <<(std::ostream &os, const SBuf &S);
#if _USE_INLINE_
#include "SBuf.cci"
#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)
+ : TextException(NULL, aFileName, aLineNo),
+ theThrowingBuf(throwingBuf),
+ accessedPosition(pos)
{
- _buf = throwingBuf;
- _pos = pos;
SBuf explanatoryText("OutOfBoundsException");
if (aLineNo != -1)
explanatoryText.appendf(" at line %d", aLineNo);
- if (aFileName != 0)
+ if (aFileName != NULL)
explanatoryText.appendf(" in file %s", aFileName);
explanatoryText.appendf(" while accessing position %d in a SBuf long %d",
pos, throwingBuf.length());
#include <ostream>
#endif
-/**
- * streambuf class for a SBuf-backed stream interface.
+/** streambuf class for a SBuf-backed stream interface.
*
+ * Auxiliary class to be able to leverage an ostream generating SBuf's
+ * analogous to std::ostrstream.
*/
class SBufStreamBuf : public std::streambuf
{
public:
/// initialize streambuf; use supplied SBuf as backing store
- SBufStreamBuf(SBuf aBuf) : theBuf(aBuf) {}
+ explicit SBufStreamBuf(SBuf aBuf) : theBuf(aBuf) {}
/// get a copy of the stream's contents
SBuf getBuf() {
return aChar;
}
- /* push the streambuf to the backing SBuf */
+ /// push the streambuf to the backing SBuf
virtual int sync() {
std::streamsize pending(pptr() - pbase());
return 0;
}
- /* write multiple characters to the store entry
- * - this is an optimisation method.
+ /** write multiple characters to the store entry
+ * \note this is an optimisation consistent with std::streambuf API
*/
virtual std::streamsize xsputn(const char * chars, std::streamsize number) {
if (number)
class SBufStream : public std::ostream
{
public:
- /** Create a SBufStream preinitialized with the argument's SBuf.
+ /** Create a SBufStream preinitialized with the contents of a SBuf
*
- * The supplied SBuf is not aliased: in order to retrieve the altered contents
- * they must be fetched using the buf() class method.
+ * The supplied SBuf copied: in order to retrieve the written-to contents
+ * they must be later fetched using the buf() class method.
*/
SBufStream(SBuf aBuf): std::ostream(0), theBuffer(aBuf) {
rdbuf(&theBuffer); // set the buffer to now-initialized theBuffer
SBuf s1(str);
CPPUNIT_ASSERT_EQUAL(s1,literal);
}
+
+ // TEST: go via std::string adapter.
+ {
+ std::string str(fox);
+ SBuf s1(str);
+ CPPUNIT_ASSERT_EQUAL(s1,literal);
+ }
+
+
}
void
Mem::Init();
testSBufConstructDestruct();
-// XXX: or perhapse ...
-// repeat all of the tests inside testSBufConstructDestructBeforeMemInit()
-// with additional checks on Mem usage stats after each operation ??
}
void
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
testSBuf::testSubscriptOpFail()
{
char c;
- c=literal.at(1234); //out of bounds
+ c=literal.at(literal.length()); //out of bounds by 1
//notreached
std::cout << c << std::endl;
}
{
SBuf s1(literal);
SBuf s2(fox1);
+ SBuf::size_type sz=s2.length();
char *rb=s2.rawSpace(strlen(fox2)+1);
- strcat(rb,fox2);
+ strcpy(rb,fox2);
+ s2.forceSize(sz+strlen(fox2));
CPPUNIT_ASSERT_EQUAL(s1,s2);
}
s2.clear();
s1.chop(5,0);
CPPUNIT_ASSERT_EQUAL(s1,s2);
+ const char *alphabet="abcdefghijklmnopqrstuvwxyz";
+ SBuf a(alphabet);
+ std::string s(alphabet); // TODO
+ { //regular chopping
+ SBuf b(a);
+ b.chop(3,3);
+ SBuf ref("def");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // chop at end
+ SBuf b(a);
+ b.chop(b.length()-3);
+ SBuf ref("xyz");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // chop at beginning
+ SBuf b(a);
+ b.chop(0,3);
+ SBuf ref("abc");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // chop to zero length
+ SBuf b(a);
+ b.chop(5,0);
+ SBuf ref("");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // chop beyond end (at npos)
+ SBuf b(a);
+ b.chop(SBuf::npos,4);
+ SBuf ref("");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // chop beyond end
+ SBuf b(a);
+ b.chop(b.length()+2,4);
+ SBuf ref("");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // null-chop
+ SBuf b(a);
+ b.chop(0,b.length());
+ SBuf ref(a);
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
+ { // overflow chopped area
+ SBuf b(a);
+ b.chop(b.length()-3,b.length());
+ SBuf ref("xyz");
+ CPPUNIT_ASSERT_EQUAL(ref,b);
+ }
}
void
CPPUNIT_ASSERT_EQUAL(s1,s2);
}
+// inspired to SBufFindTest; to be expanded.
+class SBufSubstrAutoTest
+{
+ SBuf fullString, sb;
+ std::string fullReference, str;
+ public:
+ void performEqualityTest()
+ {
+ SBuf ref(str);
+ CPPUNIT_ASSERT_EQUAL(ref,sb);
+ }
+ SBufSubstrAutoTest() : fullString(fox), fullReference(fox)
+ {
+ for (int offset=fullString.length()-1; offset >= 0; --offset ) {
+ for (int length=fullString.length()-1-offset; length >= 0; --length) {
+ sb=fullString.substr(offset,length);
+ str=fullReference.substr(offset,length);
+ performEqualityTest();
+ }
+ }
+ }
+};
+
void
testSBuf::testSubstr()
{
CPPUNIT_ASSERT_EQUAL(s2,s3);
s1.chop(4,5);
CPPUNIT_ASSERT_EQUAL(s1,s2);
+ SBufSubstrAutoTest sat; // work done in the constructor
}
void
CPPUNIT_TEST( testComparisons );
CPPUNIT_TEST( testConsume );
CPPUNIT_TEST( testRawContent );
- //CPPUNIT_TEST( testRawSpace );
+ CPPUNIT_TEST( testRawSpace );
CPPUNIT_TEST( testChop );
CPPUNIT_TEST( testChomp );
CPPUNIT_TEST( testSubstr );
CPPUNIT_TEST( testGrow );
CPPUNIT_TEST( testSBufStream );
CPPUNIT_TEST( testAutoFind );
-
// CPPUNIT_TEST( testDumpStats ); //fake test, to print alloc stats
-
CPPUNIT_TEST_SUITE_END();
protected:
void commonInit();