1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 The code included in this file is provided under the terms of the ISC license 11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission 12 To use, copy, modify, and/or distribute this software for any purpose with or 13 without fee is hereby granted provided that the above copyright notice and 14 this permission notice appear in all copies. 15 16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 18 DISCLAIMED. 19 20 ============================================================================== 21 */ 22 23 namespace juce 24 { 25 26 //============================================================================== 27 #if JUCE_WINDOWS && ! DOXYGEN 28 #define JUCE_NATIVE_WCHAR_IS_UTF8 0 29 #define JUCE_NATIVE_WCHAR_IS_UTF16 1 30 #define JUCE_NATIVE_WCHAR_IS_UTF32 0 31 #else 32 /** This macro will be set to 1 if the compiler's native wchar_t is an 8-bit type. */ 33 #define JUCE_NATIVE_WCHAR_IS_UTF8 0 34 /** This macro will be set to 1 if the compiler's native wchar_t is a 16-bit type. */ 35 #define JUCE_NATIVE_WCHAR_IS_UTF16 0 36 /** This macro will be set to 1 if the compiler's native wchar_t is a 32-bit type. */ 37 #define JUCE_NATIVE_WCHAR_IS_UTF32 1 38 #endif 39 40 #if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN 41 /** A platform-independent 32-bit unicode character type. */ 42 using juce_wchar = wchar_t; 43 #else 44 using juce_wchar = uint32; 45 #endif 46 47 #ifndef DOXYGEN 48 /** This macro is deprecated, but preserved for compatibility with old code. */ 49 #define JUCE_T(stringLiteral) (L##stringLiteral) 50 #endif 51 52 #if JUCE_DEFINE_T_MACRO 53 /** The 'T' macro is an alternative for using the "L" prefix in front of a string literal. 54 55 This macro is deprecated, but available for compatibility with old code if you set 56 JUCE_DEFINE_T_MACRO = 1. The fastest, most portable and best way to write your string 57 literals is as standard char strings, using escaped utf-8 character sequences for extended 58 characters, rather than trying to store them as wide-char strings. 59 */ 60 #define T(stringLiteral) JUCE_T(stringLiteral) 61 #endif 62 63 #if ! DOXYGEN 64 65 //============================================================================== 66 // GNU libstdc++ does not have std::make_unsigned 67 namespace internal 68 { 69 template <typename Type> struct make_unsigned { using type = Type; }; 70 template <> struct make_unsigned<signed char> { using type = unsigned char; }; 71 template <> struct make_unsigned<char> { using type = unsigned char; }; 72 template <> struct make_unsigned<short> { using type = unsigned short; }; 73 template <> struct make_unsigned<int> { using type = unsigned int; }; 74 template <> struct make_unsigned<long> { using type = unsigned long; }; 75 template <> struct make_unsigned<long long> { using type = unsigned long long; }; 76 } 77 78 #endif 79 80 //============================================================================== 81 /** 82 A collection of functions for manipulating characters and character strings. 83 84 Most of these methods are designed for internal use by the String and CharPointer 85 classes, but some of them may be useful to call directly. 86 87 @see String, CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32 88 89 @tags{Core} 90 */ 91 class JUCE_API CharacterFunctions 92 { 93 public: 94 //============================================================================== 95 /** Converts a character to upper-case. */ 96 static juce_wchar toUpperCase (juce_wchar character) noexcept; 97 /** Converts a character to lower-case. */ 98 static juce_wchar toLowerCase (juce_wchar character) noexcept; 99 100 /** Checks whether a unicode character is upper-case. */ 101 static bool isUpperCase (juce_wchar character) noexcept; 102 /** Checks whether a unicode character is lower-case. */ 103 static bool isLowerCase (juce_wchar character) noexcept; 104 105 /** Checks whether a character is whitespace. */ 106 static bool isWhitespace (char character) noexcept; 107 /** Checks whether a character is whitespace. */ 108 static bool isWhitespace (juce_wchar character) noexcept; 109 110 /** Checks whether a character is a digit. */ 111 static bool isDigit (char character) noexcept; 112 /** Checks whether a character is a digit. */ 113 static bool isDigit (juce_wchar character) noexcept; 114 115 /** Checks whether a character is alphabetic. */ 116 static bool isLetter (char character) noexcept; 117 /** Checks whether a character is alphabetic. */ 118 static bool isLetter (juce_wchar character) noexcept; 119 120 /** Checks whether a character is alphabetic or numeric. */ 121 static bool isLetterOrDigit (char character) noexcept; 122 /** Checks whether a character is alphabetic or numeric. */ 123 static bool isLetterOrDigit (juce_wchar character) noexcept; 124 125 /** Checks whether a character is a printable character, i.e. alphabetic, numeric, 126 a punctuation character or a space. 127 */ 128 static bool isPrintable (char character) noexcept; 129 130 /** Checks whether a character is a printable character, i.e. alphabetic, numeric, 131 a punctuation character or a space. 132 */ 133 static bool isPrintable (juce_wchar character) noexcept; 134 135 /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */ 136 static int getHexDigitValue (juce_wchar digit) noexcept; 137 138 /** Converts a byte of Windows 1252 codepage to unicode. */ 139 static juce_wchar getUnicodeCharFromWindows1252Codepage (uint8 windows1252Char) noexcept; 140 141 //============================================================================== 142 /** Parses a character string to read a floating-point number. 143 Note that this will advance the pointer that is passed in, leaving it at 144 the end of the number. 145 */ 146 template <typename CharPointerType> 147 static double readDoubleValue (CharPointerType& text) noexcept 148 { 149 constexpr auto inf = std::numeric_limits<double>::infinity(); 150 151 bool isNegative = false; 152 #if ! JUCE_MINGW 153 constexpr const int maxSignificantDigits = 17 + 1; // An additional digit for rounding 154 constexpr const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator 155 char buffer[(size_t) bufferSize] = {}; 156 char* writePtr = &(buffer[0]); 157 #endif 158 159 const auto endOfWhitspace = text.findEndOfWhitespace(); 160 text = endOfWhitspace; 161 162 auto c = *text; 163 164 switch (c) 165 { 166 case '-': 167 isNegative = true; 168 #if ! JUCE_MINGW 169 *writePtr++ = '-'; 170 #endif 171 JUCE_FALLTHROUGH 172 case '+': 173 c = *++text; 174 break; 175 default: 176 break; 177 } 178 179 switch (c) 180 { 181 case 'n': 182 case 'N': 183 { 184 if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N')) 185 { 186 text += 3; 187 return std::numeric_limits<double>::quiet_NaN(); 188 } 189 190 text = endOfWhitspace; 191 return 0.0; 192 } 193 194 case 'i': 195 case 'I': 196 { 197 if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F')) 198 { 199 text += 3; 200 return isNegative ? -inf : inf; 201 } 202 203 text = endOfWhitspace; 204 return 0.0; 205 } 206 207 default: 208 break; 209 } 210 211 #if JUCE_MINGW 212 // MinGW does not have access to the locale functions required for strtold, so we parse the doubles 213 // ourselves. There are some edge cases where the least significant digit will be wrong! 214 double result[3] = { 0 }, accumulator[2] = { 0 }; 215 int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 }; 216 int exponent = 0, decPointIndex = 0, digit = 0; 217 int lastDigit = 0, numSignificantDigits = 0; 218 bool digitsFound = false; 219 constexpr const int maxSignificantDigits = 17 + 1; 220 221 for (;;) 222 { 223 if (text.isDigit()) 224 { 225 lastDigit = digit; 226 digit = (int) text.getAndAdvance() - '0'; 227 digitsFound = true; 228 229 if (decPointIndex != 0) 230 exponentAdjustment[1]++; 231 232 if (numSignificantDigits == 0 && digit == 0) 233 continue; 234 235 if (++numSignificantDigits > maxSignificantDigits) 236 { 237 if (digit > 5) 238 ++accumulator [decPointIndex]; 239 else if (digit == 5 && (lastDigit & 1) != 0) 240 ++accumulator [decPointIndex]; 241 242 if (decPointIndex > 0) 243 exponentAdjustment[1]--; 244 else 245 exponentAdjustment[0]++; 246 247 while (text.isDigit()) 248 { 249 ++text; 250 if (decPointIndex == 0) 251 exponentAdjustment[0]++; 252 } 253 } 254 else 255 { 256 const auto maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10); 257 if (accumulator [decPointIndex] > maxAccumulatorValue) 258 { 259 result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) 260 + accumulator [decPointIndex]; 261 accumulator [decPointIndex] = 0; 262 exponentAccumulator [decPointIndex] = 0; 263 } 264 265 accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; 266 exponentAccumulator [decPointIndex]++; 267 } 268 } 269 else if (decPointIndex == 0 && *text == '.') 270 { 271 ++text; 272 decPointIndex = 1; 273 274 if (numSignificantDigits > maxSignificantDigits) 275 { 276 while (text.isDigit()) 277 ++text; 278 break; 279 } 280 } 281 else 282 { 283 break; 284 } 285 } 286 287 result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; 288 289 if (decPointIndex != 0) 290 result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; 291 292 c = *text; 293 if ((c == 'e' || c == 'E') && digitsFound) 294 { 295 auto negativeExponent = false; 296 297 switch (*++text) 298 { 299 case '-': negativeExponent = true; JUCE_FALLTHROUGH 300 case '+': ++text; 301 } 302 303 while (text.isDigit()) 304 exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0'); 305 306 if (negativeExponent) 307 exponent = -exponent; 308 } 309 310 auto r = mulexp10 (result[0], exponent + exponentAdjustment[0]); 311 if (decPointIndex != 0) 312 r += mulexp10 (result[1], exponent - exponentAdjustment[1]); 313 314 return isNegative ? -r : r; 315 316 #else // ! JUCE_MINGW 317 318 int numSigFigs = 0, extraExponent = 0; 319 bool decimalPointFound = false, leadingZeros = false; 320 321 for (;;) 322 { 323 if (text.isDigit()) 324 { 325 auto digit = (int) text.getAndAdvance() - '0'; 326 327 if (decimalPointFound) 328 { 329 if (numSigFigs >= maxSignificantDigits) 330 continue; 331 } 332 else 333 { 334 if (numSigFigs >= maxSignificantDigits) 335 { 336 ++extraExponent; 337 continue; 338 } 339 340 if (numSigFigs == 0 && digit == 0) 341 { 342 leadingZeros = true; 343 continue; 344 } 345 } 346 347 *writePtr++ = (char) ('0' + (char) digit); 348 numSigFigs++; 349 } 350 else if ((! decimalPointFound) && *text == '.') 351 { 352 ++text; 353 *writePtr++ = '.'; 354 decimalPointFound = true; 355 } 356 else 357 { 358 break; 359 } 360 } 361 362 if ((! leadingZeros) && (numSigFigs == 0)) 363 { 364 text = endOfWhitspace; 365 return 0.0; 366 } 367 368 auto writeExponentDigits = [] (int exponent, char* destination) 369 { 370 auto exponentDivisor = 100; 371 372 while (exponentDivisor > 1) 373 { 374 auto digit = exponent / exponentDivisor; 375 *destination++ = (char) ('0' + (char) digit); 376 exponent -= digit * exponentDivisor; 377 exponentDivisor /= 10; 378 } 379 380 *destination++ = (char) ('0' + (char) exponent); 381 }; 382 383 c = *text; 384 385 if (c == 'e' || c == 'E') 386 { 387 const auto startOfExponent = text; 388 *writePtr++ = 'e'; 389 bool parsedExponentIsPositive = true; 390 391 switch (*++text) 392 { 393 case '-': 394 parsedExponentIsPositive = false; 395 JUCE_FALLTHROUGH 396 case '+': 397 ++text; 398 break; 399 default: 400 break; 401 } 402 403 int exponent = 0; 404 const auto startOfExponentDigits = text; 405 406 while (text.isDigit()) 407 { 408 auto digit = (int) text.getAndAdvance() - '0'; 409 410 if (digit != 0 || exponent != 0) 411 exponent = (exponent * 10) + digit; 412 } 413 414 if (text == startOfExponentDigits) 415 text = startOfExponent; 416 417 exponent = extraExponent + (parsedExponentIsPositive ? exponent : -exponent); 418 419 if (exponent < 0) 420 { 421 if (exponent < std::numeric_limits<double>::min_exponent10 - 1) 422 return isNegative ? -0.0 : 0.0; 423 424 *writePtr++ = '-'; 425 exponent = -exponent; 426 } 427 else if (exponent > std::numeric_limits<double>::max_exponent10 + 1) 428 { 429 return isNegative ? -inf : inf; 430 } 431 432 writeExponentDigits (exponent, writePtr); 433 } 434 else if (extraExponent > 0) 435 { 436 *writePtr++ = 'e'; 437 writeExponentDigits (extraExponent, writePtr); 438 } 439 440 #if JUCE_WINDOWS 441 static _locale_t locale = _create_locale (LC_ALL, "C"); 442 return _strtod_l (&buffer[0], nullptr, locale); 443 #else 444 static locale_t locale = newlocale (LC_ALL_MASK, "C", nullptr); 445 #if JUCE_ANDROID 446 return (double) strtold_l (&buffer[0], nullptr, locale); 447 #else 448 return strtod_l (&buffer[0], nullptr, locale); 449 #endif 450 #endif 451 452 #endif // JUCE_MINGW 453 } 454 455 /** Parses a character string, to read a floating-point value. */ 456 template <typename CharPointerType> 457 static double getDoubleValue (CharPointerType text) noexcept 458 { 459 return readDoubleValue (text); 460 } 461 462 //============================================================================== 463 /** Parses a character string, to read an integer value. */ 464 template <typename IntType, typename CharPointerType> 465 static IntType getIntValue (const CharPointerType text) noexcept 466 { 467 using UIntType = typename internal::make_unsigned<IntType>::type; 468 469 UIntType v = 0; 470 auto s = text.findEndOfWhitespace(); 471 const bool isNeg = *s == '-'; 472 473 if (isNeg) 474 ++s; 475 476 for (;;) 477 { 478 auto c = s.getAndAdvance(); 479 480 if (c >= '0' && c <= '9') 481 v = v * 10 + (UIntType) (c - '0'); 482 else 483 break; 484 } 485 486 return isNeg ? - (IntType) v : (IntType) v; 487 } 488 489 /** Parses a character string, to read a hexadecimal value. */ 490 template <typename ResultType> 491 struct HexParser 492 { 493 template <typename CharPointerType> 494 static ResultType parse (CharPointerType t) noexcept 495 { 496 ResultType result = 0; 497 498 while (! t.isEmpty()) 499 { 500 auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); 501 502 if (hexValue >= 0) 503 result = (result << 4) | hexValue; 504 } 505 506 return result; 507 } 508 }; 509 510 //============================================================================== 511 /** Counts the number of characters in a given string, stopping if the count exceeds 512 a specified limit. */ 513 template <typename CharPointerType> 514 static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept 515 { 516 size_t len = 0; 517 518 while (len < maxCharsToCount && text.getAndAdvance() != 0) 519 ++len; 520 521 return len; 522 } 523 524 /** Counts the number of characters in a given string, stopping if the count exceeds 525 a specified end-pointer. */ 526 template <typename CharPointerType> 527 static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept 528 { 529 size_t len = 0; 530 531 while (start < end && start.getAndAdvance() != 0) 532 ++len; 533 534 return len; 535 } 536 537 /** Copies null-terminated characters from one string to another. */ 538 template <typename DestCharPointerType, typename SrcCharPointerType> 539 static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept 540 { 541 while (auto c = src.getAndAdvance()) 542 dest.write (c); 543 544 dest.writeNull(); 545 } 546 547 /** Copies characters from one string to another, up to a null terminator 548 or a given byte size limit. */ 549 template <typename DestCharPointerType, typename SrcCharPointerType> 550 static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept 551 { 552 auto startAddress = dest.getAddress(); 553 auto maxBytes = (ssize_t) maxBytesToWrite; 554 maxBytes -= (ssize_t) sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) 555 556 for (;;) 557 { 558 auto c = src.getAndAdvance(); 559 auto bytesNeeded = (ssize_t) DestCharPointerType::getBytesRequiredFor (c); 560 maxBytes -= bytesNeeded; 561 562 if (c == 0 || maxBytes < 0) 563 break; 564 565 dest.write (c); 566 } 567 568 dest.writeNull(); 569 570 return (size_t) getAddressDifference (dest.getAddress(), startAddress) 571 + sizeof (typename DestCharPointerType::CharType); 572 } 573 574 /** Copies characters from one string to another, up to a null terminator 575 or a given maximum number of characters. */ 576 template <typename DestCharPointerType, typename SrcCharPointerType> 577 static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept 578 { 579 while (--maxChars > 0) 580 { 581 auto c = src.getAndAdvance(); 582 583 if (c == 0) 584 break; 585 586 dest.write (c); 587 } 588 589 dest.writeNull(); 590 } 591 592 /** Compares two characters. */ 593 static int compare (juce_wchar char1, juce_wchar char2) noexcept 594 { 595 if (auto diff = static_cast<int> (char1) - static_cast<int> (char2)) 596 return diff < 0 ? -1 : 1; 597 598 return 0; 599 } 600 601 /** Compares two null-terminated character strings. */ 602 template <typename CharPointerType1, typename CharPointerType2> 603 static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept 604 { 605 for (;;) 606 { 607 auto c1 = s1.getAndAdvance(); 608 609 if (auto diff = compare (c1, s2.getAndAdvance())) 610 return diff; 611 612 if (c1 == 0) 613 break; 614 } 615 616 return 0; 617 } 618 619 /** Compares two null-terminated character strings, up to a given number of characters. */ 620 template <typename CharPointerType1, typename CharPointerType2> 621 static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept 622 { 623 while (--maxChars >= 0) 624 { 625 auto c1 = s1.getAndAdvance(); 626 627 if (auto diff = compare (c1, s2.getAndAdvance())) 628 return diff; 629 630 if (c1 == 0) 631 break; 632 } 633 634 return 0; 635 } 636 637 /** Compares two characters, using a case-independant match. */ 638 static int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept 639 { 640 return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0; 641 } 642 643 /** Compares two null-terminated character strings, using a case-independant match. */ 644 template <typename CharPointerType1, typename CharPointerType2> 645 static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept 646 { 647 for (;;) 648 { 649 auto c1 = s1.getAndAdvance(); 650 651 if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance())) 652 return diff; 653 654 if (c1 == 0) 655 break; 656 } 657 658 return 0; 659 } 660 661 /** Compares two null-terminated character strings, using a case-independent match. */ 662 template <typename CharPointerType1, typename CharPointerType2> 663 static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept 664 { 665 while (--maxChars >= 0) 666 { 667 auto c1 = s1.getAndAdvance(); 668 669 if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance())) 670 return diff; 671 672 if (c1 == 0) 673 break; 674 } 675 676 return 0; 677 } 678 679 /** Finds the character index of a given substring in another string. 680 Returns -1 if the substring is not found. 681 */ 682 template <typename CharPointerType1, typename CharPointerType2> 683 static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept 684 { 685 int index = 0; 686 auto substringLength = (int) substringToLookFor.length(); 687 688 for (;;) 689 { 690 if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0) 691 return index; 692 693 if (textToSearch.getAndAdvance() == 0) 694 return -1; 695 696 ++index; 697 } 698 } 699 700 /** Returns a pointer to the first occurrence of a substring in a string. 701 If the substring is not found, this will return a pointer to the string's 702 null terminator. 703 */ 704 template <typename CharPointerType1, typename CharPointerType2> 705 static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept 706 { 707 auto substringLength = (int) substringToLookFor.length(); 708 709 while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0 710 && ! textToSearch.isEmpty()) 711 ++textToSearch; 712 713 return textToSearch; 714 } 715 716 /** Returns a pointer to the first occurrence of a substring in a string. 717 If the substring is not found, this will return a pointer to the string's 718 null terminator. 719 */ 720 template <typename CharPointerType> 721 static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept 722 { 723 for (;; ++textToSearch) 724 { 725 auto c = *textToSearch; 726 727 if (c == charToLookFor || c == 0) 728 break; 729 } 730 731 return textToSearch; 732 } 733 734 /** Finds the character index of a given substring in another string, using 735 a case-independent match. 736 Returns -1 if the substring is not found. 737 */ 738 template <typename CharPointerType1, typename CharPointerType2> 739 static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept 740 { 741 int index = 0; 742 auto needleLength = (int) needle.length(); 743 744 for (;;) 745 { 746 if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0) 747 return index; 748 749 if (haystack.getAndAdvance() == 0) 750 return -1; 751 752 ++index; 753 } 754 } 755 756 /** Finds the character index of a given character in another string. 757 Returns -1 if the character is not found. 758 */ 759 template <typename Type> 760 static int indexOfChar (Type text, const juce_wchar charToFind) noexcept 761 { 762 int i = 0; 763 764 while (! text.isEmpty()) 765 { 766 if (text.getAndAdvance() == charToFind) 767 return i; 768 769 ++i; 770 } 771 772 return -1; 773 } 774 775 /** Finds the character index of a given character in another string, using 776 a case-independent match. 777 Returns -1 if the character is not found. 778 */ 779 template <typename Type> 780 static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept 781 { 782 charToFind = CharacterFunctions::toLowerCase (charToFind); 783 int i = 0; 784 785 while (! text.isEmpty()) 786 { 787 if (text.toLowerCase() == charToFind) 788 return i; 789 790 ++text; 791 ++i; 792 } 793 794 return -1; 795 } 796 797 /** Increments a pointer until it points to the first non-whitespace character 798 in a string. 799 800 If the string contains only whitespace, the pointer will point to the 801 string's null terminator. 802 */ 803 template <typename Type> 804 static void incrementToEndOfWhitespace (Type& text) noexcept 805 { 806 while (text.isWhitespace()) 807 ++text; 808 } 809 810 /** Returns a pointer to the first non-whitespace character in a string. 811 If the string contains only whitespace, this will return a pointer 812 to its null terminator. 813 */ 814 template <typename Type> 815 static Type findEndOfWhitespace (Type text) noexcept 816 { 817 incrementToEndOfWhitespace (text); 818 return text; 819 } 820 821 /** Returns a pointer to the first character in the string which is found in 822 the breakCharacters string. 823 */ 824 template <typename Type, typename BreakType> 825 static Type findEndOfToken (Type text, BreakType breakCharacters, Type quoteCharacters) 826 { 827 juce_wchar currentQuoteChar = 0; 828 829 while (! text.isEmpty()) 830 { 831 auto c = text.getAndAdvance(); 832 833 if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0) 834 { 835 --text; 836 break; 837 } 838 839 if (quoteCharacters.indexOf (c) >= 0) 840 { 841 if (currentQuoteChar == 0) 842 currentQuoteChar = c; 843 else if (currentQuoteChar == c) 844 currentQuoteChar = 0; 845 } 846 } 847 848 return text; 849 } 850 851 private: 852 static double mulexp10 (double value, int exponent) noexcept; 853 }; 854 855 } // namespace juce 856