]> git.ipfire.org Git - thirdparty/squid.git/blob - src/String.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / String.cc
1 /*
2 * Copyright (C) 1996-2020 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 /* DEBUG: section 67 String */
10
11 #include "squid.h"
12 #include "base/TextException.h"
13 #include "mgr/Registration.h"
14 #include "profiler/Profiler.h"
15 #include "Store.h"
16
17 #include <climits>
18
19 // low-level buffer allocation,
20 // does not free old buffer and does not adjust or look at len_
21 void
22 String::allocBuffer(String::size_type sz)
23 {
24 PROF_start(StringInitBuf);
25 assert (undefined());
26 char *newBuffer = (char*)memAllocString(sz, &sz);
27 setBuffer(newBuffer, sz);
28 PROF_stop(StringInitBuf);
29 }
30
31 // low-level buffer assignment
32 // does not free old buffer and does not adjust or look at len_
33 void
34 String::setBuffer(char *aBuf, String::size_type aSize)
35 {
36 assert(undefined());
37 assert(aSize <= SizeMax_);
38 buf_ = aBuf;
39 size_ = aSize;
40 }
41
42 String::String()
43 {
44 #if DEBUGSTRINGS
45 StringRegistry::Instance().add(this);
46 #endif
47 }
48
49 String::String(char const *aString)
50 {
51 if (aString)
52 allocAndFill(aString, strlen(aString));
53 #if DEBUGSTRINGS
54 StringRegistry::Instance().add(this);
55 #endif
56 }
57
58 String &
59 String::operator =(char const *aString)
60 {
61 reset(aString);
62 return *this;
63 }
64
65 String &
66 String::operator =(String const &old)
67 {
68 clean(); // TODO: optimize to avoid cleaning the buffer we can use
69 if (old.size() > 0)
70 allocAndFill(old.rawBuf(), old.size());
71 return *this;
72 }
73
74 bool
75 String::operator ==(String const &that) const
76 {
77 if (0 == this->cmp(that))
78 return true;
79
80 return false;
81 }
82
83 bool
84 String::operator !=(String const &that) const
85 {
86 if (0 == this->cmp(that))
87 return false;
88
89 return true;
90 }
91
92 // public interface, makes sure that we clean the old buffer first
93 void
94 String::assign(const char *str, int len)
95 {
96 clean(); // TODO: optimize to avoid cleaning the buffer we can use
97 allocAndFill(str, len);
98 }
99
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);
106 assert(str);
107 allocBuffer(len + 1);
108 len_ = len;
109 memcpy(buf_, str, len);
110 buf_[len] = '\0';
111 PROF_stop(StringAllocAndFill);
112 }
113
114 String::String(String const &old) : size_(0), len_(0), buf_(NULL)
115 {
116 if (old.size() > 0)
117 allocAndFill(old.rawBuf(), old.size());
118 #if DEBUGSTRINGS
119
120 StringRegistry::Instance().add(this);
121 #endif
122 }
123
124 void
125 String::clean()
126 {
127 PROF_start(StringClean);
128
129 /* TODO if mempools has already closed this will FAIL!! */
130 if (defined())
131 memFreeString(size_, buf_);
132
133 len_ = 0;
134
135 size_ = 0;
136
137 buf_ = NULL;
138 PROF_stop(StringClean);
139 }
140
141 String::~String()
142 {
143 clean();
144 #if DEBUGSTRINGS
145
146 StringRegistry::Instance().remove(this);
147 #endif
148 }
149
150 void
151 String::reset(char const *str)
152 {
153 PROF_start(StringReset);
154 clean(); // TODO: optimize to avoid cleaning the buffer if we can reuse it
155 if (str)
156 allocAndFill(str, strlen(str));
157 PROF_stop(StringReset);
158 }
159
160 void
161 String::append( char const *str, int len)
162 {
163 assert(str && len >= 0);
164
165 PROF_start(StringAppend);
166 if (len_ + len + 1 /*'\0'*/ < size_) {
167 xstrncpy(buf_+len_, str, len+1);
168 len_ += len;
169 } else {
170 // Create a temporary string and absorb it later.
171 String snew;
172 assert(canGrowBy(len)); // otherwise snew.len_ may overflow below
173 snew.len_ = len_ + len;
174 snew.allocBuffer(snew.len_ + 1);
175
176 if (len_)
177 memcpy(snew.buf_, rawBuf(), len_);
178
179 if (len)
180 memcpy(snew.buf_ + len_, str, len);
181
182 snew.buf_[snew.len_] = '\0';
183
184 absorb(snew);
185 }
186 PROF_stop(StringAppend);
187 }
188
189 void
190 String::append(char const *str)
191 {
192 assert(str);
193 append(str, strlen(str));
194 }
195
196 void
197 String::append(char const chr)
198 {
199 char myString[2];
200 myString[0]=chr;
201 myString[1]='\0';
202 append(myString, 1);
203 }
204
205 void
206 String::append(String const &old)
207 {
208 append(old.rawBuf(), old.len_);
209 }
210
211 void
212 String::absorb(String &old)
213 {
214 clean();
215 setBuffer(old.buf_, old.size_);
216 len_ = old.len_;
217 old.size_ = 0;
218 old.buf_ = NULL;
219 old.len_ = 0;
220 }
221
222 String
223 String::substr(String::size_type from, String::size_type to) const
224 {
225 // Must(from >= 0 && from < size());
226 Must(from < size());
227 Must(to > 0 && to <= size());
228 Must(to > from);
229
230 String rv;
231 rv.assign(rawBuf()+from, to-from);
232 return rv;
233 }
234
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
318 #if DEBUGSTRINGS
319 void
320 String::stat(StoreEntry *entry) const
321 {
322 storeAppendPrintf(entry, "%p : %d/%d \"%.*s\"\n",this,len_, size_, size(), rawBuf());
323 }
324
325 StringRegistry &
326 StringRegistry::Instance()
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
338 StringRegistry::StringRegistry()
339 {
340 #if DEBUGSTRINGS
341 Mgr::RegisterAction("strings",
342 "Strings in use in squid", Stat, 0, 1);
343 #endif
344 }
345
346 void
347 StringRegistry::add(String const *entry)
348 {
349 entries.insert(entry, ptrcmp);
350 }
351
352 void
353 StringRegistry::remove(String const *entry)
354 {
355 entries.remove(entry, ptrcmp);
356 }
357
358 StringRegistry StringRegistry::Instance_;
359
360 String::size_type memStringCount();
361
362 void
363 StringRegistry::Stat(StoreEntry *entry)
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
370 StringRegistry::Stater(String const * const & nodedata, void *state)
371 {
372 StoreEntry *entry = (StoreEntry *) state;
373 nodedata->stat(entry);
374 }
375
376 #endif
377
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
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))
422 ++p;
423
424 if (!*p)
425 goto error;
426
427 word = d = p;
428
429 while ((ch = *p)) {
430 switch (ch) {
431
432 case '\\':
433 if (quoted)
434 ++p;
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
455 *d = ch;
456 ++d;
457
458 if (ch)
459 ++p;
460
461 break;
462
463 case '"':
464 quoted = !quoted;
465
466 ++p;
467
468 break;
469
470 default:
471 if (!quoted && xisspace(*p)) {
472 ++p;
473 goto done;
474 }
475
476 *d = *p;
477 ++d;
478 ++p;
479 break;
480 }
481 }
482
483 done:
484 *d = '\0';
485
486 error:
487 *t = (char *) p;
488 return (char *) word;
489 }
490
491 const char *
492 checkNullString(const char *p)
493 {
494 return p ? p : "(NULL)";
495 }
496
497 const char *
498 String::pos(char const *aString) const
499 {
500 if (undefined())
501 return NULL;
502 return strstr(termedBuf(), aString);
503 }
504
505 const char *
506 String::pos(char const ch) const
507 {
508 if (undefined())
509 return NULL;
510 return strchr(termedBuf(), ch);
511 }
512
513 const char *
514 String::rpos(char const ch) const
515 {
516 if (undefined())
517 return NULL;
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