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