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