]>
Commit | Line | Data |
---|---|---|
ad80164a | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
ad80164a | 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. | |
ad80164a | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 67 String */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
3d93a84d | 12 | #include "base/TextException.h" |
8822ebee | 13 | #include "mgr/Registration.h" |
582c2af2 | 14 | #include "profiler/Profiler.h" |
ad80164a | 15 | #include "Store.h" |
696a257c | 16 | |
074d6a40 | 17 | #include <climits> |
582c2af2 | 18 | |
26ac0430 | 19 | // low-level buffer allocation, |
e7e1bf60 | 20 | // does not free old buffer and does not adjust or look at len_ |
ad80164a | 21 | void |
a7a42b14 | 22 | String::allocBuffer(String::size_type sz) |
ad80164a | 23 | { |
24 | PROF_start(StringInitBuf); | |
cb3b44fc | 25 | assert (undefined()); |
e7e1bf60 | 26 | char *newBuffer = (char*)memAllocString(sz, &sz); |
27 | setBuffer(newBuffer, sz); | |
ad80164a | 28 | PROF_stop(StringInitBuf); |
29 | } | |
30 | ||
e7e1bf60 | 31 | // low-level buffer assignment |
32 | // does not free old buffer and does not adjust or look at len_ | |
ad80164a | 33 | void |
a7a42b14 | 34 | String::setBuffer(char *aBuf, String::size_type aSize) |
ad80164a | 35 | { |
cb3b44fc | 36 | assert(undefined()); |
70df76e3 | 37 | assert(aSize <= SizeMax_); |
e7e1bf60 | 38 | buf_ = aBuf; |
39 | size_ = aSize; | |
ad80164a | 40 | } |
41 | ||
f63c4b05 AJ |
42 | String::String() |
43 | { | |
44 | #if DEBUGSTRINGS | |
45 | StringRegistry::Instance().add(this); | |
46 | #endif | |
47 | } | |
48 | ||
49 | String::String(char const *aString) | |
ad80164a | 50 | { |
e7e1bf60 | 51 | if (aString) |
52 | allocAndFill(aString, strlen(aString)); | |
ad80164a | 53 | #if DEBUGSTRINGS |
30abd221 | 54 | StringRegistry::Instance().add(this); |
ad80164a | 55 | #endif |
56 | } | |
57 | ||
30abd221 | 58 | String & |
59 | String::operator =(char const *aString) | |
ad80164a | 60 | { |
e7e1bf60 | 61 | reset(aString); |
ad80164a | 62 | return *this; |
63 | } | |
64 | ||
30abd221 | 65 | String & |
9e9ef416 | 66 | String::operator =(String const &old) |
ad80164a | 67 | { |
e7e1bf60 | 68 | clean(); // TODO: optimize to avoid cleaning the buffer we can use |
69 | if (old.size() > 0) | |
cb3b44fc | 70 | allocAndFill(old.rawBuf(), old.size()); |
ad80164a | 71 | return *this; |
72 | } | |
73 | ||
74 | bool | |
9e9ef416 | 75 | String::operator ==(String const &that) const |
ad80164a | 76 | { |
30abd221 | 77 | if (0 == this->cmp(that)) |
78 | return true; | |
ad80164a | 79 | |
30abd221 | 80 | return false; |
ad80164a | 81 | } |
82 | ||
83 | bool | |
9e9ef416 | 84 | String::operator !=(String const &that) const |
ad80164a | 85 | { |
30abd221 | 86 | if (0 == this->cmp(that)) |
87 | return false; | |
88 | ||
89 | return true; | |
ad80164a | 90 | } |
91 | ||
e7e1bf60 | 92 | // public interface, makes sure that we clean the old buffer first |
30abd221 | 93 | void |
2fe0439c | 94 | String::assign(const char *str, int len) |
ad80164a | 95 | { |
e7e1bf60 | 96 | clean(); // TODO: optimize to avoid cleaning the buffer we can use |
97 | allocAndFill(str, len); | |
98 | } | |
4fbc2a54 | 99 | |
e7e1bf60 | 100 | // Allocates the buffer to fit the supplied string and fills it. |
101 | // Does not clean. | |
102 | void | |
103 | String::allocAndFill(const char *str, int len) | |
104 | { | |
105 | PROF_start(StringAllocAndFill); | |
328a07ab | 106 | assert(str); |
e7e1bf60 | 107 | allocBuffer(len + 1); |
30abd221 | 108 | len_ = len; |
41d00cd3 | 109 | memcpy(buf_, str, len); |
30abd221 | 110 | buf_[len] = '\0'; |
e7e1bf60 | 111 | PROF_stop(StringAllocAndFill); |
ad80164a | 112 | } |
113 | ||
9e9ef416 | 114 | String::String(String const &old) : size_(0), len_(0), buf_(NULL) |
ad80164a | 115 | { |
dcb1fef8 | 116 | if (old.size() > 0) |
cb3b44fc | 117 | allocAndFill(old.rawBuf(), old.size()); |
30abd221 | 118 | #if DEBUGSTRINGS |
119 | ||
120 | StringRegistry::Instance().add(this); | |
121 | #endif | |
ad80164a | 122 | } |
123 | ||
30abd221 | 124 | void |
125 | String::clean() | |
ad80164a | 126 | { |
30abd221 | 127 | PROF_start(StringClean); |
30abd221 | 128 | |
8536ad02 | 129 | /* TODO if mempools has already closed this will FAIL!! */ |
cb3b44fc | 130 | if (defined()) |
30abd221 | 131 | memFreeString(size_, buf_); |
132 | ||
133 | len_ = 0; | |
134 | ||
135 | size_ = 0; | |
136 | ||
137 | buf_ = NULL; | |
138 | PROF_stop(StringClean); | |
ad80164a | 139 | } |
140 | ||
30abd221 | 141 | String::~String() |
ad80164a | 142 | { |
30abd221 | 143 | clean(); |
ad80164a | 144 | #if DEBUGSTRINGS |
145 | ||
30abd221 | 146 | StringRegistry::Instance().remove(this); |
ad80164a | 147 | #endif |
148 | } | |
149 | ||
150 | void | |
9e9ef416 | 151 | String::reset(char const *str) |
30abd221 | 152 | { |
153 | PROF_start(StringReset); | |
e7e1bf60 | 154 | clean(); // TODO: optimize to avoid cleaning the buffer if we can reuse it |
155 | if (str) | |
156 | allocAndFill(str, strlen(str)); | |
30abd221 | 157 | PROF_stop(StringReset); |
158 | } | |
159 | ||
160 | void | |
9e9ef416 | 161 | String::append( char const *str, int len) |
ad80164a | 162 | { |
30abd221 | 163 | assert(str && len >= 0); |
ad80164a | 164 | |
165 | PROF_start(StringAppend); | |
5358a024 AJ |
166 | if (len_ + len + 1 /*'\0'*/ < size_) { |
167 | xstrncpy(buf_+len_, str, len+1); | |
ad80164a | 168 | len_ += len; |
169 | } else { | |
e7e1bf60 | 170 | // Create a temporary string and absorb it later. |
30abd221 | 171 | String snew; |
70df76e3 | 172 | assert(canGrowBy(len)); // otherwise snew.len_ may overflow below |
30abd221 | 173 | snew.len_ = len_ + len; |
e7e1bf60 | 174 | snew.allocBuffer(snew.len_ + 1); |
ad80164a | 175 | |
e7e1bf60 | 176 | if (len_) |
41d00cd3 | 177 | memcpy(snew.buf_, rawBuf(), len_); |
ad80164a | 178 | |
179 | if (len) | |
41d00cd3 | 180 | memcpy(snew.buf_ + len_, str, len); |
ad80164a | 181 | |
30abd221 | 182 | snew.buf_[snew.len_] = '\0'; |
ad80164a | 183 | |
30abd221 | 184 | absorb(snew); |
ad80164a | 185 | } |
186 | PROF_stop(StringAppend); | |
187 | } | |
188 | ||
189 | void | |
30abd221 | 190 | String::append(char const *str) |
ad80164a | 191 | { |
9e9ef416 RP |
192 | assert(str); |
193 | append(str, strlen(str)); | |
ad80164a | 194 | } |
195 | ||
196 | void | |
9e9ef416 | 197 | String::append(char const chr) |
ad80164a | 198 | { |
199 | char myString[2]; | |
200 | myString[0]=chr; | |
201 | myString[1]='\0'; | |
9e9ef416 | 202 | append(myString, 1); |
ad80164a | 203 | } |
204 | ||
205 | void | |
30abd221 | 206 | String::append(String const &old) |
ad80164a | 207 | { |
9e9ef416 | 208 | append(old.rawBuf(), old.len_); |
ad80164a | 209 | } |
210 | ||
30abd221 | 211 | void |
212 | String::absorb(String &old) | |
73d41272 | 213 | { |
30abd221 | 214 | clean(); |
e7e1bf60 | 215 | setBuffer(old.buf_, old.size_); |
30abd221 | 216 | len_ = old.len_; |
217 | old.size_ = 0; | |
218 | old.buf_ = NULL; | |
219 | old.len_ = 0; | |
73d41272 | 220 | } |
221 | ||
9b558d8a | 222 | String |
a7a42b14 | 223 | String::substr(String::size_type from, String::size_type to) const |
9b558d8a | 224 | { |
1545d2d1 AJ |
225 | // Must(from >= 0 && from < size()); |
226 | Must(from < size()); | |
9b558d8a FC |
227 | Must(to > 0 && to <= size()); |
228 | Must(to > from); | |
229 | ||
230 | String rv; | |
2fe0439c | 231 | rv.assign(rawBuf()+from, to-from); |
9b558d8a FC |
232 | return rv; |
233 | } | |
234 | ||
f63c4b05 AJ |
235 | void |
236 | String::cut(String::size_type newLength) | |
237 | { | |
238 | // size_type is size_t, unsigned. No need to check for newLength <0 | |
239 | if (newLength > len_) return; | |
240 | ||
241 | len_ = newLength; | |
242 | ||
243 | // buf_ may be nullptr on zero-length strings. | |
244 | if (len_ == 0 && !buf_) | |
245 | return; | |
246 | ||
247 | buf_[newLength] = '\0'; | |
248 | } | |
249 | ||
250 | /// compare NULL and empty strings because str*cmp() may fail on NULL strings | |
251 | /// and because we need to return consistent results for strncmp(count == 0). | |
252 | static bool | |
253 | nilCmp(const bool thisIsNilOrEmpty, const bool otherIsNilOrEmpty, int &result) | |
254 | { | |
255 | if (!thisIsNilOrEmpty && !otherIsNilOrEmpty) | |
256 | return false; // result does not matter | |
257 | ||
258 | if (thisIsNilOrEmpty && otherIsNilOrEmpty) | |
259 | result = 0; | |
260 | else if (thisIsNilOrEmpty) | |
261 | result = -1; | |
262 | else // otherIsNilOrEmpty | |
263 | result = +1; | |
264 | ||
265 | return true; | |
266 | } | |
267 | ||
268 | int | |
269 | String::cmp(char const *aString) const | |
270 | { | |
271 | int result = 0; | |
272 | if (nilCmp(!size(), (!aString || !*aString), result)) | |
273 | return result; | |
274 | ||
275 | return strcmp(termedBuf(), aString); | |
276 | } | |
277 | ||
278 | int | |
279 | String::cmp(char const *aString, String::size_type count) const | |
280 | { | |
281 | int result = 0; | |
282 | if (nilCmp((!size() || !count), (!aString || !*aString || !count), result)) | |
283 | return result; | |
284 | ||
285 | return strncmp(termedBuf(), aString, count); | |
286 | } | |
287 | ||
288 | int | |
289 | String::cmp(String const &aString) const | |
290 | { | |
291 | int result = 0; | |
292 | if (nilCmp(!size(), !aString.size(), result)) | |
293 | return result; | |
294 | ||
295 | return strcmp(termedBuf(), aString.termedBuf()); | |
296 | } | |
297 | ||
298 | int | |
299 | String::caseCmp(char const *aString) const | |
300 | { | |
301 | int result = 0; | |
302 | if (nilCmp(!size(), (!aString || !*aString), result)) | |
303 | return result; | |
304 | ||
305 | return strcasecmp(termedBuf(), aString); | |
306 | } | |
307 | ||
308 | int | |
309 | String::caseCmp(char const *aString, String::size_type count) const | |
310 | { | |
311 | int result = 0; | |
312 | if (nilCmp((!size() || !count), (!aString || !*aString || !count), result)) | |
313 | return result; | |
314 | ||
315 | return strncasecmp(termedBuf(), aString, count); | |
316 | } | |
317 | ||
ad80164a | 318 | #if DEBUGSTRINGS |
319 | void | |
30abd221 | 320 | String::stat(StoreEntry *entry) const |
ad80164a | 321 | { |
cb3b44fc | 322 | storeAppendPrintf(entry, "%p : %d/%d \"%.*s\"\n",this,len_, size_, size(), rawBuf()); |
ad80164a | 323 | } |
324 | ||
30abd221 | 325 | StringRegistry & |
326 | StringRegistry::Instance() | |
ad80164a | 327 | { |
328 | return Instance_; | |
329 | } | |
330 | ||
331 | template <class C> | |
332 | int | |
333 | ptrcmp(C const &lhs, C const &rhs) | |
334 | { | |
335 | return lhs - rhs; | |
336 | } | |
337 | ||
6852be71 | 338 | StringRegistry::StringRegistry() |
ad80164a | 339 | { |
6852be71 | 340 | #if DEBUGSTRINGS |
8822ebee | 341 | Mgr::RegisterAction("strings", |
d9fc6862 | 342 | "Strings in use in squid", Stat, 0, 1); |
6852be71 | 343 | #endif |
ad80164a | 344 | } |
345 | ||
346 | void | |
6ca34f6f | 347 | StringRegistry::add(String const *entry) |
ad80164a | 348 | { |
349 | entries.insert(entry, ptrcmp); | |
350 | } | |
351 | ||
352 | void | |
6ca34f6f | 353 | StringRegistry::remove(String const *entry) |
ad80164a | 354 | { |
355 | entries.remove(entry, ptrcmp); | |
356 | } | |
357 | ||
30abd221 | 358 | StringRegistry StringRegistry::Instance_; |
ad80164a | 359 | |
82afb125 | 360 | String::size_type memStringCount(); |
ad80164a | 361 | |
362 | void | |
30abd221 | 363 | StringRegistry::Stat(StoreEntry *entry) |
ad80164a | 364 | { |
365 | storeAppendPrintf(entry, "%lu entries, %lu reported from MemPool\n", (unsigned long) Instance().entries.elements, (unsigned long) memStringCount()); | |
366 | Instance().entries.head->walk(Stater, entry); | |
367 | } | |
368 | ||
369 | void | |
30abd221 | 370 | StringRegistry::Stater(String const * const & nodedata, void *state) |
ad80164a | 371 | { |
372 | StoreEntry *entry = (StoreEntry *) state; | |
373 | nodedata->stat(entry); | |
374 | } | |
375 | ||
376 | #endif | |
377 | ||
30abd221 | 378 | /* TODO: move onto String */ |
379 | int | |
380 | stringHasWhitespace(const char *s) | |
381 | { | |
382 | return strpbrk(s, w_space) != NULL; | |
383 | } | |
384 | ||
385 | /* TODO: move onto String */ | |
386 | int | |
387 | stringHasCntl(const char *s) | |
388 | { | |
389 | unsigned char c; | |
390 | ||
391 | while ((c = (unsigned char) *s++) != '\0') { | |
392 | if (c <= 0x1f) | |
393 | return 1; | |
394 | ||
395 | if (c >= 0x7f && c <= 0x9f) | |
396 | return 1; | |
397 | } | |
398 | ||
399 | return 0; | |
400 | } | |
401 | ||
ad80164a | 402 | /* |
403 | * Similar to strtok, but has some rudimentary knowledge | |
404 | * of quoting | |
405 | */ | |
406 | char * | |
407 | strwordtok(char *buf, char **t) | |
408 | { | |
409 | unsigned char *word = NULL; | |
410 | unsigned char *p = (unsigned char *) buf; | |
411 | unsigned char *d; | |
412 | unsigned char ch; | |
413 | int quoted = 0; | |
414 | ||
415 | if (!p) | |
416 | p = (unsigned char *) *t; | |
417 | ||
418 | if (!p) | |
419 | goto error; | |
420 | ||
421 | while (*p && xisspace(*p)) | |
5db6bf73 | 422 | ++p; |
ad80164a | 423 | |
424 | if (!*p) | |
425 | goto error; | |
426 | ||
427 | word = d = p; | |
428 | ||
429 | while ((ch = *p)) { | |
430 | switch (ch) { | |
431 | ||
432 | case '\\': | |
b48a944e AJ |
433 | if (quoted) |
434 | ++p; | |
ad80164a | 435 | |
436 | switch (*p) { | |
437 | ||
438 | case 'n': | |
439 | ch = '\n'; | |
440 | ||
441 | break; | |
442 | ||
443 | case 'r': | |
444 | ch = '\r'; | |
445 | ||
446 | break; | |
447 | ||
448 | default: | |
449 | ch = *p; | |
450 | ||
451 | break; | |
452 | ||
453 | } | |
454 | ||
5db6bf73 FC |
455 | *d = ch; |
456 | ++d; | |
ad80164a | 457 | |
458 | if (ch) | |
5db6bf73 | 459 | ++p; |
ad80164a | 460 | |
461 | break; | |
462 | ||
463 | case '"': | |
464 | quoted = !quoted; | |
465 | ||
5db6bf73 | 466 | ++p; |
ad80164a | 467 | |
468 | break; | |
469 | ||
470 | default: | |
471 | if (!quoted && xisspace(*p)) { | |
5db6bf73 | 472 | ++p; |
ad80164a | 473 | goto done; |
474 | } | |
475 | ||
aec55359 FC |
476 | *d = *p; |
477 | ++d; | |
478 | ++p; | |
ad80164a | 479 | break; |
480 | } | |
481 | } | |
482 | ||
483 | done: | |
5db6bf73 | 484 | *d = '\0'; |
ad80164a | 485 | |
486 | error: | |
487 | *t = (char *) p; | |
488 | return (char *) word; | |
489 | } | |
490 | ||
30abd221 | 491 | const char * |
492 | checkNullString(const char *p) | |
493 | { | |
494 | return p ? p : "(NULL)"; | |
495 | } | |
496 | ||
826a1fed FC |
497 | const char * |
498 | String::pos(char const *aString) const | |
499 | { | |
4f4611bf FC |
500 | if (undefined()) |
501 | return NULL; | |
826a1fed FC |
502 | return strstr(termedBuf(), aString); |
503 | } | |
504 | ||
505 | const char * | |
506 | String::pos(char const ch) const | |
507 | { | |
4f4611bf FC |
508 | if (undefined()) |
509 | return NULL; | |
826a1fed FC |
510 | return strchr(termedBuf(), ch); |
511 | } | |
512 | ||
513 | const char * | |
514 | String::rpos(char const ch) const | |
515 | { | |
4f4611bf FC |
516 | if (undefined()) |
517 | return NULL; | |
826a1fed FC |
518 | return strrchr(termedBuf(), (ch)); |
519 | } | |
520 | ||
521 | String::size_type | |
522 | String::find(char const ch) const | |
523 | { | |
524 | const char *c; | |
525 | c=pos(ch); | |
526 | if (c==NULL) | |
527 | return npos; | |
528 | return c-rawBuf(); | |
529 | } | |
530 | ||
531 | String::size_type | |
532 | String::find(char const *aString) const | |
533 | { | |
534 | const char *c; | |
535 | c=pos(aString); | |
536 | if (c==NULL) | |
537 | return npos; | |
538 | return c-rawBuf(); | |
539 | } | |
540 | ||
541 | String::size_type | |
542 | String::rfind(char const ch) const | |
543 | { | |
544 | const char *c; | |
545 | c=rpos(ch); | |
546 | if (c==NULL) | |
547 | return npos; | |
548 | return c-rawBuf(); | |
549 | } | |
550 |