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.

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