2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "base/CharacterSet.h"
11 #include "base/RefCount.h"
13 #include "OutOfBoundsException.h"
15 #include "SBufDetailedStats.h"
16 #include "SBufExceptions.h"
26 #if defined HAVE_VA_COPY
27 #define VA_COPY va_copy
28 #elif defined HAVE___VA_COPY
29 #define VA_COPY __va_copy
32 InstanceIdDefinitions(SBuf
, "SBuf");
34 SBufStats
SBuf::stats
;
35 const SBuf::size_type
SBuf::npos
;
36 const SBuf::size_type
SBuf::maxSize
;
38 SBufStats::SBufStats()
39 : alloc(0), allocCopy(0), allocFromString(0), allocFromCString(0),
40 assignFast(0), clear(0), append(0), toStream(0), setChar(0),
41 getChar(0), compareSlow(0), compareFast(0), copyOut(0),
42 rawAccess(0), nulTerminate(0), chop(0), trim(0), find(0), scanf(0),
43 caseChange(0), cowFast(0), cowSlow(0), live(0)
47 SBufStats::operator +=(const SBufStats
& ss
)
50 allocCopy
+= ss
.allocCopy
;
51 allocFromString
+= ss
.allocFromString
;
52 allocFromCString
+= ss
.allocFromCString
;
53 assignFast
+= ss
.assignFast
;
56 toStream
+= ss
.toStream
;
57 setChar
+= ss
.setChar
;
58 getChar
+= ss
.getChar
;
59 compareSlow
+= ss
.compareSlow
;
60 compareFast
+= ss
.compareFast
;
61 copyOut
+= ss
.copyOut
;
62 rawAccess
+= ss
.rawAccess
;
63 nulTerminate
+= ss
.nulTerminate
;
68 caseChange
+= ss
.caseChange
;
69 cowFast
+= ss
.cowFast
;
70 cowSlow
+= ss
.cowSlow
;
77 : store_(GetStorePrototype()), off_(0), len_(0)
79 debugs(24, 8, id
<< " created");
84 SBuf::SBuf(const SBuf
&S
)
85 : store_(S
.store_
), off_(S
.off_
), len_(S
.len_
)
87 debugs(24, 8, id
<< " created from id " << S
.id
);
93 SBuf::SBuf(const String
&S
)
94 : store_(GetStorePrototype()), off_(0), len_(0)
96 debugs(24, 8, id
<< " created from string");
97 assign(S
.rawBuf(), S
.size());
99 ++stats
.allocFromString
;
103 SBuf::SBuf(const std::string
&s
)
104 : store_(GetStorePrototype()), off_(0), len_(0)
106 debugs(24, 8, id
<< " created from std::string");
107 lowAppend(s
.data(),s
.length());
109 ++stats
.allocFromString
;
113 SBuf::SBuf(const char *S
, size_type n
)
114 : store_(GetStorePrototype()), off_(0), len_(0)
118 ++stats
.allocFromCString
;
124 debugs(24, 8, id
<< " destructed");
126 recordSBufSizeAtDestruct(len_
);
130 SBuf::GetStorePrototype()
132 static MemBlob::Pointer InitialStore
= new MemBlob(0);
137 SBuf::assign(const SBuf
&S
)
139 debugs(24, 7, "assigning " << id
<< " from " << S
.id
);
140 if (&S
== this) //assignment to self. Noop.
150 SBuf::assign(const char *S
, size_type n
)
152 debugs(24, 6, id
<< " from c-string, n=" << n
<< ")");
154 return append(S
, n
); //bounds checked in append()
158 SBuf::reserveCapacity(size_type minCapacity
)
160 Must(minCapacity
<= maxSize
);
165 SBuf::rawSpace(size_type minSpace
)
167 Must(length() <= maxSize
- minSpace
);
168 debugs(24, 7, "reserving " << minSpace
<< " for " << id
);
170 // we're not concerned about RefCounts here,
171 // the store knows the last-used portion. If
172 // it's available, we're effectively claiming ownership
173 // of it. If it's not, we need to go away (realloc)
174 if (store_
->canAppend(off_
+len_
, minSpace
)) {
175 debugs(24, 7, "not growing");
178 // TODO: we may try to memmove before realloc'ing in order to avoid
179 // one allocation operation, if we're the sole owners of a MemBlob.
180 // Maybe some heuristic on off_ and length()?
181 cow(minSpace
+length());
189 //enabling this code path, the store will be freed and reinitialized
190 store_
= GetStorePrototype(); //uncomment to actually free storage upon clear()
192 //enabling this code path, we try to release the store without deallocating it.
193 // will be lazily reallocated if needed.
194 if (store_
->LockCount() == 1)
203 SBuf::append(const SBuf
&S
)
205 return lowAppend(S
.buf(), S
.length());
209 SBuf::append(const char * S
, size_type Ssize
)
213 if (Ssize
== SBuf::npos
)
215 debugs(24, 7, "from c-string to id " << id
);
216 // coverity[access_dbuff_in_call]
217 return lowAppend(S
, Ssize
);
221 SBuf::append(const char c
)
223 return lowAppend(&c
, 1);
227 SBuf::Printf(const char *fmt
, ...)
238 SBuf::appendf(const char *fmt
, ...)
248 SBuf::vappendf(const char *fmt
, va_list vargs
)
252 //reserve twice the format-string size, it's a likely heuristic
253 size_type requiredSpaceEstimate
= strlen(fmt
)*2;
255 char *space
= rawSpace(requiredSpaceEstimate
);
259 sz
= vsnprintf(space
, spaceSize(), fmt
, ap
);
262 sz
= vsnprintf(space
, spaceSize(), fmt
, vargs
);
265 /* check for possible overflow */
266 /* snprintf on Linux returns -1 on output errors, or the size
267 * that would have been written if enough space had been available */
268 /* vsnprintf is standard in C99 */
270 if (sz
>= static_cast<int>(spaceSize())) {
271 // not enough space on the first go, we now know how much we need
272 requiredSpaceEstimate
= sz
*2; // TODO: tune heuristics
273 space
= rawSpace(requiredSpaceEstimate
);
274 sz
= vsnprintf(space
, spaceSize(), fmt
, vargs
);
275 if (sz
< 0) // output error in vsnprintf
276 throw TextException("output error in second-go vsnprintf",__FILE__
,
280 if (sz
< 0) // output error in either vsnprintf
281 throw TextException("output error in vsnprintf",__FILE__
, __LINE__
);
283 // data was appended, update internal state
286 /* C99 specifies that the final '\0' is not counted in vsnprintf's
287 * return value. Older compilers/libraries might instead count it */
288 /* check whether '\0' was appended and counted */
289 static bool snPrintfTerminatorChecked
= false;
290 static bool snPrintfTerminatorCounted
= false;
291 if (!snPrintfTerminatorChecked
) {
293 snPrintfTerminatorCounted
= snprintf(testbuf
, sizeof(testbuf
),
295 snPrintfTerminatorChecked
= true;
297 if (snPrintfTerminatorCounted
) {
309 SBuf::print(std::ostream
&os
) const
311 os
.write(buf(), length());
317 SBuf::dump(std::ostream
&os
) const
322 os
<< ", offset:" << off_
326 os
<< '\'' << std::endl
;
329 // alternate implementation, based on Raw() API.
330 os
<< Raw("SBuf", buf(), length()) <<
332 ", offset:" << off_
<<
342 SBuf::setAt(size_type pos
, char toset
)
344 checkAccessBounds(pos
);
346 store_
->mem
[off_
+pos
] = toset
;
351 memcasecmp(const char *b1
, const char *b2
, SBuf::size_type len
)
355 rv
= tolower(*b1
)-tolower(*b2
);
366 SBuf::compare(const SBuf
&S
, const SBufCaseSensitive isCaseSensitive
, const size_type n
) const
369 return substr(0,n
).compare(S
.substr(0,n
),isCaseSensitive
);
371 const size_type byteCompareLen
= min(S
.length(), length());
374 if (isCaseSensitive
== caseSensitive
) {
375 rv
= memcmp(buf(), S
.buf(), byteCompareLen
);
377 rv
= memcasecmp(buf(), S
.buf(), byteCompareLen
);
381 if (length() == S
.length())
383 if (length() > S
.length())
389 SBuf::compare(const char *s
, const SBufCaseSensitive isCaseSensitive
, const size_type n
) const
391 // 0-length comparison is always true regardless of buffer states
397 // N-length compare MUST provide a non-NULL C-string pointer
400 // when this is a 0-length string, no need for any complexity.
406 // brute-force scan in order to avoid ever needing strlen() on a c-string.
408 const char *left
= buf();
409 const char *right
= s
;
411 // what area to scan.
412 // n may be npos, but we treat that as a huge positive value
413 size_type byteCount
= min(length(), n
);
415 // loop until we find a difference, a '\0', or reach the end of area to scan
416 if (isCaseSensitive
== caseSensitive
) {
417 while ((rv
= *left
- *right
++) == 0) {
418 if (*left
++ == '\0' || --byteCount
== 0)
422 while ((rv
= tolower(*left
) - tolower(*right
++)) == 0) {
423 if (*left
++ == '\0' || --byteCount
== 0)
428 // If we stopped scanning because we reached the end
429 // of buf() before we reached the end of s,
430 // pretend we have a 0-terminator there to compare.
431 // NP: the loop already incremented "right" ready for this comparison
432 if (!byteCount
&& length() < n
)
433 return '\0' - *right
;
435 // If we found a difference within the scan area,
436 // or we found a '\0',
437 // or all n characters were identical (and none was \0).
442 SBuf::startsWith(const SBuf
&S
, const SBufCaseSensitive isCaseSensitive
) const
444 debugs(24, 8, id
<< " startsWith " << S
.id
<< ", caseSensitive: " <<
446 if (length() < S
.length()) {
447 debugs(24, 8, "no, too short");
451 return (compare(S
, isCaseSensitive
, S
.length()) == 0);
455 SBuf::operator ==(const SBuf
& S
) const
457 debugs(24, 8, id
<< " == " << S
.id
);
458 if (length() != S
.length()) {
459 debugs(24, 8, "no, different lengths");
461 return false; //shortcut: must be equal length
463 if (store_
== S
.store_
&& off_
== S
.off_
) {
464 debugs(24, 8, "yes, same length and backing store");
466 return true; //shortcut: same store, offset and length
469 const bool rv
= (0 == memcmp(buf(), S
.buf(), length()));
470 debugs(24, 8, "returning " << rv
);
475 SBuf::operator !=(const SBuf
& S
) const
477 return !(*this == S
);
481 SBuf::consume(size_type n
)
486 n
= min(n
, length());
487 debugs(24, 8, "consume " << n
);
488 SBuf
rv(substr(0, n
));
494 SBufStats
& SBuf::GetStats()
500 SBuf::copy(char *dest
, size_type n
) const
502 size_type toexport
= min(n
,length());
503 memcpy(dest
, buf(), toexport
);
509 SBuf::rawContent() const
516 SBuf::forceSize(size_type newSize
)
518 Must(store_
->LockCount() == 1);
519 if (newSize
> min(maxSize
,store_
->capacity
-off_
))
520 throw SBufTooBigException(__FILE__
,__LINE__
);
522 store_
->size
= newSize
;
529 /* null-terminate the current buffer, by hand-appending a \0 at its tail but
530 * without increasing its length. May COW, the side-effect is to guarantee that
531 * the MemBlob's tail is availabe for us to use */
535 ++stats
.nulTerminate
;
540 SBuf::chop(size_type pos
, size_type n
)
542 if (pos
== npos
|| pos
> length() || n
== 0) {
546 if (n
== npos
|| (pos
+n
) > length())
555 SBuf::trim(const SBuf
&toRemove
, bool atBeginning
, bool atEnd
)
559 const char *p
= bufEnd()-1;
560 while (!isEmpty() && memchr(toRemove
.buf(), *p
, toRemove
.length()) != NULL
) {
561 //current end-of-buf is in the searched set
567 const char *p
= buf();
568 while (!isEmpty() && memchr(toRemove
.buf(), *p
, toRemove
.length()) != NULL
) {
580 SBuf::substr(size_type pos
, size_type n
) const
583 rv
.chop(pos
, n
); //stats handled by callee
588 SBuf::find(char c
, size_type startPos
) const
592 if (startPos
== npos
) // can't find anything if we look past end of SBuf
595 // std::string returns npos if needle is outside hay
596 if (startPos
> length())
599 const void *i
= memchr(buf()+startPos
, (int)c
, (size_type
)length()-startPos
);
604 return (static_cast<const char *>(i
)-buf());
608 SBuf::find(const SBuf
&needle
, size_type startPos
) const
610 if (startPos
== npos
) { // can't find anything if we look past end of SBuf
615 // std::string allows needle to overhang hay but not start outside
616 if (startPos
> length()) {
621 // for empty needle std::string returns startPos
622 if (needle
.length() == 0) {
627 // if needle length is 1 use the char search
628 if (needle
.length() == 1)
629 return find(needle
[0], startPos
);
633 char *begin
= buf()+startPos
;
634 char *lastPossible
= buf()+length()-needle
.length()+1;
635 char needleBegin
= needle
[0];
637 debugs(24, 7, "looking for " << needle
<< "starting at " << startPos
<<
639 while (begin
< lastPossible
) {
641 debugs(24, 8, " begin=" << (void *) begin
<<
642 ", lastPossible=" << (void*) lastPossible
);
643 tmp
= static_cast<char *>(memchr(begin
, needleBegin
, lastPossible
-begin
));
645 debugs(24, 8 , "First byte not found");
648 // lastPossible guarrantees no out-of-bounds with memcmp()
649 if (0 == memcmp(needle
.buf(), tmp
, needle
.length())) {
650 debugs(24, 8, "Found at " << (tmp
-buf()));
655 debugs(24, 8, "not found");
660 SBuf::rfind(const SBuf
&needle
, SBuf::size_type endPos
) const
662 // when the needle is 1 char, use the 1-char rfind()
663 if (needle
.length() == 1)
664 return rfind(needle
[0], endPos
);
668 // needle is bigger than haystack, impossible find
669 if (length() < needle
.length())
672 // if startPos is npos, std::string scans from the end of hay
673 if (endPos
== npos
|| endPos
> length()-needle
.length())
674 endPos
= length()-needle
.length();
676 // an empty needle found at the end of the haystack
677 if (needle
.length() == 0)
680 char *bufBegin
= buf();
681 char *cur
= bufBegin
+endPos
;
682 const char needleBegin
= needle
[0];
683 while (cur
>= bufBegin
) {
684 if (*cur
== needleBegin
) {
685 if (0 == memcmp(needle
.buf(), cur
, needle
.length())) {
696 SBuf::rfind(char c
, SBuf::size_type endPos
) const
700 // shortcut: haystack is empty, can't find anything by definition
704 // on npos input std::string compares last octet of hay
705 if (endPos
== npos
|| endPos
>= length()) {
708 // NP: off-by-one weirdness:
709 // endPos is an offset ... 0-based
710 // length() is a count ... 1-based
711 // memrhr() requires a 1-based count of space to scan.
718 const void *i
= memrchr(buf(), (int)c
, (size_type
)endPos
);
723 return (static_cast<const char *>(i
)-buf());
727 SBuf::findFirstOf(const CharacterSet
&set
, size_type startPos
) const
731 if (startPos
== npos
)
734 if (startPos
>= length())
737 debugs(24, 7, "first of characterset " << set
.name
<< " in id " << id
);
738 char *cur
= buf()+startPos
;
739 const char *end
= bufEnd();
745 debugs(24, 7, "not found");
750 SBuf::findFirstNotOf(const CharacterSet
&set
, size_type startPos
) const
754 if (startPos
== npos
)
757 if (startPos
>= length())
760 debugs(24, 7, "first not of characterset " << set
.name
<< " in id " << id
);
761 char *cur
= buf()+startPos
;
762 const char *end
= bufEnd();
768 debugs(24, 7, "not found");
773 * TODO: borrow a sscanf implementation from Linux or similar?
774 * we'd really need a vsnscanf(3)... ? As an alternative, a
775 * light-regexp-like domain-specific syntax might be an idea.
778 SBuf::scanf(const char *format
, ...)
783 va_start(arg
, format
);
784 rv
= vsscanf(c_str(), format
, arg
);
790 SBufStats::dump(std::ostream
& os
) const
792 MemBlobStats ststats
= MemBlob::GetStats();
794 "SBuf stats:\nnumber of allocations: " << alloc
<<
795 "\ncopy-allocations: " << allocCopy
<<
796 "\ncopy-allocations from SquidString: " << allocFromString
<<
797 "\ncopy-allocations from C String: " << allocFromCString
<<
798 "\nlive references: " << live
<<
799 "\nno-copy assignments: " << assignFast
<<
800 "\nclearing operations: " << clear
<<
801 "\nappend operations: " << append
<<
802 "\ndump-to-ostream: " << toStream
<<
803 "\nset-char: " << setChar
<<
804 "\nget-char: " << getChar
<<
805 "\ncomparisons with data-scan: " << compareSlow
<<
806 "\ncomparisons not requiring data-scan: " << compareFast
<<
807 "\ncopy-out ops: " << copyOut
<<
808 "\nraw access to memory: " << rawAccess
<<
809 "\nNULL terminate C string: " << nulTerminate
<<
810 "\nchop operations: " << chop
<<
811 "\ntrim operations: " << trim
<<
812 "\nfind: " << find
<<
813 "\nscanf: " << scanf
<<
814 "\ncase-change ops: " << caseChange
<<
815 "\nCOW not actually requiring a copy: " << cowFast
<<
816 "\nCOW: " << cowSlow
<<
817 "\naverage store share factor: " <<
818 (ststats
.live
!= 0 ? static_cast<float>(live
)/ststats
.live
: 0) <<
826 debugs(24, 8, "\"" << *this << "\"");
827 for (size_type j
= 0; j
< length(); ++j
) {
828 const int c
= (*this)[j
];
830 setAt(j
, tolower(c
));
832 debugs(24, 8, "result: \"" << *this << "\"");
839 debugs(24, 8, "\"" << *this << "\"");
840 for (size_type j
= 0; j
< length(); ++j
) {
841 const int c
= (*this)[j
];
843 setAt(j
, toupper(c
));
845 debugs(24, 8, "result: \"" << *this << "\"");
850 * checks whether the requested 'pos' is within the bounds of the SBuf
851 * \throw OutOfBoundsException if access is out of bounds
854 SBuf::checkAccessBounds(size_type pos
) const
857 throw OutOfBoundsException(*this, pos
, __FILE__
, __LINE__
);
861 SBuf::toString() const
864 rv
.limitInit(buf(), length());
869 /** re-allocate the backing store of the SBuf.
871 * If there are contents in the SBuf, they will be copied over.
872 * NO verifications are made on the size parameters, it's up to the caller to
873 * make sure that the new size is big enough to hold the copied contents.
874 * The re-allocated storage MAY be bigger than the requested size due to size-chunking
875 * algorithms in MemBlock, it is guarranteed NOT to be smaller.
878 SBuf::reAlloc(size_type newsize
)
880 debugs(24, 8, "new size: " << newsize
);
881 if (newsize
> maxSize
)
882 throw SBufTooBigException(__FILE__
, __LINE__
);
883 MemBlob::Pointer newbuf
= new MemBlob(newsize
);
885 newbuf
->append(buf(), length());
889 debugs(24, 7, "new store capacity: " << store_
->capacity
);
893 SBuf::lowAppend(const char * memArea
, size_type areaSize
)
895 rawSpace(areaSize
); //called method also checks n <= maxSize()
896 store_
->append(memArea
, areaSize
);
903 * copy-on-write: make sure that we are the only holder of the backing store.
904 * If not, reallocate. If a new size is specified, and it is greater than the
905 * current length, the backing store will be extended as needed
908 SBuf::cow(SBuf::size_type newsize
)
910 debugs(24, 8, "new size:" << newsize
);
911 if (newsize
== npos
|| newsize
< length())
914 if (store_
->LockCount() == 1 && newsize
== length()) {
915 debugs(24, 8, "no cow needed");