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.

String.hpp 21KB

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