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