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