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