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