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, "%f", 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, "%g", 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     // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
573     // Copyright (C) 2004-2008 René Nyffenegger
574 
asBase64(const void * const data,const std::size_t dataSize)575     static String asBase64(const void* const data, const std::size_t dataSize)
576     {
577         static const char* const kBase64Chars =
578             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
579             "abcdefghijklmnopqrstuvwxyz"
580             "0123456789+/";
581 
582         const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U);
583 
584         const uchar* bytesToEncode((const uchar*)data);
585 
586         uint i=0, j=0;
587         uint charArray3[3], charArray4[4];
588 
589         char strBuf[kTmpBufSize+1];
590         strBuf[kTmpBufSize] = '\0';
591         std::size_t strBufIndex = 0;
592 
593         String ret;
594 
595         for (std::size_t s=0; s<dataSize; ++s)
596         {
597             charArray3[i++] = *(bytesToEncode++);
598 
599             if (i == 3)
600             {
601                 charArray4[0] =  (charArray3[0] & 0xfc) >> 2;
602                 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
603                 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
604                 charArray4[3] =   charArray3[2] & 0x3f;
605 
606                 for (i=0; i<4; ++i)
607                     strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
608 
609                 if (strBufIndex >= kTmpBufSize-7)
610                 {
611                     strBuf[strBufIndex] = '\0';
612                     strBufIndex = 0;
613                     ret += strBuf;
614                 }
615 
616                 i = 0;
617             }
618         }
619 
620         if (i != 0)
621         {
622             for (j=i; j<3; ++j)
623               charArray3[j] = '\0';
624 
625             charArray4[0] =  (charArray3[0] & 0xfc) >> 2;
626             charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
627             charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
628             charArray4[3] =   charArray3[2] & 0x3f;
629 
630             for (j=0; j<4 && i<3 && j<i+1; ++j)
631                 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
632 
633             for (; i++ < 3;)
634                 strBuf[strBufIndex++] = '=';
635         }
636 
637         if (strBufIndex != 0)
638         {
639             strBuf[strBufIndex] = '\0';
640             ret += strBuf;
641         }
642 
643         return ret;
644     }
645 
646     // -------------------------------------------------------------------
647     // public operators
648 
operator const char*() const649     operator const char*() const noexcept
650     {
651         return fBuffer;
652     }
653 
operator [](const std::size_t pos) const654     char operator[](const std::size_t pos) const noexcept
655     {
656         if (pos < fBufferLen)
657             return fBuffer[pos];
658 
659         d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
660 
661         static char fallback;
662         fallback = '\0';
663         return fallback;
664     }
665 
operator [](const std::size_t pos)666     char& operator[](const std::size_t pos) 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 char * const strBuf) const678     bool operator==(const char* const strBuf) const noexcept
679     {
680         return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
681     }
682 
operator ==(const String & str) const683     bool operator==(const String& str) const noexcept
684     {
685         return operator==(str.fBuffer);
686     }
687 
operator !=(const char * const strBuf) const688     bool operator!=(const char* const strBuf) const noexcept
689     {
690         return !operator==(strBuf);
691     }
692 
operator !=(const String & str) const693     bool operator!=(const String& str) const noexcept
694     {
695         return !operator==(str.fBuffer);
696     }
697 
operator =(const char * const strBuf)698     String& operator=(const char* const strBuf) noexcept
699     {
700         _dup(strBuf);
701 
702         return *this;
703     }
704 
operator =(const String & str)705     String& operator=(const String& str) noexcept
706     {
707         _dup(str.fBuffer);
708 
709         return *this;
710     }
711 
operator +=(const char * const strBuf)712     String& operator+=(const char* const strBuf) noexcept
713     {
714         if (strBuf == nullptr)
715             return *this;
716 
717         const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
718         char              newBuf[newBufSize];
719 
720         std::strcpy(newBuf, fBuffer);
721         std::strcat(newBuf, strBuf);
722 
723         _dup(newBuf, newBufSize-1);
724 
725         return *this;
726     }
727 
operator +=(const String & str)728     String& operator+=(const String& str) noexcept
729     {
730         return operator+=(str.fBuffer);
731     }
732 
operator +(const char * const strBuf)733     String operator+(const char* const strBuf) noexcept
734     {
735         const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
736         char              newBuf[newBufSize];
737 
738         std::strcpy(newBuf, fBuffer);
739 
740         if (strBuf != nullptr)
741             std::strcat(newBuf, strBuf);
742 
743         return String(newBuf);
744     }
745 
operator +(const String & str)746     String operator+(const String& str) noexcept
747     {
748         return operator+(str.fBuffer);
749     }
750 
751     // -------------------------------------------------------------------
752 
753 private:
754     char*       fBuffer;    // the actual string buffer
755     std::size_t fBufferLen; // string length
756 
757     /*
758      * Static null string.
759      * Prevents allocation for new and/or empty strings.
760      */
_null()761     static char* _null() noexcept
762     {
763         static char sNull = '\0';
764         return &sNull;
765     }
766 
767     /*
768      * Helper function.
769      * Called whenever the string needs to be allocated.
770      *
771      * Notes:
772      * - Allocates string only if 'strBuf' is not null and new string contents are different
773      * - If 'strBuf' is null, 'size' must be 0
774      */
_dup(const char * const strBuf,const std::size_t size=0)775     void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
776     {
777         if (strBuf != nullptr)
778         {
779             // don't recreate string if contents match
780             if (std::strcmp(fBuffer, strBuf) == 0)
781                 return;
782 
783             if (fBuffer != _null())
784                 std::free(fBuffer);
785 
786             fBufferLen = (size > 0) ? size : std::strlen(strBuf);
787             fBuffer    = (char*)std::malloc(fBufferLen+1);
788 
789             if (fBuffer == nullptr)
790             {
791                 fBuffer    = _null();
792                 fBufferLen = 0;
793                 return;
794             }
795 
796             std::strcpy(fBuffer, strBuf);
797 
798             fBuffer[fBufferLen] = '\0';
799         }
800         else
801         {
802             DISTRHO_SAFE_ASSERT(size == 0);
803 
804             // don't recreate null string
805             if (fBuffer == _null())
806                 return;
807 
808             DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
809             std::free(fBuffer);
810 
811             fBuffer    = _null();
812             fBufferLen = 0;
813         }
814     }
815 
816     DISTRHO_PREVENT_HEAP_ALLOCATION
817 };
818 
819 // -----------------------------------------------------------------------
820 
821 static inline
operator +(const String & strBefore,const char * const strBufAfter)822 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
823 {
824     const char* const strBufBefore = strBefore.buffer();
825     const std::size_t newBufSize   = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
826     char newBuf[newBufSize];
827 
828     std::strcpy(newBuf, strBufBefore);
829     std::strcat(newBuf, strBufAfter);
830 
831     return String(newBuf);
832 }
833 
834 static inline
operator +(const char * const strBufBefore,const String & strAfter)835 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
836 {
837     const char* const strBufAfter = strAfter.buffer();
838     const std::size_t newBufSize  = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
839     char newBuf[newBufSize];
840 
841     std::strcpy(newBuf, strBufBefore);
842     std::strcat(newBuf, strBufAfter);
843 
844     return String(newBuf);
845 }
846 
847 // -----------------------------------------------------------------------
848 
849 END_NAMESPACE_DISTRHO
850 
851 #endif // DISTRHO_STRING_HPP_INCLUDED
852