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