String.hpp (25691B)
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 17 #ifndef DISTRHO_STRING_HPP_INCLUDED 18 #define DISTRHO_STRING_HPP_INCLUDED 19 20 #include "../DistrhoUtils.hpp" 21 #include "../extra/ScopedSafeLocale.hpp" 22 23 #include <algorithm> 24 25 START_NAMESPACE_DISTRHO 26 27 // ----------------------------------------------------------------------- 28 // String class 29 30 class String 31 { 32 public: 33 // ------------------------------------------------------------------- 34 // constructors (no explicit conversions allowed) 35 36 /* 37 * Empty string. 38 */ 39 explicit String() noexcept 40 : fBuffer(_null()), 41 fBufferLen(0), 42 fBufferAlloc(false) {} 43 44 /* 45 * Simple character. 46 */ 47 explicit String(const char c) noexcept 48 : fBuffer(_null()), 49 fBufferLen(0), 50 fBufferAlloc(false) 51 { 52 char ch[2]; 53 ch[0] = c; 54 ch[1] = '\0'; 55 56 _dup(ch); 57 } 58 59 /* 60 * Simple char string. 61 */ 62 explicit String(char* const strBuf, const bool reallocData = true) noexcept 63 : fBuffer(_null()), 64 fBufferLen(0), 65 fBufferAlloc(false) 66 { 67 if (reallocData || strBuf == nullptr) 68 { 69 _dup(strBuf); 70 } 71 else 72 { 73 fBuffer = strBuf; 74 fBufferLen = std::strlen(strBuf); 75 fBufferAlloc = true; 76 } 77 } 78 79 /* 80 * Simple const char string. 81 */ 82 explicit String(const char* const strBuf) noexcept 83 : fBuffer(_null()), 84 fBufferLen(0), 85 fBufferAlloc(false) 86 { 87 _dup(strBuf); 88 } 89 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 102 _dup(strBuf); 103 } 104 105 /* 106 * Unsigned integer, possibly in hexadecimal. 107 */ 108 explicit String(const unsigned int value, const bool hexadecimal = false) noexcept 109 : fBuffer(_null()), 110 fBufferLen(0), 111 fBufferAlloc(false) 112 { 113 char strBuf[0xff+1]; 114 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); 115 strBuf[0xff] = '\0'; 116 117 _dup(strBuf); 118 } 119 120 /* 121 * Long integer. 122 */ 123 explicit String(const long value) noexcept 124 : fBuffer(_null()), 125 fBufferLen(0), 126 fBufferAlloc(false) 127 { 128 char strBuf[0xff+1]; 129 std::snprintf(strBuf, 0xff, "%ld", value); 130 strBuf[0xff] = '\0'; 131 132 _dup(strBuf); 133 } 134 135 /* 136 * Long unsigned integer, possibly hexadecimal. 137 */ 138 explicit String(const unsigned long value, const bool hexadecimal = false) noexcept 139 : fBuffer(_null()), 140 fBufferLen(0), 141 fBufferAlloc(false) 142 { 143 char strBuf[0xff+1]; 144 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); 145 strBuf[0xff] = '\0'; 146 147 _dup(strBuf); 148 } 149 150 /* 151 * Long long integer. 152 */ 153 explicit String(const long long value) noexcept 154 : fBuffer(_null()), 155 fBufferLen(0), 156 fBufferAlloc(false) 157 { 158 char strBuf[0xff+1]; 159 std::snprintf(strBuf, 0xff, "%lld", value); 160 strBuf[0xff] = '\0'; 161 162 _dup(strBuf); 163 } 164 165 /* 166 * Long long unsigned integer, possibly hexadecimal. 167 */ 168 explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept 169 : fBuffer(_null()), 170 fBufferLen(0), 171 fBufferAlloc(false) 172 { 173 char strBuf[0xff+1]; 174 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value); 175 strBuf[0xff] = '\0'; 176 177 _dup(strBuf); 178 } 179 180 /* 181 * Single-precision floating point number. 182 */ 183 explicit String(const float value) noexcept 184 : fBuffer(_null()), 185 fBufferLen(0), 186 fBufferAlloc(false) 187 { 188 char strBuf[0xff+1]; 189 190 { 191 const ScopedSafeLocale ssl; 192 std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value)); 193 } 194 195 strBuf[0xff] = '\0'; 196 197 _dup(strBuf); 198 } 199 200 /* 201 * Double-precision floating point number. 202 */ 203 explicit String(const double value) noexcept 204 : fBuffer(_null()), 205 fBufferLen(0), 206 fBufferAlloc(false) 207 { 208 char strBuf[0xff+1]; 209 210 { 211 const ScopedSafeLocale ssl; 212 std::snprintf(strBuf, 0xff, "%.24g", value); 213 } 214 215 strBuf[0xff] = '\0'; 216 217 _dup(strBuf); 218 } 219 220 // ------------------------------------------------------------------- 221 // non-explicit constructor 222 223 /* 224 * Create string from another string. 225 */ 226 String(const String& str) noexcept 227 : fBuffer(_null()), 228 fBufferLen(0), 229 fBufferAlloc(false) 230 { 231 _dup(str.fBuffer); 232 } 233 234 // ------------------------------------------------------------------- 235 // destructor 236 237 /* 238 * Destructor. 239 */ 240 ~String() noexcept 241 { 242 DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,); 243 244 if (fBufferAlloc) 245 std::free(fBuffer); 246 247 fBuffer = nullptr; 248 fBufferLen = 0; 249 fBufferAlloc = false; 250 } 251 252 // ------------------------------------------------------------------- 253 // public methods 254 255 /* 256 * Get length of the string. 257 */ 258 std::size_t length() const noexcept 259 { 260 return fBufferLen; 261 } 262 263 /* 264 * Check if the string is empty. 265 */ 266 bool isEmpty() const noexcept 267 { 268 return (fBufferLen == 0); 269 } 270 271 /* 272 * Check if the string is not empty. 273 */ 274 bool isNotEmpty() const noexcept 275 { 276 return (fBufferLen != 0); 277 } 278 279 /* 280 * Check if the string contains a specific character, case-sensitive. 281 */ 282 bool contains(const char c) const noexcept 283 { 284 for (std::size_t i=0; i<fBufferLen; ++i) 285 { 286 if (fBuffer[i] == c) 287 return true; 288 } 289 290 return false; 291 } 292 293 /* 294 * Check if the string contains another string, optionally ignoring case. 295 */ 296 bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept 297 { 298 DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false); 299 300 if (ignoreCase) 301 { 302 #ifdef __USE_GNU 303 return (strcasestr(fBuffer, strBuf) != nullptr); 304 #else 305 String tmp1(fBuffer), tmp2(strBuf); 306 307 // memory allocation failed or empty string(s) 308 if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null()) 309 return false; 310 311 tmp1.toLower(); 312 tmp2.toLower(); 313 return (std::strstr(tmp1, tmp2) != nullptr); 314 #endif 315 } 316 317 return (std::strstr(fBuffer, strBuf) != nullptr); 318 } 319 320 /* 321 * Check if character at 'pos' is a digit. 322 */ 323 bool isDigit(const std::size_t pos) const noexcept 324 { 325 DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); 326 327 return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); 328 } 329 330 /* 331 * Check if the string starts with the character 'c'. 332 */ 333 bool startsWith(const char c) const noexcept 334 { 335 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); 336 337 return (fBufferLen > 0 && fBuffer[0] == c); 338 } 339 340 /* 341 * Check if the string starts with the string 'prefix'. 342 */ 343 bool startsWith(const char* const prefix) const noexcept 344 { 345 DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); 346 347 const std::size_t prefixLen(std::strlen(prefix)); 348 349 if (fBufferLen < prefixLen) 350 return false; 351 352 return (std::strncmp(fBuffer, prefix, prefixLen) == 0); 353 } 354 355 /* 356 * Check if the string ends with the character 'c'. 357 */ 358 bool endsWith(const char c) const noexcept 359 { 360 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); 361 362 return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c); 363 } 364 365 /* 366 * Check if the string ends with the string 'suffix'. 367 */ 368 bool endsWith(const char* const suffix) const noexcept 369 { 370 DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); 371 372 const std::size_t suffixLen(std::strlen(suffix)); 373 374 if (fBufferLen < suffixLen) 375 return false; 376 377 return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); 378 } 379 380 /* 381 * Find the first occurrence of character 'c' in the string. 382 * Returns "length()" if the character is not found. 383 */ 384 std::size_t find(const char c, bool* const found = nullptr) const noexcept 385 { 386 if (fBufferLen == 0 || c == '\0') 387 { 388 if (found != nullptr) 389 *found = false; 390 return fBufferLen; 391 } 392 393 for (std::size_t i=0; i < fBufferLen; ++i) 394 { 395 if (fBuffer[i] == c) 396 { 397 if (found != nullptr) 398 *found = true; 399 return i; 400 } 401 } 402 403 if (found != nullptr) 404 *found = false; 405 return fBufferLen; 406 } 407 408 /* 409 * Find the first occurrence of string 'strBuf' in the string. 410 * Returns "length()" if the string is not found. 411 */ 412 std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept 413 { 414 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') 415 { 416 if (found != nullptr) 417 *found = false; 418 return fBufferLen; 419 } 420 421 if (char* const subStrBuf = std::strstr(fBuffer, strBuf)) 422 { 423 const ssize_t ret(subStrBuf - fBuffer); 424 425 if (ret < 0) 426 { 427 // should never happen! 428 d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret)); 429 430 if (found != nullptr) 431 *found = false; 432 return fBufferLen; 433 } 434 435 if (found != nullptr) 436 *found = true; 437 return static_cast<std::size_t>(ret); 438 } 439 440 if (found != nullptr) 441 *found = false; 442 return fBufferLen; 443 } 444 445 /* 446 * Find the last occurrence of character 'c' in the string. 447 * Returns "length()" if the character is not found. 448 */ 449 std::size_t rfind(const char c, bool* const found = nullptr) const noexcept 450 { 451 if (fBufferLen == 0 || c == '\0') 452 { 453 if (found != nullptr) 454 *found = false; 455 return fBufferLen; 456 } 457 458 for (std::size_t i=fBufferLen; i > 0; --i) 459 { 460 if (fBuffer[i-1] == c) 461 { 462 if (found != nullptr) 463 *found = true; 464 return i-1; 465 } 466 } 467 468 if (found != nullptr) 469 *found = false; 470 return fBufferLen; 471 } 472 473 /* 474 * Find the last occurrence of string 'strBuf' in the string. 475 * Returns "length()" if the string is not found. 476 */ 477 std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept 478 { 479 if (found != nullptr) 480 *found = false; 481 482 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') 483 return fBufferLen; 484 485 const std::size_t strBufLen(std::strlen(strBuf)); 486 487 std::size_t ret = fBufferLen; 488 const char* tmpBuf = fBuffer; 489 490 for (std::size_t i=0; i < fBufferLen; ++i) 491 { 492 if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) 493 { 494 if (found != nullptr) 495 *found = true; 496 break; 497 } 498 499 --ret; 500 ++tmpBuf; 501 } 502 503 return fBufferLen-ret; 504 } 505 506 /* 507 * Clear the string. 508 */ 509 void clear() noexcept 510 { 511 truncate(0); 512 } 513 514 /* 515 * Replace all occurrences of character 'before' with character 'after'. 516 */ 517 String& replace(const char before, const char after) noexcept 518 { 519 DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); 520 521 for (std::size_t i=0; i < fBufferLen; ++i) 522 { 523 if (fBuffer[i] == before) 524 fBuffer[i] = after; 525 } 526 527 return *this; 528 } 529 530 /* 531 * Remove all occurrences of character 'c', shifting and truncating the string as necessary. 532 */ 533 String& remove(const char c) noexcept 534 { 535 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this); 536 537 if (fBufferLen == 0) 538 return *this; 539 540 for (std::size_t i=0; i < fBufferLen; ++i) 541 { 542 if (fBuffer[i] == c) 543 { 544 --fBufferLen; 545 std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i); 546 } 547 } 548 549 fBuffer[fBufferLen] = '\0'; 550 return *this; 551 } 552 553 /* 554 * Truncate the string to size 'n'. 555 */ 556 String& truncate(const std::size_t n) noexcept 557 { 558 if (n >= fBufferLen) 559 return *this; 560 561 fBuffer[n] = '\0'; 562 fBufferLen = n; 563 564 return *this; 565 } 566 567 /* 568 * Convert all non-basic characters to '_'. 569 */ 570 String& toBasic() noexcept 571 { 572 for (std::size_t i=0; i < fBufferLen; ++i) 573 { 574 if (fBuffer[i] >= '0' && fBuffer[i] <= '9') 575 continue; 576 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') 577 continue; 578 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') 579 continue; 580 if (fBuffer[i] == '_') 581 continue; 582 583 fBuffer[i] = '_'; 584 } 585 586 return *this; 587 } 588 589 /* 590 * Convert all ascii characters to lowercase. 591 */ 592 String& toLower() noexcept 593 { 594 static const char kCharDiff('a' - 'A'); 595 596 for (std::size_t i=0; i < fBufferLen; ++i) 597 { 598 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') 599 fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); 600 } 601 602 return *this; 603 } 604 605 /* 606 * Convert all ascii characters to uppercase. 607 */ 608 String& toUpper() noexcept 609 { 610 static const char kCharDiff('a' - 'A'); 611 612 for (std::size_t i=0; i < fBufferLen; ++i) 613 { 614 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') 615 fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); 616 } 617 618 return *this; 619 } 620 621 /* 622 * Create a new string where all non-basic characters are converted to '_'. 623 * @see toBasic() 624 */ 625 String asBasic() const noexcept 626 { 627 String s(*this); 628 return s.toBasic(); 629 } 630 631 /* 632 * Create a new string where all ascii characters are converted lowercase. 633 * @see toLower() 634 */ 635 String asLower() const noexcept 636 { 637 String s(*this); 638 return s.toLower(); 639 } 640 641 /* 642 * Create a new string where all ascii characters are converted to uppercase. 643 * @see toUpper() 644 */ 645 String asUpper() const noexcept 646 { 647 String s(*this); 648 return s.toUpper(); 649 } 650 651 /* 652 * Direct access to the string buffer (read-only). 653 */ 654 const char* buffer() const noexcept 655 { 656 return fBuffer; 657 } 658 659 /* 660 * Get and release the string buffer, while also clearing this string. 661 * This allows to keep a pointer to the buffer after this object is deleted. 662 * Result must be freed. 663 */ 664 char* getAndReleaseBuffer() noexcept 665 { 666 char* ret = fBufferLen > 0 ? fBuffer : nullptr; 667 fBuffer = _null(); 668 fBufferLen = 0; 669 fBufferAlloc = false; 670 return ret; 671 } 672 673 // ------------------------------------------------------------------- 674 // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html 675 // Copyright (C) 2004-2008 René Nyffenegger 676 677 static String asBase64(const void* const data, const std::size_t dataSize) 678 { 679 static const char* const kBase64Chars = 680 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 681 "abcdefghijklmnopqrstuvwxyz" 682 "0123456789+/"; 683 684 #ifndef _MSC_VER 685 const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); 686 #else 687 constexpr std::size_t kTmpBufSize = 65536U; 688 #endif 689 690 const uchar* bytesToEncode((const uchar*)data); 691 692 uint i=0, j=0; 693 uint charArray3[3], charArray4[4]; 694 695 char strBuf[kTmpBufSize + 1]; 696 strBuf[kTmpBufSize] = '\0'; 697 std::size_t strBufIndex = 0; 698 699 String ret; 700 701 for (std::size_t s=0; s<dataSize; ++s) 702 { 703 charArray3[i++] = *(bytesToEncode++); 704 705 if (i == 3) 706 { 707 charArray4[0] = (charArray3[0] & 0xfc) >> 2; 708 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); 709 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); 710 charArray4[3] = charArray3[2] & 0x3f; 711 712 for (i=0; i<4; ++i) 713 strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; 714 715 if (strBufIndex >= kTmpBufSize-7) 716 { 717 strBuf[strBufIndex] = '\0'; 718 strBufIndex = 0; 719 ret += strBuf; 720 } 721 722 i = 0; 723 } 724 } 725 726 if (i != 0) 727 { 728 for (j=i; j<3; ++j) 729 charArray3[j] = '\0'; 730 731 charArray4[0] = (charArray3[0] & 0xfc) >> 2; 732 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); 733 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); 734 charArray4[3] = charArray3[2] & 0x3f; 735 736 for (j=0; j<4 && i<3 && j<i+1; ++j) 737 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; 738 739 for (; i++ < 3;) 740 strBuf[strBufIndex++] = '='; 741 } 742 743 if (strBufIndex != 0) 744 { 745 strBuf[strBufIndex] = '\0'; 746 ret += strBuf; 747 } 748 749 return ret; 750 } 751 752 // ------------------------------------------------------------------- 753 // public operators 754 755 operator const char*() const noexcept 756 { 757 return fBuffer; 758 } 759 760 char operator[](const std::size_t pos) const noexcept 761 { 762 if (pos < fBufferLen) 763 return fBuffer[pos]; 764 765 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); 766 767 static char fallback; 768 fallback = '\0'; 769 return fallback; 770 } 771 772 char& operator[](const std::size_t pos) noexcept 773 { 774 if (pos < fBufferLen) 775 return fBuffer[pos]; 776 777 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); 778 779 static char fallback; 780 fallback = '\0'; 781 return fallback; 782 } 783 784 bool operator==(const char* const strBuf) const noexcept 785 { 786 return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); 787 } 788 789 bool operator==(const String& str) const noexcept 790 { 791 return operator==(str.fBuffer); 792 } 793 794 bool operator!=(const char* const strBuf) const noexcept 795 { 796 return !operator==(strBuf); 797 } 798 799 bool operator!=(const String& str) const noexcept 800 { 801 return !operator==(str.fBuffer); 802 } 803 804 String& operator=(const char* const strBuf) noexcept 805 { 806 _dup(strBuf); 807 808 return *this; 809 } 810 811 String& operator=(const String& str) noexcept 812 { 813 _dup(str.fBuffer); 814 815 return *this; 816 } 817 818 String& operator+=(const char* const strBuf) noexcept 819 { 820 if (strBuf == nullptr || strBuf[0] == '\0') 821 return *this; 822 823 const std::size_t strBufLen = std::strlen(strBuf); 824 825 // for empty strings, we can just take the appended string as our entire data 826 if (isEmpty()) 827 { 828 _dup(strBuf, strBufLen); 829 return *this; 830 } 831 832 // we have some data ourselves, reallocate to add the new stuff 833 char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1); 834 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this); 835 836 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); 837 838 fBuffer = newBuf; 839 fBufferLen += strBufLen; 840 841 return *this; 842 } 843 844 String& operator+=(const String& str) noexcept 845 { 846 return operator+=(str.fBuffer); 847 } 848 849 String operator+(const char* const strBuf) noexcept 850 { 851 if (strBuf == nullptr || strBuf[0] == '\0') 852 return *this; 853 if (isEmpty()) 854 return String(strBuf); 855 856 const std::size_t strBufLen = std::strlen(strBuf); 857 const std::size_t newBufSize = fBufferLen + strBufLen; 858 char* const newBuf = (char*)malloc(newBufSize + 1); 859 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); 860 861 std::memcpy(newBuf, fBuffer, fBufferLen); 862 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); 863 864 return String(newBuf, false); 865 } 866 867 String operator+(const String& str) noexcept 868 { 869 return operator+(str.fBuffer); 870 } 871 872 // needed for std::map compatibility 873 bool operator<(const String& str) const noexcept 874 { 875 return std::strcmp(fBuffer, str.fBuffer) < 0; 876 } 877 878 // ------------------------------------------------------------------- 879 880 private: 881 char* fBuffer; // the actual string buffer 882 std::size_t fBufferLen; // string length 883 bool fBufferAlloc; // wherever the buffer is allocated, not using _null() 884 885 /* 886 * Static null string. 887 * Prevents allocation for new and/or empty strings. 888 */ 889 static char* _null() noexcept 890 { 891 static char sNull = '\0'; 892 return &sNull; 893 } 894 895 /* 896 * Helper function. 897 * Called whenever the string needs to be allocated. 898 * 899 * Notes: 900 * - Allocates string only if 'strBuf' is not null and new string contents are different 901 * - If 'strBuf' is null, 'size' must be 0 902 */ 903 void _dup(const char* const strBuf, const std::size_t size = 0) noexcept 904 { 905 if (strBuf != nullptr) 906 { 907 // don't recreate string if contents match 908 if (std::strcmp(fBuffer, strBuf) == 0) 909 return; 910 911 if (fBufferAlloc) 912 std::free(fBuffer); 913 914 fBufferLen = (size > 0) ? size : std::strlen(strBuf); 915 fBuffer = (char*)std::malloc(fBufferLen+1); 916 917 if (fBuffer == nullptr) 918 { 919 fBuffer = _null(); 920 fBufferLen = 0; 921 fBufferAlloc = false; 922 return; 923 } 924 925 fBufferAlloc = true; 926 927 std::strcpy(fBuffer, strBuf); 928 fBuffer[fBufferLen] = '\0'; 929 } 930 else 931 { 932 DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size)); 933 934 // don't recreate null string 935 if (! fBufferAlloc) 936 return; 937 938 DISTRHO_SAFE_ASSERT(fBuffer != nullptr); 939 std::free(fBuffer); 940 941 fBuffer = _null(); 942 fBufferLen = 0; 943 fBufferAlloc = false; 944 } 945 } 946 947 DISTRHO_PREVENT_HEAP_ALLOCATION 948 }; 949 950 // ----------------------------------------------------------------------- 951 952 static inline 953 String operator+(const String& strBefore, const char* const strBufAfter) noexcept 954 { 955 if (strBufAfter == nullptr || strBufAfter[0] == '\0') 956 return strBefore; 957 if (strBefore.isEmpty()) 958 return String(strBufAfter); 959 960 const std::size_t strBeforeLen = strBefore.length(); 961 const std::size_t strBufAfterLen = std::strlen(strBufAfter); 962 const std::size_t newBufSize = strBeforeLen + strBufAfterLen; 963 char* const newBuf = (char*)malloc(newBufSize + 1); 964 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); 965 966 std::memcpy(newBuf, strBefore.buffer(), strBeforeLen); 967 std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1); 968 969 return String(newBuf, false); 970 } 971 972 static inline 973 String operator+(const char* const strBufBefore, const String& strAfter) noexcept 974 { 975 if (strAfter.isEmpty()) 976 return String(strBufBefore); 977 if (strBufBefore == nullptr || strBufBefore[0] == '\0') 978 return strAfter; 979 980 const std::size_t strBufBeforeLen = std::strlen(strBufBefore); 981 const std::size_t strAfterLen = strAfter.length(); 982 const std::size_t newBufSize = strBufBeforeLen + strAfterLen; 983 char* const newBuf = (char*)malloc(newBufSize + 1); 984 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); 985 986 std::memcpy(newBuf, strBufBefore, strBufBeforeLen); 987 std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1); 988 989 return String(newBuf, false); 990 } 991 992 // ----------------------------------------------------------------------- 993 994 END_NAMESPACE_DISTRHO 995 996 #endif // DISTRHO_STRING_HPP_INCLUDED