SBuf.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 #include "squid.h"
10 #include "base/CharacterSet.h"
11 #include "base/Raw.h"
12 #include "base/RefCount.h"
13 #include "debug/Stream.h"
14 #include "sbuf/SBuf.h"
15 #include "util.h"
16 
17 #include <cstring>
18 #include <functional>
19 #include <iostream>
20 #include <sstream>
21 
23 
27 
28 SBuf::SBuf() : store_(GetStorePrototype())
29 {
30  debugs(24, 8, id << " created");
31  ++stats.alloc;
32  ++stats.live;
33 }
34 
35 SBuf::SBuf(const SBuf &S)
36  : store_(S.store_), off_(S.off_), len_(S.len_)
37 {
38  debugs(24, 8, id << " created from id " << S.id);
39  ++stats.alloc;
40  ++stats.allocCopy;
41  ++stats.live;
42 }
43 
44 SBuf::SBuf(const std::string &s) : store_(GetStorePrototype())
45 {
46  debugs(24, 8, id << " created from std::string");
47  lowAppend(s.data(),s.length());
48  ++stats.alloc;
49  ++stats.live;
50 }
51 
52 SBuf::SBuf(const char *S, size_type n) : store_(GetStorePrototype())
53 {
54  append(S,n);
55  ++stats.alloc;
57  ++stats.live;
58 }
59 
60 SBuf::SBuf(const char *S) : store_(GetStorePrototype())
61 {
62  append(S,npos);
63  ++stats.alloc;
65  ++stats.live;
66 }
67 
69 {
70  debugs(24, 8, id << " destructed");
71  --stats.live;
73 }
74 
77 {
78  static MemBlob::Pointer InitialStore = new MemBlob(0);
79  return InitialStore;
80 }
81 
82 SBuf&
83 SBuf::assign(const SBuf &S)
84 {
85  debugs(24, 7, "assigning " << id << " from " << S.id);
86  if (&S == this) //assignment to self. Noop.
87  return *this;
88  ++stats.assignFast;
89  store_ = S.store_;
90  off_ = S.off_;
91  len_ = S.len_;
92  return *this;
93 }
94 
95 SBuf&
96 SBuf::assign(const char *S, size_type n)
97 {
98  const Locker blobKeeper(this, S);
99  debugs(24, 6, id << " from c-string, n=" << n << ")");
100  clear();
101  return append(S, n); //bounds checked in append()
102 }
103 
104 void
106 {
107  Must(minCapacity <= maxSize);
108  cow(minCapacity);
109 }
110 
113 {
114  debugs(24, 8, id << " was: " << off_ << '+' << len_ << '+' << spaceSize() <<
115  '=' << store_->capacity);
116 
117  const bool mustRealloc = !req.allowShared && store_->LockCount() > 1;
118 
119  if (!mustRealloc && spaceSize() >= req.minSpace)
120  return spaceSize(); // the caller is content with what we have
121 
122  /* only reallocation can make the caller happy */
123 
124  if (!mustRealloc && len_ >= req.maxCapacity)
125  return spaceSize(); // but we cannot reallocate
126 
127  const size_type desiredSpace = std::max(req.minSpace, req.idealSpace);
128  const size_type newSpace = std::min(desiredSpace, maxSize - len_);
129  reserveCapacity(std::min(len_ + newSpace, req.maxCapacity));
130  debugs(24, 7, id << " now: " << off_ << '+' << len_ << '+' << spaceSize() <<
131  '=' << store_->capacity);
132  return spaceSize(); // reallocated and probably reserved enough space
133 }
134 
135 char *
137 {
138  char *space = rawSpace(anticipatedSize);
139  debugs(24, 8, id << " start appending up to " << anticipatedSize << " bytes");
140  return space;
141 }
142 
143 void
144 SBuf::rawAppendFinish(const char *start, size_type actualSize)
145 {
146  Must(bufEnd() == start);
147  Must(store_->canAppend(off_ + len_, actualSize));
148  debugs(24, 8, id << " finish appending " << actualSize << " bytes");
149 
150  size_type newSize = length() + actualSize;
151  Must3(newSize <= min(maxSize, store_->capacity-off_), "raw append fits", Here());
152  len_ = newSize;
153  store_->size = off_ + newSize;
154 }
155 
156 char *
158 {
159  Must(length() <= maxSize - minSpace);
160  debugs(24, 7, "reserving " << minSpace << " for " << id);
161  ++stats.rawAccess;
162  // we're not concerned about RefCounts here,
163  // the store knows the last-used portion. If
164  // it's available, we're effectively claiming ownership
165  // of it. If it's not, we need to go away (realloc)
166  if (store_->canAppend(off_+len_, minSpace)) {
167  debugs(24, 7, id << " not growing");
168  return bufEnd();
169  }
170  cow(minSpace+length());
171  return bufEnd();
172 }
173 
174 void
176 {
177  if (store_->LockCount() == 1)
178  store_->clear();
179  len_ = 0;
180  off_ = 0;
181  ++stats.clear;
182 }
183 
184 SBuf&
186 {
187  if (isEmpty() && store_ == GetStorePrototype())
188  return (*this = S); // optimization: avoid needless copying
189 
190  const Locker blobKeeper(this, S.buf());
191  return lowAppend(S.buf(), S.length());
192 }
193 
194 SBuf &
195 SBuf::append(const char * S, size_type Ssize)
196 {
197  const Locker blobKeeper(this, S);
198  if (S == nullptr)
199  return *this;
200  if (Ssize == SBuf::npos)
201  Ssize = strlen(S);
202  debugs(24, 7, "from c-string to id " << id);
203  // coverity[access_dbuff_in_call]
204  return lowAppend(S, Ssize);
205 }
206 
207 void
208 SBuf::push_back(const char c)
209 {
210  lowAppend(&c, 1);
211 }
212 
213 SBuf&
214 SBuf::Printf(const char *fmt, ...)
215 {
216  // with printf() the fmt or an arg might be a dangerous char*
217  // NP: can't rely on vappendf() Locker because of clear()
218  const Locker blobKeeper(this, buf());
219 
220  va_list args;
221  va_start(args, fmt);
222  clear();
223  vappendf(fmt, args);
224  va_end(args);
225  return *this;
226 }
227 
228 SBuf&
229 SBuf::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 
238 SBuf&
239 SBuf::vappendf(const char *fmt, va_list vargs)
240 {
241  // with (v)appendf() the fmt or an arg might be a dangerous char*
242  const Locker blobKeeper(this, buf());
243 
244  Must(fmt != nullptr);
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);
250  va_list ap;
251  va_copy(ap, vargs);
252  sz = vsnprintf(space, spaceSize(), fmt, ap);
253  va_end(ap);
254  Must3(sz >= 0, "vsnprintf() succeeds", Here());
255 
256  /* check for possible overflow */
257  /* snprintf on Linux returns -1 on output errors, or the size
258  * that would have been written if enough space had been available */
259  /* vsnprintf is standard in C99 */
260 
261  if (sz >= static_cast<int>(spaceSize())) {
262  // not enough space on the first go, we now know how much we need
263  requiredSpaceEstimate = sz*2; // TODO: tune heuristics
264  space = rawSpace(requiredSpaceEstimate);
265  sz = vsnprintf(space, spaceSize(), fmt, vargs);
266  Must3(sz >= 0, "vsnprintf() succeeds (with increased buffer space)", Here());
267  }
268 
269  // data was appended, update internal state
270  len_ += sz;
271 
272  /* C99 specifies that the final '\0' is not counted in vsnprintf's
273  * return value. Older compilers/libraries might instead count it */
274  /* check whether '\0' was appended and counted */
275  static bool snPrintfTerminatorChecked = false;
276  static bool snPrintfTerminatorCounted = false;
277  if (!snPrintfTerminatorChecked) {
278  char testbuf[16];
279  snPrintfTerminatorCounted = snprintf(testbuf, sizeof(testbuf),
280  "%s", "1") == 2;
281  snPrintfTerminatorChecked = true;
282  }
283  if (snPrintfTerminatorCounted) {
284  --sz;
285  --len_;
286  }
287 
288  store_->size += sz;
289  ++stats.append;
290 
291  return *this;
292 }
293 
294 std::ostream&
295 SBuf::print(std::ostream &os) const
296 {
297  os.write(buf(), length());
298  ++stats.toStream;
299  return os;
300 }
301 
302 std::ostream&
303 SBuf::dump(std::ostream &os) const
304 {
305  os << id
306  << ": ";
307  store_->dump(os);
308  os << ", offset:" << off_
309  << ", len:" << len_
310  << ") : '";
311  print(os);
312  os << '\'' << std::endl;
313  return os;
314 # if 0
315  // alternate implementation, based on Raw() API.
316  os << Raw("SBuf", buf(), length()) <<
317  ". id: " << id <<
318  ", offset:" << off_ <<
319  ", len:" << len_ <<
320  ", store: ";
321  store_->dump(os);
322  os << std::endl;
323  return os;
324 #endif
325 }
326 
327 void
328 SBuf::setAt(size_type pos, char toset)
329 {
330  checkAccessBounds(pos);
331  cow();
332  store_->mem[off_+pos] = toset;
333  ++stats.setChar;
334 }
335 
336 static int
337 memcasecmp(const char *b1, const char *b2, SBuf::size_type len)
338 {
339  int rv=0;
340  while (len > 0) {
341  rv = tolower(*b1)-tolower(*b2);
342  if (rv != 0)
343  return rv;
344  ++b1;
345  ++b2;
346  --len;
347  }
348  return rv;
349 }
350 
351 int
352 SBuf::compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const size_type n) const
353 {
354  if (n != npos) {
355  debugs(24, 8, "length specified. substr and recurse");
356  return substr(0,n).compare(S.substr(0,n),isCaseSensitive);
357  }
358 
359  const size_type byteCompareLen = min(S.length(), length());
360  ++stats.compareSlow;
361  int rv = 0;
362  debugs(24, 8, "comparing length " << byteCompareLen);
363  if (isCaseSensitive == caseSensitive) {
364  rv = memcmp(buf(), S.buf(), byteCompareLen);
365  } else {
366  rv = memcasecmp(buf(), S.buf(), byteCompareLen);
367  }
368  if (rv != 0) {
369  debugs(24, 8, "result: " << rv);
370  return rv;
371  }
372  if (n <= length() || n <= S.length()) {
373  debugs(24, 8, "same contents and bounded length. Equal");
374  return 0;
375  }
376  if (length() == S.length()) {
377  debugs(24, 8, "same contents and same length. Equal");
378  return 0;
379  }
380  if (length() > S.length()) {
381  debugs(24, 8, "lhs is longer than rhs. Result is 1");
382  return 1;
383  }
384  debugs(24, 8, "rhs is longer than lhs. Result is -1");
385  return -1;
386 }
387 
388 int
389 SBuf::compare(const char *s, const SBufCaseSensitive isCaseSensitive, const size_type n) const
390 {
391  // 0-length comparison is always true regardless of buffer states
392  if (!n) {
393  ++stats.compareFast;
394  return 0;
395  }
396 
397  // N-length compare MUST provide a non-NULL C-string pointer
398  assert(s);
399 
400  // when this is a 0-length string, no need for any complexity.
401  if (!length()) {
402  ++stats.compareFast;
403  return '\0' - *s;
404  }
405 
406  // brute-force scan in order to avoid ever needing strlen() on a c-string.
407  ++stats.compareSlow;
408  const char *left = buf();
409  const char *right = s;
410  int rv = 0;
411  // what area to scan.
412  // n may be npos, but we treat that as a huge positive value
413  size_type byteCount = min(length(), n);
414 
415  // loop until we find a difference, a '\0', or reach the end of area to scan
416  if (isCaseSensitive == caseSensitive) {
417  while ((rv = *left - *right++) == 0) {
418  if (*left++ == '\0' || --byteCount == 0)
419  break;
420  }
421  } else {
422  while ((rv = tolower(*left) - tolower(*right++)) == 0) {
423  if (*left++ == '\0' || --byteCount == 0)
424  break;
425  }
426  }
427 
428  // If we stopped scanning because we reached the end
429  // of buf() before we reached the end of s,
430  // pretend we have a 0-terminator there to compare.
431  // NP: the loop already incremented "right" ready for this comparison
432  if (!byteCount && length() < n)
433  return '\0' - *right;
434 
435  // If we found a difference within the scan area,
436  // or we found a '\0',
437  // or all n characters were identical (and none was \0).
438  return rv;
439 }
440 
441 bool
442 SBuf::startsWith(const SBuf &S, const SBufCaseSensitive isCaseSensitive) const
443 {
444  debugs(24, 8, id << " startsWith " << S.id << ", caseSensitive: " <<
445  isCaseSensitive);
446  if (length() < S.length()) {
447  debugs(24, 8, "no, too short");
448  ++stats.compareFast;
449  return false;
450  }
451  return (compare(S, isCaseSensitive, S.length()) == 0);
452 }
453 
454 bool
455 SBuf::operator ==(const SBuf & S) const
456 {
457  debugs(24, 8, id << " == " << S.id);
458  if (length() != S.length()) {
459  debugs(24, 8, "no, different lengths");
460  ++stats.compareFast;
461  return false; //shortcut: must be equal length
462  }
463  if (store_ == S.store_ && off_ == S.off_) {
464  debugs(24, 8, "yes, same length and backing store");
465  ++stats.compareFast;
466  return true; //shortcut: same store, offset and length
467  }
468  ++stats.compareSlow;
469  const bool rv = (0 == memcmp(buf(), S.buf(), length()));
470  debugs(24, 8, "returning " << rv);
471  return rv;
472 }
473 
474 bool
475 SBuf::operator !=(const SBuf & S) const
476 {
477  return !(*this == S);
478 }
479 
480 SBuf
482 {
483  if (n == npos)
484  n = length();
485  else
486  n = min(n, length());
487  debugs(24, 8, id << " consume " << n);
488  SBuf rv(substr(0, n));
489  chop(n);
490  return rv;
491 }
492 
493 const
495 {
496  return stats;
497 }
498 
500 SBuf::copy(char *dest, size_type n) const
501 {
502  size_type toexport = min(n,length());
503  memcpy(dest, buf(), toexport);
504  ++stats.copyOut;
505  return toexport;
506 }
507 
508 const char*
510 {
511  ++stats.rawAccess;
512  return buf();
513 }
514 
515 const char*
517 {
518  ++stats.rawAccess;
519  /* null-terminate the current buffer, by hand-appending a \0 at its tail but
520  * without increasing its length. May COW, the side-effect is to guarantee that
521  * the MemBlob's tail is available for us to use */
522  *rawSpace(1) = '\0';
523  ++store_->size;
524  ++stats.setChar;
526  return buf();
527 }
528 
529 SBuf&
531 {
532  if (pos == npos || pos > length())
533  pos = length();
534 
535  if (n == npos || (pos+n) > length())
536  n = length() - pos;
537 
538  // if there will be nothing left, reset the buffer while we can
539  if (pos == length() || n == 0) {
540  clear();
541  return *this;
542  }
543 
544  ++stats.chop;
545  off_ += pos;
546  len_ = n;
547  return *this;
548 }
549 
550 SBuf&
551 SBuf::trim(const SBuf &toRemove, bool atBeginning, bool atEnd)
552 {
553  ++stats.trim;
554  if (atEnd) {
555  const char *p = bufEnd()-1;
556  while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != nullptr) {
557  //current end-of-buf is in the searched set
558  --len_;
559  --p;
560  }
561  }
562  if (atBeginning) {
563  const char *p = buf();
564  while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != nullptr) {
565  --len_;
566  ++off_;
567  ++p;
568  }
569  }
570  if (isEmpty())
571  clear();
572  return *this;
573 }
574 
575 SBuf
577 {
578  SBuf rv(*this);
579  rv.chop(pos, n); //stats handled by callee
580  return rv;
581 }
582 
584 SBuf::find(char c, size_type startPos) const
585 {
586  ++stats.find;
587 
588  if (startPos == npos) // can't find anything if we look past end of SBuf
589  return npos;
590 
591  // std::string returns npos if needle is outside hay
592  if (startPos > length())
593  return npos;
594 
595  const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos);
596 
597  if (i == nullptr)
598  return npos;
599 
600  return (static_cast<const char *>(i)-buf());
601 }
602 
604 SBuf::find(const SBuf &needle, size_type startPos) const
605 {
606  if (startPos == npos) { // can't find anything if we look past end of SBuf
607  ++stats.find;
608  return npos;
609  }
610 
611  // std::string allows needle to overhang hay but not start outside
612  if (startPos > length()) {
613  ++stats.find;
614  return npos;
615  }
616 
617  // for empty needle std::string returns startPos
618  if (needle.length() == 0) {
619  ++stats.find;
620  return startPos;
621  }
622 
623  // if needle length is 1 use the char search
624  if (needle.length() == 1)
625  return find(needle[0], startPos);
626 
627  ++stats.find;
628 
629  char *start = buf()+startPos;
630  char *lastPossible = buf()+length()-needle.length()+1;
631  char needleBegin = needle[0];
632 
633  debugs(24, 7, "looking for " << needle << "starting at " << startPos <<
634  " in id " << id);
635  while (start < lastPossible) {
636  char *tmp;
637  debugs(24, 8, " begin=" << (void *) start <<
638  ", lastPossible=" << (void*) lastPossible );
639  tmp = static_cast<char *>(memchr(start, needleBegin, lastPossible-start));
640  if (tmp == nullptr) {
641  debugs(24, 8, "First byte not found");
642  return npos;
643  }
644  // lastPossible guarantees no out-of-bounds with memcmp()
645  if (0 == memcmp(needle.buf(), tmp, needle.length())) {
646  debugs(24, 8, "Found at " << (tmp-buf()));
647  return (tmp-buf());
648  }
649  start = tmp+1;
650  }
651  debugs(24, 8, "not found");
652  return npos;
653 }
654 
656 SBuf::rfind(const SBuf &needle, SBuf::size_type endPos) const
657 {
658  // when the needle is 1 char, use the 1-char rfind()
659  if (needle.length() == 1)
660  return rfind(needle[0], endPos);
661 
662  ++stats.find;
663 
664  // needle is bigger than haystack, impossible find
665  if (length() < needle.length())
666  return npos;
667 
668  // if startPos is npos, std::string scans from the end of hay
669  if (endPos == npos || endPos > length()-needle.length())
670  endPos = length()-needle.length();
671 
672  // an empty needle found at the end of the haystack
673  if (needle.length() == 0)
674  return endPos;
675 
676  char *bufBegin = buf();
677  char *cur = bufBegin+endPos;
678  const char needleBegin = needle[0];
679  while (cur >= bufBegin) {
680  if (*cur == needleBegin) {
681  if (0 == memcmp(needle.buf(), cur, needle.length())) {
682  // found
683  return (cur-buf());
684  }
685  }
686  --cur;
687  }
688  return npos;
689 }
690 
692 SBuf::rfind(char c, SBuf::size_type endPos) const
693 {
694  ++stats.find;
695 
696  // shortcut: haystack is empty, can't find anything by definition
697  if (length() == 0)
698  return npos;
699 
700  // on npos input std::string compares last octet of hay
701  if (endPos == npos || endPos >= length()) {
702  endPos = length();
703  } else {
704  // NP: off-by-one weirdness:
705  // endPos is an offset ... 0-based
706  // length() is a count ... 1-based
707  // memrhr() requires a 1-based count of space to scan.
708  ++endPos;
709  }
710 
711  if (length() == 0)
712  return endPos;
713 
714  const void *i = memrchr(buf(), (int)c, (size_type)endPos);
715 
716  if (i == nullptr)
717  return npos;
718 
719  return (static_cast<const char *>(i)-buf());
720 }
721 
723 SBuf::findFirstOf(const CharacterSet &set, size_type startPos) const
724 {
725  ++stats.find;
726 
727  if (startPos == npos)
728  return npos;
729 
730  if (startPos >= length())
731  return npos;
732 
733  debugs(24, 7, "first of characterset " << set.name << " in id " << id);
734  char *cur = buf()+startPos;
735  const char *bufend = bufEnd();
736  while (cur < bufend) {
737  if (set[*cur])
738  return cur-buf();
739  ++cur;
740  }
741  debugs(24, 7, "not found");
742  return npos;
743 }
744 
746 SBuf::findFirstNotOf(const CharacterSet &set, size_type startPos) const
747 {
748  ++stats.find;
749 
750  if (startPos == npos)
751  return npos;
752 
753  if (startPos >= length())
754  return npos;
755 
756  debugs(24, 7, "first not of characterset " << set.name << " in id " << id);
757  char *cur = buf()+startPos;
758  const char *bufend = bufEnd();
759  while (cur < bufend) {
760  if (!set[*cur])
761  return cur-buf();
762  ++cur;
763  }
764  debugs(24, 7, "not found");
765  return npos;
766 }
767 
769 SBuf::findLastOf(const CharacterSet &set, size_type endPos) const
770 {
771  ++stats.find;
772 
773  if (isEmpty())
774  return npos;
775 
776  if (endPos == npos || endPos >= length())
777  endPos = length() - 1;
778 
779  debugs(24, 7, "last of characterset " << set.name << " in id " << id);
780  const char *start = buf();
781  for (const char *cur = start + endPos; cur >= start; --cur) {
782  if (set[*cur])
783  return cur - start;
784  }
785  debugs(24, 7, "not found");
786  return npos;
787 }
788 
790 SBuf::findLastNotOf(const CharacterSet &set, size_type endPos) const
791 {
792  ++stats.find;
793 
794  if (isEmpty())
795  return npos;
796 
797  if (endPos == npos || endPos >= length())
798  endPos = length() - 1;
799 
800  debugs(24, 7, "last not of characterset " << set.name << " in id " << id);
801  const char *start = buf();
802  for (const char *cur = start + endPos; cur >= start; --cur) {
803  if (!set[*cur])
804  return cur - start;
805  }
806  debugs(24, 7, "not found");
807  return npos;
808 }
809 
810 void
812 {
813  debugs(24, 8, "\"" << *this << "\"");
814  for (size_type j = 0; j < length(); ++j) {
815  const int c = (*this)[j];
816  if (isupper(c))
817  setAt(j, tolower(c));
818  }
819  debugs(24, 8, "result: \"" << *this << "\"");
820  ++stats.caseChange;
821 }
822 
823 void
825 {
826  debugs(24, 8, "\"" << *this << "\"");
827  for (size_type j = 0; j < length(); ++j) {
828  const int c = (*this)[j];
829  if (islower(c))
830  setAt(j, toupper(c));
831  }
832  debugs(24, 8, "result: \"" << *this << "\"");
833  ++stats.caseChange;
834 }
835 
844 void
846 {
847  debugs(24, 8, id << " new size: " << newsize);
848  Must(newsize <= maxSize);
849  // TODO: Consider realloc(3)ing in some cases instead.
850  MemBlob::Pointer newbuf = new MemBlob(newsize);
851  if (length() > 0) {
852  newbuf->append(buf(), length());
854  } else {
856  }
857  store_ = newbuf;
858  off_ = 0;
859  debugs(24, 7, id << " new store capacity: " << store_->capacity);
860 }
861 
862 SBuf&
863 SBuf::lowAppend(const char * memArea, size_type areaSize)
864 {
865  rawSpace(areaSize); //called method also checks n <= maxSize()
866  store_->append(memArea, areaSize);
867  len_ += areaSize;
868  ++stats.append;
869  return *this;
870 }
871 
877 void
879 {
880  debugs(24, 8, id << " new size:" << newsize);
881  if (newsize == npos || newsize < length())
882  newsize = length();
883 
884  if (store_->LockCount() == 1) {
885  // MemBlob::size reflects past owners. Refresh to maximize spaceSize().
886  store_->syncSize(off_ + length());
887 
888  const auto availableSpace = spaceSize();
889  const auto neededSpace = newsize - length();
890  if (neededSpace <= availableSpace) {
891  debugs(24, 8, id << " no cow needed; have " << availableSpace);
892  ++stats.cowAvoided;
893  return;
894  }
895  // consume idle leading space if doing so avoids reallocation
896  // this case is typical for fill-consume-fill-consume-... I/O buffers
897  if (neededSpace <= availableSpace + off_) {
898  debugs(24, 8, id << " no cow after shifting " << off_ << " to get " << (availableSpace + off_));
899  store_->consume(off_);
900  off_ = 0;
901  ++stats.cowShift;
902  assert(neededSpace <= spaceSize());
903  return;
904  }
905  }
906  reAlloc(newsize);
907 }
908 
void syncSize(const size_type n)
Definition: MemBlob.cc:139
void * memrchr(const void *s, int c, size_t n)
Definition: memrchr.cc:36
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:584
#define Here()
source code location of the caller
Definition: Here.h:15
void push_back(char)
Append a single character. The character may be NUL (\0).
Definition: SBuf.cc:208
const char * name
optional set label for debugging (default: "anonymous")
Definition: CharacterSet.h:72
size_type idealSpace
if allocating anyway, provide this much space
Definition: SBuf.h:710
SBuf & assign(const SBuf &S)
Definition: SBuf.cc:83
uint64_t toStream
number of write operations to ostreams
Definition: Stats.h:43
@ caseSensitive
Definition: SBuf.h:37
void clear()
extends the available space to the entire allocated blob
Definition: MemBlob.h:95
uint64_t alloc
number of calls to SBuf constructors
Definition: Stats.h:36
bool isEmpty() const
Definition: SBuf.h:435
uint64_t chop
number of chop operations
Definition: Stats.h:51
size_type len_
number of our content bytes in shared store_
Definition: SBuf.h:635
void checkAccessBounds(const size_type pos) const
Definition: SBuf.h:668
static MemBlob::Pointer GetStorePrototype()
Definition: SBuf.cc:76
SBuf & lowAppend(const char *memArea, size_type areaSize)
Definition: SBuf.cc:863
uint64_t compareFast
number of comparison operations not requiring data scan
Definition: Stats.h:47
SBuf & vappendf(const char *fmt, va_list vargs)
Definition: SBuf.cc:239
size_type maxCapacity
do not allocate more than this
Definition: SBuf.h:712
size_type size
maximum allocated memory in use by callers
Definition: MemBlob.h:112
int compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const size_type n) const
Definition: SBuf.cc:352
Definition: SBuf.h:93
Named SBuf::reserve() parameters. Defaults ask for and restrict nothing.
Definition: SBuf.h:700
const A & max(A const &lhs, A const &rhs)
void rawAppendFinish(const char *start, size_type actualSize)
Definition: SBuf.cc:144
bool operator==(const SBuf &S) const
Definition: SBuf.cc:455
uint64_t live
number of currently-allocated SBuf
Definition: Stats.h:59
char * bufEnd() const
Definition: SBuf.h:656
char * rawSpace(size_type minSize)
Definition: SBuf.cc:157
size_type spaceSize() const
Definition: SBuf.h:397
bool canAppend(const size_type off, const size_type n) const
Definition: MemBlob.h:71
void toLower()
converts all characters to lower case;
Definition: SBuf.cc:811
bool startsWith(const SBuf &S, const SBufCaseSensitive isCaseSensitive=caseSensitive) const
Definition: SBuf.cc:442
SBufCaseSensitive
Definition: SBuf.h:36
bool allowShared
whether sharing our storage with others is OK
Definition: SBuf.h:713
#define Must3(condition, description, location)
Definition: TextException.h:69
static const size_type maxSize
Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
Definition: SBuf.h:103
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:576
InstanceIdDefinitions(SBuf, "SBuf")
void clear()
Definition: SBuf.cc:175
size_type reserve(const SBufReservationRequirements &requirements)
Definition: SBuf.cc:112
SBuf & chop(size_type pos, size_type n=npos)
Definition: SBuf.cc:530
char * rawAppendStart(size_type anticipatedSize)
Definition: SBuf.cc:136
Definition: Raw.h:20
size_type rfind(char c, size_type endPos=npos) const
Definition: SBuf.cc:692
uint64_t append
number of append operations
Definition: Stats.h:41
void toUpper()
converts all characters to upper case;
Definition: SBuf.cc:824
static int memcasecmp(const char *b1, const char *b2, SBuf::size_type len)
Definition: SBuf.cc:337
const char * rawContent() const
Definition: SBuf.cc:509
SBuf consume(size_type n=npos)
Definition: SBuf.cc:481
MemBlob::size_type size_type
Definition: SBuf.h:96
uint64_t trim
number of trim operations
Definition: Stats.h:52
std::ostream & dump(std::ostream &os) const
Definition: SBuf.cc:303
uint64_t cowShift
number of cow() calls requiring just a memmove(3) inside an old buffer
Definition: Stats.h:56
SBuf & Printf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:214
SBuf()
create an empty (zero-size) SBuf
Definition: SBuf.cc:28
uint64_t assignFast
number of no-copy assignment operations
Definition: Stats.h:39
uint64_t nulTerminate
number of c_str() terminations
Definition: Stats.h:50
#define assert(EX)
Definition: assert.h:17
std::ostream & print(std::ostream &os) const
print the SBuf contents to the supplied ostream
Definition: SBuf.cc:295
void cow(size_type minsize=npos)
Definition: SBuf.cc:878
~SBuf()
Definition: SBuf.cc:68
SBuf & trim(const SBuf &toRemove, bool atBeginning=true, bool atEnd=true)
Definition: SBuf.cc:551
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
size_type off_
our content start offset from the beginning of shared store_
Definition: SBuf.h:634
int cur
Definition: ModDevPoll.cc:68
static SBufStats stats
class-wide statistics
Definition: SBuf.h:636
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
size_type findLastOf(const CharacterSet &set, size_type endPos=npos) const
Definition: SBuf.cc:769
uint64_t rawAccess
number of accesses to raw contents
Definition: Stats.h:49
static const size_type npos
Definition: SBuf.h:100
size_type findFirstNotOf(const CharacterSet &set, size_type startPos=0) const
Definition: SBuf.cc:746
size_type minSpace
allocate [at least this much] if spaceSize() is smaller
Definition: SBuf.h:711
void setAt(size_type pos, char toset)
Definition: SBuf.cc:328
uint64_t cowJustAlloc
number of cow() calls requiring just a new empty buffer
Definition: Stats.h:57
MemBlob::Pointer store_
memory block, possibly shared with other SBufs
Definition: SBuf.h:633
size_type capacity
size of the raw allocated memory block
Definition: MemBlob.h:111
char * buf() const
Definition: SBuf.h:649
uint64_t cowAvoided
number of cow() calls requiring no expensive operations
Definition: Stats.h:55
void reserveCapacity(size_type minCapacity)
Definition: SBuf.cc:105
static const SBufStats & GetStats()
gets global statistic information
Definition: SBuf.cc:494
void reAlloc(size_type newsize)
Definition: SBuf.cc:845
uint64_t caseChange
number of toUpper and toLower operations
Definition: Stats.h:54
#define Must(condition)
Definition: TextException.h:75
uint64_t find
number of find operations
Definition: Stats.h:53
bool operator!=(const SBuf &S) const
Definition: SBuf.cc:475
std::ostream & dump(std::ostream &os) const
dump debugging information
Definition: MemBlob.cc:160
uint64_t clear
number of clear operations
Definition: Stats.h:40
uint64_t cowAllocCopy
number of cow() calls requiring copying into a new buffer
Definition: Stats.h:58
void append(const char *source, const size_type n)
Definition: MemBlob.cc:127
size_type findLastNotOf(const CharacterSet &set, size_type endPos=npos) const
Definition: SBuf.cc:790
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
void consume(const size_type n)
Definition: MemBlob.cc:148
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:229
uint64_t allocCopy
number of calls to SBuf copy-constructor
Definition: Stats.h:37
uint64_t allocFromCString
number of copy-allocations from c-strings
Definition: Stats.h:38
const InstanceId< SBuf > id
Definition: SBuf.h:608
size_type findFirstOf(const CharacterSet &set, size_type startPos=0) const
Definition: SBuf.cc:723
char * mem
raw allocated memory block
Definition: MemBlob.h:110
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
uint64_t compareSlow
number of comparison operations requiring data scan
Definition: Stats.h:46
uint64_t setChar
number of calls to setAt
Definition: Stats.h:44
size_type copy(char *dest, size_type n) const
Definition: SBuf.cc:500
static void RecordSBufSizeAtDestruct(size_t)
Record the size a SBuf had when it was destructed.
Definition: Stats.cc:20
uint64_t copyOut
number of data-copies to other forms of buffers
Definition: Stats.h:48

 

Introduction

Documentation

Support

Miscellaneous