Collection of DPF-based plugins for packaging
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.

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