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