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