Audio plugin host https://kx.studio/carla
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.

747 lines
18KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2014 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 "d_leakdetector.hpp"
  19. // -----------------------------------------------------------------------
  20. // d_string class
  21. class d_string
  22. {
  23. public:
  24. // -------------------------------------------------------------------
  25. // constructors (no explicit conversions allowed)
  26. /*
  27. * Empty string.
  28. */
  29. explicit d_string() noexcept
  30. {
  31. _init();
  32. }
  33. /*
  34. * Simple character.
  35. */
  36. explicit d_string(const char c) noexcept
  37. {
  38. char ch[2];
  39. ch[0] = c;
  40. ch[1] = '\0';
  41. _init();
  42. _dup(ch);
  43. }
  44. /*
  45. * Simple char string.
  46. */
  47. explicit d_string(char* const strBuf) noexcept
  48. {
  49. _init();
  50. _dup(strBuf);
  51. }
  52. /*
  53. * Simple const char string.
  54. */
  55. explicit d_string(const char* const strBuf) noexcept
  56. {
  57. _init();
  58. _dup(strBuf);
  59. }
  60. /*
  61. * Integer.
  62. */
  63. explicit d_string(const int value) noexcept
  64. {
  65. char strBuf[0xff+1];
  66. std::snprintf(strBuf, 0xff, "%d", value);
  67. strBuf[0xff] = '\0';
  68. _init();
  69. _dup(strBuf);
  70. }
  71. /*
  72. * Unsigned integer, possibly in hexadecimal.
  73. */
  74. explicit d_string(const unsigned int value, const bool hexadecimal = false) noexcept
  75. {
  76. char strBuf[0xff+1];
  77. std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
  78. strBuf[0xff] = '\0';
  79. _init();
  80. _dup(strBuf);
  81. }
  82. /*
  83. * Long integer.
  84. */
  85. explicit d_string(const long value) noexcept
  86. {
  87. char strBuf[0xff+1];
  88. std::snprintf(strBuf, 0xff, "%ld", value);
  89. strBuf[0xff] = '\0';
  90. _init();
  91. _dup(strBuf);
  92. }
  93. /*
  94. * Long unsigned integer, possibly hexadecimal.
  95. */
  96. explicit d_string(const unsigned long value, const bool hexadecimal = false) noexcept
  97. {
  98. char strBuf[0xff+1];
  99. std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
  100. strBuf[0xff] = '\0';
  101. _init();
  102. _dup(strBuf);
  103. }
  104. /*
  105. * Long long integer.
  106. */
  107. explicit d_string(const long long value) noexcept
  108. {
  109. char strBuf[0xff+1];
  110. std::snprintf(strBuf, 0xff, "%lld", value);
  111. strBuf[0xff] = '\0';
  112. _init();
  113. _dup(strBuf);
  114. }
  115. /*
  116. * Long long unsigned integer, possibly hexadecimal.
  117. */
  118. explicit d_string(const unsigned long long value, const bool hexadecimal = false) noexcept
  119. {
  120. char strBuf[0xff+1];
  121. std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
  122. strBuf[0xff] = '\0';
  123. _init();
  124. _dup(strBuf);
  125. }
  126. /*
  127. * Single-precision floating point number.
  128. */
  129. explicit d_string(const float value) noexcept
  130. {
  131. char strBuf[0xff+1];
  132. std::snprintf(strBuf, 0xff, "%f", value);
  133. strBuf[0xff] = '\0';
  134. _init();
  135. _dup(strBuf);
  136. }
  137. /*
  138. * Double-precision floating point number.
  139. */
  140. explicit d_string(const double value) noexcept
  141. {
  142. char strBuf[0xff+1];
  143. std::snprintf(strBuf, 0xff, "%g", value);
  144. strBuf[0xff] = '\0';
  145. _init();
  146. _dup(strBuf);
  147. }
  148. // -------------------------------------------------------------------
  149. // non-explicit constructor
  150. /*
  151. * Create string from another string.
  152. */
  153. d_string(const d_string& str) noexcept
  154. {
  155. _init();
  156. _dup(str.fBuffer);
  157. }
  158. // -------------------------------------------------------------------
  159. // destructor
  160. /*
  161. * Destructor.
  162. */
  163. ~d_string() noexcept
  164. {
  165. DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  166. if (fBuffer == _null())
  167. return;
  168. std::free(fBuffer);
  169. fBuffer = nullptr;
  170. fBufferLen = 0;
  171. }
  172. // -------------------------------------------------------------------
  173. // public methods
  174. /*
  175. * Get length of the string.
  176. */
  177. size_t length() const noexcept
  178. {
  179. return fBufferLen;
  180. }
  181. /*
  182. * Check if the string is empty.
  183. */
  184. bool isEmpty() const noexcept
  185. {
  186. return (fBufferLen == 0);
  187. }
  188. /*
  189. * Check if the string is not empty.
  190. */
  191. bool isNotEmpty() const noexcept
  192. {
  193. return (fBufferLen != 0);
  194. }
  195. /*
  196. * Check if the string contains another string, optionally ignoring case.
  197. */
  198. bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
  199. {
  200. DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
  201. if (ignoreCase)
  202. {
  203. #ifdef __USE_GNU
  204. return (strcasestr(fBuffer, strBuf) != nullptr);
  205. #else
  206. d_string tmp1(fBuffer), tmp2(strBuf);
  207. // memory allocation failed or empty string(s)
  208. if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
  209. return false;
  210. tmp1.toLower();
  211. tmp2.toLower();
  212. return (std::strstr(tmp1, tmp2) != nullptr);
  213. #endif
  214. }
  215. return (std::strstr(fBuffer, strBuf) != nullptr);
  216. }
  217. /*
  218. * Check if character at 'pos' is a digit.
  219. */
  220. bool isDigit(const size_t pos) const noexcept
  221. {
  222. DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
  223. return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
  224. }
  225. /*
  226. * Check if the string starts with the character 'c'.
  227. */
  228. bool startsWith(const char c) const noexcept
  229. {
  230. DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
  231. return (fBufferLen > 0 && fBuffer[0] == c);
  232. }
  233. /*
  234. * Check if the string starts with the string 'prefix'.
  235. */
  236. bool startsWith(const char* const prefix) const noexcept
  237. {
  238. DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);
  239. const size_t prefixLen(std::strlen(prefix));
  240. if (fBufferLen < prefixLen)
  241. return false;
  242. return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
  243. }
  244. /*
  245. * Check if the string ends with the character 'c'.
  246. */
  247. bool endsWith(const char c) const noexcept
  248. {
  249. DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
  250. return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
  251. }
  252. /*
  253. * Check if the string ends with the string 'suffix'.
  254. */
  255. bool endsWith(const char* const suffix) const noexcept
  256. {
  257. DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);
  258. const size_t suffixLen(std::strlen(suffix));
  259. if (fBufferLen < suffixLen)
  260. return false;
  261. return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
  262. }
  263. /*
  264. * Find the first occurrence of character 'c' in the string.
  265. * Returns "length()" if the character is not found.
  266. */
  267. size_t find(const char c, bool* const found = nullptr) const noexcept
  268. {
  269. if (fBufferLen == 0 || c == '\0')
  270. {
  271. if (found != nullptr)
  272. *found = false;
  273. return fBufferLen;
  274. }
  275. for (size_t i=0; i < fBufferLen; ++i)
  276. {
  277. if (fBuffer[i] == c)
  278. {
  279. if (found != nullptr)
  280. *found = true;
  281. return i;
  282. }
  283. }
  284. if (found != nullptr)
  285. *found = false;
  286. return fBufferLen;
  287. }
  288. /*
  289. * Find the first occurrence of string 'strBuf' in the string.
  290. * Returns "length()" if the string is not found.
  291. */
  292. size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
  293. {
  294. if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
  295. {
  296. if (found != nullptr)
  297. *found = false;
  298. return fBufferLen;
  299. }
  300. if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
  301. {
  302. const ssize_t ret(subStrBuf - fBuffer);
  303. if (ret < 0)
  304. {
  305. // should never happen!
  306. d_safe_assert("ret >= 0", __FILE__, __LINE__);
  307. if (found != nullptr)
  308. *found = false;
  309. return fBufferLen;
  310. }
  311. if (found != nullptr)
  312. *found = true;
  313. return static_cast<size_t>(ret);
  314. }
  315. if (found != nullptr)
  316. *found = false;
  317. return fBufferLen;
  318. }
  319. /*
  320. * Find the last occurrence of character 'c' in the string.
  321. * Returns "length()" if the character is not found.
  322. */
  323. size_t rfind(const char c, bool* const found = nullptr) const noexcept
  324. {
  325. if (fBufferLen == 0 || c == '\0')
  326. {
  327. if (found != nullptr)
  328. *found = false;
  329. return fBufferLen;
  330. }
  331. for (size_t i=fBufferLen; i > 0; --i)
  332. {
  333. if (fBuffer[i-1] == c)
  334. {
  335. if (found != nullptr)
  336. *found = true;
  337. return i-1;
  338. }
  339. }
  340. if (found != nullptr)
  341. *found = false;
  342. return fBufferLen;
  343. }
  344. /*
  345. * Find the last occurrence of string 'strBuf' in the string.
  346. * Returns "length()" if the string is not found.
  347. */
  348. size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
  349. {
  350. if (found != nullptr)
  351. *found = false;
  352. if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
  353. return fBufferLen;
  354. const size_t strBufLen(std::strlen(strBuf));
  355. size_t ret = fBufferLen;
  356. const char* tmpBuf = fBuffer;
  357. for (size_t i=0; i < fBufferLen; ++i)
  358. {
  359. if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
  360. {
  361. if (found != nullptr)
  362. *found = true;
  363. break;
  364. }
  365. --ret;
  366. ++tmpBuf;
  367. }
  368. return fBufferLen-ret;
  369. }
  370. /*
  371. * Clear the string.
  372. */
  373. void clear() noexcept
  374. {
  375. truncate(0);
  376. }
  377. /*
  378. * Replace all occurrences of character 'before' with character 'after'.
  379. */
  380. void replace(const char before, const char after) noexcept
  381. {
  382. DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0',);
  383. for (size_t i=0; i < fBufferLen; ++i)
  384. {
  385. if (fBuffer[i] == before)
  386. fBuffer[i] = after;
  387. }
  388. }
  389. /*
  390. * Truncate the string to size 'n'.
  391. */
  392. void truncate(const size_t n) noexcept
  393. {
  394. if (n >= fBufferLen)
  395. return;
  396. for (size_t i=n; i < fBufferLen; ++i)
  397. fBuffer[i] = '\0';
  398. fBufferLen = n;
  399. }
  400. /*
  401. * Convert all non-basic characters to '_'.
  402. */
  403. void toBasic() noexcept
  404. {
  405. for (size_t i=0; i < fBufferLen; ++i)
  406. {
  407. if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
  408. continue;
  409. if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
  410. continue;
  411. if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
  412. continue;
  413. if (fBuffer[i] == '_')
  414. continue;
  415. fBuffer[i] = '_';
  416. }
  417. }
  418. /*
  419. * Convert to all ascii characters to lowercase.
  420. */
  421. void toLower() noexcept
  422. {
  423. static const char kCharDiff('a' - 'A');
  424. for (size_t i=0; i < fBufferLen; ++i)
  425. {
  426. if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
  427. fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
  428. }
  429. }
  430. /*
  431. * Convert to all ascii characters to uppercase.
  432. */
  433. void toUpper() noexcept
  434. {
  435. static const char kCharDiff('a' - 'A');
  436. for (size_t i=0; i < fBufferLen; ++i)
  437. {
  438. if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
  439. fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
  440. }
  441. }
  442. /*
  443. * Direct access to the string buffer (read-only).
  444. */
  445. const char* buffer() const noexcept
  446. {
  447. return fBuffer;
  448. }
  449. // -------------------------------------------------------------------
  450. // public operators
  451. operator const char*() const noexcept
  452. {
  453. return fBuffer;
  454. }
  455. char operator[](const size_t pos) const noexcept
  456. {
  457. if (pos < fBufferLen)
  458. return fBuffer[pos];
  459. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  460. static char fallback;
  461. fallback = '\0';
  462. return fallback;
  463. }
  464. char& operator[](const size_t pos) noexcept
  465. {
  466. if (pos < fBufferLen)
  467. return fBuffer[pos];
  468. d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
  469. static char fallback;
  470. fallback = '\0';
  471. return fallback;
  472. }
  473. bool operator==(const char* const strBuf) const noexcept
  474. {
  475. return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
  476. }
  477. bool operator==(const d_string& str) const noexcept
  478. {
  479. return operator==(str.fBuffer);
  480. }
  481. bool operator!=(const char* const strBuf) const noexcept
  482. {
  483. return !operator==(strBuf);
  484. }
  485. bool operator!=(const d_string& str) const noexcept
  486. {
  487. return !operator==(str.fBuffer);
  488. }
  489. d_string& operator=(const char* const strBuf) noexcept
  490. {
  491. _dup(strBuf);
  492. return *this;
  493. }
  494. d_string& operator=(const d_string& str) noexcept
  495. {
  496. _dup(str.fBuffer);
  497. return *this;
  498. }
  499. d_string& operator+=(const char* const strBuf) noexcept
  500. {
  501. if (strBuf == nullptr)
  502. return *this;
  503. const size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
  504. char newBuf[newBufSize];
  505. std::strcpy(newBuf, fBuffer);
  506. std::strcat(newBuf, strBuf);
  507. _dup(newBuf, newBufSize-1);
  508. return *this;
  509. }
  510. d_string& operator+=(const d_string& str) noexcept
  511. {
  512. return operator+=(str.fBuffer);
  513. }
  514. d_string operator+(const char* const strBuf) noexcept
  515. {
  516. const size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
  517. char newBuf[newBufSize];
  518. std::strcpy(newBuf, fBuffer);
  519. if (strBuf != nullptr)
  520. std::strcat(newBuf, strBuf);
  521. return d_string(newBuf);
  522. }
  523. d_string operator+(const d_string& str) noexcept
  524. {
  525. return operator+(str.fBuffer);
  526. }
  527. // -------------------------------------------------------------------
  528. private:
  529. char* fBuffer; // the actual string buffer
  530. size_t fBufferLen; // string length
  531. /*
  532. * Static null string.
  533. * Prevents allocation for new and/or empty strings.
  534. */
  535. static char* _null() noexcept
  536. {
  537. static char sNull = '\0';
  538. return &sNull;
  539. }
  540. /*
  541. * Shared init function.
  542. * Called on all constructors.
  543. */
  544. void _init() noexcept
  545. {
  546. fBuffer = _null();
  547. fBufferLen = 0;
  548. }
  549. /*
  550. * Helper function.
  551. * Called whenever the string needs to be allocated.
  552. *
  553. * Notes:
  554. * - Allocates string only if 'strBuf' is not null and new string contents are different
  555. * - If 'strBuf' is null, 'size' must be 0
  556. */
  557. void _dup(const char* const strBuf, const size_t size = 0) noexcept
  558. {
  559. if (strBuf != nullptr)
  560. {
  561. // don't recreate string if contents match
  562. if (std::strcmp(fBuffer, strBuf) == 0)
  563. return;
  564. if (fBuffer != _null())
  565. std::free(fBuffer);
  566. fBufferLen = (size > 0) ? size : std::strlen(strBuf);
  567. fBuffer = (char*)std::malloc(fBufferLen+1);
  568. if (fBuffer == nullptr)
  569. return _init();
  570. std::strcpy(fBuffer, strBuf);
  571. fBuffer[fBufferLen] = '\0';
  572. }
  573. else
  574. {
  575. DISTRHO_SAFE_ASSERT(size == 0);
  576. // don't recreate null string
  577. if (fBuffer == _null())
  578. return;
  579. DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
  580. std::free(fBuffer);
  581. _init();
  582. }
  583. }
  584. DISTRHO_LEAK_DETECTOR(d_string)
  585. DISTRHO_PREVENT_HEAP_ALLOCATION
  586. };
  587. // -----------------------------------------------------------------------
  588. static inline
  589. d_string operator+(const d_string& strBefore, const char* const strBufAfter) noexcept
  590. {
  591. const char* const strBufBefore = strBefore.buffer();
  592. const size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
  593. char newBuf[newBufSize];
  594. std::strcpy(newBuf, strBufBefore);
  595. std::strcat(newBuf, strBufAfter);
  596. return d_string(newBuf);
  597. }
  598. static inline
  599. d_string operator+(const char* const strBufBefore, const d_string& strAfter) noexcept
  600. {
  601. const char* const strBufAfter = strAfter.buffer();
  602. const size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
  603. char newBuf[newBufSize];
  604. std::strcpy(newBuf, strBufBefore);
  605. std::strcat(newBuf, strBufAfter);
  606. return d_string(newBuf);
  607. }
  608. // -----------------------------------------------------------------------
  609. #endif // DISTRHO_STRING_HPP_INCLUDED