OpenShot Audio Library | OpenShotAudio 0.4.0
juce_CharacterFunctions.h
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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
23namespace juce
24{
25
26//==============================================================================
27#if JUCE_WINDOWS && ! defined (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
33 #define JUCE_NATIVE_WCHAR_IS_UTF8 0
35 #define JUCE_NATIVE_WCHAR_IS_UTF16 0
37 #define JUCE_NATIVE_WCHAR_IS_UTF32 1
38#endif
39
40#if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN
42 using juce_wchar = wchar_t;
43#else
44 using juce_wchar = uint32;
45#endif
46
47#ifndef DOXYGEN
49 #define JUCE_T(stringLiteral) (L##stringLiteral)
50#endif
51
52#if JUCE_DEFINE_T_MACRO
60 #define T(stringLiteral) JUCE_T(stringLiteral)
61#endif
62
63#ifndef DOXYGEN
64
65//==============================================================================
66// GNU libstdc++ does not have std::make_unsigned
67namespace 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//==============================================================================
91class JUCE_API CharacterFunctions
92{
93public:
94 //==============================================================================
96 static juce_wchar toUpperCase (juce_wchar character) noexcept;
98 static juce_wchar toLowerCase (juce_wchar character) noexcept;
99
101 static bool isUpperCase (juce_wchar character) noexcept;
103 static bool isLowerCase (juce_wchar character) noexcept;
104
106 static bool isWhitespace (char character) noexcept;
108 static bool isWhitespace (juce_wchar character) noexcept;
109
111 static bool isDigit (char character) noexcept;
113 static bool isDigit (juce_wchar character) noexcept;
114
116 static bool isLetter (char character) noexcept;
118 static bool isLetter (juce_wchar character) noexcept;
119
121 static bool isLetterOrDigit (char character) noexcept;
123 static bool isLetterOrDigit (juce_wchar character) noexcept;
124
128 static bool isPrintable (char character) noexcept;
129
133 static bool isPrintable (juce_wchar character) noexcept;
134
136 static int getHexDigitValue (juce_wchar digit) noexcept;
137
139 static juce_wchar getUnicodeCharFromWindows1252Codepage (uint8 windows1252Char) noexcept;
140
141 //==============================================================================
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
456 template <typename CharPointerType>
457 static double getDoubleValue (CharPointerType text) noexcept
458 {
459 return readDoubleValue (text);
460 }
461
462 //==============================================================================
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
490 template <typename ResultType>
492 {
493 static_assert (std::is_unsigned_v<ResultType>, "ResultType must be unsigned because "
494 "left-shifting a negative value is UB");
495
496 template <typename CharPointerType>
497 static ResultType parse (CharPointerType t) noexcept
498 {
499 ResultType result = 0;
500
501 while (! t.isEmpty())
502 {
503 auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance());
504
505 if (hexValue >= 0)
506 result = static_cast<ResultType> (result << 4) | static_cast<ResultType> (hexValue);
507 }
508
509 return result;
510 }
511 };
512
513 //==============================================================================
516 template <typename CharPointerType>
517 static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept
518 {
519 size_t len = 0;
520
521 while (len < maxCharsToCount && text.getAndAdvance() != 0)
522 ++len;
523
524 return len;
525 }
526
529 template <typename CharPointerType>
530 static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept
531 {
532 size_t len = 0;
533
534 while (start < end && start.getAndAdvance() != 0)
535 ++len;
536
537 return len;
538 }
539
541 template <typename DestCharPointerType, typename SrcCharPointerType>
542 static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept
543 {
544 while (auto c = src.getAndAdvance())
545 dest.write (c);
546
547 dest.writeNull();
548 }
549
552 template <typename DestCharPointerType, typename SrcCharPointerType>
553 static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
554 {
555 auto startAddress = dest.getAddress();
556 auto maxBytes = (ssize_t) maxBytesToWrite;
557 maxBytes -= (ssize_t) sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null)
558
559 for (;;)
560 {
561 auto c = src.getAndAdvance();
562 auto bytesNeeded = (ssize_t) DestCharPointerType::getBytesRequiredFor (c);
563 maxBytes -= bytesNeeded;
564
565 if (c == 0 || maxBytes < 0)
566 break;
567
568 dest.write (c);
569 }
570
571 dest.writeNull();
572
573 return (size_t) getAddressDifference (dest.getAddress(), startAddress)
574 + sizeof (typename DestCharPointerType::CharType);
575 }
576
579 template <typename DestCharPointerType, typename SrcCharPointerType>
580 static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept
581 {
582 while (--maxChars > 0)
583 {
584 auto c = src.getAndAdvance();
585
586 if (c == 0)
587 break;
588
589 dest.write (c);
590 }
591
592 dest.writeNull();
593 }
594
596 static int compare (juce_wchar char1, juce_wchar char2) noexcept
597 {
598 if (auto diff = static_cast<int> (char1) - static_cast<int> (char2))
599 return diff < 0 ? -1 : 1;
600
601 return 0;
602 }
603
605 template <typename CharPointerType1, typename CharPointerType2>
606 static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept
607 {
608 for (;;)
609 {
610 auto c1 = s1.getAndAdvance();
611
612 if (auto diff = compare (c1, s2.getAndAdvance()))
613 return diff;
614
615 if (c1 == 0)
616 break;
617 }
618
619 return 0;
620 }
621
623 template <typename CharPointerType1, typename CharPointerType2>
624 static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
625 {
626 while (--maxChars >= 0)
627 {
628 auto c1 = s1.getAndAdvance();
629
630 if (auto diff = compare (c1, s2.getAndAdvance()))
631 return diff;
632
633 if (c1 == 0)
634 break;
635 }
636
637 return 0;
638 }
639
641 static int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept
642 {
643 return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0;
644 }
645
647 template <typename CharPointerType1, typename CharPointerType2>
648 static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept
649 {
650 for (;;)
651 {
652 auto c1 = s1.getAndAdvance();
653
654 if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance()))
655 return diff;
656
657 if (c1 == 0)
658 break;
659 }
660
661 return 0;
662 }
663
665 template <typename CharPointerType1, typename CharPointerType2>
666 static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
667 {
668 while (--maxChars >= 0)
669 {
670 auto c1 = s1.getAndAdvance();
671
672 if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance()))
673 return diff;
674
675 if (c1 == 0)
676 break;
677 }
678
679 return 0;
680 }
681
685 template <typename CharPointerType1, typename CharPointerType2>
686 static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
687 {
688 int index = 0;
689 auto substringLength = (int) substringToLookFor.length();
690
691 for (;;)
692 {
693 if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0)
694 return index;
695
696 if (textToSearch.getAndAdvance() == 0)
697 return -1;
698
699 ++index;
700 }
701 }
702
707 template <typename CharPointerType1, typename CharPointerType2>
708 static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
709 {
710 auto substringLength = (int) substringToLookFor.length();
711
712 while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0
713 && ! textToSearch.isEmpty())
714 ++textToSearch;
715
716 return textToSearch;
717 }
718
723 template <typename CharPointerType>
724 static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
725 {
726 for (;; ++textToSearch)
727 {
728 auto c = *textToSearch;
729
730 if (c == charToLookFor || c == 0)
731 break;
732 }
733
734 return textToSearch;
735 }
736
741 template <typename CharPointerType1, typename CharPointerType2>
742 static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept
743 {
744 int index = 0;
745 auto needleLength = (int) needle.length();
746
747 for (;;)
748 {
749 if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0)
750 return index;
751
752 if (haystack.getAndAdvance() == 0)
753 return -1;
754
755 ++index;
756 }
757 }
758
762 template <typename Type>
763 static int indexOfChar (Type text, const juce_wchar charToFind) noexcept
764 {
765 int i = 0;
766
767 while (! text.isEmpty())
768 {
769 if (text.getAndAdvance() == charToFind)
770 return i;
771
772 ++i;
773 }
774
775 return -1;
776 }
777
782 template <typename Type>
783 static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept
784 {
785 charToFind = CharacterFunctions::toLowerCase (charToFind);
786 int i = 0;
787
788 while (! text.isEmpty())
789 {
790 if (text.toLowerCase() == charToFind)
791 return i;
792
793 ++text;
794 ++i;
795 }
796
797 return -1;
798 }
799
806 template <typename Type>
807 static void incrementToEndOfWhitespace (Type& text) noexcept
808 {
809 while (text.isWhitespace())
810 ++text;
811 }
812
817 template <typename Type>
818 static Type findEndOfWhitespace (Type text) noexcept
819 {
820 incrementToEndOfWhitespace (text);
821 return text;
822 }
823
827 template <typename Type, typename BreakType>
828 static Type findEndOfToken (Type text, BreakType breakCharacters, Type quoteCharacters)
829 {
830 juce_wchar currentQuoteChar = 0;
831
832 while (! text.isEmpty())
833 {
834 auto c = text.getAndAdvance();
835
836 if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0)
837 {
838 --text;
839 break;
840 }
841
842 if (quoteCharacters.indexOf (c) >= 0)
843 {
844 if (currentQuoteChar == 0)
845 currentQuoteChar = c;
846 else if (currentQuoteChar == c)
847 currentQuoteChar = 0;
848 }
849 }
850
851 return text;
852 }
853
854private:
855 static double mulexp10 (double value, int exponent) noexcept;
856};
857
858} // namespace juce
static int indexOfIgnoreCase(CharPointerType1 haystack, const CharPointerType2 needle) noexcept
static void incrementToEndOfWhitespace(Type &text) noexcept
static int compare(juce_wchar char1, juce_wchar char2) noexcept
static juce_wchar toLowerCase(juce_wchar character) noexcept
static size_t copyWithDestByteLimit(DestCharPointerType &dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
static int indexOfCharIgnoreCase(Type text, juce_wchar charToFind) noexcept
static IntType getIntValue(const CharPointerType text) noexcept
static int compareIgnoreCaseUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
static int indexOfChar(Type text, const juce_wchar charToFind) noexcept
static int compare(CharPointerType1 s1, CharPointerType2 s2) noexcept
static int compareIgnoreCase(juce_wchar char1, juce_wchar char2) noexcept
static size_t lengthUpTo(CharPointerType start, const CharPointerType end) noexcept
static int indexOf(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
static size_t lengthUpTo(CharPointerType text, const size_t maxCharsToCount) noexcept
static double readDoubleValue(CharPointerType &text) noexcept
static CharPointerType find(CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
static Type findEndOfWhitespace(Type text) noexcept
static void copyWithCharLimit(DestCharPointerType &dest, SrcCharPointerType src, int maxChars) noexcept
static int getHexDigitValue(juce_wchar digit) noexcept
static Type findEndOfToken(Type text, BreakType breakCharacters, Type quoteCharacters)
static CharPointerType1 find(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
static int compareIgnoreCase(CharPointerType1 s1, CharPointerType2 s2) noexcept
static double getDoubleValue(CharPointerType text) noexcept
static void copyAll(DestCharPointerType &dest, SrcCharPointerType src) noexcept
static int compareUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept