OpenShot Audio Library | OpenShotAudio  0.6.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 
23 namespace 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
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 //==============================================================================
91 class JUCE_API CharacterFunctions
92 {
93 public:
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>
491  struct HexParser
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 
854 private:
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