Dusk Audio Framework
Loading...
Searching...
No Matches
String.hpp
1/*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#ifndef DAF_STRING_HPP_INCLUDED
18#define DAF_STRING_HPP_INCLUDED
19
20#include "../DafUtils.hpp"
21#include "../extra/ScopedSafeLocale.hpp"
22
23#include <algorithm>
24
25#if __cplusplus >= 201703L
26# include <string_view>
27#endif
28
30
31// -----------------------------------------------------------------------
32// String class
33
34class String
35{
36public:
37 // -------------------------------------------------------------------
38 // constructors (no explicit conversions allowed)
39
40 /*
41 * Empty string.
42 */
43 explicit String() noexcept
44 : fBuffer(_null()),
45 fBufferLen(0),
46 fBufferAlloc(false) {}
47
48 /*
49 * Simple character.
50 */
51 explicit String(const char c) noexcept
52 : fBuffer(_null()),
53 fBufferLen(0),
54 fBufferAlloc(false)
55 {
56 const char ch[2] = { c, '\0' };
57 _dup(ch);
58 }
59
60 /*
61 * Simple char string.
62 */
63 explicit String(char* const strBuf, const bool reallocData = true) noexcept
64 : fBuffer(_null()),
65 fBufferLen(0),
66 fBufferAlloc(false)
67 {
68 if (reallocData || strBuf == nullptr)
69 {
70 _dup(strBuf);
71 }
72 else
73 {
74 fBuffer = strBuf;
75 fBufferLen = std::strlen(strBuf);
76 fBufferAlloc = true;
77 }
78 }
79
80 /*
81 * Simple const char string.
82 */
83 explicit String(const char* const strBuf) noexcept
84 : fBuffer(_null()),
85 fBufferLen(0),
86 fBufferAlloc(false)
87 {
88 _dup(strBuf);
89 }
90
91 #if __cplusplus >= 201703L
92 /*
93 * std::string_view compatible variant.
94 */
95 explicit String(const std::string_view& strView) noexcept
96 : fBuffer(_null()),
97 fBufferLen(0),
98 fBufferAlloc(false)
99 {
100 _dup(strView.data(), strView.size());
101 }
102 #endif
103
104 /*
105 * Integer.
106 */
107 explicit String(const int value) noexcept
108 : fBuffer(_null()),
109 fBufferLen(0),
110 fBufferAlloc(false)
111 {
112 char strBuf[0xff+1];
113 std::snprintf(strBuf, 0xff, "%d", value);
114 strBuf[0xff] = '\0';
115
116 _dup(strBuf);
117 }
118
119 /*
120 * Unsigned integer, possibly in hexadecimal.
121 */
122 explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
123 : fBuffer(_null()),
124 fBufferLen(0),
125 fBufferAlloc(false)
126 {
127 char strBuf[0xff+1];
128 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
129 strBuf[0xff] = '\0';
130
131 _dup(strBuf);
132 }
133
134 /*
135 * Long integer.
136 */
137 explicit String(const long value) noexcept
138 : fBuffer(_null()),
139 fBufferLen(0),
140 fBufferAlloc(false)
141 {
142 char strBuf[0xff+1];
143 std::snprintf(strBuf, 0xff, "%ld", value);
144 strBuf[0xff] = '\0';
145
146 _dup(strBuf);
147 }
148
149 /*
150 * Long unsigned integer, possibly hexadecimal.
151 */
152 explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
153 : fBuffer(_null()),
154 fBufferLen(0),
155 fBufferAlloc(false)
156 {
157 char strBuf[0xff+1];
158 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
159 strBuf[0xff] = '\0';
160
161 _dup(strBuf);
162 }
163
164 /*
165 * Long long integer.
166 */
167 explicit String(const long long value) noexcept
168 : fBuffer(_null()),
169 fBufferLen(0),
170 fBufferAlloc(false)
171 {
172 char strBuf[0xff+1];
173 std::snprintf(strBuf, 0xff, "%lld", value);
174 strBuf[0xff] = '\0';
175
176 _dup(strBuf);
177 }
178
179 /*
180 * Long long unsigned integer, possibly hexadecimal.
181 */
182 explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
183 : fBuffer(_null()),
184 fBufferLen(0),
185 fBufferAlloc(false)
186 {
187 char strBuf[0xff+1];
188 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
189 strBuf[0xff] = '\0';
190
191 _dup(strBuf);
192 }
193
194 /*
195 * Single-precision floating point number.
196 */
197 explicit String(const float value) noexcept
198 : fBuffer(_null()),
199 fBufferLen(0),
200 fBufferAlloc(false)
201 {
202 char strBuf[0xff+1];
203
204 {
205 const ScopedSafeLocale ssl;
206 std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
207 }
208
209 strBuf[0xff] = '\0';
210
211 _dup(strBuf);
212 }
213
214 /*
215 * Double-precision floating point number.
216 */
217 explicit String(const double value) noexcept
218 : fBuffer(_null()),
219 fBufferLen(0),
220 fBufferAlloc(false)
221 {
222 char strBuf[0xff+1];
223
224 {
225 const ScopedSafeLocale ssl;
226 std::snprintf(strBuf, 0xff, "%.24g", value);
227 }
228
229 strBuf[0xff] = '\0';
230
231 _dup(strBuf);
232 }
233
234 // -------------------------------------------------------------------
235 // non-explicit constructor
236
237 /*
238 * Create string from another string.
239 */
240 String(const String& str) noexcept
241 : fBuffer(_null()),
242 fBufferLen(0),
243 fBufferAlloc(false)
244 {
245 _dup(str.fBuffer);
246 }
247
248 // -------------------------------------------------------------------
249 // destructor
250
251 /*
252 * Destructor.
253 */
254 ~String() noexcept
255 {
256 DAF_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
257
258 if (fBufferAlloc)
259 std::free(fBuffer);
260
261 fBuffer = nullptr;
262 fBufferLen = 0;
263 fBufferAlloc = false;
264 }
265
266 // -------------------------------------------------------------------
267 // public methods
268
269 /*
270 * Get length of the string.
271 */
272 size_t length() const noexcept
273 {
274 return fBufferLen;
275 }
276
277 /*
278 * Check if the string is empty.
279 */
280 bool isEmpty() const noexcept
281 {
282 return (fBufferLen == 0);
283 }
284
285 /*
286 * Check if the string is not empty.
287 */
288 bool isNotEmpty() const noexcept
289 {
290 return (fBufferLen != 0);
291 }
292
293 /*
294 * Check if the string contains a specific character, case-sensitive.
295 */
296 bool contains(const char c) const noexcept
297 {
298 for (size_t i=0; i<fBufferLen; ++i)
299 {
300 if (fBuffer[i] == c)
301 return true;
302 }
303
304 return false;
305 }
306
307 /*
308 * Check if the string contains another string, optionally ignoring case.
309 */
310 bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
311 {
312 DAF_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
313
314 if (ignoreCase)
315 {
316#ifdef __USE_GNU
317 return (strcasestr(fBuffer, strBuf) != nullptr);
318#else
319 String tmp1(fBuffer), tmp2(strBuf);
320
321 // memory allocation failed or empty string(s)
322 if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
323 return false;
324
325 tmp1.toLower();
326 tmp2.toLower();
327 return (std::strstr(tmp1, tmp2) != nullptr);
328#endif
329 }
330
331 return (std::strstr(fBuffer, strBuf) != nullptr);
332 }
333
334 /*
335 * Check if character at 'pos' is a digit.
336 */
337 bool isDigit(const size_t pos) const noexcept
338 {
339 DAF_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
340
341 return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
342 }
343
344 /*
345 * Check if the string starts with the character 'c'.
346 */
347 bool startsWith(const char c) const noexcept
348 {
349 DAF_SAFE_ASSERT_RETURN(c != '\0', false);
350
351 return (fBufferLen > 0 && fBuffer[0] == c);
352 }
353
354 /*
355 * Check if the string starts with the string 'prefix'.
356 */
357 bool startsWith(const char* const prefix) const noexcept
358 {
359 DAF_SAFE_ASSERT_RETURN(prefix != nullptr, false);
360
361 const size_t prefixLen(std::strlen(prefix));
362
363 if (fBufferLen < prefixLen)
364 return false;
365
366 return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
367 }
368
369 /*
370 * Check if the string ends with the character 'c'.
371 */
372 bool endsWith(const char c) const noexcept
373 {
374 DAF_SAFE_ASSERT_RETURN(c != '\0', false);
375
376 return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
377 }
378
379 /*
380 * Check if the string ends with the string 'suffix'.
381 */
382 bool endsWith(const char* const suffix) const noexcept
383 {
384 DAF_SAFE_ASSERT_RETURN(suffix != nullptr, false);
385
386 const size_t suffixLen(std::strlen(suffix));
387
388 if (fBufferLen < suffixLen)
389 return false;
390
391 return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
392 }
393
394 /*
395 * Find the first occurrence of character 'c' in the string.
396 * Returns "length()" if the character is not found.
397 */
398 size_t find(const char c, bool* const found = nullptr) const noexcept
399 {
400 if (fBufferLen == 0 || c == '\0')
401 {
402 if (found != nullptr)
403 *found = false;
404 return fBufferLen;
405 }
406
407 for (size_t i=0; i < fBufferLen; ++i)
408 {
409 if (fBuffer[i] == c)
410 {
411 if (found != nullptr)
412 *found = true;
413 return i;
414 }
415 }
416
417 if (found != nullptr)
418 *found = false;
419 return fBufferLen;
420 }
421
422 /*
423 * Find the first occurrence of string 'strBuf' in the string.
424 * Returns "length()" if the string is not found.
425 */
426 size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
427 {
428 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
429 {
430 if (found != nullptr)
431 *found = false;
432 return fBufferLen;
433 }
434
435 if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
436 {
437 const ssize_t ret(subStrBuf - fBuffer);
438
439 if (ret < 0)
440 {
441 // should never happen!
442 d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret));
443
444 if (found != nullptr)
445 *found = false;
446 return fBufferLen;
447 }
448
449 if (found != nullptr)
450 *found = true;
451 return static_cast<size_t>(ret);
452 }
453
454 if (found != nullptr)
455 *found = false;
456 return fBufferLen;
457 }
458
459 /*
460 * Find the last occurrence of character 'c' in the string.
461 * Returns "length()" if the character is not found.
462 */
463 size_t rfind(const char c, bool* const found = nullptr) const noexcept
464 {
465 if (fBufferLen == 0 || c == '\0')
466 {
467 if (found != nullptr)
468 *found = false;
469 return fBufferLen;
470 }
471
472 for (size_t i=fBufferLen; i > 0; --i)
473 {
474 if (fBuffer[i-1] == c)
475 {
476 if (found != nullptr)
477 *found = true;
478 return i-1;
479 }
480 }
481
482 if (found != nullptr)
483 *found = false;
484 return fBufferLen;
485 }
486
487 /*
488 * Find the last occurrence of string 'strBuf' in the string.
489 * Returns "length()" if the string is not found.
490 */
491 size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
492 {
493 if (found != nullptr)
494 *found = false;
495
496 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
497 return fBufferLen;
498
499 const size_t strBufLen(std::strlen(strBuf));
500
501 size_t ret = fBufferLen;
502 const char* tmpBuf = fBuffer;
503
504 for (size_t i=0; i < fBufferLen; ++i)
505 {
506 if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
507 {
508 if (found != nullptr)
509 *found = true;
510 break;
511 }
512
513 --ret;
514 ++tmpBuf;
515 }
516
517 return fBufferLen-ret;
518 }
519
520 /*
521 * Clear the string.
522 */
523 void clear() noexcept
524 {
525 truncate(0);
526 }
527
528 /*
529 * Replace all occurrences of character 'before' with character 'after'.
530 */
531 String& replace(const char before, const char after) noexcept
532 {
533 DAF_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);
534
535 for (size_t i=0; i < fBufferLen; ++i)
536 {
537 if (fBuffer[i] == before)
538 fBuffer[i] = after;
539 }
540
541 return *this;
542 }
543
544 /*
545 * Remove all occurrences of character 'c', shifting and truncating the string as necessary.
546 */
547 String& remove(const char c) noexcept
548 {
549 DAF_SAFE_ASSERT_RETURN(c != '\0', *this);
550
551 if (fBufferLen == 0)
552 return *this;
553
554 for (size_t i=0; i < fBufferLen; ++i)
555 {
556 if (fBuffer[i] == c)
557 {
558 --fBufferLen;
559 std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i);
560 }
561 }
562
563 fBuffer[fBufferLen] = '\0';
564 return *this;
565 }
566
567 /*
568 * Truncate the string to size 'n'.
569 */
570 String& truncate(const size_t n) noexcept
571 {
572 if (n >= fBufferLen)
573 return *this;
574
575 fBuffer[n] = '\0';
576 fBufferLen = n;
577
578 return *this;
579 }
580
581 /*
582 * Convert all non-basic characters to '_'.
583 */
584 String& toBasic() noexcept
585 {
586 for (size_t i=0; i < fBufferLen; ++i)
587 {
588 if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
589 continue;
590 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
591 continue;
592 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
593 continue;
594 if (fBuffer[i] == '_')
595 continue;
596
597 fBuffer[i] = '_';
598 }
599
600 return *this;
601 }
602
603 /*
604 * Convert all ascii characters to lowercase.
605 */
606 String& toLower() noexcept
607 {
608 static constexpr const char kCharDiff = 'a' - 'A';
609
610 for (size_t i=0; i < fBufferLen; ++i)
611 {
612 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
613 fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
614 }
615
616 return *this;
617 }
618
619 /*
620 * Convert all ascii characters to uppercase.
621 */
622 String& toUpper() noexcept
623 {
624 static constexpr const char kCharDiff = 'a' - 'A';
625
626 for (size_t i=0; i < fBufferLen; ++i)
627 {
628 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
629 fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
630 }
631
632 return *this;
633 }
634
635 /*
636 * Create a new string where all non-basic characters are converted to '_'.
637 * @see toBasic()
638 */
639 String asBasic() const noexcept
640 {
641 String s(*this);
642 return s.toBasic();
643 }
644
645 /*
646 * Create a new string where all ascii characters are converted lowercase.
647 * @see toLower()
648 */
649 String asLower() const noexcept
650 {
651 String s(*this);
652 return s.toLower();
653 }
654
655 /*
656 * Create a new string where all ascii characters are converted to uppercase.
657 * @see toUpper()
658 */
659 String asUpper() const noexcept
660 {
661 String s(*this);
662 return s.toUpper();
663 }
664
665 /*
666 * Direct access to the string buffer (read-only).
667 */
668 const char* buffer() const noexcept
669 {
670 return fBuffer;
671 }
672
673 /*
674 * Get and release the string buffer, while also clearing this string.
675 * This allows to keep a pointer to the buffer after this object is deleted.
676 * Result must be freed.
677 */
678 char* getAndReleaseBuffer() noexcept
679 {
680 char* ret = fBufferLen > 0 ? fBuffer : nullptr;
681 fBuffer = _null();
682 fBufferLen = 0;
683 fBufferAlloc = false;
684 return ret;
685 }
686
687 // -------------------------------------------------------------------
688 // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
689 // Copyright (C) 2004-2008 René Nyffenegger
690
691 static String asBase64(const void* const data, const size_t dataSize)
692 {
693 static constexpr const char* const kBase64Chars =
694 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
695 "abcdefghijklmnopqrstuvwxyz"
696 "0123456789+/";
697
698 const size_t strBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
699 char* strBuf = static_cast<char*>(std::malloc(strBufSize));
700 DAF_SAFE_ASSERT_RETURN(strBuf != nullptr, String());
701
702 strBuf[strBufSize] = '\0';
703 size_t strBufIndex = 0;
704
705 const uchar* bytesToEncode = static_cast<const uchar*>(data);
706
707 uint i=0, j=0;
708 uint charArray3[3], charArray4[4];
709
710 String ret;
711
712 for (size_t s = 0; s < dataSize; ++s)
713 {
714 charArray3[i++] = *(bytesToEncode++);
715
716 if (i == 3)
717 {
718 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
719 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
720 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
721 charArray4[3] = charArray3[2] & 0x3f;
722
723 for (i = 0; i < 4; ++i)
724 strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
725
726 if (strBufIndex >= strBufSize - 7)
727 {
728 strBuf[strBufIndex] = '\0';
729 strBufIndex = 0;
730 ret += strBuf;
731 }
732
733 i = 0;
734 }
735 }
736
737 if (i != 0)
738 {
739 for (j = i; j < 3; ++j)
740 charArray3[j] = '\0';
741
742 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
743 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
744 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
745 charArray4[3] = charArray3[2] & 0x3f;
746
747 for (j = 0; j < 4 && i < 3 && j < i + 1; ++j)
748 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
749
750 for (; i++ < 3;)
751 strBuf[strBufIndex++] = '=';
752 }
753
754 if (strBufIndex != 0)
755 {
756 strBuf[strBufIndex] = '\0';
757 ret += strBuf;
758 }
759
760 std::free(strBuf);
761 return ret;
762 }
763
764 /*
765 * Convert to a URL encoded string.
766 */
767 String& urlEncode() noexcept
768 {
769 static constexpr const char* const kHexChars = "0123456789ABCDEF";
770
771 if (fBufferLen == 0)
772 return *this;
773
774 char* const newbuf = static_cast<char*>(std::malloc(fBufferLen * 3 + 1));
775 DAF_SAFE_ASSERT_RETURN(newbuf != nullptr, *this);
776
777 char* newbufptr = newbuf;
778
779 for (size_t i=0; i < fBufferLen; ++i)
780 {
781 const char c = fBuffer[i];
782
783 switch (c)
784 {
785 case '!': // 33
786 case '#': // 35
787 case '$': // 36
788 case '&': // 38
789 case '\'': // 39
790 case '(': // 40
791 case ')': // 41
792 case '*': // 42
793 case '+': // 43
794 case ',': // 44
795 case '-': // 45
796 case '.': // 46
797 case '/': // 47
798 case '0': // 48
799 case '1': // 49
800 case '2': // 50
801 case '3': // 51
802 case '4': // 52
803 case '5': // 53
804 case '6': // 54
805 case '7': // 55
806 case '8': // 56
807 case '9': // 57
808 case ':': // 58
809 case ';': // 59
810 case '=': // 61
811 case '?': // 63
812 case '@': // 64
813 case 'A': // 65
814 case 'B': // 66
815 case 'C': // 67
816 case 'D': // 68
817 case 'E': // 69
818 case 'F': // 70
819 case 'G': // 71
820 case 'H': // 72
821 case 'I': // 73
822 case 'J': // 74
823 case 'K': // 75
824 case 'L': // 76
825 case 'M': // 77
826 case 'N': // 78
827 case 'O': // 79
828 case 'P': // 80
829 case 'Q': // 81
830 case 'R': // 82
831 case 'S': // 83
832 case 'T': // 84
833 case 'U': // 85
834 case 'V': // 86
835 case 'W': // 87
836 case 'X': // 88
837 case 'Y': // 89
838 case 'Z': // 90
839 case '[': // 91
840 case ']': // 93
841 case '_': // 95
842 case 'a': // 97
843 case 'b': // 98
844 case 'c': // 99
845 case 'd': // 100
846 case 'e': // 101
847 case 'f': // 102
848 case 'g': // 103
849 case 'h': // 104
850 case 'i': // 105
851 case 'j': // 106
852 case 'k': // 107
853 case 'l': // 108
854 case 'm': // 109
855 case 'n': // 110
856 case 'o': // 111
857 case 'p': // 112
858 case 'q': // 113
859 case 'r': // 114
860 case 's': // 115
861 case 't': // 116
862 case 'u': // 117
863 case 'v': // 118
864 case 'w': // 119
865 case 'x': // 120
866 case 'y': // 121
867 case 'z': // 122
868 case '~': // 126
869 *newbufptr++ = c;
870 break;
871 default:
872 *newbufptr++ = '%';
873 *newbufptr++ = kHexChars[(c >> 4) & 0xf];
874 *newbufptr++ = kHexChars[c & 0xf];
875 break;
876 }
877 }
878
879 *newbufptr = '\0';
880
881 std::free(fBuffer);
882 fBuffer = newbuf;
883 fBufferLen = std::strlen(newbuf);
884 fBufferAlloc = true;
885
886 return *this;
887 }
888
889 /*
890 * Convert to a URL decoded string.
891 */
892 String& urlDecode() noexcept
893 {
894 if (fBufferLen == 0)
895 return *this;
896
897 char* const newbuf = static_cast<char*>(std::malloc(fBufferLen + 1));
898 DAF_SAFE_ASSERT_RETURN(newbuf != nullptr, *this);
899
900 char* newbufptr = newbuf;
901
902 for (size_t i=0; i < fBufferLen; ++i)
903 {
904 const char c = fBuffer[i];
905
906 if (c == '%')
907 {
908 DAF_SAFE_ASSERT_CONTINUE(fBufferLen > i + 2);
909
910 char c1 = fBuffer[i + 1];
911 char c2 = fBuffer[i + 2];
912 i += 2;
913
914 /**/ if (c1 >= '0' && c1 <= '9')
915 c1 -= '0';
916 else if (c1 >= 'A' && c1 <= 'Z')
917 c1 -= 'A' - 10;
918 else if (c1 >= 'a' && c1 <= 'z')
919 c1 -= 'a' - 10;
920 else
921 continue;
922
923 /**/ if (c2 >= '0' && c2 <= '9')
924 c2 -= '0';
925 else if (c2 >= 'A' && c2 <= 'Z')
926 c2 -= 'A' - 10;
927 else if (c2 >= 'a' && c2 <= 'z')
928 c2 -= 'a' - 10;
929 else
930 continue;
931
932 *newbufptr++ = c1 << 4 | c2;
933 }
934 else
935 {
936 *newbufptr++ = c;
937 }
938 }
939
940 *newbufptr = '\0';
941
942 std::free(fBuffer);
943 fBuffer = newbuf;
944 fBufferLen = std::strlen(newbuf);
945 fBufferAlloc = true;
946
947 return *this;
948 }
949
950 // -------------------------------------------------------------------
951 // public operators
952
953 operator const char*() const noexcept
954 {
955 return fBuffer;
956 }
957
958 char operator[](const size_t pos) const noexcept
959 {
960 if (pos < fBufferLen)
961 return fBuffer[pos];
962
963 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
964 return '\0';
965 }
966
967 char& operator[](const size_t pos) noexcept
968 {
969 if (pos < fBufferLen)
970 return fBuffer[pos];
971
972 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
973
974 static char fallback;
975 fallback = '\0';
976 return fallback;
977 }
978
979 bool operator==(const char* const strBuf) const noexcept
980 {
981 return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
982 }
983
984 bool operator==(const String& str) const noexcept
985 {
986 return operator==(str.fBuffer);
987 }
988
989 bool operator!=(const char* const strBuf) const noexcept
990 {
991 return !operator==(strBuf);
992 }
993
994 bool operator!=(const String& str) const noexcept
995 {
996 return !operator==(str.fBuffer);
997 }
998
999 String& operator=(const char* const strBuf) noexcept
1000 {
1001 _dup(strBuf);
1002
1003 return *this;
1004 }
1005
1006 String& operator=(const String& str) noexcept
1007 {
1008 _dup(str.fBuffer);
1009
1010 return *this;
1011 }
1012
1013 String& operator+=(const char* const strBuf) noexcept
1014 {
1015 if (strBuf == nullptr || strBuf[0] == '\0')
1016 return *this;
1017
1018 const size_t strBufLen = std::strlen(strBuf);
1019
1020 // for empty strings, we can just take the appended string as our entire data
1021 if (isEmpty())
1022 {
1023 _dup(strBuf, strBufLen);
1024 return *this;
1025 }
1026
1027 // we have some data ourselves, reallocate to add the new stuff
1028 char* const newBuf = static_cast<char*>(std::realloc(fBufferAlloc ? fBuffer : nullptr, fBufferLen + strBufLen + 1));
1029 DAF_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
1030
1031 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
1032
1033 fBuffer = newBuf;
1034 fBufferLen += strBufLen;
1035 fBufferAlloc = true;
1036
1037 return *this;
1038 }
1039
1040 String& operator+=(const String& str) noexcept
1041 {
1042 return operator+=(str.fBuffer);
1043 }
1044
1045 String operator+(const char* const strBuf) noexcept
1046 {
1047 if (strBuf == nullptr || strBuf[0] == '\0')
1048 return *this;
1049 if (isEmpty())
1050 return String(strBuf);
1051
1052 const size_t strBufLen = std::strlen(strBuf);
1053 const size_t newBufSize = fBufferLen + strBufLen;
1054 char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1));
1055 DAF_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
1056
1057 std::memcpy(newBuf, fBuffer, fBufferLen);
1058 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
1059
1060 return String(newBuf, false);
1061 }
1062
1063 String operator+(const String& str) noexcept
1064 {
1065 return operator+(str.fBuffer);
1066 }
1067
1068 // needed for std::map compatibility
1069 bool operator<(const String& str) const noexcept
1070 {
1071 return std::strcmp(fBuffer, str.fBuffer) < 0;
1072 }
1073
1074 // -------------------------------------------------------------------
1075
1076private:
1077 char* fBuffer; // the actual string buffer
1078 size_t fBufferLen; // string length
1079 bool fBufferAlloc; // wherever the buffer is allocated, not using _null()
1080
1081 /*
1082 * Static null string.
1083 * Prevents allocation for new and/or empty strings.
1084 */
1085 static char* _null() noexcept
1086 {
1087 static char sNull = '\0';
1088 return &sNull;
1089 }
1090
1091 /*
1092 * Helper function.
1093 * Called whenever the string needs to be allocated.
1094 *
1095 * Notes:
1096 * - Allocates string only if 'strBuf' is not null and new string contents are different
1097 * - If 'strBuf' is null, 'size' must be 0
1098 */
1099 void _dup(const char* const strBuf, const size_t size = 0) noexcept
1100 {
1101 if (strBuf != nullptr)
1102 {
1103 // don't recreate string if contents match
1104 if (std::strcmp(fBuffer, strBuf) == 0)
1105 return;
1106
1107 if (fBufferAlloc)
1108 std::free(fBuffer);
1109
1110 fBufferLen = (size > 0) ? size : std::strlen(strBuf);
1111 fBuffer = static_cast<char*>(std::malloc(fBufferLen + 1));
1112
1113 if (fBuffer == nullptr)
1114 {
1115 fBuffer = _null();
1116 fBufferLen = 0;
1117 fBufferAlloc = false;
1118 return;
1119 }
1120
1121 fBufferAlloc = true;
1122
1123 std::strcpy(fBuffer, strBuf);
1124 fBuffer[fBufferLen] = '\0';
1125 }
1126 else
1127 {
1128 DAF_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
1129
1130 // don't recreate null string
1131 if (! fBufferAlloc)
1132 return;
1133
1134 DAF_SAFE_ASSERT(fBuffer != nullptr);
1135 std::free(fBuffer);
1136
1137 fBuffer = _null();
1138 fBufferLen = 0;
1139 fBufferAlloc = false;
1140 }
1141 }
1142
1143 DAF_PREVENT_HEAP_ALLOCATION
1144};
1145
1146// -----------------------------------------------------------------------
1147
1148static inline
1149String operator+(const String& strBefore, const char* const strBufAfter) noexcept
1150{
1151 if (strBufAfter == nullptr || strBufAfter[0] == '\0')
1152 return strBefore;
1153 if (strBefore.isEmpty())
1154 return String(strBufAfter);
1155
1156 const size_t strBeforeLen = strBefore.length();
1157 const size_t strBufAfterLen = std::strlen(strBufAfter);
1158 const size_t newBufSize = strBeforeLen + strBufAfterLen;
1159 char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
1160 DAF_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
1161
1162 std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
1163 std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
1164
1165 return String(newBuf, false);
1166}
1167
1168static inline
1169String operator+(const char* const strBufBefore, const String& strAfter) noexcept
1170{
1171 if (strAfter.isEmpty())
1172 return String(strBufBefore);
1173 if (strBufBefore == nullptr || strBufBefore[0] == '\0')
1174 return strAfter;
1175
1176 const size_t strBufBeforeLen = std::strlen(strBufBefore);
1177 const size_t strAfterLen = strAfter.length();
1178 const size_t newBufSize = strBufBeforeLen + strAfterLen;
1179 char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
1180 DAF_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
1181
1182 std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
1183 std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
1184
1185 return String(newBuf, false);
1186}
1187
1188// -----------------------------------------------------------------------
1189
1191
1192#endif // DAF_STRING_HPP_INCLUDED
Definition ScopedSafeLocale.hpp:57
Definition String.hpp:35
#define END_NAMESPACE_DAF
Definition DafInfo.hpp:996
#define START_NAMESPACE_DAF
Definition DafInfo.hpp:990