OpenShot Audio Library | OpenShotAudio  0.3.0
juce_CharacterFunctions.h
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 
33  #define JUCE_NATIVE_WCHAR_IS_UTF8 0
34 
35  #define JUCE_NATIVE_WCHAR_IS_UTF16 0
36 
37  #define JUCE_NATIVE_WCHAR_IS_UTF32 1
38 #endif
39 
40 #if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN
41 
42  using juce_wchar = wchar_t;
43 #else
44  using juce_wchar = uint32;
45 #endif
46 
47 #ifndef DOXYGEN
48 
49  #define JUCE_T(stringLiteral) (L##stringLiteral)
50 #endif
51 
52 #if JUCE_DEFINE_T_MACRO
53 
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 //==============================================================================
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  #if JUCE_MINGW
150  bool isNegative = false;
151  #else
152  JUCE_CONSTEXPR const int maxSignificantDigits = 17 + 1; // An additional digit for rounding
153  JUCE_CONSTEXPR const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator
154  char buffer[(size_t) bufferSize] = {};
155  char* currentCharacter = &(buffer[0]);
156  #endif
157 
158  text = text.findEndOfWhitespace();
159  auto c = *text;
160 
161  switch (c)
162  {
163  case '-':
164  #if JUCE_MINGW
165  isNegative = true;
166  #else
167  *currentCharacter++ = '-';
168  #endif
169  // Fall-through..
170  case '+':
171  c = *++text;
172  }
173 
174  switch (c)
175  {
176  case 'n':
177  case 'N':
178  if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N'))
179  return std::numeric_limits<double>::quiet_NaN();
180  break;
181 
182  case 'i':
183  case 'I':
184  if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F'))
185  return std::numeric_limits<double>::infinity();
186  break;
187  }
188 
189  #if JUCE_MINGW
190  // MinGW does not have access to the locale functions required for strtold, so we parse the doubles
191  // ourselves. There are some edge cases where the least significant digit will be wrong!
192  double result[3] = { 0 }, accumulator[2] = { 0 };
193  int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 };
194  int exponent = 0, decPointIndex = 0, digit = 0;
195  int lastDigit = 0, numSignificantDigits = 0;
196  bool digitsFound = false;
197  JUCE_CONSTEXPR const int maxSignificantDigits = 17 + 1;
198 
199  for (;;)
200  {
201  if (text.isDigit())
202  {
203  lastDigit = digit;
204  digit = (int) text.getAndAdvance() - '0';
205  digitsFound = true;
206 
207  if (decPointIndex != 0)
208  exponentAdjustment[1]++;
209 
210  if (numSignificantDigits == 0 && digit == 0)
211  continue;
212 
213  if (++numSignificantDigits > maxSignificantDigits)
214  {
215  if (digit > 5)
216  ++accumulator [decPointIndex];
217  else if (digit == 5 && (lastDigit & 1) != 0)
218  ++accumulator [decPointIndex];
219 
220  if (decPointIndex > 0)
221  exponentAdjustment[1]--;
222  else
223  exponentAdjustment[0]++;
224 
225  while (text.isDigit())
226  {
227  ++text;
228  if (decPointIndex == 0)
229  exponentAdjustment[0]++;
230  }
231  }
232  else
233  {
234  const auto maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10);
235  if (accumulator [decPointIndex] > maxAccumulatorValue)
236  {
237  result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex])
238  + accumulator [decPointIndex];
239  accumulator [decPointIndex] = 0;
240  exponentAccumulator [decPointIndex] = 0;
241  }
242 
243  accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit;
244  exponentAccumulator [decPointIndex]++;
245  }
246  }
247  else if (decPointIndex == 0 && *text == '.')
248  {
249  ++text;
250  decPointIndex = 1;
251 
252  if (numSignificantDigits > maxSignificantDigits)
253  {
254  while (text.isDigit())
255  ++text;
256  break;
257  }
258  }
259  else
260  {
261  break;
262  }
263  }
264 
265  result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0];
266 
267  if (decPointIndex != 0)
268  result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1];
269 
270  c = *text;
271  if ((c == 'e' || c == 'E') && digitsFound)
272  {
273  auto negativeExponent = false;
274 
275  switch (*++text)
276  {
277  case '-': negativeExponent = true; // fall-through..
278  case '+': ++text;
279  }
280 
281  while (text.isDigit())
282  exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0');
283 
284  if (negativeExponent)
285  exponent = -exponent;
286  }
287 
288  auto r = mulexp10 (result[0], exponent + exponentAdjustment[0]);
289  if (decPointIndex != 0)
290  r += mulexp10 (result[1], exponent - exponentAdjustment[1]);
291 
292  return isNegative ? -r : r;
293 
294  #else // ! JUCE_MINGW
295 
296  int numSigFigs = 0;
297  bool decimalPointFound = false;
298  int extraExponent = 0;
299 
300  for (;;)
301  {
302  if (text.isDigit())
303  {
304  auto digit = (int) text.getAndAdvance() - '0';
305 
306  if (decimalPointFound)
307  {
308  if (numSigFigs >= maxSignificantDigits)
309  continue;
310  }
311  else
312  {
313  if (numSigFigs >= maxSignificantDigits)
314  {
315  ++extraExponent;
316  continue;
317  }
318 
319  if (numSigFigs == 0 && digit == 0)
320  continue;
321  }
322 
323  *currentCharacter++ = (char) ('0' + (char) digit);
324  numSigFigs++;
325  }
326  else if ((! decimalPointFound) && *text == '.')
327  {
328  ++text;
329  *currentCharacter++ = '.';
330  decimalPointFound = true;
331  }
332  else
333  {
334  break;
335  }
336  }
337 
338  c = *text;
339 
340  auto writeExponentDigits = [](int exponent, char* destination)
341  {
342  auto exponentDivisor = 100;
343 
344  while (exponentDivisor > 1)
345  {
346  auto digit = exponent / exponentDivisor;
347  *destination++ = (char) ('0' + (char) digit);
348  exponent -= digit * exponentDivisor;
349  exponentDivisor /= 10;
350  }
351 
352  *destination++ = (char) ('0' + (char) exponent);
353  };
354 
355  if ((c == 'e' || c == 'E') && numSigFigs > 0)
356  {
357  *currentCharacter++ = 'e';
358  bool parsedExponentIsPositive = true;
359 
360  switch (*++text)
361  {
362  case '-': parsedExponentIsPositive = false; // Fall-through..
363  case '+': ++text;
364  }
365 
366  int exponent = 0;
367 
368  while (text.isDigit())
369  {
370  auto digit = (int) text.getAndAdvance() - '0';
371 
372  if (digit != 0 || exponent != 0)
373  exponent = (exponent * 10) + digit;
374  }
375 
376  exponent = extraExponent + (parsedExponentIsPositive ? exponent : -exponent);
377 
378  if (exponent < 0)
379  *currentCharacter++ = '-';
380 
381  exponent = std::abs (exponent);
382 
383  if (exponent > std::numeric_limits<double>::max_exponent10)
384  return std::numeric_limits<double>::quiet_NaN();
385 
386  writeExponentDigits (exponent, currentCharacter);
387  }
388  else if (extraExponent > 0)
389  {
390  *currentCharacter++ = 'e';
391  writeExponentDigits (extraExponent, currentCharacter);
392  }
393 
394  #if JUCE_WINDOWS
395  static _locale_t locale = _create_locale (LC_ALL, "C");
396  return _strtod_l (&buffer[0], nullptr, locale);
397  #else
398  static locale_t locale = newlocale (LC_ALL_MASK, "C", nullptr);
399  #if JUCE_ANDROID
400  return (double) strtold_l (&buffer[0], nullptr, locale);
401  #else
402  return strtod_l (&buffer[0], nullptr, locale);
403  #endif
404  #endif
405 
406  #endif // JUCE_MINGW
407  }
408 
410  template <typename CharPointerType>
411  static double getDoubleValue (CharPointerType text) noexcept
412  {
413  return readDoubleValue (text);
414  }
415 
416  //==============================================================================
418  template <typename IntType, typename CharPointerType>
419  static IntType getIntValue (const CharPointerType text) noexcept
420  {
421  using UIntType = typename internal::make_unsigned<IntType>::type;
422 
423  UIntType v = 0;
424  auto s = text.findEndOfWhitespace();
425  const bool isNeg = *s == '-';
426 
427  if (isNeg)
428  ++s;
429 
430  for (;;)
431  {
432  auto c = s.getAndAdvance();
433 
434  if (c >= '0' && c <= '9')
435  v = v * 10 + (UIntType) (c - '0');
436  else
437  break;
438  }
439 
440  return isNeg ? - (IntType) v : (IntType) v;
441  }
442 
444  template <typename ResultType>
445  struct HexParser
446  {
447  template <typename CharPointerType>
448  static ResultType parse (CharPointerType t) noexcept
449  {
450  ResultType result = 0;
451 
452  while (! t.isEmpty())
453  {
454  auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance());
455 
456  if (hexValue >= 0)
457  result = (result << 4) | hexValue;
458  }
459 
460  return result;
461  }
462  };
463 
464  //==============================================================================
467  template <typename CharPointerType>
468  static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept
469  {
470  size_t len = 0;
471 
472  while (len < maxCharsToCount && text.getAndAdvance() != 0)
473  ++len;
474 
475  return len;
476  }
477 
480  template <typename CharPointerType>
481  static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept
482  {
483  size_t len = 0;
484 
485  while (start < end && start.getAndAdvance() != 0)
486  ++len;
487 
488  return len;
489  }
490 
492  template <typename DestCharPointerType, typename SrcCharPointerType>
493  static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept
494  {
495  while (auto c = src.getAndAdvance())
496  dest.write (c);
497 
498  dest.writeNull();
499  }
500 
503  template <typename DestCharPointerType, typename SrcCharPointerType>
504  static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
505  {
506  auto startAddress = dest.getAddress();
507  auto maxBytes = (ssize_t) maxBytesToWrite;
508  maxBytes -= (ssize_t) sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null)
509 
510  for (;;)
511  {
512  auto c = src.getAndAdvance();
513  auto bytesNeeded = (ssize_t) DestCharPointerType::getBytesRequiredFor (c);
514  maxBytes -= bytesNeeded;
515 
516  if (c == 0 || maxBytes < 0)
517  break;
518 
519  dest.write (c);
520  }
521 
522  dest.writeNull();
523 
524  return (size_t) getAddressDifference (dest.getAddress(), startAddress)
525  + sizeof (typename DestCharPointerType::CharType);
526  }
527 
530  template <typename DestCharPointerType, typename SrcCharPointerType>
531  static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept
532  {
533  while (--maxChars > 0)
534  {
535  auto c = src.getAndAdvance();
536 
537  if (c == 0)
538  break;
539 
540  dest.write (c);
541  }
542 
543  dest.writeNull();
544  }
545 
547  static inline int compare (juce_wchar char1, juce_wchar char2) noexcept
548  {
549  if (auto diff = static_cast<int> (char1) - static_cast<int> (char2))
550  return diff < 0 ? -1 : 1;
551 
552  return 0;
553  }
554 
556  template <typename CharPointerType1, typename CharPointerType2>
557  static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept
558  {
559  for (;;)
560  {
561  auto c1 = s1.getAndAdvance();
562 
563  if (auto diff = compare (c1, s2.getAndAdvance()))
564  return diff;
565 
566  if (c1 == 0)
567  break;
568  }
569 
570  return 0;
571  }
572 
574  template <typename CharPointerType1, typename CharPointerType2>
575  static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
576  {
577  while (--maxChars >= 0)
578  {
579  auto c1 = s1.getAndAdvance();
580 
581  if (auto diff = compare (c1, s2.getAndAdvance()))
582  return diff;
583 
584  if (c1 == 0)
585  break;
586  }
587 
588  return 0;
589  }
590 
592  static inline int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept
593  {
594  return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0;
595  }
596 
598  template <typename CharPointerType1, typename CharPointerType2>
599  static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept
600  {
601  for (;;)
602  {
603  auto c1 = s1.getAndAdvance();
604 
605  if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance()))
606  return diff;
607 
608  if (c1 == 0)
609  break;
610  }
611 
612  return 0;
613  }
614 
616  template <typename CharPointerType1, typename CharPointerType2>
617  static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
618  {
619  while (--maxChars >= 0)
620  {
621  auto c1 = s1.getAndAdvance();
622 
623  if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance()))
624  return diff;
625 
626  if (c1 == 0)
627  break;
628  }
629 
630  return 0;
631  }
632 
636  template <typename CharPointerType1, typename CharPointerType2>
637  static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
638  {
639  int index = 0;
640  auto substringLength = (int) substringToLookFor.length();
641 
642  for (;;)
643  {
644  if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0)
645  return index;
646 
647  if (textToSearch.getAndAdvance() == 0)
648  return -1;
649 
650  ++index;
651  }
652  }
653 
658  template <typename CharPointerType1, typename CharPointerType2>
659  static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
660  {
661  auto substringLength = (int) substringToLookFor.length();
662 
663  while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0
664  && ! textToSearch.isEmpty())
665  ++textToSearch;
666 
667  return textToSearch;
668  }
669 
674  template <typename CharPointerType>
675  static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
676  {
677  for (;; ++textToSearch)
678  {
679  auto c = *textToSearch;
680 
681  if (c == charToLookFor || c == 0)
682  break;
683  }
684 
685  return textToSearch;
686  }
687 
692  template <typename CharPointerType1, typename CharPointerType2>
693  static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept
694  {
695  int index = 0;
696  auto needleLength = (int) needle.length();
697 
698  for (;;)
699  {
700  if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0)
701  return index;
702 
703  if (haystack.getAndAdvance() == 0)
704  return -1;
705 
706  ++index;
707  }
708  }
709 
713  template <typename Type>
714  static int indexOfChar (Type text, const juce_wchar charToFind) noexcept
715  {
716  int i = 0;
717 
718  while (! text.isEmpty())
719  {
720  if (text.getAndAdvance() == charToFind)
721  return i;
722 
723  ++i;
724  }
725 
726  return -1;
727  }
728 
733  template <typename Type>
734  static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept
735  {
736  charToFind = CharacterFunctions::toLowerCase (charToFind);
737  int i = 0;
738 
739  while (! text.isEmpty())
740  {
741  if (text.toLowerCase() == charToFind)
742  return i;
743 
744  ++text;
745  ++i;
746  }
747 
748  return -1;
749  }
750 
755  template <typename Type>
756  static Type findEndOfWhitespace (Type text) noexcept
757  {
758  while (text.isWhitespace())
759  ++text;
760 
761  return text;
762  }
763 
767  template <typename Type, typename BreakType>
768  static Type findEndOfToken (Type text, BreakType breakCharacters, Type quoteCharacters)
769  {
770  juce_wchar currentQuoteChar = 0;
771 
772  while (! text.isEmpty())
773  {
774  auto c = text.getAndAdvance();
775 
776  if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0)
777  {
778  --text;
779  break;
780  }
781 
782  if (quoteCharacters.indexOf (c) >= 0)
783  {
784  if (currentQuoteChar == 0)
785  currentQuoteChar = c;
786  else if (currentQuoteChar == c)
787  currentQuoteChar = 0;
788  }
789  }
790 
791  return text;
792  }
793 
794 private:
795  static double mulexp10 (double value, int exponent) noexcept;
796 };
797 
798 } // namespace juce
static double getDoubleValue(CharPointerType text) noexcept
static Type findEndOfToken(Type text, BreakType breakCharacters, Type quoteCharacters)
static size_t lengthUpTo(CharPointerType start, const CharPointerType end) noexcept
static int indexOfCharIgnoreCase(Type text, juce_wchar charToFind) noexcept
static void copyAll(DestCharPointerType &dest, SrcCharPointerType src) noexcept
static int compareIgnoreCase(juce_wchar char1, juce_wchar char2) noexcept
static double readDoubleValue(CharPointerType &text) noexcept
static Type findEndOfWhitespace(Type text) noexcept
static int compare(CharPointerType1 s1, CharPointerType2 s2) noexcept
static IntType getIntValue(const CharPointerType text) noexcept
static int indexOfChar(Type text, const juce_wchar charToFind) noexcept
static int compareIgnoreCaseUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
static CharPointerType1 find(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
static size_t lengthUpTo(CharPointerType text, const size_t maxCharsToCount) noexcept
static CharPointerType find(CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
static size_t copyWithDestByteLimit(DestCharPointerType &dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
static int getHexDigitValue(juce_wchar digit) noexcept
static juce_wchar toLowerCase(juce_wchar character) noexcept
static int compareIgnoreCase(CharPointerType1 s1, CharPointerType2 s2) noexcept
static int indexOf(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
static int compareUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
static int indexOfIgnoreCase(CharPointerType1 haystack, const CharPointerType2 needle) noexcept
static void copyWithCharLimit(DestCharPointerType &dest, SrcCharPointerType src, int maxChars) noexcept
static int compare(juce_wchar char1, juce_wchar char2) noexcept