Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

852 lines
21KB

  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. #ifndef DISTRHO_STRING_HPP_INCLUDED
  17. #define DISTRHO_STRING_HPP_INCLUDED
  18. #include "../DistrhoUtils.hpp"
  19. #include <algorithm>
  20. START_NAMESPACE_DISTRHO
  21. // -----------------------------------------------------------------------
  22. // String class
  23. class String
  24. {
  25. public:
  26. // -------------------------------------------------------------------
  27. // constructors (no explicit conversions allowed)
  28. /*
  29. * Empty string.
  30. */
  31. explicit String() noexcept
  32. : fBuffer(_null()),
  33. fBufferLen(0) {}
  34. /*
  35. * Simple character.
  36. */
  37. explicit String(const char c) noexcept
  38. : fBuffer(_null()),
  39. fBufferLen(0)
  40. {
  41. char ch[2];
  42. ch[0] = c;
  43. ch[1] = '\0';
  44. _dup(ch);
  45. }
  46. /*
  47. * Simple char string.
  48. */
  49. explicit String(char* const strBuf, const bool copyData = true) noexcept
  50. : fBuffer(_null()),
  51. fBufferLen(0)
  52. {
  53. if (copyData || strBuf == nullptr)
  54. {
  55. _dup(strBuf);
  56. }
  57. else
  58. {
  59. fBuffer = strBuf;
  60. fBufferLen = std::strlen(strBuf);
  61. }
  62. }
  63. /*
  64. * Simple const char string.
  65. */
  66. explicit String(const char* const strBuf) noexcept
  67. : fBuffer(_null()),
  68. fBufferLen(0)
  69. {
  70. _dup(strBuf);
  71. }
  72. /*
  73. * Integer.
  74. */
  75. explicit String(const int value) noexcept
  76. : fBuffer(_null()),
  77. fBufferLen(0)
  78. {
  79. char strBuf[0xff+1];
  80. std::snprintf(strBuf, 0xff, "%d", value);
  81. strBuf[0xff] = '\0';
  82. _dup(strBuf);
  83. }
  84. /*
  85. * Unsigned integer, possibly in hexadecimal.
  86. */
  87. explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
  88. : fBuffer(_null()),
  89. fBufferLen(0)
  90. {
  91. char strBuf[0xff+1];
  92. std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
  93. strBuf[0xff] = '\0';
  94. _dup(strBuf);
  95. }
  96. /*
  97. * Long integer.
  98. */
  99. explicit String(const long value) noexcept
  100. : fBuffer(_null()),
  101. fBufferLen(0)
  102. {
  103. char strBuf[0xff+1];
  104. std::snprintf(strBuf, 0xff, "%ld", value);
  105. strBuf[0xff] = '\0';
  106. _dup(strBuf);
  107. }
  108. /*
  109. * Long unsigned integer, possibly hexadecimal.
  110. */
  111. explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
  112. : fBuffer(_null()),
  113. fBufferLen(0)
  114. {
  115. char strBuf[0xff+1];
  116. std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
  117. strBuf[0xff] = '\0';
  118. _dup(strBuf);
  119. }
  120. /*
  121. * Long long integer.
  122. */
  123. explicit String(const long long value) noexcept
  124. : fBuffer(_null()),
  125. fBufferLen(0)
  126. {
  127. char strBuf[0xff+1];
  128. std::snprintf(strBuf, 0xff, "%lld", value);
  129. strBuf[0xff] = '\0';
  130. _dup(strBuf);
  131. }
  132. /*
  133. * Long long unsigned integer, possibly hexadecimal.
  134. */
  135. explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
  136. : fBuffer(_null()),
  137. fBufferLen(0)
  138. {
  139. char strBuf[0xff+1];
  140. std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
  141. strBuf[0xff] = '\0';
  142. _dup(strBuf);
  143. }
  144. /*
  145. * Single-precision floating point number.
  146. */
  147. explicit String(const float value) noexcept
  148. : fBuffer(_null()),
  149. fBufferLen(0)
  150. {
  151. char strBuf[0xff+1];
  152. std::snprintf(strBuf, 0xff, "%f", value);
  153. strBuf[0xff] = '\0';
  154. _dup(strBuf);
  155. }
  156. /*
  157. * Double-precision floating point number.
  158. */
  159. explicit String(const double value) noexcept
  160. : fBuffer(_null()),
  161. fBufferLen(0)
  162. {
  163. char strBuf[0xff+1];
  164. std::snprintf(strBuf, 0xff, "%g", value);
  165. strBuf[0xff] = '\0';
  166. _dup(strBuf);
  167. }
  168. // -------------------------------------------------------------------
  169. // non-explicit constructor
  170. /*
  171. * Create string from another string.
  172. */
  173. String(const String& str) noexcept
  174. : fBuffer(_null()),
  175. fBufferLen(0)
  176. {
  177. _dup(str.fBuffer);
  178. }
  179. // -------------------------------------------------------------------
  180. // destructor
  181. /*
  182. * Destructor.
  183. */
  184. ~String() noexcept
  185. {
  186. DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  187. if (fBuffer == _null())
  188. return;
  189. std::free(fBuffer);
  190. fBuffer = nullptr;
  191. fBufferLen = 0;
  192. }
  193. // -------------------------------------------------------------------
  194. // public methods
  195. /*
  196. * Get length of the string.
  197. */
  198. std::size_t length() const noexcept
  199. {
  200. return fBufferLen;
  201. }
  202. /*
  203. * Check if the string is empty.
  204. */
  205. bool isEmpty() const noexcept
  206. {
  207. return (fBufferLen == 0);
  208. }
  209. /*
  210. * Check if the string is not empty.
  211. */
  212. bool isNotEmpty() const noexcept
  213. {
  214. return (fBufferLen != 0);
  215. }
  216. /*
  217. * Check if the string contains another string, optionally ignoring case.
  218. */
  219. bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
  220. {
  221. DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
  222. if (ignoreCase)
  223. {
  224. #ifdef __USE_GNU
  225. return (strcasestr(fBuffer, strBuf) != nullptr);
  226. #else
  227. String tmp1(fBuffer), tmp2(strBuf);
  228. // memory allocation failed or empty string(s)
  229. if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
  230. return false;
  231. tmp1.toLower();
  232. tmp2.toLower();
  233. return (std::strstr(tmp1, tmp2) != nullptr);
  234. #endif
  235. }
  236. return (std::strstr(fBuffer, strBuf) != nullptr);
  237. }
  238. /*
  239. * Check if character at 'pos' is a digit.
  240. */
  241. bool isDigit(const std::size_t pos) const noexcept
  242. {
  243. DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
  244. return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
  245. }
  246. /*
  247. * Check if the string starts with the character 'c'.
  248. */
  249. bool startsWith(const char c) const noexcept
  250. {
  251. DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
  252. return (fBufferLen > 0 && fBuffer[0] == c);
  253. }
  254. /*
  255. * Check if the string starts with the string 'prefix'.
  256. */
  257. bool startsWith(const char* const prefix) const noexcept
  258. {
  259. DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);
  260. const std::size_t prefixLen(std::strlen(prefix));
  261. if (fBufferLen < prefixLen)
  262. return false;
  263. return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
  264. }
  265. /*
  266. * Check if the string ends with the character 'c'.
  267. */
  268. bool endsWith(const char c) const noexcept
  269. {
  270. DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
  271. return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
  272. }
  273. /*
  274. * Check if the string ends with the string 'suffix'.
  275. */
  276. bool endsWith(const char* const suffix) const noexcept
  277. {
  278. DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);
  279. const std::size_t suffixLen(std::strlen(suffix));
  280. if (fBufferLen < suffixLen)
  281. return false;
  282. return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
  283. }
  284. /*
  285. * Find the first occurrence of character 'c' in the string.
  286. * Returns "length()" if the character is not found.
  287. */
  288. std::size_t find(const char c, bool* const found = nullptr) const noexcept
  289. {
  290. if (fBufferLen == 0 || c == '\0')
  291. {
  292. if (found != nullptr)
  293. *found = false;
  294. return fBufferLen;
  295. }
  296. for (std::size_t i=0; i < fBufferLen; ++i)
  297. {
  298. if (fBuffer[i] == c)
  299. {
  300. if (found != nullptr)
  301. *found = true;
  302. return i;
  303. }
  304. }
  305. if (found != nullptr)
  306. *found = false;
  307. return fBufferLen;
  308. }
  309. /*
  310. * Find the first occurrence of string 'strBuf' in the string.
  311. * Returns "length()" if the string is not found.
  312. */
  313. std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
  314. {
  315. if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
  316. {
  317. if (found != nullptr)
  318. *found = false;
  319. return fBufferLen;
  320. }
  321. if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
  322. {
  323. const ssize_t ret(subStrBuf - fBuffer);
  324. if (ret < 0)
  325. {
  326. // should never happen!
  327. d_safe_assert("ret >= 0", __FILE__, __LINE__);
  328. if (found != nullptr)
  329. *found = false;
  330. return fBufferLen;
  331. }
  332. if (found != nullptr)
  333. *found = true;
  334. return static_cast<std::size_t>(ret);
  335. }
  336. if (found != nullptr)
  337. *found = false;
  338. return fBufferLen;
  339. }
  340. /*
  341. * Find the last occurrence of character 'c' in the string.
  342. * Returns "length()" if the character is not found.
  343. */
  344. std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
  345. {
  346. if (fBufferLen == 0 || c == '\0')
  347. {
  348. if (found != nullptr)
  349. *found = false;
  350. return fBufferLen;
  351. }
  352. for (std::size_t i=fBufferLen; i > 0; --i)
  353. {
  354. if (fBuffer[i-1] == c)
  355. {
  356. if (found != nullptr)
  357. *found = true;
  358. return i-1;
  359. }
  360. }
  361. if (found != nullptr)
  362. *found = false;
  363. return fBufferLen;
  364. }
  365. /*
  366. * Find the last occurrence of string 'strBuf' in the string.
  367. * Returns "length()" if the string is not found.
  368. */
  369. std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
  370. {
  371. if (found != nullptr)
  372. *found = false;
  373. if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
  374. return fBufferLen;
  375. const std::size_t strBufLen(std::strlen(strBuf));
  376. std::size_t ret = fBufferLen;
  377. const char* tmpBuf = fBuffer;
  378. for (std::size_t i=0; i < fBufferLen; ++i)
  379. {
  380. if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
  381. {
  382. if (found != nullptr)
  383. *found = true;
  384. break;
  385. }
  386. --ret;
  387. ++tmpBuf;
  388. }
  389. return fBufferLen-ret;
  390. }
  391. /*
  392. * Clear the string.
  393. */
  394. void clear() noexcept
  395. {
  396. truncate(0);
  397. }
  398. /*
  399. * Replace all occurrences of character 'before' with character 'after'.
  400. */
  401. String& replace(const char before, const char after) noexcept
  402. {
  403. DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this);
  404. for (std::size_t i=0; i < fBufferLen; ++i)
  405. {
  406. if (fBuffer[i] == before)
  407. fBuffer[i] = after;
  408. }
  409. return *this;
  410. }
  411. /*
  412. * Truncate the string to size 'n'.
  413. */
  414. String& truncate(const std::size_t n) noexcept
  415. {
  416. if (n >= fBufferLen)
  417. return *this;
  418. for (std::size_t i=n; i < fBufferLen; ++i)
  419. fBuffer[i] = '\0';
  420. fBufferLen = n;
  421. return *this;
  422. }
  423. /*
  424. * Convert all non-basic characters to '_'.
  425. */
  426. String& toBasic() noexcept
  427. {
  428. for (std::size_t i=0; i < fBufferLen; ++i)
  429. {
  430. if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
  431. continue;
  432. if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
  433. continue;
  434. if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
  435. continue;
  436. if (fBuffer[i] == '_')
  437. continue;
  438. fBuffer[i] = '_';
  439. }
  440. return *this;
  441. }
  442. /*
  443. * Convert to all ascii characters to lowercase.
  444. */
  445. String& toLower() noexcept
  446. {
  447. static const char kCharDiff('a' - 'A');
  448. for (std::size_t i=0; i < fBufferLen; ++i)
  449. {
  450. if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
  451. fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
  452. }
  453. return *this;
  454. }
  455. /*
  456. * Convert to all ascii characters to uppercase.
  457. */
  458. String& toUpper() noexcept
  459. {
  460. static const char kCharDiff('a' - 'A');
  461. for (std::size_t i=0; i < fBufferLen; ++i)
  462. {
  463. if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
  464. fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
  465. }
  466. return *this;
  467. }
  468. /*
  469. * Direct access to the string buffer (read-only).
  470. */
  471. const char* buffer() const noexcept
  472. {
  473. return fBuffer;
  474. }
  475. // -------------------------------------------------------------------
  476. // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
  477. // Copyright (C) 2004-2008 René Nyffenegger
  478. static String asBase64(const void* const data, const std::size_t dataSize)
  479. {
  480. static const char* const kBase64Chars =
  481. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  482. "abcdefghijklmnopqrstuvwxyz"
  483. "0123456789+/";
  484. const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
  485. const uchar* bytesToEncode((const uchar*)data);
  486. uint i=0, j=0;
  487. uint charArray3[3], charArray4[4];
  488. char strBuf[kTmpBufSize+1];
  489. strBuf[kTmpBufSize] = '\0';
  490. std::size_t strBufIndex = 0;
  491. String ret;
  492. for (std::size_t s=0; s<dataSize; ++s)
  493. {
  494. charArray3[i++] = *(bytesToEncode++);
  495. if (i == 3)
  496. {
  497. charArray4[0] = (charArray3[0] & 0xfc) >> 2;
  498. charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
  499. charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
  500. charArray4[3] = charArray3[2] & 0x3f;
  501. for (i=0; i<4; ++i)
  502. strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
  503. if (strBufIndex >= kTmpBufSize-7)
  504. {
  505. strBuf[strBufIndex] = '\0';
  506. strBufIndex = 0;
  507. ret += strBuf;
  508. }
  509. i = 0;
  510. }
  511. }
  512. if (i != 0)
  513. {
  514. for (j=i; j<3; ++j)
  515. charArray3[j] = '\0';
  516. charArray4[0] = (charArray3[0] & 0xfc) >> 2;
  517. charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
  518. charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
  519. charArray4[3] = charArray3[2] & 0x3f;
  520. for (j=0; j<4 && i<3 && j<i+1; ++j)
  521. strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
  522. for (; i++ < 3;)
  523. strBuf[strBufIndex++] = '=';
  524. }
  525. if (strBufIndex != 0)
  526. {
  527. strBuf[strBufIndex] = '\0';
  528. ret += strBuf;
  529. }
  530. return ret;
  531. }
  532. // -------------------------------------------------------------------
  533. // public operators
  534. operator const char*() const noexcept
  535. {
  536. return fBuffer;
  537. }
  538. char operator[](const std::size_t pos) const noexcept
  539. {
  540. if (pos < fBufferLen)
  541. return fBuffer[pos];
  542. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  543. static char fallback;
  544. fallback = '\0';
  545. return fallback;
  546. }
  547. char& operator[](const std::size_t pos) noexcept
  548. {
  549. if (pos < fBufferLen)
  550. return fBuffer[pos];
  551. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  552. static char fallback;
  553. fallback = '\0';
  554. return fallback;
  555. }
  556. bool operator==(const char* const strBuf) const noexcept
  557. {
  558. return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
  559. }
  560. bool operator==(const String& str) const noexcept
  561. {
  562. return operator==(str.fBuffer);
  563. }
  564. bool operator!=(const char* const strBuf) const noexcept
  565. {
  566. return !operator==(strBuf);
  567. }
  568. bool operator!=(const String& str) const noexcept
  569. {
  570. return !operator==(str.fBuffer);
  571. }
  572. String& operator=(const char* const strBuf) noexcept
  573. {
  574. _dup(strBuf);
  575. return *this;
  576. }
  577. String& operator=(const String& str) noexcept
  578. {
  579. _dup(str.fBuffer);
  580. return *this;
  581. }
  582. String& operator+=(const char* const strBuf) noexcept
  583. {
  584. if (strBuf == nullptr)
  585. return *this;
  586. const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
  587. char newBuf[newBufSize];
  588. std::strcpy(newBuf, fBuffer);
  589. std::strcat(newBuf, strBuf);
  590. _dup(newBuf, newBufSize-1);
  591. return *this;
  592. }
  593. String& operator+=(const String& str) noexcept
  594. {
  595. return operator+=(str.fBuffer);
  596. }
  597. String operator+(const char* const strBuf) noexcept
  598. {
  599. const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
  600. char newBuf[newBufSize];
  601. std::strcpy(newBuf, fBuffer);
  602. if (strBuf != nullptr)
  603. std::strcat(newBuf, strBuf);
  604. return String(newBuf);
  605. }
  606. String operator+(const String& str) noexcept
  607. {
  608. return operator+(str.fBuffer);
  609. }
  610. // -------------------------------------------------------------------
  611. private:
  612. char* fBuffer; // the actual string buffer
  613. std::size_t fBufferLen; // string length
  614. /*
  615. * Static null string.
  616. * Prevents allocation for new and/or empty strings.
  617. */
  618. static char* _null() noexcept
  619. {
  620. static char sNull = '\0';
  621. return &sNull;
  622. }
  623. /*
  624. * Helper function.
  625. * Called whenever the string needs to be allocated.
  626. *
  627. * Notes:
  628. * - Allocates string only if 'strBuf' is not null and new string contents are different
  629. * - If 'strBuf' is null, 'size' must be 0
  630. */
  631. void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
  632. {
  633. if (strBuf != nullptr)
  634. {
  635. // don't recreate string if contents match
  636. if (std::strcmp(fBuffer, strBuf) == 0)
  637. return;
  638. if (fBuffer != _null())
  639. std::free(fBuffer);
  640. fBufferLen = (size > 0) ? size : std::strlen(strBuf);
  641. fBuffer = (char*)std::malloc(fBufferLen+1);
  642. if (fBuffer == nullptr)
  643. {
  644. fBuffer = _null();
  645. fBufferLen = 0;
  646. return;
  647. }
  648. std::strcpy(fBuffer, strBuf);
  649. fBuffer[fBufferLen] = '\0';
  650. }
  651. else
  652. {
  653. DISTRHO_SAFE_ASSERT(size == 0);
  654. // don't recreate null string
  655. if (fBuffer == _null())
  656. return;
  657. DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
  658. std::free(fBuffer);
  659. fBuffer = _null();
  660. fBufferLen = 0;
  661. }
  662. }
  663. DISTRHO_PREVENT_HEAP_ALLOCATION
  664. };
  665. // -----------------------------------------------------------------------
  666. static inline
  667. String operator+(const String& strBefore, const char* const strBufAfter) noexcept
  668. {
  669. const char* const strBufBefore = strBefore.buffer();
  670. const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
  671. char newBuf[newBufSize];
  672. std::strcpy(newBuf, strBufBefore);
  673. std::strcat(newBuf, strBufAfter);
  674. return String(newBuf);
  675. }
  676. static inline
  677. String operator+(const char* const strBufBefore, const String& strAfter) noexcept
  678. {
  679. const char* const strBufAfter = strAfter.buffer();
  680. const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
  681. char newBuf[newBufSize];
  682. std::strcpy(newBuf, strBufBefore);
  683. std::strcat(newBuf, strBufAfter);
  684. return String(newBuf);
  685. }
  686. // -----------------------------------------------------------------------
  687. END_NAMESPACE_DISTRHO
  688. #endif // DISTRHO_STRING_HPP_INCLUDED