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.

1011 lines
25KB

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