DISTRHO Plugin Framework
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.

864 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, "%.12g", static_cast<double>(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, "%.24g", 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. * Get and release the string buffer, while also clearing this string.
  477. * Result must be freed.
  478. */
  479. char* getAndReleaseBuffer() noexcept
  480. {
  481. char* const ret = fBuffer;
  482. fBuffer = _null();
  483. fBufferLen = 0;
  484. return ret;
  485. }
  486. // -------------------------------------------------------------------
  487. // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
  488. // Copyright (C) 2004-2008 René Nyffenegger
  489. static String asBase64(const void* const data, const std::size_t dataSize)
  490. {
  491. static const char* const kBase64Chars =
  492. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  493. "abcdefghijklmnopqrstuvwxyz"
  494. "0123456789+/";
  495. const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U);
  496. const uchar* bytesToEncode((const uchar*)data);
  497. uint i=0, j=0;
  498. uint charArray3[3], charArray4[4];
  499. char strBuf[kTmpBufSize+1];
  500. strBuf[kTmpBufSize] = '\0';
  501. std::size_t strBufIndex = 0;
  502. String ret;
  503. for (std::size_t s=0; s<dataSize; ++s)
  504. {
  505. charArray3[i++] = *(bytesToEncode++);
  506. if (i == 3)
  507. {
  508. charArray4[0] = (charArray3[0] & 0xfc) >> 2;
  509. charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
  510. charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
  511. charArray4[3] = charArray3[2] & 0x3f;
  512. for (i=0; i<4; ++i)
  513. strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
  514. if (strBufIndex >= kTmpBufSize-7)
  515. {
  516. strBuf[strBufIndex] = '\0';
  517. strBufIndex = 0;
  518. ret += strBuf;
  519. }
  520. i = 0;
  521. }
  522. }
  523. if (i != 0)
  524. {
  525. for (j=i; j<3; ++j)
  526. charArray3[j] = '\0';
  527. charArray4[0] = (charArray3[0] & 0xfc) >> 2;
  528. charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
  529. charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
  530. charArray4[3] = charArray3[2] & 0x3f;
  531. for (j=0; j<4 && i<3 && j<i+1; ++j)
  532. strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
  533. for (; i++ < 3;)
  534. strBuf[strBufIndex++] = '=';
  535. }
  536. if (strBufIndex != 0)
  537. {
  538. strBuf[strBufIndex] = '\0';
  539. ret += strBuf;
  540. }
  541. return ret;
  542. }
  543. // -------------------------------------------------------------------
  544. // public operators
  545. operator const char*() const noexcept
  546. {
  547. return fBuffer;
  548. }
  549. char operator[](const std::size_t pos) const noexcept
  550. {
  551. if (pos < fBufferLen)
  552. return fBuffer[pos];
  553. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  554. static char fallback;
  555. fallback = '\0';
  556. return fallback;
  557. }
  558. char& operator[](const std::size_t pos) noexcept
  559. {
  560. if (pos < fBufferLen)
  561. return fBuffer[pos];
  562. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  563. static char fallback;
  564. fallback = '\0';
  565. return fallback;
  566. }
  567. bool operator==(const char* const strBuf) const noexcept
  568. {
  569. return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
  570. }
  571. bool operator==(const String& str) const noexcept
  572. {
  573. return operator==(str.fBuffer);
  574. }
  575. bool operator!=(const char* const strBuf) const noexcept
  576. {
  577. return !operator==(strBuf);
  578. }
  579. bool operator!=(const String& str) const noexcept
  580. {
  581. return !operator==(str.fBuffer);
  582. }
  583. String& operator=(const char* const strBuf) noexcept
  584. {
  585. _dup(strBuf);
  586. return *this;
  587. }
  588. String& operator=(const String& str) noexcept
  589. {
  590. _dup(str.fBuffer);
  591. return *this;
  592. }
  593. String& operator+=(const char* const strBuf) noexcept
  594. {
  595. if (strBuf == nullptr)
  596. return *this;
  597. const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
  598. char newBuf[newBufSize];
  599. std::strcpy(newBuf, fBuffer);
  600. std::strcat(newBuf, strBuf);
  601. _dup(newBuf, newBufSize-1);
  602. return *this;
  603. }
  604. String& operator+=(const String& str) noexcept
  605. {
  606. return operator+=(str.fBuffer);
  607. }
  608. String operator+(const char* const strBuf) noexcept
  609. {
  610. const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
  611. char newBuf[newBufSize];
  612. std::strcpy(newBuf, fBuffer);
  613. if (strBuf != nullptr)
  614. std::strcat(newBuf, strBuf);
  615. return String(newBuf);
  616. }
  617. String operator+(const String& str) noexcept
  618. {
  619. return operator+(str.fBuffer);
  620. }
  621. // -------------------------------------------------------------------
  622. private:
  623. char* fBuffer; // the actual string buffer
  624. std::size_t fBufferLen; // string length
  625. /*
  626. * Static null string.
  627. * Prevents allocation for new and/or empty strings.
  628. */
  629. static char* _null() noexcept
  630. {
  631. static char sNull = '\0';
  632. return &sNull;
  633. }
  634. /*
  635. * Helper function.
  636. * Called whenever the string needs to be allocated.
  637. *
  638. * Notes:
  639. * - Allocates string only if 'strBuf' is not null and new string contents are different
  640. * - If 'strBuf' is null, 'size' must be 0
  641. */
  642. void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
  643. {
  644. if (strBuf != nullptr)
  645. {
  646. // don't recreate string if contents match
  647. if (std::strcmp(fBuffer, strBuf) == 0)
  648. return;
  649. if (fBuffer != _null())
  650. std::free(fBuffer);
  651. fBufferLen = (size > 0) ? size : std::strlen(strBuf);
  652. fBuffer = (char*)std::malloc(fBufferLen+1);
  653. if (fBuffer == nullptr)
  654. {
  655. fBuffer = _null();
  656. fBufferLen = 0;
  657. return;
  658. }
  659. std::strcpy(fBuffer, strBuf);
  660. fBuffer[fBufferLen] = '\0';
  661. }
  662. else
  663. {
  664. DISTRHO_SAFE_ASSERT(size == 0);
  665. // don't recreate null string
  666. if (fBuffer == _null())
  667. return;
  668. DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
  669. std::free(fBuffer);
  670. fBuffer = _null();
  671. fBufferLen = 0;
  672. }
  673. }
  674. DISTRHO_PREVENT_HEAP_ALLOCATION
  675. };
  676. // -----------------------------------------------------------------------
  677. static inline
  678. String operator+(const String& strBefore, const char* const strBufAfter) noexcept
  679. {
  680. const char* const strBufBefore = strBefore.buffer();
  681. const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
  682. char newBuf[newBufSize];
  683. std::strcpy(newBuf, strBufBefore);
  684. std::strcat(newBuf, strBufAfter);
  685. return String(newBuf);
  686. }
  687. static inline
  688. String operator+(const char* const strBufBefore, const String& strAfter) noexcept
  689. {
  690. const char* const strBufAfter = strAfter.buffer();
  691. const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
  692. char newBuf[newBufSize];
  693. std::strcpy(newBuf, strBufBefore);
  694. std::strcat(newBuf, strBufAfter);
  695. return String(newBuf);
  696. }
  697. // -----------------------------------------------------------------------
  698. END_NAMESPACE_DISTRHO
  699. #endif // DISTRHO_STRING_HPP_INCLUDED