]> git.ipfire.org Git - thirdparty/squid.git/blame - src/SBuf.cc
Decoupled reserveCapacity from reserveSpace.
[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
190void
191SBuf::reserveSpace(size_type minSpace)
192{
577bcebc 193 Must(0 <= minSpace); //upper bound checked in cow -> reAlloc
412da427
FC
194 debugs(24, 7, "reserving " << minSpace << " for " << id);
195 // we're not concerned about RefCounts here,
196 // the store knows the last-used portion. If
197 // it's available, we're effectively claiming ownership
198 // of it. If it's not, we need to go away (realloc)
199 if (store_->canAppend(off_+len_, minSpace)) {
200 debugs(24, 7, "not growing");
201 return;
202 }
203 // TODO: we may try to memmove before realloc'ing in order to avoid
204 // one allocation operation, if we're the sole owners of a MemBlob.
205 // Maybe some heuristic on off_ and length()?
206 reAlloc(estimateCapacity(minSpace+length()));
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{
7cbd3256 235 Must (Ssize == npos || Ssize >= 0);
412da427
FC
236
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{
269#ifdef VA_COPY
270 va_list ap;
271#endif
272 int sz = 0;
273
274 Must(fmt != NULL);
275
7cbd3256 276 //reserve twice the format-string size, it's a likely heuristic
412da427
FC
277 reserveSpace(strlen(fmt)*2);
278
279 while (length() <= maxSize) {
280#ifdef VA_COPY
281 /* Fix of bug 753r. The value of vargs is undefined
282 * after vsnprintf() returns. Make a copy of vargs
283 * in case we loop around and call vsnprintf() again.
284 */
285 VA_COPY(ap, vargs);
286 sz = vsnprintf(bufEnd(), store_->spaceSize(), fmt, ap);
287 va_end(ap);
288#else /* VA_COPY */
289 sz = vsnprintf(bufEnd(), store_->spaceSize(), fmt, vargs);
290#endif /* VA_COPY*/
291 /* check for possible overflow */
292 /* snprintf on Linux returns -1 on overflows */
293 /* snprintf on FreeBSD returns at least free_space on overflows */
294
496a9e97 295 if (sz >= static_cast<int>(store_->spaceSize()))
412da427 296 reserveSpace(sz*2); // TODO: tune heuristics
496a9e97
FC
297 else if (sz < 0) // output error in vsnprintf
298 throw TextException("output error in vsnprintf",__FILE__, __LINE__);
412da427
FC
299 else
300 break;
301 }
302
303 len_ += sz;
304 // TODO: this does NOT belong here, but to class-init or autoconf
305 /* on Linux and FreeBSD, '\0' is not counted in return value */
306 /* on XXX it might be counted */
307 /* check that '\0' is appended and not counted */
308
496a9e97 309 if (operator[](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
468char *
469SBuf::rawSpace(size_type minSize)
470{
471 cow(minSize+length());
472 ++stats.rawAccess;
473 return bufEnd();
474}
475
476void
7cbd3256 477SBuf::forceSize(size_type newSize)
412da427
FC
478{
479 Must(store_->LockCount() == 1);
7cbd3256
FC
480 if (newSize > min(maxSize,store_->capacity-off_))
481 throw SBufTooBigException(__FILE__,__LINE__);
412da427
FC
482 len_ = newSize;
483 store_->size = newSize;
484}
485
486const char*
487SBuf::c_str()
488{
489 ++stats.rawAccess;
490 /* null-terminate the current buffer, by hand-appending a \0 at its tail but
491 * without increasing its length. May COW, the side-effect is to guarantee that
492 * the MemBlob's tail is availabe for us to use */
493 *rawSpace(1) = '\0';
494 ++store_->size;
495 ++stats.setChar;
496 return buf();
497}
498
499SBuf&
7cbd3256 500SBuf::chop(size_type pos, size_type n)
412da427 501{
496a9e97
FC
502 if (pos != npos && pos < 0)
503 pos = 0;
504 if (n != npos && n < 0)
505 n = npos;
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
7cbd3256
FC
556 // for npos with char sd::string returns npos
557 // this differs from how std::string handles 1-length string
558 if (startPos == npos)
559 return npos;
412da427
FC
560
561 // std::string returns npos if needle is outside hay
562 if (startPos >= length())
7cbd3256 563 return npos;
412da427
FC
564
565 // ignore invalid startPos
566 if (startPos < 0)
567 startPos = 0;
568
569 const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos);
570
571 if (i == NULL)
7cbd3256 572 return npos;
412da427
FC
573
574 return (static_cast<const char *>(i)-buf());
575}
576
577SBuf::size_type
578SBuf::find(const SBuf &needle, size_type startPos) const
579{
412da427 580 // std::string allows needle to overhang hay but not start outside
7cbd3256
FC
581 if (startPos != npos && startPos > length()) {
582 ++stats.find;
583 return npos;
584 }
412da427
FC
585
586 // for empty needle std::string returns startPos
7cbd3256
FC
587 if (needle.length() == 0) {
588 ++stats.find;
412da427 589 return startPos;
7cbd3256
FC
590 }
591
592 // for npos with char* std::string scans entire hay
593 // this differs from how std::string handles single char from npos
594 if (startPos == npos)
595 return npos;
596
597 // if needle length is 1 use the char search
598 if (needle.length() == 1)
599 return find(needle[0], startPos);
600
601 ++stats.find;
412da427
FC
602
603 char *begin = buf()+startPos;
604 char *lastPossible = buf()+length()-needle.length()+1;
605 char needleBegin = needle[0];
606
607 debugs(24, 7, "looking for " << needle << "starting at " << startPos <<
7cbd3256 608 " in id " << id);
412da427
FC
609 while (begin < lastPossible) {
610 char *tmp;
611 debugs(24, 8, " begin=" << (void *) begin <<
612 ", lastPossible=" << (void*) lastPossible );
613 tmp = static_cast<char *>(memchr(begin, needleBegin, lastPossible-begin));
614 if (tmp == NULL) {
615 debugs(24, 8 , "First byte not found");
7cbd3256 616 return npos;
412da427
FC
617 }
618 // lastPossible guarrantees no out-of-bounds with memcmp()
619 if (0 == memcmp(needle.buf(), tmp, needle.length())) {
620 debugs(24, 8, "Found at " << (tmp-buf()));
621 return (tmp-buf());
622 }
623 begin = tmp+1;
624 }
625 debugs(24, 8, "not found");
7cbd3256 626 return npos;
412da427
FC
627}
628
629SBuf::size_type
630SBuf::rfind(const SBuf &needle, SBuf::size_type endPos) const
631{
632 // when the needle is 1 char, use the 1-char rfind()
633 if (needle.length() == 1)
634 return rfind(needle[0], endPos);
635
636 ++stats.find;
637
638 // on npos input std::string scans from the end of hay
7cbd3256 639 if (endPos == npos || endPos > length())
412da427
FC
640 endPos=length();
641
642 // on empty hay std::string returns npos
643 if (length() < needle.length())
7cbd3256 644 return npos;
412da427
FC
645
646 // on empty needle std::string returns the position the search starts
647 if (needle.length() == 0)
648 return endPos;
649
7cbd3256
FC
650/* std::string permits needle to overhang endPos
651 if (endPos <= needle.length())
652 return npos;
653*/
412da427
FC
654
655 char *bufBegin = buf();
656 char *cur = bufBegin+endPos;
657 char needleBegin = needle[0];
658 while (cur >= bufBegin) {
659 if (*cur == needleBegin) {
660 if (0 == memcmp(needle.buf(), cur, needle.length())) {
661 // found
662 return (cur-buf());
663 }
664 }
665 --cur;
666 }
667 return npos;
668}
669
670SBuf::size_type
671SBuf::rfind(char c, SBuf::size_type endPos) const
672{
673 ++stats.find;
674
675 // on empty hay std::string returns size of hay
7cbd3256
FC
676 if (length() == 0)
677 return npos;
412da427
FC
678
679 // on npos input std::string compares last octet of hay
7cbd3256
FC
680 if (endPos == npos || endPos >= length()) {
681 endPos = length();
682 } else if (endPos < 0) {
683 return npos;
412da427
FC
684 } else {
685 // NP: off-by-one weirdness:
686 // endPos is an offset ... 0-based
687 // length() is a count ... 1-based
7cbd3256 688 // memrhr() requires a 1-based count of space to scan.
412da427
FC
689 ++endPos;
690 }
691
692 const void *i = memrchr(buf(), (int)c, (size_type)endPos);
693
694 if (i == NULL)
7cbd3256 695 return npos;
412da427
FC
696
697 return (static_cast<const char *>(i)-buf());
698}
699
700SBuf::size_type
701SBuf::find_first_of(const SBuf &set, size_type startPos) const
702{
703 // if set is 1 char big, use the char search. Stats updated there
704 if (set.length() == 1)
705 return find(set[0], startPos);
706
707 ++stats.find;
708
709 if (startPos == npos)
7cbd3256 710 return npos;
412da427
FC
711
712 if (startPos > length())
7cbd3256 713 return npos;
412da427
FC
714
715 if (startPos < 0)
716 startPos = 0;
717
718 if (set.length() == 0)
719 return npos;
720
721 debugs(24, 7, "any of '" << set << "' " << " in id " << id);
722 char *cur = buf()+startPos, *end = bufEnd();
723 while (cur < end) {
724 if (memchr(set.buf(), *cur, set.length()))
725 return (cur-buf());
726 ++cur;
727 }
728 debugs(24, 7, "not found");
7cbd3256 729 return npos;
412da427
FC
730}
731
732/*
733 * TODO: borrow a sscanf implementation from Linux or similar?
734 * we'd really need a vsnscanf(3)... ? As an alternative, a
735 * light-regexp-like domain-specific syntax might be an idea.
736 */
737int
738SBuf::scanf(const char *format, ...)
739{
740 va_list arg;
741 int rv;
742 ++stats.scanf;
743 va_start(arg, format);
744 rv = vsscanf(c_str(), format, arg);
745 va_end(arg);
746 return rv;
747}
748
412da427
FC
749std::ostream &
750SBufStats::dump(std::ostream& os) const
751{
752 MemBlobStats ststats = MemBlob::GetStats();
753 os <<
754 "SBuf stats:\nnumber of allocations: " << alloc <<
755 "\ncopy-allocations: " << allocCopy <<
756 "\ncopy-allocations from SquidString: " << allocFromString <<
757 "\ncopy-allocations from C String: " << allocFromCString <<
758 "\nlive references: " << live <<
759 "\nno-copy assignments: " << assignFast <<
760 "\nclearing operations: " << clear <<
761 "\nappend operations: " << append <<
762 "\ndump-to-ostream: " << toStream <<
763 "\nset-char: " << setChar <<
764 "\nget-char: " << getChar <<
765 "\ncomparisons with data-scan: " << compareSlow <<
766 "\ncomparisons not requiring data-scan: " << compareFast <<
767 "\ncopy-out ops: " << copyOut <<
768 "\nraw access to memory: " << rawAccess <<
769 "\nchop operations: " << chop <<
770 "\ntrim operations: " << trim <<
771 "\nfind: " << find <<
772 "\nscanf: " << scanf <<
773 "\ncase-change ops: " << caseChange <<
774 "\nCOW not actually requiring a copy: " << cowFast <<
775 "\nCOW: " << cowSlow <<
776 "\naverage store share factor: " <<
777 (ststats.live != 0 ? static_cast<float>(live)/ststats.live : 0) <<
778 std::endl;
779 return os;
780}
781
782SBuf
783SBuf::toLower() const
784{
785 debugs(24, 8, "\"" << *this << "\"");
786 SBuf rv(*this);
787 for (size_type j = 0; j < length(); ++j) {
788 const int c = (*this)[j];
789 if (isupper(c))
790 rv.setAt(j, tolower(c)); //will cow() if needed
791 }
792 debugs(24, 8, "result: \"" << *this << "\"");
793 ++stats.caseChange;
794 return rv;
795}
796
797SBuf
798SBuf::toUpper() const
799{
800 debugs(24, 8, "\"" << *this << "\"");
801 SBuf rv(*this);
802 for (size_type j = 0; j < length(); ++j) {
803 const int c = (*this)[j];
804 if (islower(c))
805 rv.setAt(j, toupper(c)); //will cow() if needed
806 }
807 debugs(24, 8, "result: \"" << *this << "\"");
808 ++stats.caseChange;
809 return rv;
810}
811
812/**
813 * checks whether the requested 'pos' is within the bounds of the SBuf
814 * \throw OutOfBoundsException if access is out of bounds
815 */
816void
7cbd3256 817SBuf::checkAccessBounds(size_type pos) const
412da427
FC
818{
819 if (pos < 0)
820 throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
496a9e97 821 if (pos >= length())
412da427
FC
822 throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
823}
824
825String
826SBuf::toString() const
827{
828 String rv;
829 rv.limitInit(buf(), length());
830 ++stats.copyOut;
831 return rv;
832}
833
496a9e97
FC
834/** re-allocate the backing store of the SBuf.
835 *
412da427
FC
836 * If there are contents in the SBuf, they will be copied over.
837 * NO verifications are made on the size parameters, it's up to the caller to
838 * make sure that the new size is big enough to hold the copied contents.
839 * The re-allocated storage MAY be bigger than the requested size due to size-chunking
840 * algorithms in MemBlock, it is guarranteed NOT to be smaller.
841 */
842void
7cbd3256 843SBuf::reAlloc(size_type newsize)
412da427
FC
844{
845 debugs(24, DBG_DATA, "new size: " << newsize);
846 if (newsize > maxSize)
847 throw SBufTooBigException(__FILE__, __LINE__);
848 MemBlob::Pointer newbuf = new MemBlob(newsize);
849 if (length() > 0)
850 newbuf->append(buf(), length());
851 store_ = newbuf;
852 off_ = 0;
853 ++stats.cowSlow;
854 debugs(24, 7, "new store capacity: " << store_->capacity);
855}
7cbd3256
FC
856
857SBuf&
858SBuf::lowAppend(const char * memArea, size_type areaSize)
859{
860 reserveSpace(areaSize); //called method also checks n <= maxSize()
861 store_->append(memArea, areaSize);
862 len_ += areaSize;
863 ++stats.append;
864 return *this;
865}
866
867/**
868 * copy-on-write: make sure that we are the only holder of the backing store.
869 * If not, reallocate. If a new size is specified, and it is greater than the
870 * current length, the backing store will be extended as needed
871 * \retval false no grow was needed
872 * \retval true had to copy
873 */
874bool
875SBuf::cow(SBuf::size_type newsize)
876{
877 debugs(24, DBG_DATA, "new size:" << newsize);
878 if (newsize == npos || newsize < length())
879 newsize = length();
880
881 if (store_->LockCount() == 1 && newsize == length()) {
882 debugs(24, DBG_DATA, "no cow needed");
883 ++stats.cowFast;
884 return false;
885 }
886 reAlloc(newsize);
887 return true;
888}