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