]> git.ipfire.org Git - thirdparty/squid.git/blame - src/SBuf.cc
Silence SBufDetailedStats stubs
[thirdparty/squid.git] / src / SBuf.cc
CommitLineData
412da427 1/*
412da427
FC
2 * SQUID Web Proxy Cache http://www.squid-cache.org/
3 * ----------------------------------------------------------
4 *
5 * Squid is the result of efforts by numerous individuals from
6 * the Internet community; see the CONTRIBUTORS file for full
7 * details. Many organizations have provided support for Squid's
8 * development; see the SPONSORS file for full details. Squid is
9 * Copyrighted (C) 2001 by the Regents of the University of
10 * California; see the COPYRIGHT file for full details. Squid
11 * incorporates software developed and/or copyrighted by other
12 * sources; see the CREDITS file for full details.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
27 */
28
29#include "squid.h"
30#include "base/RefCount.h"
31#include "Debug.h"
32#include "OutOfBoundsException.h"
33#include "SBuf.h"
ff1eb053 34#include "SBufDetailedStats.h"
412da427
FC
35#include "SBufExceptions.h"
36#include "util.h"
37
38#if HAVE_STRING_H
39#include <string.h>
40#endif
41
42#if HAVE_SSTREAM
43#include <sstream>
44#endif
45
46#if HAVE_IOSTREAM
47#include <iostream>
48#endif
49
50#ifdef VA_COPY
51#undef VA_COPY
52#endif
53#if defined HAVE_VA_COPY
54#define VA_COPY va_copy
55#elif defined HAVE___VA_COPY
56#define VA_COPY __va_copy
57#endif
58
59InstanceIdDefinitions(SBuf, "SBuf");
60
61SBufStats SBuf::stats;
7cbd3256
FC
62const SBuf::size_type SBuf::npos;
63const SBuf::size_type SBuf::maxSize;
412da427
FC
64
65SBufStats::SBufStats()
66 : alloc(0), allocCopy(0), allocFromString(0), allocFromCString(0),
67 assignFast(0), clear(0), append(0), toStream(0), setChar(0),
68 getChar(0), compareSlow(0), compareFast(0), copyOut(0),
69 rawAccess(0), chop(0), trim(0), find(0), scanf(0),
70 caseChange(0), cowFast(0), cowSlow(0), live(0)
71{}
72
73SBufStats&
74SBufStats::operator +=(const SBufStats& ss)
75{
76 alloc += ss.alloc;
77 allocCopy += ss.allocCopy;
78 allocFromString += ss.allocFromString;
79 allocFromCString += ss.allocFromCString;
80 assignFast += ss.assignFast;
81 clear += ss.clear;
82 append += ss.append;
83 toStream += ss.toStream;
84 setChar += ss.setChar;
85 getChar += ss.getChar;
86 compareSlow += ss.compareSlow;
87 compareFast += ss.compareFast;
88 copyOut += ss.copyOut;
89 rawAccess += ss.rawAccess;
90 chop += ss.chop;
91 trim += ss.trim;
92 find += ss.find;
93 scanf += ss.scanf;
94 caseChange += ss.caseChange;
95 cowFast += ss.cowFast;
96 cowSlow += ss.cowSlow;
97 live += ss.live;
98
99 return *this;
100}
101
102SBuf::SBuf()
103 : store_(GetStorePrototype()), off_(0), len_(0)
104{
105 debugs(24, 8, id << " created");
106 ++stats.alloc;
107 ++stats.live;
108}
109
110SBuf::SBuf(const SBuf &S)
111 : store_(S.store_), off_(S.off_), len_(S.len_)
112{
113 debugs(24, 8, id << " created from id " << S.id);
114 ++stats.alloc;
115 ++stats.allocCopy;
116 ++stats.live;
117}
118
119SBuf::SBuf(const String &S)
120 : store_(GetStorePrototype()), off_(0), len_(0)
121{
122 debugs(24, 8, id << " created from string");
7cbd3256 123 assign(S.rawBuf(), S.size());
412da427
FC
124 ++stats.alloc;
125 ++stats.allocFromString;
126 ++stats.live;
127}
128
496a9e97 129SBuf::SBuf(const std::string &s)
62842d40 130 : store_(GetStorePrototype()), off_(0), len_(0)
496a9e97 131{
7cbd3256
FC
132 debugs(24, 8, id << " created from std::string");
133 lowAppend(s.data(),s.length());
496a9e97
FC
134 ++stats.alloc;
135 ++stats.allocFromString;
136 ++stats.live;
137}
138
7cbd3256 139SBuf::SBuf(const char *S, size_type n)
412da427
FC
140 : store_(GetStorePrototype()), off_(0), len_(0)
141{
7cbd3256 142 append(S,n);
412da427
FC
143 ++stats.alloc;
144 ++stats.allocFromCString;
145 ++stats.live;
146}
147
148SBuf::~SBuf()
149{
150 debugs(24, 8, id << " destructed");
151 --stats.live;
ff1eb053 152 recordSBufSizeAtDestruct(len_);
412da427
FC
153}
154
62842d40
FC
155MemBlob::Pointer
156SBuf::GetStorePrototype()
157{
7cbd3256 158 static MemBlob::Pointer InitialStore = new MemBlob(0);
62842d40
FC
159 return InitialStore;
160}
161
412da427
FC
162SBuf&
163SBuf::assign(const SBuf &S)
164{
165 debugs(24, 7, "assigning " << id << " from " << S.id);
166 if (&S == this) //assignment to self. Noop.
167 return *this;
168 ++stats.assignFast;
169 store_ = S.store_;
170 off_ = S.off_;
171 len_ = S.len_;
172 return *this;
173}
174
175SBuf&
7cbd3256 176SBuf::assign(const char *S, size_type n)
412da427 177{
7cbd3256 178 debugs(24, 6, id << " from c-string, n=" << n << ")");
412da427 179 clear();
7cbd3256 180 return append(S, n); //bounds checked in append()
412da427
FC
181}
182
183void
184SBuf::reserveCapacity(size_type minCapacity)
185{
e842ecce 186 Must(minCapacity <= maxSize);
577bcebc 187 cow(minCapacity);
412da427
FC
188}
189
ac7947fd
FC
190char *
191SBuf::rawSpace(size_type minSpace)
412da427 192{
a44b8b80 193 Must(length() <= maxSize - minSpace);
412da427 194 debugs(24, 7, "reserving " << minSpace << " for " << id);
ac7947fd 195 ++stats.rawAccess;
412da427
FC
196 // we're not concerned about RefCounts here,
197 // the store knows the last-used portion. If
198 // it's available, we're effectively claiming ownership
199 // of it. If it's not, we need to go away (realloc)
200 if (store_->canAppend(off_+len_, minSpace)) {
201 debugs(24, 7, "not growing");
ac7947fd 202 return bufEnd();
412da427
FC
203 }
204 // TODO: we may try to memmove before realloc'ing in order to avoid
205 // one allocation operation, if we're the sole owners of a MemBlob.
206 // Maybe some heuristic on off_ and length()?
ac7947fd
FC
207 cow(minSpace+length());
208 return bufEnd();
412da427
FC
209}
210
211void
212SBuf::clear()
213{
214#if 0
215 //enabling this code path, the store will be freed and reinitialized
216 store_ = GetStorePrototype(); //uncomment to actually free storage upon clear()
217#else
218 //enabling this code path, we try to release the store without deallocating it.
219 // will be lazily reallocated if needed.
220 if (store_->LockCount() == 1)
221 store_->clear();
222#endif
223 len_ = 0;
224 off_ = 0;
225 ++stats.clear;
226}
227
228SBuf&
229SBuf::append(const SBuf &S)
230{
7cbd3256 231 return lowAppend(S.buf(), S.length());
412da427
FC
232}
233
7cbd3256
FC
234SBuf &
235SBuf::append(const char * S, size_type Ssize)
412da427 236{
412da427
FC
237 if (S == NULL)
238 return *this;
7cbd3256
FC
239 if (Ssize == npos)
240 Ssize = strlen(S);
412da427 241 debugs(24, 7, "from c-string to id " << id);
7cbd3256 242 return lowAppend(S, Ssize);
412da427
FC
243}
244
245SBuf&
246SBuf::Printf(const char *fmt, ...)
247{
248 va_list args;
249 va_start(args, fmt);
250 clear();
251 vappendf(fmt, args);
252 va_end(args);
253 return *this;
254}
255
256SBuf&
257SBuf::appendf(const char *fmt, ...)
258{
259 va_list args;
260 va_start(args, fmt);
261 vappendf(fmt, args);
262 va_end(args);
263 return *this;
264}
265
266SBuf&
267SBuf::vappendf(const char *fmt, va_list vargs)
268{
8aa34dad
FC
269 Must(fmt != NULL);
270 int sz = 0;
271 //reserve twice the format-string size, it's a likely heuristic
272 size_type requiredSpaceEstimate = strlen(fmt)*2;
273
274 char *space = rawSpace(requiredSpaceEstimate);
412da427
FC
275#ifdef VA_COPY
276 va_list ap;
8aa34dad
FC
277 VA_COPY(ap, vargs);
278 sz = vsnprintf(space, spaceSize(), fmt, ap);
279 va_end(ap);
280#else
281 sz = vsnprintf(space, spaceSize(), fmt, vargs);
412da427 282#endif
412da427 283
8aa34dad
FC
284 /* check for possible overflow */
285 /* snprintf on Linux returns -1 on output errors, or the size
286 * that would have been written if enough space had been available */
287 /* vsnprintf is standard in C99 */
412da427 288
8aa34dad
FC
289 if (sz >= static_cast<int>(spaceSize())) {
290 // not enough space on the first go, we now know how much we need
291 requiredSpaceEstimate = sz*2; // TODO: tune heuristics
292 space = rawSpace(requiredSpaceEstimate);
293 sz = vsnprintf(space, spaceSize(), fmt, vargs);
a452bc8b
FC
294 if (sz < 0) // output error in vsnprintf
295 throw TextException("output error in second-go vsnprintf",__FILE__,
551f2d57 296 __LINE__);
412da427
FC
297 }
298
8aa34dad
FC
299 if (sz < 0) // output error in either vsnprintf
300 throw TextException("output error in vsnprintf",__FILE__, __LINE__);
301
302 // data was appended, update internal state
412da427 303 len_ += sz;
8aa34dad 304
8aa34dad
FC
305 /* C99 specifies that the final '\0' is not counted in vsnprintf's
306 * return value. Older compilers/libraries might instead count it */
307 /* check whether '\0' was appended and counted */
a452bc8b
FC
308 static bool snPrintfTerminatorChecked = false;
309 static bool snPrintfTerminatorCounted = false;
a44b8b80
FC
310 if (!snPrintfTerminatorChecked) {
311 char testbuf[16];
312 snPrintfTerminatorCounted = snprintf(testbuf, sizeof(testbuf),
551f2d57 313 "%s", "1") == 2;
a452bc8b 314 snPrintfTerminatorChecked = true;
a44b8b80
FC
315 }
316 if (snPrintfTerminatorCounted) {
412da427
FC
317 --sz;
318 --len_;
319 }
320
321 store_->size += sz;
322 ++stats.append;
323
324 return *this;
325}
326
327std::ostream&
328SBuf::print(std::ostream &os) const
329{
330 os.write(buf(), length());
331 ++stats.toStream;
332 return os;
333}
334
335std::ostream&
336SBuf::dump(std::ostream &os) const
337{
338 os << id
339 << ": ";
340 store_->dump(os);
a452bc8b
FC
341 os << ", offset:" << off_
342 << ", len:" << len_
412da427
FC
343 << ") : '";
344 print(os);
7cbd3256 345 os << '\'' << std::endl;
412da427 346 return os;
a452bc8b
FC
347# if 0
348 // alternate implementation, based on Raw() API.
349 os << Raw("SBuf", buf(), length()) <<
350 ". id: " << id <<
351 ", offset:" << off_ <<
352 ", len:" << len_ <<
353 ", store: ";
354 store_->dump(os);
355 os << std::endl;
356 return os;
357#endif
412da427
FC
358}
359
360void
7cbd3256 361SBuf::setAt(size_type pos, char toset)
412da427
FC
362{
363 checkAccessBounds(pos);
364 cow();
365 store_->mem[off_+pos] = toset;
366 ++stats.setChar;
367}
368
7cbd3256
FC
369static int
370memcasecmp(const char *b1, const char *b2, SBuf::size_type len)
371{
372 int rv=0;
373 while (len > 0) {
374 rv = tolower(*b1)-tolower(*b2);
375 if (rv != 0)
376 return rv;
377 ++b1;
378 ++b2;
379 --len;
380 }
381 return rv;
382}
383
412da427
FC
384int
385SBuf::compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n) const
386{
a452bc8b 387 if (n != npos)
7cbd3256 388 return substr(0,n).compare(S.substr(0,n),isCaseSensitive);
a452bc8b 389
7cbd3256 390 size_type byteCompareLen = min(S.length(), length());
496a9e97 391 ++stats.compareSlow;
7cbd3256
FC
392 int rv = 0;
393 if (isCaseSensitive == caseSensitive) {
394 rv = memcmp(buf(), S.buf(), byteCompareLen);
395 } else {
396 rv = memcasecmp(buf(), S.buf(), byteCompareLen);
397 }
412da427
FC
398 if (rv != 0)
399 return rv;
7cbd3256 400 if (length() == S.length())
412da427 401 return 0;
7cbd3256
FC
402 if (length() > S.length())
403 return 1;
404 return -1;
412da427
FC
405}
406
407bool
408SBuf::startsWith(const SBuf &S, SBufCaseSensitive isCaseSensitive) const
409{
496a9e97 410 debugs(24, 8, id << " startsWith " << S.id << ", caseSensitive: " <<
62842d40 411 isCaseSensitive);
412da427 412 if (length() < S.length()) {
496a9e97 413 debugs(24, 8, "no, too short");
412da427
FC
414 ++stats.compareFast;
415 return false;
416 }
417 return (compare(S, isCaseSensitive, S.length()) == 0);
418}
419
420bool
421SBuf::operator ==(const SBuf & S) const
422{
423 debugs(24, 8, id << " == " << S.id);
424 if (length() != S.length()) {
425 debugs(24, 8, "no, different lengths");
426 ++stats.compareFast;
427 return false; //shortcut: must be equal length
428 }
429 if (store_ == S.store_ && off_ == S.off_) {
430 debugs(24, 8, "yes, same length and backing store");
431 ++stats.compareFast;
432 return true; //shortcut: same store, offset and length
433 }
434 ++stats.compareSlow;
7cbd3256 435 const bool rv = (0 == memcmp(buf(), S.buf(), length()));
412da427
FC
436 debugs(24, 8, "returning " << rv);
437 return rv;
438}
439
440bool
441SBuf::operator !=(const SBuf & S) const
442{
443 return !(*this == S);
444}
445
446SBuf
7cbd3256 447SBuf::consume(size_type n)
412da427 448{
412da427
FC
449 if (n == npos)
450 n = length();
451 else
452 n = min(n, length());
453 SBuf rv(substr(0, n));
454 chop(n);
455 return rv;
456}
457
458const
459SBufStats& SBuf::GetStats()
460{
461 return stats;
462}
463
464SBuf::size_type
7cbd3256 465SBuf::copy(char *dest, size_type n) const
412da427 466{
7cbd3256 467 size_type toexport = min(n,length());
412da427 468 memcpy(dest, buf(), toexport);
412da427
FC
469 ++stats.copyOut;
470 return toexport;
471}
472
473const char*
474SBuf::rawContent() const
475{
476 ++stats.rawAccess;
477 return buf();
478}
479
412da427 480void
7cbd3256 481SBuf::forceSize(size_type newSize)
412da427
FC
482{
483 Must(store_->LockCount() == 1);
7cbd3256
FC
484 if (newSize > min(maxSize,store_->capacity-off_))
485 throw SBufTooBigException(__FILE__,__LINE__);
412da427
FC
486 len_ = newSize;
487 store_->size = newSize;
488}
489
490const char*
491SBuf::c_str()
492{
493 ++stats.rawAccess;
494 /* null-terminate the current buffer, by hand-appending a \0 at its tail but
495 * without increasing its length. May COW, the side-effect is to guarantee that
496 * the MemBlob's tail is availabe for us to use */
497 *rawSpace(1) = '\0';
498 ++store_->size;
499 ++stats.setChar;
500 return buf();
501}
502
503SBuf&
7cbd3256 504SBuf::chop(size_type pos, size_type n)
412da427 505{
496a9e97 506 if (pos == npos || pos > length() || n == 0) {
412da427
FC
507 clear();
508 return *this;
509 }
510 if (n == npos || (pos+n) > length())
511 n = length()-pos;
512 ++stats.chop;
513 off_ += pos;
514 len_ = n;
515 return *this;
516}
517
518SBuf&
519SBuf::trim(const SBuf &toRemove, bool atBeginning, bool atEnd)
520{
521 ++stats.trim;
522 if (atEnd) {
523 const char *p = bufEnd()-1;
524 while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != NULL) {
525 //current end-of-buf is in the searched set
526 --len_;
527 --p;
528 }
529 }
530 if (atBeginning) {
531 const char *p = buf();
532 while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != NULL) {
533 --len_;
534 ++off_;
535 ++p;
536 }
537 }
538 if (isEmpty())
539 clear();
540 return *this;
541}
542
543SBuf
7cbd3256 544SBuf::substr(size_type pos, size_type n) const
412da427
FC
545{
546 SBuf rv(*this);
547 rv.chop(pos, n); //stats handled by callee
548 return rv;
549}
550
551SBuf::size_type
7cbd3256 552SBuf::find(char c, size_type startPos) const
412da427
FC
553{
554 ++stats.find;
555
a452bc8b 556 if (startPos == npos) // can't find anything if we look past end of SBuf
7cbd3256 557 return npos;
412da427
FC
558
559 // std::string returns npos if needle is outside hay
a452bc8b 560 if (startPos > length())
7cbd3256 561 return npos;
412da427 562
412da427
FC
563 const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos);
564
565 if (i == NULL)
7cbd3256 566 return npos;
412da427
FC
567
568 return (static_cast<const char *>(i)-buf());
569}
570
571SBuf::size_type
572SBuf::find(const SBuf &needle, size_type startPos) const
573{
a452bc8b
FC
574 if (startPos == npos) { // can't find anything if we look past end of SBuf
575 ++stats.find;
576 return npos;
577 }
578
412da427 579 // std::string allows needle to overhang hay but not start outside
a452bc8b 580 if (startPos > length()) {
7cbd3256
FC
581 ++stats.find;
582 return npos;
583 }
412da427
FC
584
585 // for empty needle std::string returns startPos
7cbd3256
FC
586 if (needle.length() == 0) {
587 ++stats.find;
412da427 588 return startPos;
7cbd3256
FC
589 }
590
7cbd3256
FC
591 // if needle length is 1 use the char search
592 if (needle.length() == 1)
593 return find(needle[0], startPos);
594
595 ++stats.find;
412da427
FC
596
597 char *begin = buf()+startPos;
598 char *lastPossible = buf()+length()-needle.length()+1;
599 char needleBegin = needle[0];
600
601 debugs(24, 7, "looking for " << needle << "starting at " << startPos <<
551f2d57 602 " in id " << id);
412da427
FC
603 while (begin < lastPossible) {
604 char *tmp;
605 debugs(24, 8, " begin=" << (void *) begin <<
606 ", lastPossible=" << (void*) lastPossible );
607 tmp = static_cast<char *>(memchr(begin, needleBegin, lastPossible-begin));
608 if (tmp == NULL) {
609 debugs(24, 8 , "First byte not found");
7cbd3256 610 return npos;
412da427
FC
611 }
612 // lastPossible guarrantees no out-of-bounds with memcmp()
613 if (0 == memcmp(needle.buf(), tmp, needle.length())) {
614 debugs(24, 8, "Found at " << (tmp-buf()));
615 return (tmp-buf());
616 }
617 begin = tmp+1;
618 }
619 debugs(24, 8, "not found");
7cbd3256 620 return npos;
412da427
FC
621}
622
623SBuf::size_type
624SBuf::rfind(const SBuf &needle, SBuf::size_type endPos) const
625{
626 // when the needle is 1 char, use the 1-char rfind()
627 if (needle.length() == 1)
628 return rfind(needle[0], endPos);
629
630 ++stats.find;
631
632 // on npos input std::string scans from the end of hay
7cbd3256 633 if (endPos == npos || endPos > length())
a452bc8b 634 endPos = length();
412da427
FC
635
636 // on empty hay std::string returns npos
637 if (length() < needle.length())
7cbd3256 638 return npos;
412da427 639
a452bc8b 640 // consistent with std::string: on empty needle return min(endpos,length())
412da427
FC
641 if (needle.length() == 0)
642 return endPos;
643
412da427
FC
644 char *bufBegin = buf();
645 char *cur = bufBegin+endPos;
646 char needleBegin = needle[0];
647 while (cur >= bufBegin) {
648 if (*cur == needleBegin) {
649 if (0 == memcmp(needle.buf(), cur, needle.length())) {
650 // found
651 return (cur-buf());
652 }
653 }
654 --cur;
655 }
656 return npos;
657}
658
659SBuf::size_type
660SBuf::rfind(char c, SBuf::size_type endPos) const
661{
662 ++stats.find;
663
a452bc8b 664 // shortcut: haystack is empty, can't find anything by definition
7cbd3256
FC
665 if (length() == 0)
666 return npos;
412da427
FC
667
668 // on npos input std::string compares last octet of hay
7cbd3256
FC
669 if (endPos == npos || endPos >= length()) {
670 endPos = length();
412da427
FC
671 } else {
672 // NP: off-by-one weirdness:
673 // endPos is an offset ... 0-based
674 // length() is a count ... 1-based
7cbd3256 675 // memrhr() requires a 1-based count of space to scan.
412da427
FC
676 ++endPos;
677 }
678
a452bc8b
FC
679 if (length() == 0)
680 return endPos;
681
412da427
FC
682 const void *i = memrchr(buf(), (int)c, (size_type)endPos);
683
684 if (i == NULL)
7cbd3256 685 return npos;
412da427
FC
686
687 return (static_cast<const char *>(i)-buf());
688}
689
690SBuf::size_type
691SBuf::find_first_of(const SBuf &set, size_type startPos) const
692{
693 // if set is 1 char big, use the char search. Stats updated there
694 if (set.length() == 1)
695 return find(set[0], startPos);
696
697 ++stats.find;
698
699 if (startPos == npos)
7cbd3256 700 return npos;
412da427 701
a452bc8b 702 if (startPos >= length())
7cbd3256 703 return npos;
412da427 704
412da427
FC
705 if (set.length() == 0)
706 return npos;
707
708 debugs(24, 7, "any of '" << set << "' " << " in id " << id);
709 char *cur = buf()+startPos, *end = bufEnd();
710 while (cur < end) {
711 if (memchr(set.buf(), *cur, set.length()))
712 return (cur-buf());
713 ++cur;
714 }
715 debugs(24, 7, "not found");
7cbd3256 716 return npos;
412da427
FC
717}
718
719/*
720 * TODO: borrow a sscanf implementation from Linux or similar?
721 * we'd really need a vsnscanf(3)... ? As an alternative, a
722 * light-regexp-like domain-specific syntax might be an idea.
723 */
724int
725SBuf::scanf(const char *format, ...)
726{
727 va_list arg;
728 int rv;
729 ++stats.scanf;
730 va_start(arg, format);
731 rv = vsscanf(c_str(), format, arg);
732 va_end(arg);
733 return rv;
734}
735
412da427
FC
736std::ostream &
737SBufStats::dump(std::ostream& os) const
738{
739 MemBlobStats ststats = MemBlob::GetStats();
740 os <<
741 "SBuf stats:\nnumber of allocations: " << alloc <<
742 "\ncopy-allocations: " << allocCopy <<
743 "\ncopy-allocations from SquidString: " << allocFromString <<
744 "\ncopy-allocations from C String: " << allocFromCString <<
745 "\nlive references: " << live <<
746 "\nno-copy assignments: " << assignFast <<
747 "\nclearing operations: " << clear <<
748 "\nappend operations: " << append <<
749 "\ndump-to-ostream: " << toStream <<
750 "\nset-char: " << setChar <<
751 "\nget-char: " << getChar <<
752 "\ncomparisons with data-scan: " << compareSlow <<
753 "\ncomparisons not requiring data-scan: " << compareFast <<
754 "\ncopy-out ops: " << copyOut <<
755 "\nraw access to memory: " << rawAccess <<
756 "\nchop operations: " << chop <<
757 "\ntrim operations: " << trim <<
758 "\nfind: " << find <<
759 "\nscanf: " << scanf <<
760 "\ncase-change ops: " << caseChange <<
761 "\nCOW not actually requiring a copy: " << cowFast <<
762 "\nCOW: " << cowSlow <<
763 "\naverage store share factor: " <<
764 (ststats.live != 0 ? static_cast<float>(live)/ststats.live : 0) <<
765 std::endl;
766 return os;
767}
768
769SBuf
770SBuf::toLower() const
771{
772 debugs(24, 8, "\"" << *this << "\"");
773 SBuf rv(*this);
774 for (size_type j = 0; j < length(); ++j) {
775 const int c = (*this)[j];
776 if (isupper(c))
777 rv.setAt(j, tolower(c)); //will cow() if needed
778 }
a452bc8b 779 debugs(24, 8, "result: \"" << rv << "\"");
412da427
FC
780 ++stats.caseChange;
781 return rv;
782}
783
784SBuf
785SBuf::toUpper() const
786{
787 debugs(24, 8, "\"" << *this << "\"");
788 SBuf rv(*this);
789 for (size_type j = 0; j < length(); ++j) {
790 const int c = (*this)[j];
791 if (islower(c))
792 rv.setAt(j, toupper(c)); //will cow() if needed
793 }
a452bc8b 794 debugs(24, 8, "result: \"" << rv << "\"");
412da427
FC
795 ++stats.caseChange;
796 return rv;
797}
798
799/**
800 * checks whether the requested 'pos' is within the bounds of the SBuf
801 * \throw OutOfBoundsException if access is out of bounds
802 */
803void
7cbd3256 804SBuf::checkAccessBounds(size_type pos) const
412da427 805{
496a9e97 806 if (pos >= length())
412da427
FC
807 throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
808}
809
810String
811SBuf::toString() const
812{
813 String rv;
814 rv.limitInit(buf(), length());
815 ++stats.copyOut;
816 return rv;
817}
818
496a9e97
FC
819/** re-allocate the backing store of the SBuf.
820 *
412da427
FC
821 * If there are contents in the SBuf, they will be copied over.
822 * NO verifications are made on the size parameters, it's up to the caller to
823 * make sure that the new size is big enough to hold the copied contents.
824 * The re-allocated storage MAY be bigger than the requested size due to size-chunking
825 * algorithms in MemBlock, it is guarranteed NOT to be smaller.
826 */
827void
7cbd3256 828SBuf::reAlloc(size_type newsize)
412da427 829{
a452bc8b 830 debugs(24, 8, "new size: " << newsize);
412da427
FC
831 if (newsize > maxSize)
832 throw SBufTooBigException(__FILE__, __LINE__);
833 MemBlob::Pointer newbuf = new MemBlob(newsize);
834 if (length() > 0)
835 newbuf->append(buf(), length());
836 store_ = newbuf;
837 off_ = 0;
838 ++stats.cowSlow;
839 debugs(24, 7, "new store capacity: " << store_->capacity);
840}
7cbd3256
FC
841
842SBuf&
843SBuf::lowAppend(const char * memArea, size_type areaSize)
844{
ac7947fd 845 rawSpace(areaSize); //called method also checks n <= maxSize()
7cbd3256
FC
846 store_->append(memArea, areaSize);
847 len_ += areaSize;
848 ++stats.append;
849 return *this;
850}
851
852/**
853 * copy-on-write: make sure that we are the only holder of the backing store.
854 * If not, reallocate. If a new size is specified, and it is greater than the
855 * current length, the backing store will be extended as needed
7cbd3256 856 */
a452bc8b 857void
7cbd3256
FC
858SBuf::cow(SBuf::size_type newsize)
859{
a452bc8b 860 debugs(24, 8, "new size:" << newsize);
7cbd3256
FC
861 if (newsize == npos || newsize < length())
862 newsize = length();
863
864 if (store_->LockCount() == 1 && newsize == length()) {
a452bc8b 865 debugs(24, 8, "no cow needed");
7cbd3256 866 ++stats.cowFast;
a452bc8b 867 return;
7cbd3256
FC
868 }
869 reAlloc(newsize);
7cbd3256 870}