Collection of DPF-based plugins for packaging
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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