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.

964 lines
24KB

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