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