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.

1193 lines
30KB

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