comparison DPF-Prymula-audioplugins/dpf/distrho/extra/String.hpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2023 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 DISTRHO_STRING_HPP_INCLUDED
18 #define DISTRHO_STRING_HPP_INCLUDED
19
20 #include "../DistrhoUtils.hpp"
21 #include "../extra/ScopedSafeLocale.hpp"
22
23 #include <algorithm>
24
25 #if __cplusplus >= 201703L
26 # include <string_view>
27 #endif
28
29 START_NAMESPACE_DISTRHO
30
31 // -----------------------------------------------------------------------
32 // String class
33
34 class String
35 {
36 public:
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 char ch[2];
57 ch[0] = c;
58 ch[1] = '\0';
59
60 _dup(ch);
61 }
62
63 /*
64 * Simple char string.
65 */
66 explicit String(char* const strBuf, const bool reallocData = true) noexcept
67 : fBuffer(_null()),
68 fBufferLen(0),
69 fBufferAlloc(false)
70 {
71 if (reallocData || strBuf == nullptr)
72 {
73 _dup(strBuf);
74 }
75 else
76 {
77 fBuffer = strBuf;
78 fBufferLen = std::strlen(strBuf);
79 fBufferAlloc = true;
80 }
81 }
82
83 /*
84 * Simple const char string.
85 */
86 explicit String(const char* const strBuf) noexcept
87 : fBuffer(_null()),
88 fBufferLen(0),
89 fBufferAlloc(false)
90 {
91 _dup(strBuf);
92 }
93
94 #if __cplusplus >= 201703L
95 /*
96 * constexpr compatible variant.
97 */
98 explicit constexpr String(const std::string_view& strView) noexcept
99 : fBuffer(const_cast<char*>(strView.data())),
100 fBufferLen(strView.size()),
101 fBufferAlloc(false) {}
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 DISTRHO_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 std::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 (std::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 DISTRHO_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 std::size_t pos) const noexcept
338 {
339 DISTRHO_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 DISTRHO_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 DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);
360
361 const std::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 DISTRHO_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 DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);
385
386 const std::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 std::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 (std::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 std::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<std::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 std::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 (std::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 std::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 std::size_t strBufLen(std::strlen(strBuf));
500
501 std::size_t ret = fBufferLen;
502 const char* tmpBuf = fBuffer;
503
504 for (std::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 DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);
534
535 for (std::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 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this);
550
551 if (fBufferLen == 0)
552 return *this;
553
554 for (std::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 std::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 (std::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 const char kCharDiff('a' - 'A');
609
610 for (std::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 const char kCharDiff('a' - 'A');
625
626 for (std::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 std::size_t dataSize)
692 {
693 static const char* const kBase64Chars =
694 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
695 "abcdefghijklmnopqrstuvwxyz"
696 "0123456789+/";
697
698 #ifndef _MSC_VER
699 const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
700 #else
701 constexpr std::size_t kTmpBufSize = 65536U;
702 #endif
703
704 const uchar* bytesToEncode((const uchar*)data);
705
706 uint i=0, j=0;
707 uint charArray3[3], charArray4[4];
708
709 char strBuf[kTmpBufSize + 1];
710 strBuf[kTmpBufSize] = '\0';
711 std::size_t strBufIndex = 0;
712
713 String ret;
714
715 for (std::size_t s=0; s<dataSize; ++s)
716 {
717 charArray3[i++] = *(bytesToEncode++);
718
719 if (i == 3)
720 {
721 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
722 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
723 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
724 charArray4[3] = charArray3[2] & 0x3f;
725
726 for (i=0; i<4; ++i)
727 strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
728
729 if (strBufIndex >= kTmpBufSize-7)
730 {
731 strBuf[strBufIndex] = '\0';
732 strBufIndex = 0;
733 ret += strBuf;
734 }
735
736 i = 0;
737 }
738 }
739
740 if (i != 0)
741 {
742 for (j=i; j<3; ++j)
743 charArray3[j] = '\0';
744
745 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
746 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
747 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
748 charArray4[3] = charArray3[2] & 0x3f;
749
750 for (j=0; j<4 && i<3 && j<i+1; ++j)
751 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
752
753 for (; i++ < 3;)
754 strBuf[strBufIndex++] = '=';
755 }
756
757 if (strBufIndex != 0)
758 {
759 strBuf[strBufIndex] = '\0';
760 ret += strBuf;
761 }
762
763 return ret;
764 }
765
766 // -------------------------------------------------------------------
767 // public operators
768
769 operator const char*() const noexcept
770 {
771 return fBuffer;
772 }
773
774 char operator[](const std::size_t pos) const noexcept
775 {
776 if (pos < fBufferLen)
777 return fBuffer[pos];
778
779 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
780
781 static char fallback;
782 fallback = '\0';
783 return fallback;
784 }
785
786 char& operator[](const std::size_t pos) noexcept
787 {
788 if (pos < fBufferLen)
789 return fBuffer[pos];
790
791 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
792
793 static char fallback;
794 fallback = '\0';
795 return fallback;
796 }
797
798 bool operator==(const char* const strBuf) const noexcept
799 {
800 return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
801 }
802
803 bool operator==(const String& str) const noexcept
804 {
805 return operator==(str.fBuffer);
806 }
807
808 bool operator!=(const char* const strBuf) const noexcept
809 {
810 return !operator==(strBuf);
811 }
812
813 bool operator!=(const String& str) const noexcept
814 {
815 return !operator==(str.fBuffer);
816 }
817
818 String& operator=(const char* const strBuf) noexcept
819 {
820 _dup(strBuf);
821
822 return *this;
823 }
824
825 String& operator=(const String& str) noexcept
826 {
827 _dup(str.fBuffer);
828
829 return *this;
830 }
831
832 String& operator+=(const char* const strBuf) noexcept
833 {
834 if (strBuf == nullptr || strBuf[0] == '\0')
835 return *this;
836
837 const std::size_t strBufLen = std::strlen(strBuf);
838
839 // for empty strings, we can just take the appended string as our entire data
840 if (isEmpty())
841 {
842 _dup(strBuf, strBufLen);
843 return *this;
844 }
845
846 // we have some data ourselves, reallocate to add the new stuff
847 char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
848 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
849
850 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
851
852 fBuffer = newBuf;
853 fBufferLen += strBufLen;
854
855 return *this;
856 }
857
858 String& operator+=(const String& str) noexcept
859 {
860 return operator+=(str.fBuffer);
861 }
862
863 String operator+(const char* const strBuf) noexcept
864 {
865 if (strBuf == nullptr || strBuf[0] == '\0')
866 return *this;
867 if (isEmpty())
868 return String(strBuf);
869
870 const std::size_t strBufLen = std::strlen(strBuf);
871 const std::size_t newBufSize = fBufferLen + strBufLen;
872 char* const newBuf = (char*)malloc(newBufSize + 1);
873 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
874
875 std::memcpy(newBuf, fBuffer, fBufferLen);
876 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
877
878 return String(newBuf, false);
879 }
880
881 String operator+(const String& str) noexcept
882 {
883 return operator+(str.fBuffer);
884 }
885
886 // needed for std::map compatibility
887 bool operator<(const String& str) const noexcept
888 {
889 return std::strcmp(fBuffer, str.fBuffer) < 0;
890 }
891
892 // -------------------------------------------------------------------
893
894 private:
895 char* fBuffer; // the actual string buffer
896 std::size_t fBufferLen; // string length
897 bool fBufferAlloc; // wherever the buffer is allocated, not using _null()
898
899 /*
900 * Static null string.
901 * Prevents allocation for new and/or empty strings.
902 */
903 static char* _null() noexcept
904 {
905 static char sNull = '\0';
906 return &sNull;
907 }
908
909 /*
910 * Helper function.
911 * Called whenever the string needs to be allocated.
912 *
913 * Notes:
914 * - Allocates string only if 'strBuf' is not null and new string contents are different
915 * - If 'strBuf' is null, 'size' must be 0
916 */
917 void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
918 {
919 if (strBuf != nullptr)
920 {
921 // don't recreate string if contents match
922 if (std::strcmp(fBuffer, strBuf) == 0)
923 return;
924
925 if (fBufferAlloc)
926 std::free(fBuffer);
927
928 fBufferLen = (size > 0) ? size : std::strlen(strBuf);
929 fBuffer = (char*)std::malloc(fBufferLen+1);
930
931 if (fBuffer == nullptr)
932 {
933 fBuffer = _null();
934 fBufferLen = 0;
935 fBufferAlloc = false;
936 return;
937 }
938
939 fBufferAlloc = true;
940
941 std::strcpy(fBuffer, strBuf);
942 fBuffer[fBufferLen] = '\0';
943 }
944 else
945 {
946 DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
947
948 // don't recreate null string
949 if (! fBufferAlloc)
950 return;
951
952 DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
953 std::free(fBuffer);
954
955 fBuffer = _null();
956 fBufferLen = 0;
957 fBufferAlloc = false;
958 }
959 }
960
961 DISTRHO_PREVENT_HEAP_ALLOCATION
962 };
963
964 // -----------------------------------------------------------------------
965
966 static inline
967 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
968 {
969 if (strBufAfter == nullptr || strBufAfter[0] == '\0')
970 return strBefore;
971 if (strBefore.isEmpty())
972 return String(strBufAfter);
973
974 const std::size_t strBeforeLen = strBefore.length();
975 const std::size_t strBufAfterLen = std::strlen(strBufAfter);
976 const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
977 char* const newBuf = (char*)malloc(newBufSize + 1);
978 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
979
980 std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
981 std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
982
983 return String(newBuf, false);
984 }
985
986 static inline
987 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
988 {
989 if (strAfter.isEmpty())
990 return String(strBufBefore);
991 if (strBufBefore == nullptr || strBufBefore[0] == '\0')
992 return strAfter;
993
994 const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
995 const std::size_t strAfterLen = strAfter.length();
996 const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
997 char* const newBuf = (char*)malloc(newBufSize + 1);
998 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
999
1000 std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
1001 std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
1002
1003 return String(newBuf, false);
1004 }
1005
1006 // -----------------------------------------------------------------------
1007
1008 END_NAMESPACE_DISTRHO
1009
1010 #endif // DISTRHO_STRING_HPP_INCLUDED