1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2016 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
22 #include <algorithm>
23
24 START_NAMESPACE_DISTRHO
25
26 // -----------------------------------------------------------------------
27 // String class
28
29 class String
30 {
31 public:
32 // -------------------------------------------------------------------
33 // constructors (no explicit conversions allowed)
34
35 /*
36 * Empty string.
37 */
String()38 explicit String() noexcept
39 : fBuffer(_null()),
40 fBufferLen(0) {}
41
42 /*
43 * Simple character.
44 */
String(const char c)45 explicit String(const char c) noexcept
46 : fBuffer(_null()),
47 fBufferLen(0)
48 {
49 char ch[2];
50 ch[0] = c;
51 ch[1] = '\0';
52
53 _dup(ch);
54 }
55
56 /*
57 * Simple char string.
58 */
String(char * const strBuf,const bool copyData=true)59 explicit String(char* const strBuf, const bool copyData = true) noexcept
60 : fBuffer(_null()),
61 fBufferLen(0)
62 {
63 if (copyData || strBuf == nullptr)
64 {
65 _dup(strBuf);
66 }
67 else
68 {
69 fBuffer = strBuf;
70 fBufferLen = std::strlen(strBuf);
71 }
72
73 }
74
75 /*
76 * Simple const char string.
77 */
String(const char * const strBuf)78 explicit String(const char* const strBuf) noexcept
79 : fBuffer(_null()),
80 fBufferLen(0)
81 {
82 _dup(strBuf);
83 }
84
85 /*
86 * Integer.
87 */
String(const int value)88 explicit String(const int value) noexcept
89 : fBuffer(_null()),
90 fBufferLen(0)
91 {
92 char strBuf[0xff+1];
93 std::snprintf(strBuf, 0xff, "%d", value);
94 strBuf[0xff] = '\0';
95
96 _dup(strBuf);
97 }
98
99 /*
100 * Unsigned integer, possibly in hexadecimal.
101 */
String(const unsigned int value,const bool hexadecimal=false)102 explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
103 : fBuffer(_null()),
104 fBufferLen(0)
105 {
106 char strBuf[0xff+1];
107 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
108 strBuf[0xff] = '\0';
109
110 _dup(strBuf);
111 }
112
113 /*
114 * Long integer.
115 */
String(const long value)116 explicit String(const long value) noexcept
117 : fBuffer(_null()),
118 fBufferLen(0)
119 {
120 char strBuf[0xff+1];
121 std::snprintf(strBuf, 0xff, "%ld", value);
122 strBuf[0xff] = '\0';
123
124 _dup(strBuf);
125 }
126
127 /*
128 * Long unsigned integer, possibly hexadecimal.
129 */
String(const unsigned long value,const bool hexadecimal=false)130 explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
131 : fBuffer(_null()),
132 fBufferLen(0)
133 {
134 char strBuf[0xff+1];
135 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
136 strBuf[0xff] = '\0';
137
138 _dup(strBuf);
139 }
140
141 /*
142 * Long long integer.
143 */
String(const long long value)144 explicit String(const long long value) noexcept
145 : fBuffer(_null()),
146 fBufferLen(0)
147 {
148 char strBuf[0xff+1];
149 std::snprintf(strBuf, 0xff, "%lld", value);
150 strBuf[0xff] = '\0';
151
152 _dup(strBuf);
153 }
154
155 /*
156 * Long long unsigned integer, possibly hexadecimal.
157 */
String(const unsigned long long value,const bool hexadecimal=false)158 explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
159 : fBuffer(_null()),
160 fBufferLen(0)
161 {
162 char strBuf[0xff+1];
163 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
164 strBuf[0xff] = '\0';
165
166 _dup(strBuf);
167 }
168
169 /*
170 * Single-precision floating point number.
171 */
String(const float value)172 explicit String(const float value) noexcept
173 : fBuffer(_null()),
174 fBufferLen(0)
175 {
176 char strBuf[0xff+1];
177 std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
178 strBuf[0xff] = '\0';
179
180 _dup(strBuf);
181 }
182
183 /*
184 * Double-precision floating point number.
185 */
String(const double value)186 explicit String(const double value) noexcept
187 : fBuffer(_null()),
188 fBufferLen(0)
189 {
190 char strBuf[0xff+1];
191 std::snprintf(strBuf, 0xff, "%.24g", value);
192 strBuf[0xff] = '\0';
193
194 _dup(strBuf);
195 }
196
197 // -------------------------------------------------------------------
198 // non-explicit constructor
199
200 /*
201 * Create string from another string.
202 */
String(const String & str)203 String(const String& str) noexcept
204 : fBuffer(_null()),
205 fBufferLen(0)
206 {
207 _dup(str.fBuffer);
208 }
209
210 // -------------------------------------------------------------------
211 // destructor
212
213 /*
214 * Destructor.
215 */
~String()216 ~String() noexcept
217 {
218 DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
219
220 if (fBuffer == _null())
221 return;
222
223 std::free(fBuffer);
224
225 fBuffer = nullptr;
226 fBufferLen = 0;
227 }
228
229 // -------------------------------------------------------------------
230 // public methods
231
232 /*
233 * Get length of the string.
234 */
length() const235 std::size_t length() const noexcept
236 {
237 return fBufferLen;
238 }
239
240 /*
241 * Check if the string is empty.
242 */
isEmpty() const243 bool isEmpty() const noexcept
244 {
245 return (fBufferLen == 0);
246 }
247
248 /*
249 * Check if the string is not empty.
250 */
isNotEmpty() const251 bool isNotEmpty() const noexcept
252 {
253 return (fBufferLen != 0);
254 }
255
256 /*
257 * Check if the string contains another string, optionally ignoring case.
258 */
contains(const char * const strBuf,const bool ignoreCase=false) const259 bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
260 {
261 DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
262
263 if (ignoreCase)
264 {
265 #ifdef __USE_GNU
266 return (strcasestr(fBuffer, strBuf) != nullptr);
267 #else
268 String tmp1(fBuffer), tmp2(strBuf);
269
270 // memory allocation failed or empty string(s)
271 if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
272 return false;
273
274 tmp1.toLower();
275 tmp2.toLower();
276 return (std::strstr(tmp1, tmp2) != nullptr);
277 #endif
278 }
279
280 return (std::strstr(fBuffer, strBuf) != nullptr);
281 }
282
283 /*
284 * Check if character at 'pos' is a digit.
285 */
isDigit(const std::size_t pos) const286 bool isDigit(const std::size_t pos) const noexcept
287 {
288 DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
289
290 return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
291 }
292
293 /*
294 * Check if the string starts with the character 'c'.
295 */
startsWith(const char c) const296 bool startsWith(const char c) const noexcept
297 {
298 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
299
300 return (fBufferLen > 0 && fBuffer[0] == c);
301 }
302
303 /*
304 * Check if the string starts with the string 'prefix'.
305 */
startsWith(const char * const prefix) const306 bool startsWith(const char* const prefix) const noexcept
307 {
308 DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);
309
310 const std::size_t prefixLen(std::strlen(prefix));
311
312 if (fBufferLen < prefixLen)
313 return false;
314
315 return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
316 }
317
318 /*
319 * Check if the string ends with the character 'c'.
320 */
endsWith(const char c) const321 bool endsWith(const char c) const noexcept
322 {
323 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
324
325 return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
326 }
327
328 /*
329 * Check if the string ends with the string 'suffix'.
330 */
endsWith(const char * const suffix) const331 bool endsWith(const char* const suffix) const noexcept
332 {
333 DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);
334
335 const std::size_t suffixLen(std::strlen(suffix));
336
337 if (fBufferLen < suffixLen)
338 return false;
339
340 return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
341 }
342
343 /*
344 * Find the first occurrence of character 'c' in the string.
345 * Returns "length()" if the character is not found.
346 */
find(const char c,bool * const found=nullptr) const347 std::size_t find(const char c, bool* const found = nullptr) const noexcept
348 {
349 if (fBufferLen == 0 || c == '\0')
350 {
351 if (found != nullptr)
352 *found = false;
353 return fBufferLen;
354 }
355
356 for (std::size_t i=0; i < fBufferLen; ++i)
357 {
358 if (fBuffer[i] == c)
359 {
360 if (found != nullptr)
361 *found = true;
362 return i;
363 }
364 }
365
366 if (found != nullptr)
367 *found = false;
368 return fBufferLen;
369 }
370
371 /*
372 * Find the first occurrence of string 'strBuf' in the string.
373 * Returns "length()" if the string is not found.
374 */
find(const char * const strBuf,bool * const found=nullptr) const375 std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
376 {
377 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
378 {
379 if (found != nullptr)
380 *found = false;
381 return fBufferLen;
382 }
383
384 if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
385 {
386 const ssize_t ret(subStrBuf - fBuffer);
387
388 if (ret < 0)
389 {
390 // should never happen!
391 d_safe_assert("ret >= 0", __FILE__, __LINE__);
392
393 if (found != nullptr)
394 *found = false;
395 return fBufferLen;
396 }
397
398 if (found != nullptr)
399 *found = true;
400 return static_cast<std::size_t>(ret);
401 }
402
403 if (found != nullptr)
404 *found = false;
405 return fBufferLen;
406 }
407
408 /*
409 * Find the last occurrence of character 'c' in the string.
410 * Returns "length()" if the character is not found.
411 */
rfind(const char c,bool * const found=nullptr) const412 std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
413 {
414 if (fBufferLen == 0 || c == '\0')
415 {
416 if (found != nullptr)
417 *found = false;
418 return fBufferLen;
419 }
420
421 for (std::size_t i=fBufferLen; i > 0; --i)
422 {
423 if (fBuffer[i-1] == c)
424 {
425 if (found != nullptr)
426 *found = true;
427 return i-1;
428 }
429 }
430
431 if (found != nullptr)
432 *found = false;
433 return fBufferLen;
434 }
435
436 /*
437 * Find the last occurrence of string 'strBuf' in the string.
438 * Returns "length()" if the string is not found.
439 */
rfind(const char * const strBuf,bool * const found=nullptr) const440 std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
441 {
442 if (found != nullptr)
443 *found = false;
444
445 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
446 return fBufferLen;
447
448 const std::size_t strBufLen(std::strlen(strBuf));
449
450 std::size_t ret = fBufferLen;
451 const char* tmpBuf = fBuffer;
452
453 for (std::size_t i=0; i < fBufferLen; ++i)
454 {
455 if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
456 {
457 if (found != nullptr)
458 *found = true;
459 break;
460 }
461
462 --ret;
463 ++tmpBuf;
464 }
465
466 return fBufferLen-ret;
467 }
468
469 /*
470 * Clear the string.
471 */
clear()472 void clear() noexcept
473 {
474 truncate(0);
475 }
476
477 /*
478 * Replace all occurrences of character 'before' with character 'after'.
479 */
replace(const char before,const char after)480 String& replace(const char before, const char after) noexcept
481 {
482 DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this);
483
484 for (std::size_t i=0; i < fBufferLen; ++i)
485 {
486 if (fBuffer[i] == before)
487 fBuffer[i] = after;
488 }
489
490 return *this;
491 }
492
493 /*
494 * Truncate the string to size 'n'.
495 */
truncate(const std::size_t n)496 String& truncate(const std::size_t n) noexcept
497 {
498 if (n >= fBufferLen)
499 return *this;
500
501 for (std::size_t i=n; i < fBufferLen; ++i)
502 fBuffer[i] = '\0';
503
504 fBufferLen = n;
505
506 return *this;
507 }
508
509 /*
510 * Convert all non-basic characters to '_'.
511 */
toBasic()512 String& toBasic() noexcept
513 {
514 for (std::size_t i=0; i < fBufferLen; ++i)
515 {
516 if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
517 continue;
518 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
519 continue;
520 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
521 continue;
522 if (fBuffer[i] == '_')
523 continue;
524
525 fBuffer[i] = '_';
526 }
527
528 return *this;
529 }
530
531 /*
532 * Convert to all ascii characters to lowercase.
533 */
toLower()534 String& toLower() noexcept
535 {
536 static const char kCharDiff('a' - 'A');
537
538 for (std::size_t i=0; i < fBufferLen; ++i)
539 {
540 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
541 fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
542 }
543
544 return *this;
545 }
546
547 /*
548 * Convert to all ascii characters to uppercase.
549 */
toUpper()550 String& toUpper() noexcept
551 {
552 static const char kCharDiff('a' - 'A');
553
554 for (std::size_t i=0; i < fBufferLen; ++i)
555 {
556 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
557 fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
558 }
559
560 return *this;
561 }
562
563 /*
564 * Direct access to the string buffer (read-only).
565 */
buffer() const566 const char* buffer() const noexcept
567 {
568 return fBuffer;
569 }
570
571 /*
572 * Get and release the string buffer, while also clearing this string.
573 * Result must be freed.
574 */
getAndReleaseBuffer()575 char* getAndReleaseBuffer() noexcept
576 {
577 char* const ret = fBuffer;
578 fBuffer = _null();
579 fBufferLen = 0;
580 return ret;
581 }
582
583 // -------------------------------------------------------------------
584 // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
585 // Copyright (C) 2004-2008 René Nyffenegger
586
asBase64(const void * const data,const std::size_t dataSize)587 static String asBase64(const void* const data, const std::size_t dataSize)
588 {
589 static const char* const kBase64Chars =
590 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
591 "abcdefghijklmnopqrstuvwxyz"
592 "0123456789+/";
593
594 const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U);
595
596 const uchar* bytesToEncode((const uchar*)data);
597
598 uint i=0, j=0;
599 uint charArray3[3], charArray4[4];
600
601 char strBuf[kTmpBufSize+1];
602 strBuf[kTmpBufSize] = '\0';
603 std::size_t strBufIndex = 0;
604
605 String ret;
606
607 for (std::size_t s=0; s<dataSize; ++s)
608 {
609 charArray3[i++] = *(bytesToEncode++);
610
611 if (i == 3)
612 {
613 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
614 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
615 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
616 charArray4[3] = charArray3[2] & 0x3f;
617
618 for (i=0; i<4; ++i)
619 strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
620
621 if (strBufIndex >= kTmpBufSize-7)
622 {
623 strBuf[strBufIndex] = '\0';
624 strBufIndex = 0;
625 ret += strBuf;
626 }
627
628 i = 0;
629 }
630 }
631
632 if (i != 0)
633 {
634 for (j=i; j<3; ++j)
635 charArray3[j] = '\0';
636
637 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
638 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
639 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
640 charArray4[3] = charArray3[2] & 0x3f;
641
642 for (j=0; j<4 && i<3 && j<i+1; ++j)
643 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
644
645 for (; i++ < 3;)
646 strBuf[strBufIndex++] = '=';
647 }
648
649 if (strBufIndex != 0)
650 {
651 strBuf[strBufIndex] = '\0';
652 ret += strBuf;
653 }
654
655 return ret;
656 }
657
658 // -------------------------------------------------------------------
659 // public operators
660
operator const char*() const661 operator const char*() const noexcept
662 {
663 return fBuffer;
664 }
665
operator [](const std::size_t pos) const666 char operator[](const std::size_t pos) const noexcept
667 {
668 if (pos < fBufferLen)
669 return fBuffer[pos];
670
671 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
672
673 static char fallback;
674 fallback = '\0';
675 return fallback;
676 }
677
operator [](const std::size_t pos)678 char& operator[](const std::size_t pos) noexcept
679 {
680 if (pos < fBufferLen)
681 return fBuffer[pos];
682
683 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
684
685 static char fallback;
686 fallback = '\0';
687 return fallback;
688 }
689
operator ==(const char * const strBuf) const690 bool operator==(const char* const strBuf) const noexcept
691 {
692 return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
693 }
694
operator ==(const String & str) const695 bool operator==(const String& str) const noexcept
696 {
697 return operator==(str.fBuffer);
698 }
699
operator !=(const char * const strBuf) const700 bool operator!=(const char* const strBuf) const noexcept
701 {
702 return !operator==(strBuf);
703 }
704
operator !=(const String & str) const705 bool operator!=(const String& str) const noexcept
706 {
707 return !operator==(str.fBuffer);
708 }
709
operator =(const char * const strBuf)710 String& operator=(const char* const strBuf) noexcept
711 {
712 _dup(strBuf);
713
714 return *this;
715 }
716
operator =(const String & str)717 String& operator=(const String& str) noexcept
718 {
719 _dup(str.fBuffer);
720
721 return *this;
722 }
723
operator +=(const char * const strBuf)724 String& operator+=(const char* const strBuf) noexcept
725 {
726 if (strBuf == nullptr)
727 return *this;
728
729 const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
730 char newBuf[newBufSize];
731
732 std::strcpy(newBuf, fBuffer);
733 std::strcat(newBuf, strBuf);
734
735 _dup(newBuf, newBufSize-1);
736
737 return *this;
738 }
739
operator +=(const String & str)740 String& operator+=(const String& str) noexcept
741 {
742 return operator+=(str.fBuffer);
743 }
744
operator +(const char * const strBuf)745 String operator+(const char* const strBuf) noexcept
746 {
747 const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
748 char newBuf[newBufSize];
749
750 std::strcpy(newBuf, fBuffer);
751
752 if (strBuf != nullptr)
753 std::strcat(newBuf, strBuf);
754
755 return String(newBuf);
756 }
757
operator +(const String & str)758 String operator+(const String& str) noexcept
759 {
760 return operator+(str.fBuffer);
761 }
762
763 // -------------------------------------------------------------------
764
765 private:
766 char* fBuffer; // the actual string buffer
767 std::size_t fBufferLen; // string length
768
769 /*
770 * Static null string.
771 * Prevents allocation for new and/or empty strings.
772 */
_null()773 static char* _null() noexcept
774 {
775 static char sNull = '\0';
776 return &sNull;
777 }
778
779 /*
780 * Helper function.
781 * Called whenever the string needs to be allocated.
782 *
783 * Notes:
784 * - Allocates string only if 'strBuf' is not null and new string contents are different
785 * - If 'strBuf' is null, 'size' must be 0
786 */
_dup(const char * const strBuf,const std::size_t size=0)787 void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
788 {
789 if (strBuf != nullptr)
790 {
791 // don't recreate string if contents match
792 if (std::strcmp(fBuffer, strBuf) == 0)
793 return;
794
795 if (fBuffer != _null())
796 std::free(fBuffer);
797
798 fBufferLen = (size > 0) ? size : std::strlen(strBuf);
799 fBuffer = (char*)std::malloc(fBufferLen+1);
800
801 if (fBuffer == nullptr)
802 {
803 fBuffer = _null();
804 fBufferLen = 0;
805 return;
806 }
807
808 std::strcpy(fBuffer, strBuf);
809
810 fBuffer[fBufferLen] = '\0';
811 }
812 else
813 {
814 DISTRHO_SAFE_ASSERT(size == 0);
815
816 // don't recreate null string
817 if (fBuffer == _null())
818 return;
819
820 DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
821 std::free(fBuffer);
822
823 fBuffer = _null();
824 fBufferLen = 0;
825 }
826 }
827
828 DISTRHO_PREVENT_HEAP_ALLOCATION
829 };
830
831 // -----------------------------------------------------------------------
832
833 static inline
operator +(const String & strBefore,const char * const strBufAfter)834 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
835 {
836 const char* const strBufBefore = strBefore.buffer();
837 const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
838 char newBuf[newBufSize];
839
840 std::strcpy(newBuf, strBufBefore);
841 std::strcat(newBuf, strBufAfter);
842
843 return String(newBuf);
844 }
845
846 static inline
operator +(const char * const strBufBefore,const String & strAfter)847 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
848 {
849 const char* const strBufAfter = strAfter.buffer();
850 const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
851 char newBuf[newBufSize];
852
853 std::strcpy(newBuf, strBufBefore);
854 std::strcat(newBuf, strBufAfter);
855
856 return String(newBuf);
857 }
858
859 // -----------------------------------------------------------------------
860
861 END_NAMESPACE_DISTRHO
862
863 #endif // DISTRHO_STRING_HPP_INCLUDED
864