Cleanup
[carla.git] / source / modules / distrho / extra / String.hpp
blob02383958c9d401e6b047910fa3f89f06f9ec4a0f
1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
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.
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.
17 #ifndef DISTRHO_STRING_HPP_INCLUDED
18 #define DISTRHO_STRING_HPP_INCLUDED
20 #include "../DistrhoUtils.hpp"
21 #include "../extra/ScopedSafeLocale.hpp"
23 #include <algorithm>
25 START_NAMESPACE_DISTRHO
27 // -----------------------------------------------------------------------
28 // String class
30 class String
32 public:
33 // -------------------------------------------------------------------
34 // constructors (no explicit conversions allowed)
37 * Empty string.
39 explicit String() noexcept
40 : fBuffer(_null()),
41 fBufferLen(0),
42 fBufferAlloc(false) {}
45 * Simple character.
47 explicit String(const char c) noexcept
48 : fBuffer(_null()),
49 fBufferLen(0),
50 fBufferAlloc(false)
52 char ch[2];
53 ch[0] = c;
54 ch[1] = '\0';
56 _dup(ch);
60 * Simple char string.
62 explicit String(char* const strBuf, const bool reallocData = true) noexcept
63 : fBuffer(_null()),
64 fBufferLen(0),
65 fBufferAlloc(false)
67 if (reallocData || strBuf == nullptr)
69 _dup(strBuf);
71 else
73 fBuffer = strBuf;
74 fBufferLen = std::strlen(strBuf);
75 fBufferAlloc = true;
80 * Simple const char string.
82 explicit String(const char* const strBuf) noexcept
83 : fBuffer(_null()),
84 fBufferLen(0),
85 fBufferAlloc(false)
87 _dup(strBuf);
91 * Integer.
93 explicit String(const int value) noexcept
94 : fBuffer(_null()),
95 fBufferLen(0),
96 fBufferAlloc(false)
98 char strBuf[0xff+1];
99 std::snprintf(strBuf, 0xff, "%d", value);
100 strBuf[0xff] = '\0';
102 _dup(strBuf);
106 * Unsigned integer, possibly in hexadecimal.
108 explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
109 : fBuffer(_null()),
110 fBufferLen(0),
111 fBufferAlloc(false)
113 char strBuf[0xff+1];
114 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
115 strBuf[0xff] = '\0';
117 _dup(strBuf);
121 * Long integer.
123 explicit String(const long value) noexcept
124 : fBuffer(_null()),
125 fBufferLen(0),
126 fBufferAlloc(false)
128 char strBuf[0xff+1];
129 std::snprintf(strBuf, 0xff, "%ld", value);
130 strBuf[0xff] = '\0';
132 _dup(strBuf);
136 * Long unsigned integer, possibly hexadecimal.
138 explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
139 : fBuffer(_null()),
140 fBufferLen(0),
141 fBufferAlloc(false)
143 char strBuf[0xff+1];
144 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
145 strBuf[0xff] = '\0';
147 _dup(strBuf);
151 * Long long integer.
153 explicit String(const long long value) noexcept
154 : fBuffer(_null()),
155 fBufferLen(0),
156 fBufferAlloc(false)
158 char strBuf[0xff+1];
159 std::snprintf(strBuf, 0xff, "%lld", value);
160 strBuf[0xff] = '\0';
162 _dup(strBuf);
166 * Long long unsigned integer, possibly hexadecimal.
168 explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
169 : fBuffer(_null()),
170 fBufferLen(0),
171 fBufferAlloc(false)
173 char strBuf[0xff+1];
174 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
175 strBuf[0xff] = '\0';
177 _dup(strBuf);
181 * Single-precision floating point number.
183 explicit String(const float value) noexcept
184 : fBuffer(_null()),
185 fBufferLen(0),
186 fBufferAlloc(false)
188 char strBuf[0xff+1];
191 const ScopedSafeLocale ssl;
192 std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
195 strBuf[0xff] = '\0';
197 _dup(strBuf);
201 * Double-precision floating point number.
203 explicit String(const double value) noexcept
204 : fBuffer(_null()),
205 fBufferLen(0),
206 fBufferAlloc(false)
208 char strBuf[0xff+1];
211 const ScopedSafeLocale ssl;
212 std::snprintf(strBuf, 0xff, "%.24g", value);
215 strBuf[0xff] = '\0';
217 _dup(strBuf);
220 // -------------------------------------------------------------------
221 // non-explicit constructor
224 * Create string from another string.
226 String(const String& str) noexcept
227 : fBuffer(_null()),
228 fBufferLen(0),
229 fBufferAlloc(false)
231 _dup(str.fBuffer);
234 // -------------------------------------------------------------------
235 // destructor
238 * Destructor.
240 ~String() noexcept
242 DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
244 if (fBufferAlloc)
245 std::free(fBuffer);
247 fBuffer = nullptr;
248 fBufferLen = 0;
249 fBufferAlloc = false;
252 // -------------------------------------------------------------------
253 // public methods
256 * Get length of the string.
258 std::size_t length() const noexcept
260 return fBufferLen;
264 * Check if the string is empty.
266 bool isEmpty() const noexcept
268 return (fBufferLen == 0);
272 * Check if the string is not empty.
274 bool isNotEmpty() const noexcept
276 return (fBufferLen != 0);
280 * Check if the string contains a specific character, case-sensitive.
282 bool contains(const char c) const noexcept
284 for (std::size_t i=0; i<fBufferLen; ++i)
286 if (fBuffer[i] == c)
287 return true;
290 return false;
294 * Check if the string contains another string, optionally ignoring case.
296 bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
298 DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
300 if (ignoreCase)
302 #ifdef __USE_GNU
303 return (strcasestr(fBuffer, strBuf) != nullptr);
304 #else
305 String tmp1(fBuffer), tmp2(strBuf);
307 // memory allocation failed or empty string(s)
308 if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
309 return false;
311 tmp1.toLower();
312 tmp2.toLower();
313 return (std::strstr(tmp1, tmp2) != nullptr);
314 #endif
317 return (std::strstr(fBuffer, strBuf) != nullptr);
321 * Check if character at 'pos' is a digit.
323 bool isDigit(const std::size_t pos) const noexcept
325 DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
327 return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
331 * Check if the string starts with the character 'c'.
333 bool startsWith(const char c) const noexcept
335 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
337 return (fBufferLen > 0 && fBuffer[0] == c);
341 * Check if the string starts with the string 'prefix'.
343 bool startsWith(const char* const prefix) const noexcept
345 DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);
347 const std::size_t prefixLen(std::strlen(prefix));
349 if (fBufferLen < prefixLen)
350 return false;
352 return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
356 * Check if the string ends with the character 'c'.
358 bool endsWith(const char c) const noexcept
360 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
362 return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
366 * Check if the string ends with the string 'suffix'.
368 bool endsWith(const char* const suffix) const noexcept
370 DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);
372 const std::size_t suffixLen(std::strlen(suffix));
374 if (fBufferLen < suffixLen)
375 return false;
377 return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
381 * Find the first occurrence of character 'c' in the string.
382 * Returns "length()" if the character is not found.
384 std::size_t find(const char c, bool* const found = nullptr) const noexcept
386 if (fBufferLen == 0 || c == '\0')
388 if (found != nullptr)
389 *found = false;
390 return fBufferLen;
393 for (std::size_t i=0; i < fBufferLen; ++i)
395 if (fBuffer[i] == c)
397 if (found != nullptr)
398 *found = true;
399 return i;
403 if (found != nullptr)
404 *found = false;
405 return fBufferLen;
409 * Find the first occurrence of string 'strBuf' in the string.
410 * Returns "length()" if the string is not found.
412 std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
414 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
416 if (found != nullptr)
417 *found = false;
418 return fBufferLen;
421 if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
423 const ssize_t ret(subStrBuf - fBuffer);
425 if (ret < 0)
427 // should never happen!
428 d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret));
430 if (found != nullptr)
431 *found = false;
432 return fBufferLen;
435 if (found != nullptr)
436 *found = true;
437 return static_cast<std::size_t>(ret);
440 if (found != nullptr)
441 *found = false;
442 return fBufferLen;
446 * Find the last occurrence of character 'c' in the string.
447 * Returns "length()" if the character is not found.
449 std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
451 if (fBufferLen == 0 || c == '\0')
453 if (found != nullptr)
454 *found = false;
455 return fBufferLen;
458 for (std::size_t i=fBufferLen; i > 0; --i)
460 if (fBuffer[i-1] == c)
462 if (found != nullptr)
463 *found = true;
464 return i-1;
468 if (found != nullptr)
469 *found = false;
470 return fBufferLen;
474 * Find the last occurrence of string 'strBuf' in the string.
475 * Returns "length()" if the string is not found.
477 std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
479 if (found != nullptr)
480 *found = false;
482 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
483 return fBufferLen;
485 const std::size_t strBufLen(std::strlen(strBuf));
487 std::size_t ret = fBufferLen;
488 const char* tmpBuf = fBuffer;
490 for (std::size_t i=0; i < fBufferLen; ++i)
492 if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
494 if (found != nullptr)
495 *found = true;
496 break;
499 --ret;
500 ++tmpBuf;
503 return fBufferLen-ret;
507 * Clear the string.
509 void clear() noexcept
511 truncate(0);
515 * Replace all occurrences of character 'before' with character 'after'.
517 String& replace(const char before, const char after) noexcept
519 DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);
521 for (std::size_t i=0; i < fBufferLen; ++i)
523 if (fBuffer[i] == before)
524 fBuffer[i] = after;
527 return *this;
531 * Remove all occurrences of character 'c', shifting and truncating the string as necessary.
533 String& remove(const char c) noexcept
535 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this);
537 if (fBufferLen == 0)
538 return *this;
540 for (std::size_t i=0; i < fBufferLen; ++i)
542 if (fBuffer[i] == c)
544 --fBufferLen;
545 std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i);
549 fBuffer[fBufferLen] = '\0';
550 return *this;
554 * Truncate the string to size 'n'.
556 String& truncate(const std::size_t n) noexcept
558 if (n >= fBufferLen)
559 return *this;
561 fBuffer[n] = '\0';
562 fBufferLen = n;
564 return *this;
568 * Convert all non-basic characters to '_'.
570 String& toBasic() noexcept
572 for (std::size_t i=0; i < fBufferLen; ++i)
574 if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
575 continue;
576 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
577 continue;
578 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
579 continue;
580 if (fBuffer[i] == '_')
581 continue;
583 fBuffer[i] = '_';
586 return *this;
590 * Convert all ascii characters to lowercase.
592 String& toLower() noexcept
594 static const char kCharDiff('a' - 'A');
596 for (std::size_t i=0; i < fBufferLen; ++i)
598 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
599 fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
602 return *this;
606 * Convert all ascii characters to uppercase.
608 String& toUpper() noexcept
610 static const char kCharDiff('a' - 'A');
612 for (std::size_t i=0; i < fBufferLen; ++i)
614 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
615 fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
618 return *this;
622 * Create a new string where all non-basic characters are converted to '_'.
623 * @see toBasic()
625 String asBasic() const noexcept
627 String s(*this);
628 return s.toBasic();
632 * Create a new string where all ascii characters are converted lowercase.
633 * @see toLower()
635 String asLower() const noexcept
637 String s(*this);
638 return s.toLower();
642 * Create a new string where all ascii characters are converted to uppercase.
643 * @see toUpper()
645 String asUpper() const noexcept
647 String s(*this);
648 return s.toUpper();
652 * Direct access to the string buffer (read-only).
654 const char* buffer() const noexcept
656 return fBuffer;
660 * Get and release the string buffer, while also clearing this string.
661 * This allows to keep a pointer to the buffer after this object is deleted.
662 * Result must be freed.
664 char* getAndReleaseBuffer() noexcept
666 char* ret = fBufferLen > 0 ? fBuffer : nullptr;
667 fBuffer = _null();
668 fBufferLen = 0;
669 fBufferAlloc = false;
670 return ret;
673 // -------------------------------------------------------------------
674 // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
675 // Copyright (C) 2004-2008 René Nyffenegger
677 static String asBase64(const void* const data, const std::size_t dataSize)
679 static const char* const kBase64Chars =
680 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
681 "abcdefghijklmnopqrstuvwxyz"
682 "0123456789+/";
684 #ifndef _MSC_VER
685 const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
686 #else
687 constexpr std::size_t kTmpBufSize = 65536U;
688 #endif
690 const uchar* bytesToEncode((const uchar*)data);
692 uint i=0, j=0;
693 uint charArray3[3], charArray4[4];
695 char strBuf[kTmpBufSize + 1];
696 strBuf[kTmpBufSize] = '\0';
697 std::size_t strBufIndex = 0;
699 String ret;
701 for (std::size_t s=0; s<dataSize; ++s)
703 charArray3[i++] = *(bytesToEncode++);
705 if (i == 3)
707 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
708 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
709 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
710 charArray4[3] = charArray3[2] & 0x3f;
712 for (i=0; i<4; ++i)
713 strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
715 if (strBufIndex >= kTmpBufSize-7)
717 strBuf[strBufIndex] = '\0';
718 strBufIndex = 0;
719 ret += strBuf;
722 i = 0;
726 if (i != 0)
728 for (j=i; j<3; ++j)
729 charArray3[j] = '\0';
731 charArray4[0] = (charArray3[0] & 0xfc) >> 2;
732 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
733 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
734 charArray4[3] = charArray3[2] & 0x3f;
736 for (j=0; j<4 && i<3 && j<i+1; ++j)
737 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
739 for (; i++ < 3;)
740 strBuf[strBufIndex++] = '=';
743 if (strBufIndex != 0)
745 strBuf[strBufIndex] = '\0';
746 ret += strBuf;
749 return ret;
752 // -------------------------------------------------------------------
753 // public operators
755 operator const char*() const noexcept
757 return fBuffer;
760 char operator[](const std::size_t pos) const noexcept
762 if (pos < fBufferLen)
763 return fBuffer[pos];
765 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
767 static char fallback;
768 fallback = '\0';
769 return fallback;
772 char& operator[](const std::size_t pos) noexcept
774 if (pos < fBufferLen)
775 return fBuffer[pos];
777 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
779 static char fallback;
780 fallback = '\0';
781 return fallback;
784 bool operator==(const char* const strBuf) const noexcept
786 return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
789 bool operator==(const String& str) const noexcept
791 return operator==(str.fBuffer);
794 bool operator!=(const char* const strBuf) const noexcept
796 return !operator==(strBuf);
799 bool operator!=(const String& str) const noexcept
801 return !operator==(str.fBuffer);
804 String& operator=(const char* const strBuf) noexcept
806 _dup(strBuf);
808 return *this;
811 String& operator=(const String& str) noexcept
813 _dup(str.fBuffer);
815 return *this;
818 String& operator+=(const char* const strBuf) noexcept
820 if (strBuf == nullptr || strBuf[0] == '\0')
821 return *this;
823 const std::size_t strBufLen = std::strlen(strBuf);
825 // for empty strings, we can just take the appended string as our entire data
826 if (isEmpty())
828 _dup(strBuf, strBufLen);
829 return *this;
832 // we have some data ourselves, reallocate to add the new stuff
833 char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
834 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
836 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
838 fBuffer = newBuf;
839 fBufferLen += strBufLen;
841 return *this;
844 String& operator+=(const String& str) noexcept
846 return operator+=(str.fBuffer);
849 String operator+(const char* const strBuf) noexcept
851 if (strBuf == nullptr || strBuf[0] == '\0')
852 return *this;
853 if (isEmpty())
854 return String(strBuf);
856 const std::size_t strBufLen = std::strlen(strBuf);
857 const std::size_t newBufSize = fBufferLen + strBufLen;
858 char* const newBuf = (char*)malloc(newBufSize + 1);
859 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
861 std::memcpy(newBuf, fBuffer, fBufferLen);
862 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
864 return String(newBuf, false);
867 String operator+(const String& str) noexcept
869 return operator+(str.fBuffer);
872 // needed for std::map compatibility
873 bool operator<(const String& str) const noexcept
875 return std::strcmp(fBuffer, str.fBuffer) < 0;
878 // -------------------------------------------------------------------
880 private:
881 char* fBuffer; // the actual string buffer
882 std::size_t fBufferLen; // string length
883 bool fBufferAlloc; // wherever the buffer is allocated, not using _null()
886 * Static null string.
887 * Prevents allocation for new and/or empty strings.
889 static char* _null() noexcept
891 static char sNull = '\0';
892 return &sNull;
896 * Helper function.
897 * Called whenever the string needs to be allocated.
899 * Notes:
900 * - Allocates string only if 'strBuf' is not null and new string contents are different
901 * - If 'strBuf' is null, 'size' must be 0
903 void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
905 if (strBuf != nullptr)
907 // don't recreate string if contents match
908 if (std::strcmp(fBuffer, strBuf) == 0)
909 return;
911 if (fBufferAlloc)
912 std::free(fBuffer);
914 fBufferLen = (size > 0) ? size : std::strlen(strBuf);
915 fBuffer = (char*)std::malloc(fBufferLen+1);
917 if (fBuffer == nullptr)
919 fBuffer = _null();
920 fBufferLen = 0;
921 fBufferAlloc = false;
922 return;
925 fBufferAlloc = true;
927 std::strcpy(fBuffer, strBuf);
928 fBuffer[fBufferLen] = '\0';
930 else
932 DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
934 // don't recreate null string
935 if (! fBufferAlloc)
936 return;
938 DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
939 std::free(fBuffer);
941 fBuffer = _null();
942 fBufferLen = 0;
943 fBufferAlloc = false;
947 DISTRHO_PREVENT_HEAP_ALLOCATION
950 // -----------------------------------------------------------------------
952 static inline
953 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
955 if (strBufAfter == nullptr || strBufAfter[0] == '\0')
956 return strBefore;
957 if (strBefore.isEmpty())
958 return String(strBufAfter);
960 const std::size_t strBeforeLen = strBefore.length();
961 const std::size_t strBufAfterLen = std::strlen(strBufAfter);
962 const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
963 char* const newBuf = (char*)malloc(newBufSize + 1);
964 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
966 std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
967 std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
969 return String(newBuf, false);
972 static inline
973 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
975 if (strAfter.isEmpty())
976 return String(strBufBefore);
977 if (strBufBefore == nullptr || strBufBefore[0] == '\0')
978 return strAfter;
980 const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
981 const std::size_t strAfterLen = strAfter.length();
982 const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
983 char* const newBuf = (char*)malloc(newBufSize + 1);
984 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
986 std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
987 std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
989 return String(newBuf, false);
992 // -----------------------------------------------------------------------
994 END_NAMESPACE_DISTRHO
996 #endif // DISTRHO_STRING_HPP_INCLUDED