Loading...
Searching...
No Matches
juce_CharacterFunctions.h
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
#if JUCE_WINDOWS && ! defined (DOXYGEN)
#define JUCE_NATIVE_WCHAR_IS_UTF8 0
#define JUCE_NATIVE_WCHAR_IS_UTF16 1
#define JUCE_NATIVE_WCHAR_IS_UTF32 0
#else
#define JUCE_NATIVE_WCHAR_IS_UTF8 0
#define JUCE_NATIVE_WCHAR_IS_UTF16 0
#define JUCE_NATIVE_WCHAR_IS_UTF32 1
#endif
#if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN
using juce_wchar = wchar_t;
#else
using juce_wchar = uint32;
#endif
#ifndef DOXYGEN
#define JUCE_T(stringLiteral) (L##stringLiteral)
#endif
#if JUCE_DEFINE_T_MACRO
#define T(stringLiteral) JUCE_T(stringLiteral)
#endif
#ifndef DOXYGEN
//==============================================================================
// GNU libstdc++ does not have std::make_unsigned
namespace internal
{
template <typename Type> struct make_unsigned { using type = Type; };
template <> struct make_unsigned<signed char> { using type = unsigned char; };
template <> struct make_unsigned<char> { using type = unsigned char; };
template <> struct make_unsigned<short> { using type = unsigned short; };
template <> struct make_unsigned<int> { using type = unsigned int; };
template <> struct make_unsigned<long> { using type = unsigned long; };
template <> struct make_unsigned<long long> { using type = unsigned long long; };
}
#endif
//==============================================================================
class JUCE_API CharacterFunctions
{
public:
//==============================================================================
static juce_wchar toUpperCase (juce_wchar character) noexcept;
static juce_wchar toLowerCase (juce_wchar character) noexcept;
static bool isUpperCase (juce_wchar character) noexcept;
static bool isLowerCase (juce_wchar character) noexcept;
static bool isWhitespace (char character) noexcept;
static bool isWhitespace (juce_wchar character) noexcept;
static bool isDigit (char character) noexcept;
static bool isDigit (juce_wchar character) noexcept;
static bool isLetter (char character) noexcept;
static bool isLetter (juce_wchar character) noexcept;
static bool isLetterOrDigit (char character) noexcept;
static bool isLetterOrDigit (juce_wchar character) noexcept;
static bool isPrintable (char character) noexcept;
static bool isPrintable (juce_wchar character) noexcept;
static int getHexDigitValue (juce_wchar digit) noexcept;
static juce_wchar getUnicodeCharFromWindows1252Codepage (uint8 windows1252Char) noexcept;
//==============================================================================
template <typename CharPointerType>
static double readDoubleValue (CharPointerType& text) noexcept
{
constexpr auto inf = std::numeric_limits<double>::infinity();
bool isNegative = false;
#if ! JUCE_MINGW
constexpr const int maxSignificantDigits = 17 + 1; // An additional digit for rounding
constexpr const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator
char buffer[(size_t) bufferSize] = {};
char* writePtr = &(buffer[0]);
#endif
const auto endOfWhitspace = text.findEndOfWhitespace();
text = endOfWhitspace;
auto c = *text;
switch (c)
{
case '-':
isNegative = true;
#if ! JUCE_MINGW
*writePtr++ = '-';
#endif
JUCE_FALLTHROUGH
case '+':
c = *++text;
break;
default:
break;
}
switch (c)
{
case 'n':
case 'N':
{
if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N'))
{
text += 3;
return std::numeric_limits<double>::quiet_NaN();
}
text = endOfWhitspace;
return 0.0;
}
case 'i':
case 'I':
{
if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F'))
{
text += 3;
return isNegative ? -inf : inf;
}
text = endOfWhitspace;
return 0.0;
}
default:
break;
}
#if JUCE_MINGW
// MinGW does not have access to the locale functions required for strtold, so we parse the doubles
// ourselves. There are some edge cases where the least significant digit will be wrong!
double result[3] = { 0 }, accumulator[2] = { 0 };
int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 };
int exponent = 0, decPointIndex = 0, digit = 0;
int lastDigit = 0, numSignificantDigits = 0;
bool digitsFound = false;
constexpr const int maxSignificantDigits = 17 + 1;
for (;;)
{
if (text.isDigit())
{
lastDigit = digit;
digit = (int) text.getAndAdvance() - '0';
digitsFound = true;
if (decPointIndex != 0)
exponentAdjustment[1]++;
if (numSignificantDigits == 0 && digit == 0)
continue;
if (++numSignificantDigits > maxSignificantDigits)
{
if (digit > 5)
++accumulator [decPointIndex];
else if (digit == 5 && (lastDigit & 1) != 0)
++accumulator [decPointIndex];
if (decPointIndex > 0)
exponentAdjustment[1]--;
else
exponentAdjustment[0]++;
while (text.isDigit())
{
++text;
if (decPointIndex == 0)
exponentAdjustment[0]++;
}
}
else
{
const auto maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10);
if (accumulator [decPointIndex] > maxAccumulatorValue)
{
result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex])
+ accumulator [decPointIndex];
accumulator [decPointIndex] = 0;
exponentAccumulator [decPointIndex] = 0;
}
accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit;
exponentAccumulator [decPointIndex]++;
}
}
else if (decPointIndex == 0 && *text == '.')
{
++text;
decPointIndex = 1;
if (numSignificantDigits > maxSignificantDigits)
{
while (text.isDigit())
++text;
break;
}
}
else
{
break;
}
}
result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0];
if (decPointIndex != 0)
result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1];
c = *text;
if ((c == 'e' || c == 'E') && digitsFound)
{
auto negativeExponent = false;
switch (*++text)
{
case '-': negativeExponent = true; JUCE_FALLTHROUGH
case '+': ++text;
}
while (text.isDigit())
exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0');
if (negativeExponent)
exponent = -exponent;
}
auto r = mulexp10 (result[0], exponent + exponentAdjustment[0]);
if (decPointIndex != 0)
r += mulexp10 (result[1], exponent - exponentAdjustment[1]);
return isNegative ? -r : r;
#else // ! JUCE_MINGW
int numSigFigs = 0, extraExponent = 0;
bool decimalPointFound = false, leadingZeros = false;
for (;;)
{
if (text.isDigit())
{
auto digit = (int) text.getAndAdvance() - '0';
if (decimalPointFound)
{
if (numSigFigs >= maxSignificantDigits)
continue;
}
else
{
if (numSigFigs >= maxSignificantDigits)
{
++extraExponent;
continue;
}
if (numSigFigs == 0 && digit == 0)
{
leadingZeros = true;
continue;
}
}
*writePtr++ = (char) ('0' + (char) digit);
numSigFigs++;
}
else if ((! decimalPointFound) && *text == '.')
{
++text;
*writePtr++ = '.';
decimalPointFound = true;
}
else
{
break;
}
}
if ((! leadingZeros) && (numSigFigs == 0))
{
text = endOfWhitspace;
return 0.0;
}
auto writeExponentDigits = [] (int exponent, char* destination)
{
auto exponentDivisor = 100;
while (exponentDivisor > 1)
{
auto digit = exponent / exponentDivisor;
*destination++ = (char) ('0' + (char) digit);
exponent -= digit * exponentDivisor;
exponentDivisor /= 10;
}
*destination++ = (char) ('0' + (char) exponent);
};
c = *text;
if (c == 'e' || c == 'E')
{
const auto startOfExponent = text;
*writePtr++ = 'e';
bool parsedExponentIsPositive = true;
switch (*++text)
{
case '-':
parsedExponentIsPositive = false;
JUCE_FALLTHROUGH
case '+':
++text;
break;
default:
break;
}
int exponent = 0;
const auto startOfExponentDigits = text;
while (text.isDigit())
{
auto digit = (int) text.getAndAdvance() - '0';
if (digit != 0 || exponent != 0)
exponent = (exponent * 10) + digit;
}
if (text == startOfExponentDigits)
text = startOfExponent;
exponent = extraExponent + (parsedExponentIsPositive ? exponent : -exponent);
if (exponent < 0)
{
if (exponent < std::numeric_limits<double>::min_exponent10 - 1)
return isNegative ? -0.0 : 0.0;
*writePtr++ = '-';
exponent = -exponent;
}
else if (exponent > std::numeric_limits<double>::max_exponent10 + 1)
{
return isNegative ? -inf : inf;
}
writeExponentDigits (exponent, writePtr);
}
else if (extraExponent > 0)
{
*writePtr++ = 'e';
writeExponentDigits (extraExponent, writePtr);
}
#if JUCE_WINDOWS
static _locale_t locale = _create_locale (LC_ALL, "C");
return _strtod_l (&buffer[0], nullptr, locale);
#else
static locale_t locale = newlocale (LC_ALL_MASK, "C", nullptr);
#if JUCE_ANDROID
return (double) strtold_l (&buffer[0], nullptr, locale);
#else
return strtod_l (&buffer[0], nullptr, locale);
#endif
#endif
#endif // JUCE_MINGW
}
template <typename CharPointerType>
static double getDoubleValue (CharPointerType text) noexcept
{
return readDoubleValue (text);
}
//==============================================================================
template <typename IntType, typename CharPointerType>
static IntType getIntValue (const CharPointerType text) noexcept
{
using UIntType = typename internal::make_unsigned<IntType>::type;
UIntType v = 0;
auto s = text.findEndOfWhitespace();
const bool isNeg = *s == '-';
if (isNeg)
++s;
for (;;)
{
auto c = s.getAndAdvance();
if (c >= '0' && c <= '9')
v = v * 10 + (UIntType) (c - '0');
else
break;
}
return isNeg ? - (IntType) v : (IntType) v;
}
template <typename ResultType>
struct HexParser
{
static_assert (std::is_unsigned_v<ResultType>, "ResultType must be unsigned because "
"left-shifting a negative value is UB");
template <typename CharPointerType>
static ResultType parse (CharPointerType t) noexcept
{
ResultType result = 0;
while (! t.isEmpty())
{
auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance());
if (hexValue >= 0)
result = static_cast<ResultType> (result << 4) | static_cast<ResultType> (hexValue);
}
return result;
}
};
//==============================================================================
template <typename CharPointerType>
static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept
{
size_t len = 0;
while (len < maxCharsToCount && text.getAndAdvance() != 0)
++len;
return len;
}
template <typename CharPointerType>
static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept
{
size_t len = 0;
while (start < end && start.getAndAdvance() != 0)
++len;
return len;
}
template <typename DestCharPointerType, typename SrcCharPointerType>
static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept
{
while (auto c = src.getAndAdvance())
dest.write (c);
dest.writeNull();
}
template <typename DestCharPointerType, typename SrcCharPointerType>
static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
{
auto startAddress = dest.getAddress();
auto maxBytes = (ssize_t) maxBytesToWrite;
maxBytes -= (ssize_t) sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null)
for (;;)
{
auto c = src.getAndAdvance();
auto bytesNeeded = (ssize_t) DestCharPointerType::getBytesRequiredFor (c);
maxBytes -= bytesNeeded;
if (c == 0 || maxBytes < 0)
break;
dest.write (c);
}
dest.writeNull();
return (size_t) getAddressDifference (dest.getAddress(), startAddress)
+ sizeof (typename DestCharPointerType::CharType);
}
template <typename DestCharPointerType, typename SrcCharPointerType>
static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept
{
while (--maxChars > 0)
{
auto c = src.getAndAdvance();
if (c == 0)
break;
dest.write (c);
}
dest.writeNull();
}
static int compare (juce_wchar char1, juce_wchar char2) noexcept
{
if (auto diff = static_cast<int> (char1) - static_cast<int> (char2))
return diff < 0 ? -1 : 1;
return 0;
}
template <typename CharPointerType1, typename CharPointerType2>
static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept
{
for (;;)
{
auto c1 = s1.getAndAdvance();
if (auto diff = compare (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
template <typename CharPointerType1, typename CharPointerType2>
static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
{
while (--maxChars >= 0)
{
auto c1 = s1.getAndAdvance();
if (auto diff = compare (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
static int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept
{
return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0;
}
template <typename CharPointerType1, typename CharPointerType2>
static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept
{
for (;;)
{
auto c1 = s1.getAndAdvance();
if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
template <typename CharPointerType1, typename CharPointerType2>
static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
{
while (--maxChars >= 0)
{
auto c1 = s1.getAndAdvance();
if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
template <typename CharPointerType1, typename CharPointerType2>
static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
{
int index = 0;
auto substringLength = (int) substringToLookFor.length();
for (;;)
{
if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0)
return index;
if (textToSearch.getAndAdvance() == 0)
return -1;
++index;
}
}
template <typename CharPointerType1, typename CharPointerType2>
static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
{
auto substringLength = (int) substringToLookFor.length();
while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0
&& ! textToSearch.isEmpty())
++textToSearch;
return textToSearch;
}
template <typename CharPointerType>
static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
{
for (;; ++textToSearch)
{
auto c = *textToSearch;
if (c == charToLookFor || c == 0)
break;
}
return textToSearch;
}
template <typename CharPointerType1, typename CharPointerType2>
static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept
{
int index = 0;
auto needleLength = (int) needle.length();
for (;;)
{
if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0)
return index;
if (haystack.getAndAdvance() == 0)
return -1;
++index;
}
}
template <typename Type>
static int indexOfChar (Type text, const juce_wchar charToFind) noexcept
{
int i = 0;
while (! text.isEmpty())
{
if (text.getAndAdvance() == charToFind)
return i;
++i;
}
return -1;
}
template <typename Type>
static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept
{
charToFind = CharacterFunctions::toLowerCase (charToFind);
int i = 0;
while (! text.isEmpty())
{
if (text.toLowerCase() == charToFind)
return i;
++text;
++i;
}
return -1;
}
template <typename Type>
static void incrementToEndOfWhitespace (Type& text) noexcept
{
while (text.isWhitespace())
++text;
}
template <typename Type>
static Type findEndOfWhitespace (Type text) noexcept
{
incrementToEndOfWhitespace (text);
return text;
}
template <typename Type, typename BreakType>
static Type findEndOfToken (Type text, BreakType breakCharacters, Type quoteCharacters)
{
juce_wchar currentQuoteChar = 0;
while (! text.isEmpty())
{
auto c = text.getAndAdvance();
if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0)
{
--text;
break;
}
if (quoteCharacters.indexOf (c) >= 0)
{
if (currentQuoteChar == 0)
currentQuoteChar = c;
else if (currentQuoteChar == c)
currentQuoteChar = 0;
}
}
return text;
}
private:
static double mulexp10 (double value, int exponent) noexcept;
};
} // namespace juce
juce::CharacterFunctions
Definition juce_CharacterFunctions.h:92
juce::CharacterFunctions::indexOfIgnoreCase
static int indexOfIgnoreCase(CharPointerType1 haystack, const CharPointerType2 needle) noexcept
Definition juce_CharacterFunctions.h:742
juce::CharacterFunctions::incrementToEndOfWhitespace
static void incrementToEndOfWhitespace(Type &text) noexcept
Definition juce_CharacterFunctions.h:807
juce::CharacterFunctions::compare
static int compare(juce_wchar char1, juce_wchar char2) noexcept
Definition juce_CharacterFunctions.h:596
juce::CharacterFunctions::toLowerCase
static juce_wchar toLowerCase(juce_wchar character) noexcept
Definition juce_CharacterFunctions.cpp:33
juce::CharacterFunctions::copyWithDestByteLimit
static size_t copyWithDestByteLimit(DestCharPointerType &dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
Definition juce_CharacterFunctions.h:553
juce::CharacterFunctions::indexOfCharIgnoreCase
static int indexOfCharIgnoreCase(Type text, juce_wchar charToFind) noexcept
Definition juce_CharacterFunctions.h:783
juce::CharacterFunctions::isDigit
static bool isDigit(char character) noexcept
Definition juce_CharacterFunctions.cpp:69
juce::CharacterFunctions::getIntValue
static IntType getIntValue(const CharPointerType text) noexcept
Definition juce_CharacterFunctions.h:465
juce::CharacterFunctions::compareIgnoreCaseUpTo
static int compareIgnoreCaseUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
Definition juce_CharacterFunctions.h:666
juce::CharacterFunctions::indexOfChar
static int indexOfChar(Type text, const juce_wchar charToFind) noexcept
Definition juce_CharacterFunctions.h:763
juce::CharacterFunctions::compare
static int compare(CharPointerType1 s1, CharPointerType2 s2) noexcept
Definition juce_CharacterFunctions.h:606
juce::CharacterFunctions::compareIgnoreCase
static int compareIgnoreCase(juce_wchar char1, juce_wchar char2) noexcept
Definition juce_CharacterFunctions.h:641
juce::CharacterFunctions::lengthUpTo
static size_t lengthUpTo(CharPointerType start, const CharPointerType end) noexcept
Definition juce_CharacterFunctions.h:530
juce::CharacterFunctions::isLowerCase
static bool isLowerCase(juce_wchar character) noexcept
Definition juce_CharacterFunctions.cpp:47
juce::CharacterFunctions::isLetter
static bool isLetter(char character) noexcept
Definition juce_CharacterFunctions.cpp:79
juce::CharacterFunctions::indexOf
static int indexOf(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
Definition juce_CharacterFunctions.h:686
juce::CharacterFunctions::isWhitespace
static bool isWhitespace(char character) noexcept
Definition juce_CharacterFunctions.cpp:59
juce::CharacterFunctions::lengthUpTo
static size_t lengthUpTo(CharPointerType text, const size_t maxCharsToCount) noexcept
Definition juce_CharacterFunctions.h:517
juce::CharacterFunctions::readDoubleValue
static double readDoubleValue(CharPointerType &text) noexcept
Definition juce_CharacterFunctions.h:147
juce::CharacterFunctions::find
static CharPointerType find(CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
Definition juce_CharacterFunctions.h:724
juce::CharacterFunctions::findEndOfWhitespace
static Type findEndOfWhitespace(Type text) noexcept
Definition juce_CharacterFunctions.h:818
juce::CharacterFunctions::copyWithCharLimit
static void copyWithCharLimit(DestCharPointerType &dest, SrcCharPointerType src, int maxChars) noexcept
Definition juce_CharacterFunctions.h:580
juce::CharacterFunctions::getHexDigitValue
static int getHexDigitValue(juce_wchar digit) noexcept
Definition juce_CharacterFunctions.cpp:112
juce::CharacterFunctions::isLetterOrDigit
static bool isLetterOrDigit(char character) noexcept
Definition juce_CharacterFunctions.cpp:90
juce::CharacterFunctions::toUpperCase
static juce_wchar toUpperCase(juce_wchar character) noexcept
Definition juce_CharacterFunctions.cpp:28
juce::CharacterFunctions::findEndOfToken
static Type findEndOfToken(Type text, BreakType breakCharacters, Type quoteCharacters)
Definition juce_CharacterFunctions.h:828
juce::CharacterFunctions::isUpperCase
static bool isUpperCase(juce_wchar character) noexcept
Definition juce_CharacterFunctions.cpp:38
juce::CharacterFunctions::isPrintable
static bool isPrintable(char character) noexcept
Definition juce_CharacterFunctions.cpp:102
juce::CharacterFunctions::find
static CharPointerType1 find(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
Definition juce_CharacterFunctions.h:708
juce::CharacterFunctions::compareIgnoreCase
static int compareIgnoreCase(CharPointerType1 s1, CharPointerType2 s2) noexcept
Definition juce_CharacterFunctions.h:648
juce::CharacterFunctions::getDoubleValue
static double getDoubleValue(CharPointerType text) noexcept
Definition juce_CharacterFunctions.h:457
juce::CharacterFunctions::getUnicodeCharFromWindows1252Codepage
static juce_wchar getUnicodeCharFromWindows1252Codepage(uint8 windows1252Char) noexcept
Definition juce_CharacterFunctions.cpp:164
juce::CharacterFunctions::copyAll
static void copyAll(DestCharPointerType &dest, SrcCharPointerType src) noexcept
Definition juce_CharacterFunctions.h:542
juce::CharacterFunctions::compareUpTo
static int compareUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
Definition juce_CharacterFunctions.h:624
juce::CharacterFunctions::HexParser
Definition juce_CharacterFunctions.h:492