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