1 /*
2  * Carla String
3  * Copyright (C) 2013-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #ifndef CARLA_STRING_HPP_INCLUDED
19 #define CARLA_STRING_HPP_INCLUDED
20 
21 #include "CarlaMathUtils.hpp"
22 #include "CarlaScopeUtils.hpp"
23 
24 #include <algorithm>
25 
26 // -----------------------------------------------------------------------
27 // CarlaString class
28 
29 class CarlaString
30 {
31 public:
32     // -------------------------------------------------------------------
33     // constructors (no explicit conversions allowed)
34 
35     /*
36      * Empty string.
37      */
CarlaString()38     explicit CarlaString() noexcept
39         : fBuffer(_null()),
40           fBufferLen(0),
41           fBufferAlloc(false) {}
42 
43     /*
44      * Simple character.
45      */
CarlaString(const char c)46     explicit CarlaString(const char c) noexcept
47         : fBuffer(_null()),
48           fBufferLen(0),
49           fBufferAlloc(false)
50     {
51         char ch[2];
52         ch[0] = c;
53         ch[1] = '\0';
54 
55         _dup(ch);
56     }
57 
58     /*
59      * Simple char string.
60      */
CarlaString(char * const strBuf,const bool reallocData=true)61     explicit CarlaString(char* const strBuf, const bool reallocData = true) noexcept
62         : fBuffer(_null()),
63           fBufferLen(0),
64           fBufferAlloc(false)
65     {
66         if (reallocData || strBuf == nullptr)
67         {
68             _dup(strBuf);
69         }
70         else
71         {
72             fBuffer      = strBuf;
73             fBufferLen   = std::strlen(strBuf);
74             fBufferAlloc = true;
75         }
76     }
77 
78     /*
79      * Simple const char string.
80      */
CarlaString(const char * const strBuf)81     explicit CarlaString(const char* const strBuf) noexcept
82         : fBuffer(_null()),
83           fBufferLen(0),
84           fBufferAlloc(false)
85     {
86         _dup(strBuf);
87     }
88 
89     /*
90      * Integer.
91      */
CarlaString(const int value)92     explicit CarlaString(const int value) noexcept
93         : fBuffer(_null()),
94           fBufferLen(0),
95           fBufferAlloc(false)
96     {
97         char strBuf[0xff+1];
98         std::snprintf(strBuf, 0xff, "%d", value);
99         strBuf[0xff] = '\0';
100 
101         _dup(strBuf);
102     }
103 
104     /*
105      * Unsigned integer, possibly in hexadecimal.
106      */
CarlaString(const unsigned int value,const bool hexadecimal=false)107     explicit CarlaString(const unsigned int value, const bool hexadecimal = false) noexcept
108         : fBuffer(_null()),
109           fBufferLen(0),
110           fBufferAlloc(false)
111     {
112         char strBuf[0xff+1];
113         std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
114         strBuf[0xff] = '\0';
115 
116         _dup(strBuf);
117     }
118 
119     /*
120      * Long integer.
121      */
CarlaString(const long value)122     explicit CarlaString(const long value) noexcept
123         : fBuffer(_null()),
124           fBufferLen(0),
125           fBufferAlloc(false)
126     {
127         char strBuf[0xff+1];
128         std::snprintf(strBuf, 0xff, "%ld", value);
129         strBuf[0xff] = '\0';
130 
131         _dup(strBuf);
132     }
133 
134     /*
135      * Long unsigned integer, possibly hexadecimal.
136      */
CarlaString(const unsigned long value,const bool hexadecimal=false)137     explicit CarlaString(const unsigned long value, const bool hexadecimal = false) noexcept
138         : fBuffer(_null()),
139           fBufferLen(0),
140           fBufferAlloc(false)
141     {
142         char strBuf[0xff+1];
143         std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
144         strBuf[0xff] = '\0';
145 
146         _dup(strBuf);
147     }
148 
149     /*
150      * Long long integer.
151      */
CarlaString(const long long value)152     explicit CarlaString(const long long value) noexcept
153         : fBuffer(_null()),
154           fBufferLen(0),
155           fBufferAlloc(false)
156     {
157         char strBuf[0xff+1];
158         std::snprintf(strBuf, 0xff, "%lld", value);
159         strBuf[0xff] = '\0';
160 
161         _dup(strBuf);
162     }
163 
164     /*
165      * Long long unsigned integer, possibly hexadecimal.
166      */
CarlaString(const unsigned long long value,const bool hexadecimal=false)167     explicit CarlaString(const unsigned long long value, const bool hexadecimal = false) noexcept
168         : fBuffer(_null()),
169           fBufferLen(0),
170           fBufferAlloc(false)
171     {
172         char strBuf[0xff+1];
173         std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
174         strBuf[0xff] = '\0';
175 
176         _dup(strBuf);
177     }
178 
179     /*
180      * Single-precision floating point number.
181      */
CarlaString(const float value)182     explicit CarlaString(const float value) noexcept
183         : fBuffer(_null()),
184           fBufferLen(0),
185           fBufferAlloc(false)
186     {
187         char strBuf[0xff+1];
188 
189         {
190             const CarlaScopedLocale csl;
191             std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
192         }
193 
194         strBuf[0xff] = '\0';
195 
196         _dup(strBuf);
197     }
198 
199     /*
200      * Double-precision floating point number.
201      */
CarlaString(const double value)202     explicit CarlaString(const double value) noexcept
203         : fBuffer(_null()),
204           fBufferLen(0),
205           fBufferAlloc(false)
206     {
207         char strBuf[0xff+1];
208 
209         {
210             const CarlaScopedLocale csl;
211             std::snprintf(strBuf, 0xff, "%.24g", value);
212         }
213 
214         strBuf[0xff] = '\0';
215 
216         _dup(strBuf);
217     }
218 
219     // -------------------------------------------------------------------
220     // non-explicit constructor
221 
222     /*
223      * Create string from another string.
224      */
CarlaString(const CarlaString & str)225     CarlaString(const CarlaString& str) noexcept
226         : fBuffer(_null()),
227           fBufferLen(0),
228           fBufferAlloc(false)
229     {
230         _dup(str.fBuffer);
231     }
232 
233     // -------------------------------------------------------------------
234     // destructor
235 
236     /*
237      * Destructor.
238      */
~CarlaString()239     ~CarlaString() noexcept
240     {
241         CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
242 
243         if (fBufferAlloc)
244             std::free(fBuffer);
245 
246         fBuffer      = nullptr;
247         fBufferLen   = 0;
248         fBufferAlloc = false;
249     }
250 
251     // -------------------------------------------------------------------
252     // public methods
253 
254     /*
255      * Get length of the string.
256      */
length() const257     std::size_t length() const noexcept
258     {
259         return fBufferLen;
260     }
261 
262     /*
263      * Check if the string is empty.
264      */
isEmpty() const265     bool isEmpty() const noexcept
266     {
267         return (fBufferLen == 0);
268     }
269 
270     /*
271      * Check if the string is not empty.
272      */
isNotEmpty() const273     bool isNotEmpty() const noexcept
274     {
275         return (fBufferLen != 0);
276     }
277 
278     /*
279      * Check if the string contains a specific character, case-sensitive.
280      */
contains(const char c) const281     bool contains(const char c) const noexcept
282     {
283         for (std::size_t i=0; i<fBufferLen; ++i)
284         {
285             if (fBuffer[i] == c)
286                 return true;
287         }
288 
289         return false;
290     }
291 
292     /*
293      * Check if the string contains another string, optionally ignoring case.
294      */
contains(const char * const strBuf,const bool ignoreCase=false) const295     bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
296     {
297         CARLA_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
298 
299         if (ignoreCase)
300         {
301 #ifdef __USE_GNU
302             return (strcasestr(fBuffer, strBuf) != nullptr);
303 #else
304             CarlaString tmp1(fBuffer), tmp2(strBuf);
305 
306             // memory allocation failed or empty string(s)
307             if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
308                 return false;
309 
310             tmp1.toLower();
311             tmp2.toLower();
312             return (std::strstr(tmp1, tmp2) != nullptr);
313 #endif
314         }
315 
316         return (std::strstr(fBuffer, strBuf) != nullptr);
317     }
318 
319     /*
320      * Check if character at 'pos' is a digit.
321      */
isDigit(const std::size_t pos) const322     bool isDigit(const std::size_t pos) const noexcept
323     {
324         CARLA_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
325 
326         return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
327     }
328 
329     /*
330      * Check if the string starts with the character 'c'.
331      */
startsWith(const char c) const332     bool startsWith(const char c) const noexcept
333     {
334         CARLA_SAFE_ASSERT_RETURN(c != '\0', false);
335 
336         return (fBufferLen > 0 && fBuffer[0] == c);
337     }
338 
339     /*
340      * Check if the string starts with the string 'prefix'.
341      */
startsWith(const char * const prefix) const342     bool startsWith(const char* const prefix) const noexcept
343     {
344         CARLA_SAFE_ASSERT_RETURN(prefix != nullptr, false);
345 
346         const std::size_t prefixLen(std::strlen(prefix));
347 
348         if (fBufferLen < prefixLen)
349             return false;
350 
351         return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
352     }
353 
354     /*
355      * Check if the string ends with the character 'c'.
356      */
endsWith(const char c) const357     bool endsWith(const char c) const noexcept
358     {
359         CARLA_SAFE_ASSERT_RETURN(c != '\0', false);
360 
361         return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
362     }
363 
364     /*
365      * Check if the string ends with the string 'suffix'.
366      */
endsWith(const char * const suffix) const367     bool endsWith(const char* const suffix) const noexcept
368     {
369         CARLA_SAFE_ASSERT_RETURN(suffix != nullptr, false);
370 
371         const std::size_t suffixLen(std::strlen(suffix));
372 
373         if (fBufferLen < suffixLen)
374             return false;
375 
376         return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
377     }
378 
379     /*
380      * Find the first occurrence of character 'c' in the string.
381      * Returns "length()" if the character is not found.
382      */
find(const char c,bool * const found=nullptr) const383     std::size_t find(const char c, bool* const found = nullptr) const noexcept
384     {
385         if (fBufferLen == 0 || c == '\0')
386         {
387             if (found != nullptr)
388                 *found = false;
389             return fBufferLen;
390         }
391 
392         for (std::size_t i=0; i < fBufferLen; ++i)
393         {
394             if (fBuffer[i] == c)
395             {
396                 if (found != nullptr)
397                     *found = true;
398                 return i;
399             }
400         }
401 
402         if (found != nullptr)
403             *found = false;
404         return fBufferLen;
405     }
406 
407     /*
408      * Find the first occurrence of string 'strBuf' in the string.
409      * Returns "length()" if the string is not found.
410      */
find(const char * const strBuf,bool * const found=nullptr) const411     std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
412     {
413         if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
414         {
415             if (found != nullptr)
416                 *found = false;
417             return fBufferLen;
418         }
419 
420         if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
421         {
422             const ssize_t ret(subStrBuf - fBuffer);
423 
424             if (ret < 0)
425             {
426                 // should never happen!
427                 carla_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret));
428 
429                 if (found != nullptr)
430                     *found = false;
431                 return fBufferLen;
432             }
433 
434             if (found != nullptr)
435                 *found = true;
436             return static_cast<std::size_t>(ret);
437         }
438 
439         if (found != nullptr)
440             *found = false;
441         return fBufferLen;
442     }
443 
444     /*
445      * Find the last occurrence of character 'c' in the string.
446      * Returns "length()" if the character is not found.
447      */
rfind(const char c,bool * const found=nullptr) const448     std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
449     {
450         if (fBufferLen == 0 || c == '\0')
451         {
452             if (found != nullptr)
453                 *found = false;
454             return fBufferLen;
455         }
456 
457         for (std::size_t i=fBufferLen; i > 0; --i)
458         {
459             if (fBuffer[i-1] == c)
460             {
461                 if (found != nullptr)
462                     *found = true;
463                 return i-1;
464             }
465         }
466 
467         if (found != nullptr)
468             *found = false;
469         return fBufferLen;
470     }
471 
472     /*
473      * Find the last occurrence of string 'strBuf' in the string.
474      * Returns "length()" if the string is not found.
475      */
rfind(const char * const strBuf,bool * const found=nullptr) const476     std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
477     {
478         if (found != nullptr)
479             *found = false;
480 
481         if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
482             return fBufferLen;
483 
484         const std::size_t strBufLen(std::strlen(strBuf));
485 
486         std::size_t ret = fBufferLen;
487         const char* tmpBuf = fBuffer;
488 
489         for (std::size_t i=0; i < fBufferLen; ++i)
490         {
491             if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
492             {
493                 if (found != nullptr)
494                     *found = true;
495                 break;
496             }
497 
498             --ret;
499             ++tmpBuf;
500         }
501 
502         return fBufferLen-ret;
503     }
504 
505     /*
506      * Clear the string.
507      */
clear()508     void clear() noexcept
509     {
510         truncate(0);
511     }
512 
513     /*
514      * Replace all occurrences of character 'before' with character 'after'.
515      */
replace(const char before,const char after)516     CarlaString& replace(const char before, const char after) noexcept
517     {
518         CARLA_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this);
519 
520         for (std::size_t i=0; i < fBufferLen; ++i)
521         {
522             if (fBuffer[i] == before)
523                 fBuffer[i] = after;
524         }
525 
526         return *this;
527     }
528 
529     /*
530      * Truncate the string to size 'n'.
531      */
truncate(const std::size_t n)532     CarlaString& truncate(const std::size_t n) noexcept
533     {
534         if (n >= fBufferLen)
535             return *this;
536 
537         fBuffer[n] = '\0';
538         fBufferLen = n;
539 
540         return *this;
541     }
542 
543     /*
544      * Convert all non-basic characters to '_'.
545      */
toBasic()546     CarlaString& toBasic() noexcept
547     {
548         for (std::size_t i=0; i < fBufferLen; ++i)
549         {
550             if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
551                 continue;
552             if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
553                 continue;
554             if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
555                 continue;
556             if (fBuffer[i] == '_')
557                 continue;
558 
559             fBuffer[i] = '_';
560         }
561 
562         return *this;
563     }
564 
565     /*
566      * Convert all ascii characters to lowercase.
567      */
toLower()568     CarlaString& toLower() noexcept
569     {
570         static const char kCharDiff('a' - 'A');
571 
572         for (std::size_t i=0; i < fBufferLen; ++i)
573         {
574             if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
575                 fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
576         }
577 
578         return *this;
579     }
580 
581     /*
582      * Convert all ascii characters to uppercase.
583      */
toUpper()584     CarlaString& toUpper() noexcept
585     {
586         static const char kCharDiff('a' - 'A');
587 
588         for (std::size_t i=0; i < fBufferLen; ++i)
589         {
590             if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
591                 fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
592         }
593 
594         return *this;
595     }
596 
597     /*
598      * Direct access to the string buffer (read-only).
599      */
buffer() const600     const char* buffer() const noexcept
601     {
602         return fBuffer;
603     }
604 
605     /*
606      * Return a duplicate string buffer.
607      * May throw.
608      */
dup() const609     const char* dup() const
610     {
611         return carla_strdup(fBuffer);
612     }
613 
614     /*
615      * Return a duplicate string buffer or null.
616      */
dupSafe() const617     const char* dupSafe() const noexcept
618     {
619         return carla_strdup_safe(fBuffer);
620     }
621 
622     /*
623      * Release the buffer pointer while clearing this string.
624      * This allows to keep a pointer to the buffer after this object is deleted.
625      */
releaseBufferPointer()626     char* releaseBufferPointer() noexcept
627     {
628         char* ret = fBufferLen > 0 ? fBuffer : nullptr;
629         fBuffer = _null();
630         fBufferLen = 0;
631         fBufferAlloc = false;
632         return ret;
633     }
634 
635     // -------------------------------------------------------------------
636     // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
637     // Copyright (C) 2004-2008 René Nyffenegger
638 
asBase64(const void * const data,const std::size_t dataSize)639     static CarlaString asBase64(const void* const data, const std::size_t dataSize)
640     {
641         static const char* const kBase64Chars =
642             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
643             "abcdefghijklmnopqrstuvwxyz"
644             "0123456789+/";
645 
646         const std::size_t kTmpBufSize = std::min(carla_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
647 
648         const uchar* bytesToEncode((const uchar*)data);
649 
650         uint i=0, j=0;
651         uint charArray3[3], charArray4[4];
652 
653         char strBuf[kTmpBufSize+1];
654         strBuf[kTmpBufSize] = '\0';
655         std::size_t strBufIndex = 0;
656 
657         CarlaString ret;
658 
659         for (std::size_t s=0; s<dataSize; ++s)
660         {
661             charArray3[i++] = *(bytesToEncode++);
662 
663             if (i == 3)
664             {
665                 charArray4[0] =  (charArray3[0] & 0xfc) >> 2;
666                 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
667                 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
668                 charArray4[3] =   charArray3[2] & 0x3f;
669 
670                 for (i=0; i<4; ++i)
671                     strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
672 
673                 if (strBufIndex >= kTmpBufSize-7)
674                 {
675                     strBuf[strBufIndex] = '\0';
676                     strBufIndex = 0;
677                     ret += strBuf;
678                 }
679 
680                 i = 0;
681             }
682         }
683 
684         if (i != 0)
685         {
686             for (j=i; j<3; ++j)
687               charArray3[j] = '\0';
688 
689             charArray4[0] =  (charArray3[0] & 0xfc) >> 2;
690             charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
691             charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
692             charArray4[3] =   charArray3[2] & 0x3f;
693 
694             for (j=0; j<4 && i<3 && j<i+1; ++j)
695                 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
696 
697             for (; i++ < 3;)
698                 strBuf[strBufIndex++] = '=';
699         }
700 
701         if (strBufIndex != 0)
702         {
703             strBuf[strBufIndex] = '\0';
704             ret += strBuf;
705         }
706 
707         return ret;
708     }
709 
710     // -------------------------------------------------------------------
711     // public operators
712 
operator const char*() const713     operator const char*() const noexcept
714     {
715         return fBuffer;
716     }
717 
operator [](const std::size_t pos) const718     char operator[](const std::size_t pos) const noexcept
719     {
720         if (pos < fBufferLen)
721             return fBuffer[pos];
722 
723         carla_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
724 
725         static char fallback;
726         fallback = '\0';
727         return fallback;
728     }
729 
operator [](const std::size_t pos)730     char& operator[](const std::size_t pos) noexcept
731     {
732         if (pos < fBufferLen)
733             return fBuffer[pos];
734 
735         carla_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
736 
737         static char fallback;
738         fallback = '\0';
739         return fallback;
740     }
741 
operator ==(const char * const strBuf) const742     bool operator==(const char* const strBuf) const noexcept
743     {
744         return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
745     }
746 
operator ==(const CarlaString & str) const747     bool operator==(const CarlaString& str) const noexcept
748     {
749         return operator==(str.fBuffer);
750     }
751 
operator !=(const char * const strBuf) const752     bool operator!=(const char* const strBuf) const noexcept
753     {
754         return !operator==(strBuf);
755     }
756 
operator !=(const CarlaString & str) const757     bool operator!=(const CarlaString& str) const noexcept
758     {
759         return !operator==(str.fBuffer);
760     }
761 
operator =(const char * const strBuf)762     CarlaString& operator=(const char* const strBuf) noexcept
763     {
764         _dup(strBuf);
765 
766         return *this;
767     }
768 
operator =(const CarlaString & str)769     CarlaString& operator=(const CarlaString& str) noexcept
770     {
771         _dup(str.fBuffer);
772 
773         return *this;
774     }
775 
operator +=(const char * const strBuf)776     CarlaString& operator+=(const char* const strBuf) noexcept
777     {
778         if (strBuf == nullptr || strBuf[0] == '\0')
779             return *this;
780 
781         const std::size_t strBufLen = std::strlen(strBuf);
782 
783         // for empty strings, we can just take the appended string as our entire data
784         if (isEmpty())
785         {
786             _dup(strBuf, strBufLen);
787             return *this;
788         }
789 
790         // we have some data ourselves, reallocate to add the new stuff
791         char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
792         CARLA_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
793 
794         std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
795 
796         fBuffer = newBuf;
797         fBufferLen += strBufLen;
798 
799         return *this;
800     }
801 
operator +=(const CarlaString & str)802     CarlaString& operator+=(const CarlaString& str) noexcept
803     {
804         return operator+=(str.fBuffer);
805     }
806 
operator +(const char * const strBuf)807     CarlaString operator+(const char* const strBuf) noexcept
808     {
809         if (strBuf == nullptr || strBuf[0] == '\0')
810             return *this;
811         if (isEmpty())
812             return CarlaString(strBuf);
813 
814         const std::size_t strBufLen = std::strlen(strBuf);
815         const std::size_t newBufSize = fBufferLen + strBufLen;
816         char* const newBuf = (char*)malloc(newBufSize + 1);
817         CARLA_SAFE_ASSERT_RETURN(newBuf != nullptr, CarlaString());
818 
819         std::memcpy(newBuf, fBuffer, fBufferLen);
820         std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
821 
822         return CarlaString(newBuf);
823     }
824 
operator +(const CarlaString & str)825     CarlaString operator+(const CarlaString& str) noexcept
826     {
827         return operator+(str.fBuffer);
828     }
829 
830     // -------------------------------------------------------------------
831 
832 private:
833     char*       fBuffer;      // the actual string buffer
834     std::size_t fBufferLen;   // string length
835     bool        fBufferAlloc; // wherever the buffer is allocated, not using _null()
836 
837     /*
838      * Static null string.
839      * Prevents allocation for new and/or empty strings.
840      */
_null()841     static char* _null() noexcept
842     {
843         static char sNull = '\0';
844         return &sNull;
845     }
846 
847     /*
848      * Helper function.
849      * Called whenever the string needs to be allocated.
850      *
851      * Notes:
852      * - Allocates string only if 'strBuf' is not null and new string contents are different
853      * - If 'strBuf' is null, 'size' must be 0
854      */
_dup(const char * const strBuf,const std::size_t size=0)855     void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
856     {
857         if (strBuf != nullptr)
858         {
859             // don't recreate string if contents match
860             if (std::strcmp(fBuffer, strBuf) == 0)
861                 return;
862 
863             if (fBufferAlloc)
864                 std::free(fBuffer);
865 
866             fBufferLen = (size > 0) ? size : std::strlen(strBuf);
867             fBuffer    = (char*)std::malloc(fBufferLen+1);
868 
869             if (fBuffer == nullptr)
870             {
871                 fBuffer      = _null();
872                 fBufferLen   = 0;
873                 fBufferAlloc = false;
874                 return;
875             }
876 
877             fBufferAlloc = true;
878 
879             std::strcpy(fBuffer, strBuf);
880             fBuffer[fBufferLen] = '\0';
881         }
882         else
883         {
884             CARLA_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
885 
886             // don't recreate null string
887             if (! fBufferAlloc)
888                 return;
889 
890             CARLA_SAFE_ASSERT(fBuffer != nullptr);
891             std::free(fBuffer);
892 
893             fBuffer      = _null();
894             fBufferLen   = 0;
895             fBufferAlloc = false;
896         }
897     }
898 
899     CARLA_PREVENT_HEAP_ALLOCATION
900 };
901 
902 // -----------------------------------------------------------------------
903 
904 static inline
operator +(const CarlaString & strBefore,const char * const strBufAfter)905 CarlaString operator+(const CarlaString& strBefore, const char* const strBufAfter) noexcept
906 {
907     if (strBufAfter == nullptr || strBufAfter[0] == '\0')
908         return strBefore;
909     if (strBefore.isEmpty())
910         return CarlaString(strBufAfter);
911 
912     const std::size_t strBeforeLen = strBefore.length();
913     const std::size_t strBufAfterLen = std::strlen(strBufAfter);
914     const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
915     char* const newBuf = (char*)malloc(newBufSize + 1);
916     CARLA_SAFE_ASSERT_RETURN(newBuf != nullptr, CarlaString());
917 
918     std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
919     std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
920 
921     return CarlaString(newBuf, false);
922 }
923 
924 static inline
operator +(const char * const strBufBefore,const CarlaString & strAfter)925 CarlaString operator+(const char* const strBufBefore, const CarlaString& strAfter) noexcept
926 {
927     if (strAfter.isEmpty())
928         return CarlaString(strBufBefore);
929     if (strBufBefore == nullptr || strBufBefore[0] == '\0')
930         return strAfter;
931 
932     const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
933     const std::size_t strAfterLen = strAfter.length();
934     const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
935     char* const newBuf = (char*)malloc(newBufSize + 1);
936     CARLA_SAFE_ASSERT_RETURN(newBuf != nullptr, CarlaString());
937 
938     std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
939     std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
940 
941     return CarlaString(newBuf, false);
942 }
943 
944 // -----------------------------------------------------------------------
945 
946 #endif // CARLA_STRING_HPP_INCLUDED
947