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.

1197 lines
30KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2024 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. * std::string_view, not requiring a null terminator
  84. */
  85. explicit 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 constexpr 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 constexpr 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 constexpr 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. * Convert to a URL encoded string.
  641. */
  642. String& urlEncode() noexcept
  643. {
  644. static constexpr const char* const kHexChars = "0123456789ABCDEF";
  645. if (fBufferLen == 0)
  646. return *this;
  647. char* const newbuf = static_cast<char*>(std::malloc(fBufferLen * 3 + 1));
  648. DISTRHO_SAFE_ASSERT_RETURN(newbuf != nullptr, *this);
  649. char* newbufptr = newbuf;
  650. for (std::size_t i=0; i < fBufferLen; ++i)
  651. {
  652. const char c = fBuffer[i];
  653. switch (c)
  654. {
  655. case '!': // 33
  656. case '#': // 35
  657. case '$': // 36
  658. case '&': // 38
  659. case '\'': // 39
  660. case '(': // 40
  661. case ')': // 41
  662. case '*': // 42
  663. case '+': // 43
  664. case ',': // 44
  665. case '-': // 45
  666. case '.': // 46
  667. case '/': // 47
  668. case '0': // 48
  669. case '1': // 49
  670. case '2': // 50
  671. case '3': // 51
  672. case '4': // 52
  673. case '5': // 53
  674. case '6': // 54
  675. case '7': // 55
  676. case '8': // 56
  677. case '9': // 57
  678. case ':': // 58
  679. case ';': // 59
  680. case '=': // 61
  681. case '?': // 63
  682. case '@': // 64
  683. case 'A': // 65
  684. case 'B': // 66
  685. case 'C': // 67
  686. case 'D': // 68
  687. case 'E': // 69
  688. case 'F': // 70
  689. case 'G': // 71
  690. case 'H': // 72
  691. case 'I': // 73
  692. case 'J': // 74
  693. case 'K': // 75
  694. case 'L': // 76
  695. case 'M': // 77
  696. case 'N': // 78
  697. case 'O': // 79
  698. case 'P': // 80
  699. case 'Q': // 81
  700. case 'R': // 82
  701. case 'S': // 83
  702. case 'T': // 84
  703. case 'U': // 85
  704. case 'V': // 86
  705. case 'W': // 87
  706. case 'X': // 88
  707. case 'Y': // 89
  708. case 'Z': // 90
  709. case '[': // 91
  710. case ']': // 93
  711. case '_': // 95
  712. case 'a': // 97
  713. case 'b': // 98
  714. case 'c': // 99
  715. case 'd': // 100
  716. case 'e': // 101
  717. case 'f': // 102
  718. case 'g': // 103
  719. case 'h': // 104
  720. case 'i': // 105
  721. case 'j': // 106
  722. case 'k': // 107
  723. case 'l': // 108
  724. case 'm': // 109
  725. case 'n': // 110
  726. case 'o': // 111
  727. case 'p': // 112
  728. case 'q': // 113
  729. case 'r': // 114
  730. case 's': // 115
  731. case 't': // 116
  732. case 'u': // 117
  733. case 'v': // 118
  734. case 'w': // 119
  735. case 'x': // 120
  736. case 'y': // 121
  737. case 'z': // 122
  738. case '~': // 126
  739. *newbufptr++ = c;
  740. break;
  741. default:
  742. *newbufptr++ = '%';
  743. *newbufptr++ = kHexChars[(c >> 4) & 0xf];
  744. *newbufptr++ = kHexChars[c & 0xf];
  745. break;
  746. }
  747. }
  748. *newbufptr = '\0';
  749. std::free(fBuffer);
  750. fBuffer = newbuf;
  751. fBufferLen = std::strlen(newbuf);
  752. fBufferAlloc = true;
  753. return *this;
  754. }
  755. /*
  756. * Convert to a URL decoded string.
  757. */
  758. String& urlDecode() noexcept
  759. {
  760. if (fBufferLen == 0)
  761. return *this;
  762. char* const newbuf = static_cast<char*>(std::malloc(fBufferLen + 1));
  763. DISTRHO_SAFE_ASSERT_RETURN(newbuf != nullptr, *this);
  764. char* newbufptr = newbuf;
  765. for (std::size_t i=0; i < fBufferLen; ++i)
  766. {
  767. const char c = fBuffer[i];
  768. if (c == '%')
  769. {
  770. DISTRHO_SAFE_ASSERT_CONTINUE(fBufferLen > i + 2);
  771. char c1 = fBuffer[i + 1];
  772. char c2 = fBuffer[i + 2];
  773. i += 2;
  774. /**/ if (c1 >= '0' && c1 <= '9')
  775. c1 -= '0';
  776. else if (c1 >= 'A' && c1 <= 'Z')
  777. c1 -= 'A' - 10;
  778. else if (c1 >= 'a' && c1 <= 'z')
  779. c1 -= 'a' - 10;
  780. else
  781. continue;
  782. /**/ if (c2 >= '0' && c2 <= '9')
  783. c2 -= '0';
  784. else if (c2 >= 'A' && c2 <= 'Z')
  785. c2 -= 'A' - 10;
  786. else if (c2 >= 'a' && c2 <= 'z')
  787. c2 -= 'a' - 10;
  788. else
  789. continue;
  790. *newbufptr++ = c1 << 4 | c2;
  791. }
  792. else
  793. {
  794. *newbufptr++ = c;
  795. }
  796. }
  797. *newbufptr = '\0';
  798. std::free(fBuffer);
  799. fBuffer = newbuf;
  800. fBufferLen = std::strlen(newbuf);
  801. fBufferAlloc = true;
  802. return *this;
  803. }
  804. // -------------------------------------------------------------------
  805. // public operators
  806. operator const char*() const noexcept
  807. {
  808. return fBuffer;
  809. }
  810. char operator[](const std::size_t pos) const noexcept
  811. {
  812. if (pos < fBufferLen)
  813. return fBuffer[pos];
  814. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  815. static char fallback;
  816. fallback = '\0';
  817. return fallback;
  818. }
  819. char& operator[](const std::size_t pos) noexcept
  820. {
  821. if (pos < fBufferLen)
  822. return fBuffer[pos];
  823. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  824. static char fallback;
  825. fallback = '\0';
  826. return fallback;
  827. }
  828. bool operator==(const char* const strBuf) const noexcept
  829. {
  830. return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
  831. }
  832. bool operator==(const String& str) const noexcept
  833. {
  834. return operator==(str.fBuffer);
  835. }
  836. bool operator!=(const char* const strBuf) const noexcept
  837. {
  838. return !operator==(strBuf);
  839. }
  840. bool operator!=(const String& str) const noexcept
  841. {
  842. return !operator==(str.fBuffer);
  843. }
  844. String& operator=(const char* const strBuf) noexcept
  845. {
  846. _dup(strBuf);
  847. return *this;
  848. }
  849. String& operator=(const String& str) noexcept
  850. {
  851. _dup(str.fBuffer);
  852. return *this;
  853. }
  854. String& operator+=(const char* const strBuf) noexcept
  855. {
  856. if (strBuf == nullptr || strBuf[0] == '\0')
  857. return *this;
  858. const std::size_t strBufLen = std::strlen(strBuf);
  859. // for empty strings, we can just take the appended string as our entire data
  860. if (isEmpty())
  861. {
  862. _dup(strBuf, strBufLen);
  863. return *this;
  864. }
  865. // we have some data ourselves, reallocate to add the new stuff
  866. char* const newBuf = static_cast<char*>(std::realloc(fBuffer, fBufferLen + strBufLen + 1));
  867. DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
  868. std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
  869. fBuffer = newBuf;
  870. fBufferLen += strBufLen;
  871. return *this;
  872. }
  873. String& operator+=(const String& str) noexcept
  874. {
  875. return operator+=(str.fBuffer);
  876. }
  877. String operator+(const char* const strBuf) noexcept
  878. {
  879. if (strBuf == nullptr || strBuf[0] == '\0')
  880. return *this;
  881. if (isEmpty())
  882. return String(strBuf);
  883. const std::size_t strBufLen = std::strlen(strBuf);
  884. const std::size_t newBufSize = fBufferLen + strBufLen;
  885. char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1));
  886. DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
  887. std::memcpy(newBuf, fBuffer, fBufferLen);
  888. std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
  889. return String(newBuf, false);
  890. }
  891. String operator+(const String& str) noexcept
  892. {
  893. return operator+(str.fBuffer);
  894. }
  895. // needed for std::map compatibility
  896. bool operator<(const String& str) const noexcept
  897. {
  898. return std::strcmp(fBuffer, str.fBuffer) < 0;
  899. }
  900. // -------------------------------------------------------------------
  901. private:
  902. char* fBuffer; // the actual string buffer
  903. std::size_t fBufferLen; // string length
  904. bool fBufferAlloc; // wherever the buffer is allocated, not using _null()
  905. /*
  906. * Static null string.
  907. * Prevents allocation for new and/or empty strings.
  908. */
  909. static char* _null() noexcept
  910. {
  911. static char sNull = '\0';
  912. return &sNull;
  913. }
  914. /*
  915. * Helper function.
  916. * Called whenever the string needs to be allocated.
  917. *
  918. * Notes:
  919. * - Allocates string only if 'strBuf' is not null and new string contents are different
  920. * - If 'strBuf' is null, 'size' must be 0
  921. */
  922. void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
  923. {
  924. if (strBuf != nullptr)
  925. {
  926. // don't recreate string if contents match
  927. if (std::strcmp(fBuffer, strBuf) == 0)
  928. return;
  929. if (fBufferAlloc)
  930. std::free(fBuffer);
  931. fBufferLen = (size > 0) ? size : std::strlen(strBuf);
  932. fBuffer = static_cast<char*>(std::malloc(fBufferLen + 1));
  933. if (fBuffer == nullptr)
  934. {
  935. fBuffer = _null();
  936. fBufferLen = 0;
  937. fBufferAlloc = false;
  938. return;
  939. }
  940. fBufferAlloc = true;
  941. std::strcpy(fBuffer, strBuf);
  942. fBuffer[fBufferLen] = '\0';
  943. }
  944. else
  945. {
  946. DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
  947. // don't recreate null string
  948. if (! fBufferAlloc)
  949. return;
  950. DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
  951. std::free(fBuffer);
  952. fBuffer = _null();
  953. fBufferLen = 0;
  954. fBufferAlloc = false;
  955. }
  956. }
  957. DISTRHO_PREVENT_HEAP_ALLOCATION
  958. };
  959. // -----------------------------------------------------------------------
  960. static inline
  961. String operator+(const String& strBefore, const char* const strBufAfter) noexcept
  962. {
  963. if (strBufAfter == nullptr || strBufAfter[0] == '\0')
  964. return strBefore;
  965. if (strBefore.isEmpty())
  966. return String(strBufAfter);
  967. const std::size_t strBeforeLen = strBefore.length();
  968. const std::size_t strBufAfterLen = std::strlen(strBufAfter);
  969. const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
  970. char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
  971. DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
  972. std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
  973. std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
  974. return String(newBuf, false);
  975. }
  976. static inline
  977. String operator+(const char* const strBufBefore, const String& strAfter) noexcept
  978. {
  979. if (strAfter.isEmpty())
  980. return String(strBufBefore);
  981. if (strBufBefore == nullptr || strBufBefore[0] == '\0')
  982. return strAfter;
  983. const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
  984. const std::size_t strAfterLen = strAfter.length();
  985. const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
  986. char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
  987. DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
  988. std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
  989. std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
  990. return String(newBuf, false);
  991. }
  992. // -----------------------------------------------------------------------
  993. END_NAMESPACE_DISTRHO
  994. #endif // DISTRHO_STRING_HPP_INCLUDED