git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
165
deps/juce/modules/juce_core/text/juce_Base64.cpp
vendored
Normal file
165
deps/juce/modules/juce_core/text/juce_Base64.cpp
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
bool Base64::convertToBase64 (OutputStream& base64Result, const void* sourceData, size_t sourceDataSize)
|
||||
{
|
||||
static const char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
auto* source = static_cast<const uint8*> (sourceData);
|
||||
|
||||
while (sourceDataSize > 0)
|
||||
{
|
||||
char frame[4];
|
||||
auto byte0 = *source++;
|
||||
frame[0] = lookup [(byte0 & 0xfcu) >> 2];
|
||||
uint32 bits = (byte0 & 0x03u) << 4;
|
||||
|
||||
if (sourceDataSize > 1)
|
||||
{
|
||||
auto byte1 = *source++;
|
||||
frame[1] = lookup[bits | ((byte1 & 0xf0u) >> 4)];
|
||||
bits = (byte1 & 0x0fu) << 2;
|
||||
|
||||
if (sourceDataSize > 2)
|
||||
{
|
||||
auto byte2 = *source++;
|
||||
frame[2] = lookup[bits | ((byte2 & 0xc0u) >> 6)];
|
||||
frame[3] = lookup[byte2 & 0x3fu];
|
||||
sourceDataSize -= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
frame[2] = lookup[bits];
|
||||
frame[3] = '=';
|
||||
sourceDataSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
frame[1] = lookup[bits];
|
||||
frame[2] = '=';
|
||||
frame[3] = '=';
|
||||
sourceDataSize = 0;
|
||||
}
|
||||
|
||||
if (! base64Result.write (frame, 4))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Base64::convertFromBase64 (OutputStream& binaryOutput, StringRef base64TextInput)
|
||||
{
|
||||
for (auto s = base64TextInput.text; ! s.isEmpty();)
|
||||
{
|
||||
uint8 data[4];
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
auto c = (uint32) s.getAndAdvance();
|
||||
|
||||
if (c >= 'A' && c <= 'Z') c -= 'A';
|
||||
else if (c >= 'a' && c <= 'z') c -= 'a' - 26;
|
||||
else if (c >= '0' && c <= '9') c += 52 - '0';
|
||||
else if (c == '+') c = 62;
|
||||
else if (c == '/') c = 63;
|
||||
else if (c == '=') { c = 64; if (i <= 1) return false; }
|
||||
else return false;
|
||||
|
||||
data[i] = (uint8) c;
|
||||
}
|
||||
|
||||
binaryOutput.writeByte ((char) ((data[0] << 2) | (data[1] >> 4)));
|
||||
|
||||
if (data[2] < 64)
|
||||
{
|
||||
binaryOutput.writeByte ((char) ((data[1] << 4) | (data[2] >> 2)));
|
||||
|
||||
if (data[3] < 64)
|
||||
binaryOutput.writeByte ((char) ((data[2] << 6) | data[3]));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String Base64::toBase64 (const void* sourceData, size_t sourceDataSize)
|
||||
{
|
||||
MemoryOutputStream m ((sourceDataSize * 4) / 3 + 3);
|
||||
bool ok = convertToBase64 (m, sourceData, sourceDataSize);
|
||||
jassertquiet (ok); // should always succeed for this simple case
|
||||
return m.toString();
|
||||
}
|
||||
|
||||
String Base64::toBase64 (const String& text)
|
||||
{
|
||||
return toBase64 (text.toRawUTF8(), strlen (text.toRawUTF8()));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class Base64Tests : public UnitTest
|
||||
{
|
||||
public:
|
||||
Base64Tests()
|
||||
: UnitTest ("Base64 class", UnitTestCategories::text)
|
||||
{}
|
||||
|
||||
static MemoryBlock createRandomData (Random& r)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
|
||||
for (int i = r.nextInt (400); --i >= 0;)
|
||||
m.writeByte ((char) r.nextInt (256));
|
||||
|
||||
return m.getMemoryBlock();
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Base64");
|
||||
|
||||
auto r = getRandom();
|
||||
|
||||
for (int i = 1000; --i >= 0;)
|
||||
{
|
||||
auto original = createRandomData (r);
|
||||
auto asBase64 = Base64::toBase64 (original.getData(), original.getSize());
|
||||
MemoryOutputStream out;
|
||||
expect (Base64::convertFromBase64 (out, asBase64));
|
||||
auto result = out.getMemoryBlock();
|
||||
expect (result == original);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static Base64Tests base64Tests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
53
deps/juce/modules/juce_core/text/juce_Base64.h
vendored
Normal file
53
deps/juce/modules/juce_core/text/juce_Base64.h
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
/**
|
||||
Contains some static methods for converting between binary and the
|
||||
standard base-64 encoding format.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
struct JUCE_API Base64
|
||||
{
|
||||
/** Converts a binary block of data into a base-64 string.
|
||||
This will write the resulting string data to the given stream.
|
||||
If a write error occurs with the stream, the method will terminate and return false.
|
||||
*/
|
||||
static bool convertToBase64 (OutputStream& base64Result, const void* sourceData, size_t sourceDataSize);
|
||||
|
||||
/** Converts a base-64 string back to its binary representation.
|
||||
This will write the decoded binary data to the given stream.
|
||||
If the string is not valid base-64, the method will terminate and return false.
|
||||
*/
|
||||
static bool convertFromBase64 (OutputStream& binaryOutput, StringRef base64TextInput);
|
||||
|
||||
/** Converts a block of binary data to a base-64 string. */
|
||||
static String toBase64 (const void* sourceData, size_t sourceDataSize);
|
||||
|
||||
/** Converts a string's UTF-8 representation to a base-64 string. */
|
||||
static String toBase64 (const String& textToEncode);
|
||||
};
|
||||
|
||||
} // namespace juce
|
380
deps/juce/modules/juce_core/text/juce_CharPointer_ASCII.h
vendored
Normal file
380
deps/juce/modules/juce_core/text/juce_CharPointer_ASCII.h
vendored
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wraps a pointer to a null-terminated ASCII character string, and provides
|
||||
various methods to operate on the data.
|
||||
|
||||
A valid ASCII string is assumed to not contain any characters above 127.
|
||||
|
||||
@see CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class CharPointer_ASCII final
|
||||
{
|
||||
public:
|
||||
using CharType = char;
|
||||
|
||||
inline explicit CharPointer_ASCII (const CharType* rawPointer) noexcept
|
||||
: data (const_cast<CharType*> (rawPointer))
|
||||
{
|
||||
}
|
||||
|
||||
inline CharPointer_ASCII (const CharPointer_ASCII& other) = default;
|
||||
|
||||
inline CharPointer_ASCII operator= (const CharPointer_ASCII other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CharPointer_ASCII operator= (const CharType* text) noexcept
|
||||
{
|
||||
data = const_cast<CharType*> (text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This is a pointer comparison, it doesn't compare the actual text. */
|
||||
inline bool operator== (CharPointer_ASCII other) const noexcept { return data == other.data; }
|
||||
inline bool operator!= (CharPointer_ASCII other) const noexcept { return data != other.data; }
|
||||
inline bool operator<= (CharPointer_ASCII other) const noexcept { return data <= other.data; }
|
||||
inline bool operator< (CharPointer_ASCII other) const noexcept { return data < other.data; }
|
||||
inline bool operator>= (CharPointer_ASCII other) const noexcept { return data >= other.data; }
|
||||
inline bool operator> (CharPointer_ASCII other) const noexcept { return data > other.data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
inline CharType* getAddress() const noexcept { return data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
inline operator const CharType*() const noexcept { return data; }
|
||||
|
||||
/** Returns true if this pointer is pointing to a null character. */
|
||||
inline bool isEmpty() const noexcept { return *data == 0; }
|
||||
|
||||
/** Returns true if this pointer is not pointing to a null character. */
|
||||
inline bool isNotEmpty() const noexcept { return *data != 0; }
|
||||
|
||||
/** Returns the unicode character that this pointer is pointing to. */
|
||||
inline juce_wchar operator*() const noexcept { return (juce_wchar) (uint8) *data; }
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
inline CharPointer_ASCII operator++() noexcept
|
||||
{
|
||||
++data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Moves this pointer to the previous character in the string. */
|
||||
inline CharPointer_ASCII operator--() noexcept
|
||||
{
|
||||
--data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the character that this pointer is currently pointing to, and then
|
||||
advances the pointer to point to the next character. */
|
||||
inline juce_wchar getAndAdvance() noexcept { return (juce_wchar) (uint8) *data++; }
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
CharPointer_ASCII operator++ (int) noexcept
|
||||
{
|
||||
auto temp (*this);
|
||||
++data;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/** Moves this pointer forwards by the specified number of characters. */
|
||||
inline void operator+= (const int numToSkip) noexcept
|
||||
{
|
||||
data += numToSkip;
|
||||
}
|
||||
|
||||
inline void operator-= (const int numToSkip) noexcept
|
||||
{
|
||||
data -= numToSkip;
|
||||
}
|
||||
|
||||
/** Returns the character at a given character index from the start of the string. */
|
||||
inline juce_wchar operator[] (const int characterIndex) const noexcept
|
||||
{
|
||||
return (juce_wchar) (uint8) data [characterIndex];
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved forwards from this one by the specified number of characters. */
|
||||
CharPointer_ASCII operator+ (const int numToSkip) const noexcept
|
||||
{
|
||||
return CharPointer_ASCII (data + numToSkip);
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved backwards from this one by the specified number of characters. */
|
||||
CharPointer_ASCII operator- (const int numToSkip) const noexcept
|
||||
{
|
||||
return CharPointer_ASCII (data - numToSkip);
|
||||
}
|
||||
|
||||
/** Writes a unicode character to this string, and advances this pointer to point to the next position. */
|
||||
inline void write (const juce_wchar charToWrite) noexcept
|
||||
{
|
||||
*data++ = (char) charToWrite;
|
||||
}
|
||||
|
||||
inline void replaceChar (const juce_wchar newChar) noexcept
|
||||
{
|
||||
*data = (char) newChar;
|
||||
}
|
||||
|
||||
/** Writes a null character to this string (leaving the pointer's position unchanged). */
|
||||
inline void writeNull() const noexcept
|
||||
{
|
||||
*data = 0;
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string. */
|
||||
size_t length() const noexcept
|
||||
{
|
||||
return (size_t) strlen (data);
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or the given value, whichever is lower. */
|
||||
size_t lengthUpTo (const size_t maxCharsToCount) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, maxCharsToCount);
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */
|
||||
size_t lengthUpTo (const CharPointer_ASCII end) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, end);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that are used to represent this string.
|
||||
This includes the terminating null character.
|
||||
*/
|
||||
size_t sizeInBytes() const noexcept
|
||||
{
|
||||
return length() + 1;
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
unicode character in this encoding format.
|
||||
*/
|
||||
static size_t getBytesRequiredFor (const juce_wchar) noexcept
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
string in this encoding format.
|
||||
The value returned does NOT include the terminating null character.
|
||||
*/
|
||||
template <class CharPointer>
|
||||
static size_t getBytesRequiredFor (const CharPointer text) noexcept
|
||||
{
|
||||
return text.length();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the null character that terminates this string. */
|
||||
CharPointer_ASCII findTerminatingNull() const noexcept
|
||||
{
|
||||
return CharPointer_ASCII (data + length());
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
template <typename CharPointer>
|
||||
void writeAll (const CharPointer src) noexcept
|
||||
{
|
||||
CharacterFunctions::copyAll (*this, src);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxDestBytes parameter specifies the maximum number of bytes that can be written
|
||||
to the destination buffer before stopping.
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept
|
||||
{
|
||||
return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxChars parameter specifies the maximum number of characters that can be
|
||||
written to the destination buffer before stopping (including the terminating null).
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept
|
||||
{
|
||||
CharacterFunctions::copyWithCharLimit (*this, src, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compare (const CharPointer other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compare (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
int compare (const CharPointer_ASCII other) const noexcept
|
||||
{
|
||||
return strcmp (data, other.data);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareUpTo (const CharPointer other, const int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
int compareUpTo (const CharPointer_ASCII other, const int maxChars) const noexcept
|
||||
{
|
||||
return strncmp (data, other.data, (size_t) maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCase (const CharPointer other) const
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCase (*this, other);
|
||||
}
|
||||
|
||||
int compareIgnoreCase (const CharPointer_ASCII other) const
|
||||
{
|
||||
#if JUCE_MINGW || (JUCE_WINDOWS && JUCE_CLANG)
|
||||
return CharacterFunctions::compareIgnoreCase (*this, other);
|
||||
#elif JUCE_WINDOWS
|
||||
return stricmp (data, other.data);
|
||||
#else
|
||||
return strcasecmp (data, other.data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Returns the character index of a substring, or -1 if it isn't found. */
|
||||
template <typename CharPointer>
|
||||
int indexOf (const CharPointer stringToFind) const noexcept
|
||||
{
|
||||
return CharacterFunctions::indexOf (*this, stringToFind);
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (const juce_wchar charToFind) const noexcept
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (data[i] != 0)
|
||||
{
|
||||
if (data[i] == (char) charToFind)
|
||||
return i;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept
|
||||
{
|
||||
return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind)
|
||||
: CharacterFunctions::indexOfChar (*this, charToFind);
|
||||
}
|
||||
|
||||
/** Returns true if the first character of this string is whitespace. */
|
||||
bool isWhitespace() const { return CharacterFunctions::isWhitespace (*data) != 0; }
|
||||
/** Returns true if the first character of this string is a digit. */
|
||||
bool isDigit() const { return CharacterFunctions::isDigit (*data) != 0; }
|
||||
/** Returns true if the first character of this string is a letter. */
|
||||
bool isLetter() const { return CharacterFunctions::isLetter (*data) != 0; }
|
||||
/** Returns true if the first character of this string is a letter or digit. */
|
||||
bool isLetterOrDigit() const { return CharacterFunctions::isLetterOrDigit (*data) != 0; }
|
||||
/** Returns true if the first character of this string is upper-case. */
|
||||
bool isUpperCase() const { return CharacterFunctions::isUpperCase ((juce_wchar) (uint8) *data) != 0; }
|
||||
/** Returns true if the first character of this string is lower-case. */
|
||||
bool isLowerCase() const { return CharacterFunctions::isLowerCase ((juce_wchar) (uint8) *data) != 0; }
|
||||
|
||||
/** Returns an upper-case version of the first character of this string. */
|
||||
juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase ((juce_wchar) (uint8) *data); }
|
||||
/** Returns a lower-case version of the first character of this string. */
|
||||
juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase ((juce_wchar) (uint8) *data); }
|
||||
|
||||
/** Parses this string as a 32-bit integer. */
|
||||
int getIntValue32() const noexcept { return atoi (data); }
|
||||
|
||||
/** Parses this string as a 64-bit integer. */
|
||||
int64 getIntValue64() const noexcept
|
||||
{
|
||||
#if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID || JUCE_MINGW
|
||||
return atoll (data);
|
||||
#elif JUCE_WINDOWS
|
||||
return _atoi64 (data);
|
||||
#else
|
||||
return CharacterFunctions::getIntValue <int64, CharPointer_ASCII> (*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Parses this string as a floating point double. */
|
||||
double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); }
|
||||
|
||||
/** Returns the first non-whitespace character in the string. */
|
||||
CharPointer_ASCII findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); }
|
||||
|
||||
/** Move this pointer to the first non-whitespace character in the string. */
|
||||
void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); }
|
||||
|
||||
/** Returns true if the given unicode character can be represented in this encoding. */
|
||||
static bool canRepresent (juce_wchar character) noexcept
|
||||
{
|
||||
return ((unsigned int) character) < (unsigned int) 128;
|
||||
}
|
||||
|
||||
/** Returns true if this data contains a valid string in this encoding. */
|
||||
static bool isValidString (const CharType* dataToTest, int maxBytesToRead)
|
||||
{
|
||||
while (--maxBytesToRead >= 0)
|
||||
{
|
||||
if (((signed char) *dataToTest) <= 0)
|
||||
return *dataToTest == 0;
|
||||
|
||||
++dataToTest;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
CharType* data;
|
||||
};
|
||||
|
||||
} // namespace juce
|
526
deps/juce/modules/juce_core/text/juce_CharPointer_UTF16.h
vendored
Normal file
526
deps/juce/modules/juce_core/text/juce_CharPointer_UTF16.h
vendored
Normal file
@ -0,0 +1,526 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wraps a pointer to a null-terminated UTF-16 character string, and provides
|
||||
various methods to operate on the data.
|
||||
@see CharPointer_UTF8, CharPointer_UTF32
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class CharPointer_UTF16 final
|
||||
{
|
||||
public:
|
||||
#if JUCE_NATIVE_WCHAR_IS_UTF16
|
||||
using CharType = wchar_t;
|
||||
#else
|
||||
using CharType = int16;
|
||||
#endif
|
||||
|
||||
inline explicit CharPointer_UTF16 (const CharType* rawPointer) noexcept
|
||||
: data (const_cast<CharType*> (rawPointer))
|
||||
{
|
||||
}
|
||||
|
||||
inline CharPointer_UTF16 (const CharPointer_UTF16& other) = default;
|
||||
|
||||
inline CharPointer_UTF16 operator= (CharPointer_UTF16 other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CharPointer_UTF16 operator= (const CharType* text) noexcept
|
||||
{
|
||||
data = const_cast<CharType*> (text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This is a pointer comparison, it doesn't compare the actual text. */
|
||||
inline bool operator== (CharPointer_UTF16 other) const noexcept { return data == other.data; }
|
||||
inline bool operator!= (CharPointer_UTF16 other) const noexcept { return data != other.data; }
|
||||
inline bool operator<= (CharPointer_UTF16 other) const noexcept { return data <= other.data; }
|
||||
inline bool operator< (CharPointer_UTF16 other) const noexcept { return data < other.data; }
|
||||
inline bool operator>= (CharPointer_UTF16 other) const noexcept { return data >= other.data; }
|
||||
inline bool operator> (CharPointer_UTF16 other) const noexcept { return data > other.data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
inline CharType* getAddress() const noexcept { return data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
inline operator const CharType*() const noexcept { return data; }
|
||||
|
||||
/** Returns true if this pointer is pointing to a null character. */
|
||||
inline bool isEmpty() const noexcept { return *data == 0; }
|
||||
|
||||
/** Returns true if this pointer is not pointing to a null character. */
|
||||
inline bool isNotEmpty() const noexcept { return *data != 0; }
|
||||
|
||||
/** Returns the unicode character that this pointer is pointing to. */
|
||||
juce_wchar operator*() const noexcept
|
||||
{
|
||||
auto n = (uint32) (uint16) *data;
|
||||
|
||||
if (n >= 0xd800 && n <= 0xdfff && ((uint32) (uint16) data[1]) >= 0xdc00)
|
||||
n = 0x10000 + (((n - 0xd800) << 10) | (((uint32) (uint16) data[1]) - 0xdc00));
|
||||
|
||||
return (juce_wchar) n;
|
||||
}
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
CharPointer_UTF16 operator++() noexcept
|
||||
{
|
||||
auto n = (uint32) (uint16) *data++;
|
||||
|
||||
if (n >= 0xd800 && n <= 0xdfff && ((uint32) (uint16) *data) >= 0xdc00)
|
||||
++data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Moves this pointer back to the previous character in the string. */
|
||||
CharPointer_UTF16 operator--() noexcept
|
||||
{
|
||||
auto n = (uint32) (uint16) (*--data);
|
||||
|
||||
if (n >= 0xdc00 && n <= 0xdfff)
|
||||
--data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the character that this pointer is currently pointing to, and then
|
||||
advances the pointer to point to the next character. */
|
||||
juce_wchar getAndAdvance() noexcept
|
||||
{
|
||||
auto n = (uint32) (uint16) *data++;
|
||||
|
||||
if (n >= 0xd800 && n <= 0xdfff && ((uint32) (uint16) *data) >= 0xdc00)
|
||||
n = 0x10000 + ((((n - 0xd800) << 10) | (((uint32) (uint16) *data++) - 0xdc00)));
|
||||
|
||||
return (juce_wchar) n;
|
||||
}
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
CharPointer_UTF16 operator++ (int) noexcept
|
||||
{
|
||||
auto temp (*this);
|
||||
++*this;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/** Moves this pointer forwards by the specified number of characters. */
|
||||
void operator+= (int numToSkip) noexcept
|
||||
{
|
||||
if (numToSkip < 0)
|
||||
{
|
||||
while (++numToSkip <= 0)
|
||||
--*this;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (--numToSkip >= 0)
|
||||
++*this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves this pointer backwards by the specified number of characters. */
|
||||
void operator-= (int numToSkip) noexcept
|
||||
{
|
||||
operator+= (-numToSkip);
|
||||
}
|
||||
|
||||
/** Returns the character at a given character index from the start of the string. */
|
||||
juce_wchar operator[] (int characterIndex) const noexcept
|
||||
{
|
||||
auto p (*this);
|
||||
p += characterIndex;
|
||||
return *p;
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved forwards from this one by the specified number of characters. */
|
||||
CharPointer_UTF16 operator+ (int numToSkip) const noexcept
|
||||
{
|
||||
auto p (*this);
|
||||
p += numToSkip;
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved backwards from this one by the specified number of characters. */
|
||||
CharPointer_UTF16 operator- (int numToSkip) const noexcept
|
||||
{
|
||||
auto p (*this);
|
||||
p += -numToSkip;
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Writes a unicode character to this string, and advances this pointer to point to the next position. */
|
||||
void write (juce_wchar charToWrite) noexcept
|
||||
{
|
||||
if (charToWrite >= 0x10000)
|
||||
{
|
||||
charToWrite -= 0x10000;
|
||||
*data++ = (CharType) (0xd800 + (charToWrite >> 10));
|
||||
*data++ = (CharType) (0xdc00 + (charToWrite & 0x3ff));
|
||||
}
|
||||
else
|
||||
{
|
||||
*data++ = (CharType) charToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes a null character to this string (leaving the pointer's position unchanged). */
|
||||
inline void writeNull() const noexcept
|
||||
{
|
||||
*data = 0;
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string. */
|
||||
size_t length() const noexcept
|
||||
{
|
||||
auto* d = data;
|
||||
size_t count = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto n = (uint32) (uint16) *d++;
|
||||
|
||||
if (n >= 0xd800 && n <= 0xdfff)
|
||||
{
|
||||
if (*d++ == 0)
|
||||
break;
|
||||
}
|
||||
else if (n == 0)
|
||||
break;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or the given value, whichever is lower. */
|
||||
size_t lengthUpTo (size_t maxCharsToCount) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, maxCharsToCount);
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */
|
||||
size_t lengthUpTo (CharPointer_UTF16 end) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, end);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that are used to represent this string.
|
||||
This includes the terminating null character.
|
||||
*/
|
||||
size_t sizeInBytes() const noexcept
|
||||
{
|
||||
return sizeof (CharType) * (findNullIndex (data) + 1);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
unicode character in this encoding format.
|
||||
*/
|
||||
static size_t getBytesRequiredFor (juce_wchar charToWrite) noexcept
|
||||
{
|
||||
return (charToWrite >= 0x10000) ? (sizeof (CharType) * 2) : sizeof (CharType);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
string in this encoding format.
|
||||
The value returned does NOT include the terminating null character.
|
||||
*/
|
||||
template <class CharPointer>
|
||||
static size_t getBytesRequiredFor (CharPointer text) noexcept
|
||||
{
|
||||
size_t count = 0;
|
||||
juce_wchar n;
|
||||
|
||||
while ((n = text.getAndAdvance()) != 0)
|
||||
count += getBytesRequiredFor (n);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the null character that terminates this string. */
|
||||
CharPointer_UTF16 findTerminatingNull() const noexcept
|
||||
{
|
||||
auto* t = data;
|
||||
|
||||
while (*t != 0)
|
||||
++t;
|
||||
|
||||
return CharPointer_UTF16 (t);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
template <typename CharPointer>
|
||||
void writeAll (CharPointer src) noexcept
|
||||
{
|
||||
CharacterFunctions::copyAll (*this, src);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
void writeAll (CharPointer_UTF16 src) noexcept
|
||||
{
|
||||
auto* s = src.data;
|
||||
|
||||
while ((*data = *s) != 0)
|
||||
{
|
||||
++data;
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxDestBytes parameter specifies the maximum number of bytes that can be written
|
||||
to the destination buffer before stopping.
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
size_t writeWithDestByteLimit (CharPointer src, size_t maxDestBytes) noexcept
|
||||
{
|
||||
return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxChars parameter specifies the maximum number of characters that can be
|
||||
written to the destination buffer before stopping (including the terminating null).
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
void writeWithCharLimit (CharPointer src, int maxChars) noexcept
|
||||
{
|
||||
CharacterFunctions::copyWithCharLimit (*this, src, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compare (CharPointer other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compare (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareUpTo (CharPointer other, int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCase (CharPointer other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCase (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCaseUpTo (CharPointer other, int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
#if JUCE_MSVC && ! defined (DOXYGEN)
|
||||
int compareIgnoreCase (CharPointer_UTF16 other) const noexcept
|
||||
{
|
||||
return _wcsicmp (data, other.data);
|
||||
}
|
||||
|
||||
int compareIgnoreCaseUpTo (CharPointer_UTF16 other, int maxChars) const noexcept
|
||||
{
|
||||
return _wcsnicmp (data, other.data, (size_t) maxChars);
|
||||
}
|
||||
|
||||
int indexOf (CharPointer_UTF16 stringToFind) const noexcept
|
||||
{
|
||||
const CharType* const t = wcsstr (data, stringToFind.getAddress());
|
||||
return t == nullptr ? -1 : (int) (t - data);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Returns the character index of a substring, or -1 if it isn't found. */
|
||||
template <typename CharPointer>
|
||||
int indexOf (CharPointer stringToFind) const noexcept
|
||||
{
|
||||
return CharacterFunctions::indexOf (*this, stringToFind);
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (juce_wchar charToFind) const noexcept
|
||||
{
|
||||
return CharacterFunctions::indexOfChar (*this, charToFind);
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (juce_wchar charToFind, bool ignoreCase) const noexcept
|
||||
{
|
||||
return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind)
|
||||
: CharacterFunctions::indexOfChar (*this, charToFind);
|
||||
}
|
||||
|
||||
/** Returns true if the first character of this string is whitespace. */
|
||||
bool isWhitespace() const noexcept { return CharacterFunctions::isWhitespace (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is a digit. */
|
||||
bool isDigit() const noexcept { return CharacterFunctions::isDigit (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is a letter. */
|
||||
bool isLetter() const noexcept { return CharacterFunctions::isLetter (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is a letter or digit. */
|
||||
bool isLetterOrDigit() const noexcept { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is upper-case. */
|
||||
bool isUpperCase() const noexcept { return CharacterFunctions::isUpperCase (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is lower-case. */
|
||||
bool isLowerCase() const noexcept { return CharacterFunctions::isLowerCase (operator*()) != 0; }
|
||||
|
||||
/** Returns an upper-case version of the first character of this string. */
|
||||
juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (operator*()); }
|
||||
/** Returns a lower-case version of the first character of this string. */
|
||||
juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (operator*()); }
|
||||
|
||||
/** Parses this string as a 32-bit integer. */
|
||||
int getIntValue32() const noexcept
|
||||
{
|
||||
#if JUCE_MSVC
|
||||
return _wtoi (data);
|
||||
#else
|
||||
return CharacterFunctions::getIntValue<int, CharPointer_UTF16> (*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Parses this string as a 64-bit integer. */
|
||||
int64 getIntValue64() const noexcept
|
||||
{
|
||||
#if JUCE_MSVC
|
||||
return _wtoi64 (data);
|
||||
#else
|
||||
return CharacterFunctions::getIntValue<int64, CharPointer_UTF16> (*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Parses this string as a floating point double. */
|
||||
double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); }
|
||||
|
||||
/** Returns the first non-whitespace character in the string. */
|
||||
CharPointer_UTF16 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); }
|
||||
|
||||
/** Move this pointer to the first non-whitespace character in the string. */
|
||||
void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); }
|
||||
|
||||
/** Returns true if the given unicode character can be represented in this encoding. */
|
||||
static bool canRepresent (juce_wchar character) noexcept
|
||||
{
|
||||
auto n = (uint32) character;
|
||||
return n < 0x10ffff && (n < 0xd800 || n > 0xdfff);
|
||||
}
|
||||
|
||||
/** Returns true if this data contains a valid string in this encoding. */
|
||||
static bool isValidString (const CharType* dataToTest, int maxBytesToRead)
|
||||
{
|
||||
maxBytesToRead /= (int) sizeof (CharType);
|
||||
|
||||
while (--maxBytesToRead >= 0 && *dataToTest != 0)
|
||||
{
|
||||
auto n = (uint32) (uint16) *dataToTest++;
|
||||
|
||||
if (n >= 0xd800)
|
||||
{
|
||||
if (n > 0x10ffff)
|
||||
return false;
|
||||
|
||||
if (n <= 0xdfff)
|
||||
{
|
||||
if (n > 0xdc00)
|
||||
return false;
|
||||
|
||||
auto nextChar = (uint32) (uint16) *dataToTest++;
|
||||
|
||||
if (nextChar < 0xdc00 || nextChar > 0xdfff)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Atomically swaps this pointer for a new value, returning the previous value. */
|
||||
CharPointer_UTF16 atomicSwap (CharPointer_UTF16 newValue)
|
||||
{
|
||||
return CharPointer_UTF16 (reinterpret_cast<Atomic<CharType*>&> (data).exchange (newValue.data));
|
||||
}
|
||||
|
||||
/** These values are the byte-order-mark (BOM) values for a UTF-16 stream. */
|
||||
enum
|
||||
{
|
||||
byteOrderMarkBE1 = 0xfe,
|
||||
byteOrderMarkBE2 = 0xff,
|
||||
byteOrderMarkLE1 = 0xff,
|
||||
byteOrderMarkLE2 = 0xfe
|
||||
};
|
||||
|
||||
/** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (big endian).
|
||||
The pointer must not be null, and must contain at least two valid bytes.
|
||||
*/
|
||||
static bool isByteOrderMarkBigEndian (const void* possibleByteOrder) noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182)
|
||||
jassert (possibleByteOrder != nullptr);
|
||||
auto c = static_cast<const uint8*> (possibleByteOrder);
|
||||
|
||||
return c[0] == (uint8) byteOrderMarkBE1
|
||||
&& c[1] == (uint8) byteOrderMarkBE2;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (little endian).
|
||||
The pointer must not be null, and must contain at least two valid bytes.
|
||||
*/
|
||||
static bool isByteOrderMarkLittleEndian (const void* possibleByteOrder) noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182)
|
||||
jassert (possibleByteOrder != nullptr);
|
||||
auto c = static_cast<const uint8*> (possibleByteOrder);
|
||||
|
||||
return c[0] == (uint8) byteOrderMarkLE1
|
||||
&& c[1] == (uint8) byteOrderMarkLE2;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
private:
|
||||
CharType* data;
|
||||
|
||||
static unsigned int findNullIndex (const CharType* t) noexcept
|
||||
{
|
||||
unsigned int n = 0;
|
||||
|
||||
while (t[n] != 0)
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
375
deps/juce/modules/juce_core/text/juce_CharPointer_UTF32.h
vendored
Normal file
375
deps/juce/modules/juce_core/text/juce_CharPointer_UTF32.h
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wraps a pointer to a null-terminated UTF-32 character string, and provides
|
||||
various methods to operate on the data.
|
||||
@see CharPointer_UTF8, CharPointer_UTF16
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class CharPointer_UTF32 final
|
||||
{
|
||||
public:
|
||||
using CharType = juce_wchar;
|
||||
|
||||
inline explicit CharPointer_UTF32 (const CharType* rawPointer) noexcept
|
||||
: data (const_cast<CharType*> (rawPointer))
|
||||
{
|
||||
}
|
||||
|
||||
inline CharPointer_UTF32 (const CharPointer_UTF32& other) = default;
|
||||
|
||||
inline CharPointer_UTF32 operator= (CharPointer_UTF32 other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CharPointer_UTF32 operator= (const CharType* text) noexcept
|
||||
{
|
||||
data = const_cast<CharType*> (text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This is a pointer comparison, it doesn't compare the actual text. */
|
||||
inline bool operator== (CharPointer_UTF32 other) const noexcept { return data == other.data; }
|
||||
inline bool operator!= (CharPointer_UTF32 other) const noexcept { return data != other.data; }
|
||||
inline bool operator<= (CharPointer_UTF32 other) const noexcept { return data <= other.data; }
|
||||
inline bool operator< (CharPointer_UTF32 other) const noexcept { return data < other.data; }
|
||||
inline bool operator>= (CharPointer_UTF32 other) const noexcept { return data >= other.data; }
|
||||
inline bool operator> (CharPointer_UTF32 other) const noexcept { return data > other.data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
inline CharType* getAddress() const noexcept { return data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
inline operator const CharType*() const noexcept { return data; }
|
||||
|
||||
/** Returns true if this pointer is pointing to a null character. */
|
||||
inline bool isEmpty() const noexcept { return *data == 0; }
|
||||
|
||||
/** Returns true if this pointer is not pointing to a null character. */
|
||||
inline bool isNotEmpty() const noexcept { return *data != 0; }
|
||||
|
||||
/** Returns the unicode character that this pointer is pointing to. */
|
||||
inline juce_wchar operator*() const noexcept { return *data; }
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
inline CharPointer_UTF32 operator++() noexcept
|
||||
{
|
||||
++data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Moves this pointer to the previous character in the string. */
|
||||
inline CharPointer_UTF32 operator--() noexcept
|
||||
{
|
||||
--data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the character that this pointer is currently pointing to, and then
|
||||
advances the pointer to point to the next character. */
|
||||
inline juce_wchar getAndAdvance() noexcept { return *data++; }
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
CharPointer_UTF32 operator++ (int) noexcept
|
||||
{
|
||||
auto temp (*this);
|
||||
++data;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/** Moves this pointer forwards by the specified number of characters. */
|
||||
inline void operator+= (int numToSkip) noexcept
|
||||
{
|
||||
data += numToSkip;
|
||||
}
|
||||
|
||||
inline void operator-= (int numToSkip) noexcept
|
||||
{
|
||||
data -= numToSkip;
|
||||
}
|
||||
|
||||
/** Returns the character at a given character index from the start of the string. */
|
||||
inline juce_wchar& operator[] (int characterIndex) const noexcept
|
||||
{
|
||||
return data [characterIndex];
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved forwards from this one by the specified number of characters. */
|
||||
CharPointer_UTF32 operator+ (int numToSkip) const noexcept
|
||||
{
|
||||
return CharPointer_UTF32 (data + numToSkip);
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved backwards from this one by the specified number of characters. */
|
||||
CharPointer_UTF32 operator- (int numToSkip) const noexcept
|
||||
{
|
||||
return CharPointer_UTF32 (data - numToSkip);
|
||||
}
|
||||
|
||||
/** Writes a unicode character to this string, and advances this pointer to point to the next position. */
|
||||
inline void write (juce_wchar charToWrite) noexcept
|
||||
{
|
||||
*data++ = charToWrite;
|
||||
}
|
||||
|
||||
inline void replaceChar (juce_wchar newChar) noexcept
|
||||
{
|
||||
*data = newChar;
|
||||
}
|
||||
|
||||
/** Writes a null character to this string (leaving the pointer's position unchanged). */
|
||||
inline void writeNull() const noexcept
|
||||
{
|
||||
*data = 0;
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string. */
|
||||
size_t length() const noexcept
|
||||
{
|
||||
#if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID
|
||||
return wcslen (data);
|
||||
#else
|
||||
size_t n = 0;
|
||||
while (data[n] != 0)
|
||||
++n;
|
||||
return n;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or the given value, whichever is lower. */
|
||||
size_t lengthUpTo (size_t maxCharsToCount) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, maxCharsToCount);
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */
|
||||
size_t lengthUpTo (CharPointer_UTF32 end) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, end);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that are used to represent this string.
|
||||
This includes the terminating null character.
|
||||
*/
|
||||
size_t sizeInBytes() const noexcept
|
||||
{
|
||||
return sizeof (CharType) * (length() + 1);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
unicode character in this encoding format.
|
||||
*/
|
||||
static size_t getBytesRequiredFor (juce_wchar) noexcept
|
||||
{
|
||||
return sizeof (CharType);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
string in this encoding format.
|
||||
The value returned does NOT include the terminating null character.
|
||||
*/
|
||||
template <class CharPointer>
|
||||
static size_t getBytesRequiredFor (CharPointer text) noexcept
|
||||
{
|
||||
return sizeof (CharType) * text.length();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the null character that terminates this string. */
|
||||
CharPointer_UTF32 findTerminatingNull() const noexcept
|
||||
{
|
||||
return CharPointer_UTF32 (data + length());
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
template <typename CharPointer>
|
||||
void writeAll (CharPointer src) noexcept
|
||||
{
|
||||
CharacterFunctions::copyAll (*this, src);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
void writeAll (CharPointer_UTF32 src) noexcept
|
||||
{
|
||||
auto* s = src.data;
|
||||
|
||||
while ((*data = *s) != 0)
|
||||
{
|
||||
++data;
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxDestBytes parameter specifies the maximum number of bytes that can be written
|
||||
to the destination buffer before stopping.
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
size_t writeWithDestByteLimit (CharPointer src, size_t maxDestBytes) noexcept
|
||||
{
|
||||
return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxChars parameter specifies the maximum number of characters that can be
|
||||
written to the destination buffer before stopping (including the terminating null).
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
void writeWithCharLimit (CharPointer src, int maxChars) noexcept
|
||||
{
|
||||
CharacterFunctions::copyWithCharLimit (*this, src, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compare (CharPointer other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compare (*this, other);
|
||||
}
|
||||
|
||||
#if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID
|
||||
/** Compares this string with another one. */
|
||||
int compare (CharPointer_UTF32 other) const noexcept
|
||||
{
|
||||
return wcscmp (data, other.data);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareUpTo (CharPointer other, int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCase (CharPointer other) const
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCase (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCaseUpTo (CharPointer other, int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Returns the character index of a substring, or -1 if it isn't found. */
|
||||
template <typename CharPointer>
|
||||
int indexOf (CharPointer stringToFind) const noexcept
|
||||
{
|
||||
return CharacterFunctions::indexOf (*this, stringToFind);
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (juce_wchar charToFind) const noexcept
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (data[i] != 0)
|
||||
{
|
||||
if (data[i] == charToFind)
|
||||
return i;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (juce_wchar charToFind, bool ignoreCase) const noexcept
|
||||
{
|
||||
return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind)
|
||||
: CharacterFunctions::indexOfChar (*this, charToFind);
|
||||
}
|
||||
|
||||
/** Returns true if the first character of this string is whitespace. */
|
||||
bool isWhitespace() const { return CharacterFunctions::isWhitespace (*data) != 0; }
|
||||
/** Returns true if the first character of this string is a digit. */
|
||||
bool isDigit() const { return CharacterFunctions::isDigit (*data) != 0; }
|
||||
/** Returns true if the first character of this string is a letter. */
|
||||
bool isLetter() const { return CharacterFunctions::isLetter (*data) != 0; }
|
||||
/** Returns true if the first character of this string is a letter or digit. */
|
||||
bool isLetterOrDigit() const { return CharacterFunctions::isLetterOrDigit (*data) != 0; }
|
||||
/** Returns true if the first character of this string is upper-case. */
|
||||
bool isUpperCase() const { return CharacterFunctions::isUpperCase (*data) != 0; }
|
||||
/** Returns true if the first character of this string is lower-case. */
|
||||
bool isLowerCase() const { return CharacterFunctions::isLowerCase (*data) != 0; }
|
||||
|
||||
/** Returns an upper-case version of the first character of this string. */
|
||||
juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (*data); }
|
||||
/** Returns a lower-case version of the first character of this string. */
|
||||
juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (*data); }
|
||||
|
||||
/** Parses this string as a 32-bit integer. */
|
||||
int getIntValue32() const noexcept { return CharacterFunctions::getIntValue <int, CharPointer_UTF32> (*this); }
|
||||
/** Parses this string as a 64-bit integer. */
|
||||
int64 getIntValue64() const noexcept { return CharacterFunctions::getIntValue <int64, CharPointer_UTF32> (*this); }
|
||||
|
||||
/** Parses this string as a floating point double. */
|
||||
double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); }
|
||||
|
||||
/** Returns the first non-whitespace character in the string. */
|
||||
CharPointer_UTF32 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); }
|
||||
|
||||
/** Move this pointer to the first non-whitespace character in the string. */
|
||||
void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); }
|
||||
|
||||
/** Returns true if the given unicode character can be represented in this encoding. */
|
||||
static bool canRepresent (juce_wchar character) noexcept
|
||||
{
|
||||
return ((uint32) character) < (uint32) 0x10ffff;
|
||||
}
|
||||
|
||||
/** Returns true if this data contains a valid string in this encoding. */
|
||||
static bool isValidString (const CharType* dataToTest, int maxBytesToRead)
|
||||
{
|
||||
maxBytesToRead /= (int) sizeof (CharType);
|
||||
|
||||
while (--maxBytesToRead >= 0 && *dataToTest != 0)
|
||||
if (! canRepresent (*dataToTest++))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Atomically swaps this pointer for a new value, returning the previous value. */
|
||||
CharPointer_UTF32 atomicSwap (CharPointer_UTF32 newValue)
|
||||
{
|
||||
return CharPointer_UTF32 (reinterpret_cast<Atomic<CharType*>&> (data).exchange (newValue.data));
|
||||
}
|
||||
|
||||
private:
|
||||
CharType* data;
|
||||
};
|
||||
|
||||
} // namespace juce
|
571
deps/juce/modules/juce_core/text/juce_CharPointer_UTF8.h
vendored
Normal file
571
deps/juce/modules/juce_core/text/juce_CharPointer_UTF8.h
vendored
Normal file
@ -0,0 +1,571 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wraps a pointer to a null-terminated UTF-8 character string, and provides
|
||||
various methods to operate on the data.
|
||||
@see CharPointer_UTF16, CharPointer_UTF32
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class CharPointer_UTF8 final
|
||||
{
|
||||
public:
|
||||
using CharType = char;
|
||||
|
||||
explicit CharPointer_UTF8 (const CharType* rawPointer) noexcept
|
||||
: data (const_cast<CharType*> (rawPointer))
|
||||
{
|
||||
}
|
||||
|
||||
CharPointer_UTF8 (const CharPointer_UTF8& other) = default;
|
||||
|
||||
CharPointer_UTF8 operator= (CharPointer_UTF8 other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CharPointer_UTF8 operator= (const CharType* text) noexcept
|
||||
{
|
||||
data = const_cast<CharType*> (text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This is a pointer comparison, it doesn't compare the actual text. */
|
||||
bool operator== (CharPointer_UTF8 other) const noexcept { return data == other.data; }
|
||||
bool operator!= (CharPointer_UTF8 other) const noexcept { return data != other.data; }
|
||||
bool operator<= (CharPointer_UTF8 other) const noexcept { return data <= other.data; }
|
||||
bool operator< (CharPointer_UTF8 other) const noexcept { return data < other.data; }
|
||||
bool operator>= (CharPointer_UTF8 other) const noexcept { return data >= other.data; }
|
||||
bool operator> (CharPointer_UTF8 other) const noexcept { return data > other.data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
CharType* getAddress() const noexcept { return data; }
|
||||
|
||||
/** Returns the address that this pointer is pointing to. */
|
||||
operator const CharType*() const noexcept { return data; }
|
||||
|
||||
/** Returns true if this pointer is pointing to a null character. */
|
||||
bool isEmpty() const noexcept { return *data == 0; }
|
||||
|
||||
/** Returns true if this pointer is not pointing to a null character. */
|
||||
bool isNotEmpty() const noexcept { return *data != 0; }
|
||||
|
||||
/** Returns the unicode character that this pointer is pointing to. */
|
||||
juce_wchar operator*() const noexcept
|
||||
{
|
||||
auto byte = (signed char) *data;
|
||||
|
||||
if (byte >= 0)
|
||||
return (juce_wchar) (uint8) byte;
|
||||
|
||||
uint32 n = (uint32) (uint8) byte;
|
||||
uint32 mask = 0x7f;
|
||||
uint32 bit = 0x40;
|
||||
int numExtraValues = 0;
|
||||
|
||||
while ((n & bit) != 0 && bit > 0x8)
|
||||
{
|
||||
mask >>= 1;
|
||||
++numExtraValues;
|
||||
bit >>= 1;
|
||||
}
|
||||
|
||||
n &= mask;
|
||||
|
||||
for (int i = 1; i <= numExtraValues; ++i)
|
||||
{
|
||||
auto nextByte = (uint32) (uint8) data[i];
|
||||
|
||||
if ((nextByte & 0xc0) != 0x80)
|
||||
break;
|
||||
|
||||
n <<= 6;
|
||||
n |= (nextByte & 0x3f);
|
||||
}
|
||||
|
||||
return (juce_wchar) n;
|
||||
}
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
CharPointer_UTF8& operator++() noexcept
|
||||
{
|
||||
jassert (*data != 0); // trying to advance past the end of the string?
|
||||
auto n = (signed char) *data++;
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
uint8 bit = 0x40;
|
||||
|
||||
while ((static_cast<uint8> (n) & bit) != 0 && bit > 0x8)
|
||||
{
|
||||
++data;
|
||||
bit = static_cast<uint8> (bit >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Moves this pointer back to the previous character in the string. */
|
||||
CharPointer_UTF8 operator--() noexcept
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
while ((*--data & 0xc0) == 0x80 && ++count < 4)
|
||||
{}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the character that this pointer is currently pointing to, and then
|
||||
advances the pointer to point to the next character. */
|
||||
juce_wchar getAndAdvance() noexcept
|
||||
{
|
||||
auto byte = (signed char) *data++;
|
||||
|
||||
if (byte >= 0)
|
||||
return (juce_wchar) (uint8) byte;
|
||||
|
||||
uint32 n = (uint32) (uint8) byte;
|
||||
uint32 mask = 0x7f;
|
||||
uint32 bit = 0x40;
|
||||
int numExtraValues = 0;
|
||||
|
||||
while ((n & bit) != 0 && bit > 0x8)
|
||||
{
|
||||
mask >>= 1;
|
||||
++numExtraValues;
|
||||
bit >>= 1;
|
||||
}
|
||||
|
||||
n &= mask;
|
||||
|
||||
while (--numExtraValues >= 0)
|
||||
{
|
||||
auto nextByte = (uint32) (uint8) *data;
|
||||
|
||||
if ((nextByte & 0xc0) != 0x80)
|
||||
break;
|
||||
|
||||
++data;
|
||||
n <<= 6;
|
||||
n |= (nextByte & 0x3f);
|
||||
}
|
||||
|
||||
return (juce_wchar) n;
|
||||
}
|
||||
|
||||
/** Moves this pointer along to the next character in the string. */
|
||||
CharPointer_UTF8 operator++ (int) noexcept
|
||||
{
|
||||
CharPointer_UTF8 temp (*this);
|
||||
++*this;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/** Moves this pointer forwards by the specified number of characters. */
|
||||
void operator+= (int numToSkip) noexcept
|
||||
{
|
||||
if (numToSkip < 0)
|
||||
{
|
||||
while (++numToSkip <= 0)
|
||||
--*this;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (--numToSkip >= 0)
|
||||
++*this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves this pointer backwards by the specified number of characters. */
|
||||
void operator-= (int numToSkip) noexcept
|
||||
{
|
||||
operator+= (-numToSkip);
|
||||
}
|
||||
|
||||
/** Returns the character at a given character index from the start of the string. */
|
||||
juce_wchar operator[] (int characterIndex) const noexcept
|
||||
{
|
||||
auto p (*this);
|
||||
p += characterIndex;
|
||||
return *p;
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved forwards from this one by the specified number of characters. */
|
||||
CharPointer_UTF8 operator+ (int numToSkip) const noexcept
|
||||
{
|
||||
auto p (*this);
|
||||
p += numToSkip;
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Returns a pointer which is moved backwards from this one by the specified number of characters. */
|
||||
CharPointer_UTF8 operator- (int numToSkip) const noexcept
|
||||
{
|
||||
auto p (*this);
|
||||
p += -numToSkip;
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string. */
|
||||
size_t length() const noexcept
|
||||
{
|
||||
auto* d = data;
|
||||
size_t count = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto n = (uint32) (uint8) *d++;
|
||||
|
||||
if ((n & 0x80) != 0)
|
||||
{
|
||||
while ((*d & 0xc0) == 0x80)
|
||||
++d;
|
||||
}
|
||||
else if (n == 0)
|
||||
break;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or the given value, whichever is lower. */
|
||||
size_t lengthUpTo (const size_t maxCharsToCount) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, maxCharsToCount);
|
||||
}
|
||||
|
||||
/** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */
|
||||
size_t lengthUpTo (const CharPointer_UTF8 end) const noexcept
|
||||
{
|
||||
return CharacterFunctions::lengthUpTo (*this, end);
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that are used to represent this string.
|
||||
This includes the terminating null character.
|
||||
*/
|
||||
size_t sizeInBytes() const noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387)
|
||||
jassert (data != nullptr);
|
||||
return strlen (data) + 1;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
unicode character in this encoding format.
|
||||
*/
|
||||
static size_t getBytesRequiredFor (const juce_wchar charToWrite) noexcept
|
||||
{
|
||||
size_t num = 1;
|
||||
auto c = (uint32) charToWrite;
|
||||
|
||||
if (c >= 0x80)
|
||||
{
|
||||
++num;
|
||||
if (c >= 0x800)
|
||||
{
|
||||
++num;
|
||||
if (c >= 0x10000)
|
||||
++num;
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/** Returns the number of bytes that would be needed to represent the given
|
||||
string in this encoding format.
|
||||
The value returned does NOT include the terminating null character.
|
||||
*/
|
||||
template <class CharPointer>
|
||||
static size_t getBytesRequiredFor (CharPointer text) noexcept
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
while (auto n = text.getAndAdvance())
|
||||
count += getBytesRequiredFor (n);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the null character that terminates this string. */
|
||||
CharPointer_UTF8 findTerminatingNull() const noexcept
|
||||
{
|
||||
return CharPointer_UTF8 (data + strlen (data));
|
||||
}
|
||||
|
||||
/** Writes a unicode character to this string, and advances this pointer to point to the next position. */
|
||||
void write (const juce_wchar charToWrite) noexcept
|
||||
{
|
||||
auto c = (uint32) charToWrite;
|
||||
|
||||
if (c >= 0x80)
|
||||
{
|
||||
int numExtraBytes = 1;
|
||||
if (c >= 0x800)
|
||||
{
|
||||
++numExtraBytes;
|
||||
if (c >= 0x10000)
|
||||
++numExtraBytes;
|
||||
}
|
||||
|
||||
*data++ = (CharType) ((uint32) (0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6)));
|
||||
|
||||
while (--numExtraBytes >= 0)
|
||||
*data++ = (CharType) (0x80 | (0x3f & (c >> (numExtraBytes * 6))));
|
||||
}
|
||||
else
|
||||
{
|
||||
*data++ = (CharType) c;
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes a null character to this string (leaving the pointer's position unchanged). */
|
||||
void writeNull() const noexcept
|
||||
{
|
||||
*data = 0;
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
template <typename CharPointer>
|
||||
void writeAll (const CharPointer src) noexcept
|
||||
{
|
||||
CharacterFunctions::copyAll (*this, src);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes. */
|
||||
void writeAll (const CharPointer_UTF8 src) noexcept
|
||||
{
|
||||
auto* s = src.data;
|
||||
|
||||
while ((*data = *s) != 0)
|
||||
{
|
||||
++data;
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxDestBytes parameter specifies the maximum number of bytes that can be written
|
||||
to the destination buffer before stopping.
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept
|
||||
{
|
||||
return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes);
|
||||
}
|
||||
|
||||
/** Copies a source string to this pointer, advancing this pointer as it goes.
|
||||
The maxChars parameter specifies the maximum number of characters that can be
|
||||
written to the destination buffer before stopping (including the terminating null).
|
||||
*/
|
||||
template <typename CharPointer>
|
||||
void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept
|
||||
{
|
||||
CharacterFunctions::copyWithCharLimit (*this, src, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compare (const CharPointer other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compare (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareUpTo (const CharPointer other, const int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCase (const CharPointer other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCase (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one. */
|
||||
int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCase (*this, other);
|
||||
}
|
||||
|
||||
/** Compares this string with another one, up to a specified number of characters. */
|
||||
template <typename CharPointer>
|
||||
int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept
|
||||
{
|
||||
return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars);
|
||||
}
|
||||
|
||||
/** Returns the character index of a substring, or -1 if it isn't found. */
|
||||
template <typename CharPointer>
|
||||
int indexOf (const CharPointer stringToFind) const noexcept
|
||||
{
|
||||
return CharacterFunctions::indexOf (*this, stringToFind);
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (const juce_wchar charToFind) const noexcept
|
||||
{
|
||||
return CharacterFunctions::indexOfChar (*this, charToFind);
|
||||
}
|
||||
|
||||
/** Returns the character index of a unicode character, or -1 if it isn't found. */
|
||||
int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept
|
||||
{
|
||||
return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind)
|
||||
: CharacterFunctions::indexOfChar (*this, charToFind);
|
||||
}
|
||||
|
||||
/** Returns true if the first character of this string is whitespace. */
|
||||
bool isWhitespace() const noexcept { const CharType c = *data; return c == ' ' || (c <= 13 && c >= 9); }
|
||||
/** Returns true if the first character of this string is a digit. */
|
||||
bool isDigit() const noexcept { const CharType c = *data; return c >= '0' && c <= '9'; }
|
||||
/** Returns true if the first character of this string is a letter. */
|
||||
bool isLetter() const noexcept { return CharacterFunctions::isLetter (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is a letter or digit. */
|
||||
bool isLetterOrDigit() const noexcept { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is upper-case. */
|
||||
bool isUpperCase() const noexcept { return CharacterFunctions::isUpperCase (operator*()) != 0; }
|
||||
/** Returns true if the first character of this string is lower-case. */
|
||||
bool isLowerCase() const noexcept { return CharacterFunctions::isLowerCase (operator*()) != 0; }
|
||||
|
||||
/** Returns an upper-case version of the first character of this string. */
|
||||
juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (operator*()); }
|
||||
/** Returns a lower-case version of the first character of this string. */
|
||||
juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (operator*()); }
|
||||
|
||||
/** Parses this string as a 32-bit integer. */
|
||||
int getIntValue32() const noexcept { return atoi (data); }
|
||||
|
||||
/** Parses this string as a 64-bit integer. */
|
||||
int64 getIntValue64() const noexcept
|
||||
{
|
||||
#if JUCE_WINDOWS && ! JUCE_MINGW
|
||||
return _atoi64 (data);
|
||||
#else
|
||||
return atoll (data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Parses this string as a floating point double. */
|
||||
double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); }
|
||||
|
||||
/** Returns the first non-whitespace character in the string. */
|
||||
CharPointer_UTF8 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); }
|
||||
|
||||
/** Move this pointer to the first non-whitespace character in the string. */
|
||||
void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); }
|
||||
|
||||
/** Returns true if the given unicode character can be represented in this encoding. */
|
||||
static bool canRepresent (juce_wchar character) noexcept
|
||||
{
|
||||
return ((uint32) character) < (uint32) 0x10ffff;
|
||||
}
|
||||
|
||||
/** Returns true if this data contains a valid string in this encoding. */
|
||||
static bool isValidString (const CharType* dataToTest, int maxBytesToRead)
|
||||
{
|
||||
while (--maxBytesToRead >= 0 && *dataToTest != 0)
|
||||
{
|
||||
auto byte = (signed char) *dataToTest++;
|
||||
|
||||
if (byte < 0)
|
||||
{
|
||||
int bit = 0x40;
|
||||
int numExtraValues = 0;
|
||||
|
||||
while ((byte & bit) != 0)
|
||||
{
|
||||
if (bit < 8)
|
||||
return false;
|
||||
|
||||
++numExtraValues;
|
||||
bit >>= 1;
|
||||
|
||||
if (bit == 8 && (numExtraValues > maxBytesToRead
|
||||
|| *CharPointer_UTF8 (dataToTest - 1) > 0x10ffff))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numExtraValues == 0)
|
||||
return false;
|
||||
|
||||
maxBytesToRead -= numExtraValues;
|
||||
if (maxBytesToRead < 0)
|
||||
return false;
|
||||
|
||||
while (--numExtraValues >= 0)
|
||||
if ((*dataToTest++ & 0xc0) != 0x80)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Atomically swaps this pointer for a new value, returning the previous value. */
|
||||
CharPointer_UTF8 atomicSwap (const CharPointer_UTF8 newValue)
|
||||
{
|
||||
return CharPointer_UTF8 (reinterpret_cast<Atomic<CharType*>&> (data).exchange (newValue.data));
|
||||
}
|
||||
|
||||
/** These values are the byte-order mark (BOM) values for a UTF-8 stream. */
|
||||
enum
|
||||
{
|
||||
byteOrderMark1 = 0xef,
|
||||
byteOrderMark2 = 0xbb,
|
||||
byteOrderMark3 = 0xbf
|
||||
};
|
||||
|
||||
/** Returns true if the first three bytes in this pointer are the UTF8 byte-order mark (BOM).
|
||||
The pointer must not be null, and must point to at least 3 valid bytes.
|
||||
*/
|
||||
static bool isByteOrderMark (const void* possibleByteOrder) noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182)
|
||||
jassert (possibleByteOrder != nullptr);
|
||||
auto c = static_cast<const uint8*> (possibleByteOrder);
|
||||
|
||||
return c[0] == (uint8) byteOrderMark1
|
||||
&& c[1] == (uint8) byteOrderMark2
|
||||
&& c[2] == (uint8) byteOrderMark3;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
private:
|
||||
CharType* data;
|
||||
};
|
||||
|
||||
} // namespace juce
|
551
deps/juce/modules/juce_core/text/juce_CharacterFunctions.cpp
vendored
Normal file
551
deps/juce/modules/juce_core/text/juce_CharacterFunctions.cpp
vendored
Normal file
@ -0,0 +1,551 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4996)
|
||||
|
||||
juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept
|
||||
{
|
||||
return (juce_wchar) towupper ((wint_t) character);
|
||||
}
|
||||
|
||||
juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) noexcept
|
||||
{
|
||||
return (juce_wchar) towlower ((wint_t) character);
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
return iswupper ((wint_t) character) != 0;
|
||||
#else
|
||||
return toLowerCase (character) != character;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
return iswlower ((wint_t) character) != 0;
|
||||
#else
|
||||
return toUpperCase (character) != character;
|
||||
#endif
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
//==============================================================================
|
||||
bool CharacterFunctions::isWhitespace (const char character) noexcept
|
||||
{
|
||||
return character == ' ' || (character <= 13 && character >= 9);
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isWhitespace (const juce_wchar character) noexcept
|
||||
{
|
||||
return iswspace ((wint_t) character) != 0;
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isDigit (const char character) noexcept
|
||||
{
|
||||
return (character >= '0' && character <= '9');
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isDigit (const juce_wchar character) noexcept
|
||||
{
|
||||
return iswdigit ((wint_t) character) != 0;
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isLetter (const char character) noexcept
|
||||
{
|
||||
return (character >= 'a' && character <= 'z')
|
||||
|| (character >= 'A' && character <= 'Z');
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isLetter (const juce_wchar character) noexcept
|
||||
{
|
||||
return iswalpha ((wint_t) character) != 0;
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isLetterOrDigit (const char character) noexcept
|
||||
{
|
||||
return (character >= 'a' && character <= 'z')
|
||||
|| (character >= 'A' && character <= 'Z')
|
||||
|| (character >= '0' && character <= '9');
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept
|
||||
{
|
||||
return iswalnum ((wint_t) character) != 0;
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isPrintable (const char character) noexcept
|
||||
{
|
||||
return (character >= ' ' && character <= '~');
|
||||
}
|
||||
|
||||
bool CharacterFunctions::isPrintable (const juce_wchar character) noexcept
|
||||
{
|
||||
return iswprint ((wint_t) character) != 0;
|
||||
}
|
||||
|
||||
int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept
|
||||
{
|
||||
auto d = (unsigned int) (digit - '0');
|
||||
|
||||
if (d < (unsigned int) 10)
|
||||
return (int) d;
|
||||
|
||||
d += (unsigned int) ('0' - 'a');
|
||||
|
||||
if (d < (unsigned int) 6)
|
||||
return (int) d + 10;
|
||||
|
||||
d += (unsigned int) ('a' - 'A');
|
||||
|
||||
if (d < (unsigned int) 6)
|
||||
return (int) d + 10;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
double CharacterFunctions::mulexp10 (const double value, int exponent) noexcept
|
||||
{
|
||||
if (exponent == 0)
|
||||
return value;
|
||||
|
||||
if (value == 0.0)
|
||||
return 0;
|
||||
|
||||
const bool negative = (exponent < 0);
|
||||
|
||||
if (negative)
|
||||
exponent = -exponent;
|
||||
|
||||
double result = 1.0, power = 10.0;
|
||||
|
||||
for (int bit = 1; exponent != 0; bit <<= 1)
|
||||
{
|
||||
if ((exponent & bit) != 0)
|
||||
{
|
||||
exponent ^= bit;
|
||||
result *= power;
|
||||
|
||||
if (exponent == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
power *= power;
|
||||
}
|
||||
|
||||
return negative ? (value / result) : (value * result);
|
||||
}
|
||||
|
||||
juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint8 c) noexcept
|
||||
{
|
||||
if (c < 0x80 || c >= 0xa0)
|
||||
return (juce_wchar) c;
|
||||
|
||||
static const uint16 lookup[] = { 0x20AC, 0x0007, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0007, 0x017D, 0x0007,
|
||||
0x0007, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0007, 0x017E, 0x0178 };
|
||||
|
||||
return (juce_wchar) lookup[c - 0x80];
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
#define QUOTE(x) #x
|
||||
#define STR(value) QUOTE(value)
|
||||
#define ASYM_CHARPTR_DOUBLE_PAIR(str, value) std::pair<const char*, double> (STR(str), value)
|
||||
#define CHARPTR_DOUBLE_PAIR(value) ASYM_CHARPTR_DOUBLE_PAIR(value, value)
|
||||
#define CHARPTR_DOUBLE_PAIR_COMBOS(value) \
|
||||
CHARPTR_DOUBLE_PAIR(value), \
|
||||
CHARPTR_DOUBLE_PAIR(-value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(+value, value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(000000 ## value, value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(+000 ## value, value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(-0 ## value, -value)
|
||||
|
||||
namespace characterFunctionsTests
|
||||
{
|
||||
|
||||
template <typename CharPointerType>
|
||||
MemoryBlock memoryBlockFromCharPtr (const typename CharPointerType::CharType* charPtr)
|
||||
{
|
||||
using CharType = typename CharPointerType::CharType;
|
||||
|
||||
MemoryBlock result;
|
||||
CharPointerType source (charPtr);
|
||||
|
||||
result.setSize (CharPointerType::getBytesRequiredFor (source) + sizeof (CharType));
|
||||
CharPointerType dest { (CharType*) result.getData() };
|
||||
dest.writeAll (source);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename FromCharPointerType, typename ToCharPointerType>
|
||||
MemoryBlock convert (const MemoryBlock& source, bool removeNullTerminator = false)
|
||||
{
|
||||
using ToCharType = typename ToCharPointerType ::CharType;
|
||||
using FromCharType = typename FromCharPointerType::CharType;
|
||||
|
||||
FromCharPointerType sourcePtr { (FromCharType*) source.getData() };
|
||||
|
||||
std::vector<juce_wchar> sourceChars;
|
||||
size_t requiredSize = 0;
|
||||
juce_wchar c;
|
||||
|
||||
while ((c = sourcePtr.getAndAdvance()) != '\0')
|
||||
{
|
||||
requiredSize += ToCharPointerType::getBytesRequiredFor (c);
|
||||
sourceChars.push_back (c);
|
||||
}
|
||||
|
||||
if (! removeNullTerminator)
|
||||
requiredSize += sizeof (ToCharType);
|
||||
|
||||
MemoryBlock result;
|
||||
result.setSize (requiredSize);
|
||||
|
||||
ToCharPointerType dest { (ToCharType*) result.getData() };
|
||||
|
||||
for (auto wc : sourceChars)
|
||||
dest.write (wc);
|
||||
|
||||
if (! removeNullTerminator)
|
||||
dest.writeNull();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SeparatorStrings
|
||||
{
|
||||
std::vector<MemoryBlock> terminals, nulls;
|
||||
};
|
||||
|
||||
template <typename CharPointerType>
|
||||
SeparatorStrings getSeparators()
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_ASCII>()
|
||||
{
|
||||
SeparatorStrings result;
|
||||
|
||||
const CharPointer_ASCII::CharType* terminalCharPtrs[] = {
|
||||
"", "-", "+", "e", "e+", "E-", "f", " ", ",", ";", "<", "'", "\"", "_", "k",
|
||||
" +", " -", " -e", "-In ", " +n", "n", " r"
|
||||
};
|
||||
|
||||
for (auto ptr : terminalCharPtrs)
|
||||
result.terminals.push_back (memoryBlockFromCharPtr<CharPointer_ASCII> (ptr));
|
||||
|
||||
const CharPointer_ASCII::CharType* nullCharPtrs[] = { "." };
|
||||
|
||||
result.nulls = result.terminals;
|
||||
|
||||
for (auto ptr : nullCharPtrs)
|
||||
result.nulls.push_back (memoryBlockFromCharPtr<CharPointer_ASCII> (ptr));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_UTF8>()
|
||||
{
|
||||
auto result = getSeparators<CharPointer_ASCII>();
|
||||
|
||||
const CharPointer_UTF8::CharType* terminalCharPtrs[] = {
|
||||
"\xe2\x82\xac", // €
|
||||
"\xf0\x90\x90\xB7", // 𐐷
|
||||
"\xf0\x9f\x98\x83", // 😃
|
||||
"\xf0\x9f\x8f\x81\xF0\x9F\x9A\x97" // 🏁🚗
|
||||
};
|
||||
|
||||
for (auto ptr : terminalCharPtrs)
|
||||
{
|
||||
auto block = memoryBlockFromCharPtr<CharPointer_UTF8> (ptr);
|
||||
|
||||
for (auto vec : { &result.terminals, &result.nulls })
|
||||
vec->push_back (block);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename CharPointerType, typename StorageType>
|
||||
SeparatorStrings prefixWithAsciiSeparators (const std::vector<std::vector<StorageType>>& terminalCharPtrs)
|
||||
{
|
||||
auto asciiSeparators = getSeparators<CharPointer_ASCII>();
|
||||
|
||||
SeparatorStrings result;
|
||||
|
||||
for (const auto& block : asciiSeparators.terminals)
|
||||
result.terminals.push_back (convert<CharPointer_ASCII, CharPointerType> (block));
|
||||
|
||||
for (const auto& block : asciiSeparators.nulls)
|
||||
result.nulls.push_back (convert<CharPointer_ASCII, CharPointerType> (block));
|
||||
|
||||
for (auto& t : terminalCharPtrs)
|
||||
{
|
||||
const auto block = memoryBlockFromCharPtr<CharPointerType> ((typename CharPointerType::CharType*) t.data());
|
||||
|
||||
for (auto vec : { &result.terminals, &result.nulls })
|
||||
vec->push_back (block);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_UTF16>()
|
||||
{
|
||||
const std::vector<std::vector<char16_t>> terminalCharPtrs {
|
||||
{ 0x0 },
|
||||
{ 0x0076, 0x0 }, // v
|
||||
{ 0x20ac, 0x0 }, // €
|
||||
{ 0xd801, 0xdc37, 0x0 }, // 𐐷
|
||||
{ 0x0065, 0xd83d, 0xde03, 0x0 }, // e😃
|
||||
{ 0xd83c, 0xdfc1, 0xd83d, 0xde97, 0x0 } // 🏁🚗
|
||||
};
|
||||
|
||||
return prefixWithAsciiSeparators<CharPointer_UTF16> (terminalCharPtrs);
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_UTF32>()
|
||||
{
|
||||
const std::vector<std::vector<char32_t>> terminalCharPtrs = {
|
||||
{ 0x00000076, 0x0 }, // v
|
||||
{ 0x000020aC, 0x0 }, // €
|
||||
{ 0x00010437, 0x0 }, // 𐐷
|
||||
{ 0x00000065, 0x0001f603, 0x0 }, // e😃
|
||||
{ 0x0001f3c1, 0x0001f697, 0x0 } // 🏁🚗
|
||||
};
|
||||
|
||||
return prefixWithAsciiSeparators<CharPointer_UTF32> (terminalCharPtrs);
|
||||
}
|
||||
|
||||
template <typename TestFunction>
|
||||
void withAllPrefixesAndSuffixes (const std::vector<MemoryBlock>& prefixes,
|
||||
const std::vector<MemoryBlock>& suffixes,
|
||||
const std::vector<MemoryBlock>& testValues,
|
||||
TestFunction&& test)
|
||||
{
|
||||
for (const auto& prefix : prefixes)
|
||||
{
|
||||
for (const auto& testValue : testValues)
|
||||
{
|
||||
MemoryBlock testBlock = prefix;
|
||||
testBlock.append (testValue.getData(), testValue.getSize());
|
||||
|
||||
for (const auto& suffix : suffixes)
|
||||
{
|
||||
MemoryBlock data = testBlock;
|
||||
data.append (suffix.getData(), suffix.getSize());
|
||||
|
||||
test (data, suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CharPointerType>
|
||||
class CharacterFunctionsTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
using CharType = typename CharPointerType::CharType;
|
||||
|
||||
CharacterFunctionsTests()
|
||||
: UnitTest ("CharacterFunctions", UnitTestCategories::text)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("readDoubleValue");
|
||||
|
||||
const std::pair<const char*, double> trials[] =
|
||||
{
|
||||
// Integers
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (3),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (4931),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (5000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (9862097),
|
||||
|
||||
// Floating point numbers
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (9.),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (7.000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.2),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (.298630),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.118),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.9000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.0000001),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (500.0000001),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (9862098.2398604),
|
||||
|
||||
// Exponents
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0e0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.e0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.00000e0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (.0e7),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0e-5),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (2E0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (4.E0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.2000000E0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.2000000E6),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (.398e3),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10e10),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.4962e+2),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (3198693.0973e4),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10973097.2087E-4),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.3986e00006),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (2087.3087e+00006),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (6.0872e-00006),
|
||||
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.7976931348623157e+308),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (2.2250738585072014e-308),
|
||||
|
||||
// Too many sig figs. The parsing routine on MinGW gets the last
|
||||
// significant figure wrong.
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (17654321098765432.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (183456789012345678.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1934567890123456789.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (20345678901234567891.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e3),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e100),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-5),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000005e-40),
|
||||
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890e-111),
|
||||
};
|
||||
|
||||
auto asciiToMemoryBlock = [] (const char* asciiPtr, bool removeNullTerminator)
|
||||
{
|
||||
auto block = memoryBlockFromCharPtr<CharPointer_ASCII> (asciiPtr);
|
||||
return convert<CharPointer_ASCII, CharPointerType> (block, removeNullTerminator);
|
||||
};
|
||||
|
||||
const auto separators = getSeparators<CharPointerType>();
|
||||
|
||||
for (const auto& trial : trials)
|
||||
{
|
||||
for (const auto& terminal : separators.terminals)
|
||||
{
|
||||
MemoryBlock data { asciiToMemoryBlock (trial.first, true) };
|
||||
data.append (terminal.getData(), terminal.getSize());
|
||||
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), trial.second);
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) terminal.getData())));
|
||||
}
|
||||
}
|
||||
|
||||
auto asciiToMemoryBlocks = [&] (const std::vector<const char*>& asciiPtrs, bool removeNullTerminator)
|
||||
{
|
||||
std::vector<MemoryBlock> result;
|
||||
|
||||
for (auto* ptr : asciiPtrs)
|
||||
result.push_back (asciiToMemoryBlock (ptr, removeNullTerminator));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
std::vector<const char*> prefixCharPtrs = { "" , "+", "-" };
|
||||
const auto prefixes = asciiToMemoryBlocks (prefixCharPtrs, true);
|
||||
|
||||
{
|
||||
std::vector<const char*> nanCharPtrs = { "NaN", "nan", "NAN", "naN" };
|
||||
auto nans = asciiToMemoryBlocks (nanCharPtrs, true);
|
||||
|
||||
withAllPrefixesAndSuffixes (prefixes, separators.terminals, nans, [this] (const MemoryBlock& data,
|
||||
const MemoryBlock& suffix)
|
||||
{
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
expect (std::isnan (CharacterFunctions::readDoubleValue (charPtr)));
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData())));
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<const char*> infCharPtrs = { "Inf", "inf", "INF", "InF", "1.0E1024", "1.23456789012345678901234567890e123456789" };
|
||||
auto infs = asciiToMemoryBlocks (infCharPtrs, true);
|
||||
|
||||
withAllPrefixesAndSuffixes (prefixes, separators.terminals, infs, [this] (const MemoryBlock& data,
|
||||
const MemoryBlock& suffix)
|
||||
{
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
auto expected = charPtr[0] == '-' ? -std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::infinity();
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), expected);
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData())));
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<const char*> zeroCharPtrs = { "1.0E-400", "1.23456789012345678901234567890e-123456789" };
|
||||
auto zeros = asciiToMemoryBlocks (zeroCharPtrs, true);
|
||||
|
||||
withAllPrefixesAndSuffixes (prefixes, separators.terminals, zeros, [this] (const MemoryBlock& data,
|
||||
const MemoryBlock& suffix)
|
||||
{
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
auto expected = charPtr[0] == '-' ? -0.0 : 0.0;
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), expected);
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData())));
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
for (const auto& n : separators.nulls)
|
||||
{
|
||||
MemoryBlock data { n.getData(), n.getSize() };
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), 0.0);
|
||||
expect (charPtr == CharPointerType { (CharType*) data.getData() }.findEndOfWhitespace());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CharacterFunctionsTests<CharPointer_ASCII> characterFunctionsTestsAscii;
|
||||
static CharacterFunctionsTests<CharPointer_UTF8> characterFunctionsTestsUtf8;
|
||||
static CharacterFunctionsTests<CharPointer_UTF16> characterFunctionsTestsUtf16;
|
||||
static CharacterFunctionsTests<CharPointer_UTF32> characterFunctionsTestsUtf32;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
855
deps/juce/modules/juce_core/text/juce_CharacterFunctions.h
vendored
Normal file
855
deps/juce/modules/juce_core/text/juce_CharacterFunctions.h
vendored
Normal file
@ -0,0 +1,855 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
/** This macro will be set to 1 if the compiler's native wchar_t is an 8-bit type. */
|
||||
#define JUCE_NATIVE_WCHAR_IS_UTF8 0
|
||||
/** This macro will be set to 1 if the compiler's native wchar_t is a 16-bit type. */
|
||||
#define JUCE_NATIVE_WCHAR_IS_UTF16 0
|
||||
/** This macro will be set to 1 if the compiler's native wchar_t is a 32-bit type. */
|
||||
#define JUCE_NATIVE_WCHAR_IS_UTF32 1
|
||||
#endif
|
||||
|
||||
#if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN
|
||||
/** A platform-independent 32-bit unicode character type. */
|
||||
using juce_wchar = wchar_t;
|
||||
#else
|
||||
using juce_wchar = uint32;
|
||||
#endif
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/** This macro is deprecated, but preserved for compatibility with old code. */
|
||||
#define JUCE_T(stringLiteral) (L##stringLiteral)
|
||||
#endif
|
||||
|
||||
#if JUCE_DEFINE_T_MACRO
|
||||
/** The 'T' macro is an alternative for using the "L" prefix in front of a string literal.
|
||||
|
||||
This macro is deprecated, but available for compatibility with old code if you set
|
||||
JUCE_DEFINE_T_MACRO = 1. The fastest, most portable and best way to write your string
|
||||
literals is as standard char strings, using escaped utf-8 character sequences for extended
|
||||
characters, rather than trying to store them as wide-char strings.
|
||||
*/
|
||||
#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
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A collection of functions for manipulating characters and character strings.
|
||||
|
||||
Most of these methods are designed for internal use by the String and CharPointer
|
||||
classes, but some of them may be useful to call directly.
|
||||
|
||||
@see String, CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API CharacterFunctions
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Converts a character to upper-case. */
|
||||
static juce_wchar toUpperCase (juce_wchar character) noexcept;
|
||||
/** Converts a character to lower-case. */
|
||||
static juce_wchar toLowerCase (juce_wchar character) noexcept;
|
||||
|
||||
/** Checks whether a unicode character is upper-case. */
|
||||
static bool isUpperCase (juce_wchar character) noexcept;
|
||||
/** Checks whether a unicode character is lower-case. */
|
||||
static bool isLowerCase (juce_wchar character) noexcept;
|
||||
|
||||
/** Checks whether a character is whitespace. */
|
||||
static bool isWhitespace (char character) noexcept;
|
||||
/** Checks whether a character is whitespace. */
|
||||
static bool isWhitespace (juce_wchar character) noexcept;
|
||||
|
||||
/** Checks whether a character is a digit. */
|
||||
static bool isDigit (char character) noexcept;
|
||||
/** Checks whether a character is a digit. */
|
||||
static bool isDigit (juce_wchar character) noexcept;
|
||||
|
||||
/** Checks whether a character is alphabetic. */
|
||||
static bool isLetter (char character) noexcept;
|
||||
/** Checks whether a character is alphabetic. */
|
||||
static bool isLetter (juce_wchar character) noexcept;
|
||||
|
||||
/** Checks whether a character is alphabetic or numeric. */
|
||||
static bool isLetterOrDigit (char character) noexcept;
|
||||
/** Checks whether a character is alphabetic or numeric. */
|
||||
static bool isLetterOrDigit (juce_wchar character) noexcept;
|
||||
|
||||
/** Checks whether a character is a printable character, i.e. alphabetic, numeric,
|
||||
a punctuation character or a space.
|
||||
*/
|
||||
static bool isPrintable (char character) noexcept;
|
||||
|
||||
/** Checks whether a character is a printable character, i.e. alphabetic, numeric,
|
||||
a punctuation character or a space.
|
||||
*/
|
||||
static bool isPrintable (juce_wchar character) noexcept;
|
||||
|
||||
/** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */
|
||||
static int getHexDigitValue (juce_wchar digit) noexcept;
|
||||
|
||||
/** Converts a byte of Windows 1252 codepage to unicode. */
|
||||
static juce_wchar getUnicodeCharFromWindows1252Codepage (uint8 windows1252Char) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Parses a character string to read a floating-point number.
|
||||
Note that this will advance the pointer that is passed in, leaving it at
|
||||
the end of the number.
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/** Parses a character string, to read a floating-point value. */
|
||||
template <typename CharPointerType>
|
||||
static double getDoubleValue (CharPointerType text) noexcept
|
||||
{
|
||||
return readDoubleValue (text);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Parses a character string, to read an integer value. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Parses a character string, to read a hexadecimal value. */
|
||||
template <typename ResultType>
|
||||
struct HexParser
|
||||
{
|
||||
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 = (result << 4) | hexValue;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Counts the number of characters in a given string, stopping if the count exceeds
|
||||
a specified limit. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Counts the number of characters in a given string, stopping if the count exceeds
|
||||
a specified end-pointer. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Copies null-terminated characters from one string to another. */
|
||||
template <typename DestCharPointerType, typename SrcCharPointerType>
|
||||
static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept
|
||||
{
|
||||
while (auto c = src.getAndAdvance())
|
||||
dest.write (c);
|
||||
|
||||
dest.writeNull();
|
||||
}
|
||||
|
||||
/** Copies characters from one string to another, up to a null terminator
|
||||
or a given byte size limit. */
|
||||
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);
|
||||
}
|
||||
|
||||
/** Copies characters from one string to another, up to a null terminator
|
||||
or a given maximum number of characters. */
|
||||
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();
|
||||
}
|
||||
|
||||
/** Compares two characters. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Compares two null-terminated character strings. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Compares two null-terminated character strings, up to a given number of characters. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Compares two characters, using a case-independant match. */
|
||||
static int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept
|
||||
{
|
||||
return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0;
|
||||
}
|
||||
|
||||
/** Compares two null-terminated character strings, using a case-independant match. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Compares two null-terminated character strings, using a case-independent match. */
|
||||
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;
|
||||
}
|
||||
|
||||
/** Finds the character index of a given substring in another string.
|
||||
Returns -1 if the substring is not found.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first occurrence of a substring in a string.
|
||||
If the substring is not found, this will return a pointer to the string's
|
||||
null terminator.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first occurrence of a substring in a string.
|
||||
If the substring is not found, this will return a pointer to the string's
|
||||
null terminator.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Finds the character index of a given substring in another string, using
|
||||
a case-independent match.
|
||||
Returns -1 if the substring is not found.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Finds the character index of a given character in another string.
|
||||
Returns -1 if the character is not found.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Finds the character index of a given character in another string, using
|
||||
a case-independent match.
|
||||
Returns -1 if the character is not found.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Increments a pointer until it points to the first non-whitespace character
|
||||
in a string.
|
||||
|
||||
If the string contains only whitespace, the pointer will point to the
|
||||
string's null terminator.
|
||||
*/
|
||||
template <typename Type>
|
||||
static void incrementToEndOfWhitespace (Type& text) noexcept
|
||||
{
|
||||
while (text.isWhitespace())
|
||||
++text;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first non-whitespace character in a string.
|
||||
If the string contains only whitespace, this will return a pointer
|
||||
to its null terminator.
|
||||
*/
|
||||
template <typename Type>
|
||||
static Type findEndOfWhitespace (Type text) noexcept
|
||||
{
|
||||
incrementToEndOfWhitespace (text);
|
||||
return text;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first character in the string which is found in
|
||||
the breakCharacters string.
|
||||
*/
|
||||
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
|
74
deps/juce/modules/juce_core/text/juce_Identifier.cpp
vendored
Normal file
74
deps/juce/modules/juce_core/text/juce_Identifier.cpp
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
Identifier::Identifier() noexcept {}
|
||||
Identifier::~Identifier() noexcept {}
|
||||
|
||||
Identifier::Identifier (const Identifier& other) noexcept : name (other.name) {}
|
||||
|
||||
Identifier::Identifier (Identifier&& other) noexcept : name (std::move (other.name)) {}
|
||||
|
||||
Identifier& Identifier::operator= (Identifier&& other) noexcept
|
||||
{
|
||||
name = std::move (other.name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Identifier& Identifier::operator= (const Identifier& other) noexcept
|
||||
{
|
||||
name = other.name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Identifier::Identifier (const String& nm)
|
||||
: name (StringPool::getGlobalPool().getPooledString (nm))
|
||||
{
|
||||
// An Identifier cannot be created from an empty string!
|
||||
jassert (nm.isNotEmpty());
|
||||
}
|
||||
|
||||
Identifier::Identifier (const char* nm)
|
||||
: name (StringPool::getGlobalPool().getPooledString (nm))
|
||||
{
|
||||
// An Identifier cannot be created from an empty string!
|
||||
jassert (nm != nullptr && nm[0] != 0);
|
||||
}
|
||||
|
||||
Identifier::Identifier (String::CharPointerType start, String::CharPointerType end)
|
||||
: name (StringPool::getGlobalPool().getPooledString (start, end))
|
||||
{
|
||||
// An Identifier cannot be created from an empty string!
|
||||
jassert (start < end);
|
||||
}
|
||||
|
||||
Identifier Identifier::null;
|
||||
|
||||
bool Identifier::isValidIdentifier (const String& possibleIdentifier) noexcept
|
||||
{
|
||||
return possibleIdentifier.isNotEmpty()
|
||||
&& possibleIdentifier.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:#@$%");
|
||||
}
|
||||
|
||||
} // namespace juce
|
132
deps/juce/modules/juce_core/text/juce_Identifier.h
vendored
Normal file
132
deps/juce/modules/juce_core/text/juce_Identifier.h
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a string identifier, designed for accessing properties by name.
|
||||
|
||||
Comparing two Identifier objects is very fast (an O(1) operation), but creating
|
||||
them can be slower than just using a String directly, so the optimal way to use them
|
||||
is to keep some static Identifier objects for the things you use often.
|
||||
|
||||
@see NamedValueSet, ValueTree
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API Identifier final
|
||||
{
|
||||
public:
|
||||
/** Creates a null identifier. */
|
||||
Identifier() noexcept;
|
||||
|
||||
/** Creates an identifier with a specified name.
|
||||
Because this name may need to be used in contexts such as script variables or XML
|
||||
tags, it must only contain ascii letters and digits, or the underscore character.
|
||||
*/
|
||||
Identifier (const char* name);
|
||||
|
||||
/** Creates an identifier with a specified name.
|
||||
Because this name may need to be used in contexts such as script variables or XML
|
||||
tags, it must only contain ascii letters and digits, or the underscore character.
|
||||
*/
|
||||
Identifier (const String& name);
|
||||
|
||||
/** Creates an identifier with a specified name.
|
||||
Because this name may need to be used in contexts such as script variables or XML
|
||||
tags, it must only contain ascii letters and digits, or the underscore character.
|
||||
*/
|
||||
Identifier (String::CharPointerType nameStart, String::CharPointerType nameEnd);
|
||||
|
||||
/** Creates a copy of another identifier. */
|
||||
Identifier (const Identifier& other) noexcept;
|
||||
|
||||
/** Creates a copy of another identifier. */
|
||||
Identifier& operator= (const Identifier& other) noexcept;
|
||||
|
||||
/** Creates a copy of another identifier. */
|
||||
Identifier (Identifier&& other) noexcept;
|
||||
|
||||
/** Creates a copy of another identifier. */
|
||||
Identifier& operator= (Identifier&& other) noexcept;
|
||||
|
||||
/** Destructor */
|
||||
~Identifier() noexcept;
|
||||
|
||||
/** Compares two identifiers. This is a very fast operation. */
|
||||
inline bool operator== (const Identifier& other) const noexcept { return name.getCharPointer() == other.name.getCharPointer(); }
|
||||
|
||||
/** Compares two identifiers. This is a very fast operation. */
|
||||
inline bool operator!= (const Identifier& other) const noexcept { return name.getCharPointer() != other.name.getCharPointer(); }
|
||||
|
||||
/** Compares the identifier with a string. */
|
||||
inline bool operator== (StringRef other) const noexcept { return name == other; }
|
||||
|
||||
/** Compares the identifier with a string. */
|
||||
inline bool operator!= (StringRef other) const noexcept { return name != other; }
|
||||
|
||||
/** Compares the identifier with a string. */
|
||||
inline bool operator< (StringRef other) const noexcept { return name < other; }
|
||||
|
||||
/** Compares the identifier with a string. */
|
||||
inline bool operator<= (StringRef other) const noexcept { return name <= other; }
|
||||
|
||||
/** Compares the identifier with a string. */
|
||||
inline bool operator> (StringRef other) const noexcept { return name > other; }
|
||||
|
||||
/** Compares the identifier with a string. */
|
||||
inline bool operator>= (StringRef other) const noexcept { return name >= other; }
|
||||
|
||||
/** Returns this identifier as a string. */
|
||||
const String& toString() const noexcept { return name; }
|
||||
|
||||
/** Returns this identifier's raw string pointer. */
|
||||
operator String::CharPointerType() const noexcept { return name.getCharPointer(); }
|
||||
|
||||
/** Returns this identifier's raw string pointer. */
|
||||
String::CharPointerType getCharPointer() const noexcept { return name.getCharPointer(); }
|
||||
|
||||
/** Returns this identifier as a StringRef. */
|
||||
operator StringRef() const noexcept { return name; }
|
||||
|
||||
/** Returns true if this Identifier is not null */
|
||||
bool isValid() const noexcept { return name.isNotEmpty(); }
|
||||
|
||||
/** Returns true if this Identifier is null */
|
||||
bool isNull() const noexcept { return name.isEmpty(); }
|
||||
|
||||
/** A null identifier. */
|
||||
static Identifier null;
|
||||
|
||||
/** Checks a given string for characters that might not be valid in an Identifier.
|
||||
Since Identifiers are used as a script variables and XML attributes, they should only contain
|
||||
alphanumeric characters, underscores, or the '-' and ':' characters.
|
||||
*/
|
||||
static bool isValidIdentifier (const String& possibleIdentifier) noexcept;
|
||||
|
||||
private:
|
||||
String name;
|
||||
};
|
||||
|
||||
} // namespace juce
|
206
deps/juce/modules/juce_core/text/juce_LocalisedStrings.cpp
vendored
Normal file
206
deps/juce/modules/juce_core/text/juce_LocalisedStrings.cpp
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
LocalisedStrings::LocalisedStrings (const String& fileContents, bool ignoreCase)
|
||||
{
|
||||
loadFromText (fileContents, ignoreCase);
|
||||
}
|
||||
|
||||
LocalisedStrings::LocalisedStrings (const File& fileToLoad, bool ignoreCase)
|
||||
{
|
||||
loadFromText (fileToLoad.loadFileAsString(), ignoreCase);
|
||||
}
|
||||
|
||||
LocalisedStrings::LocalisedStrings (const LocalisedStrings& other)
|
||||
: languageName (other.languageName), countryCodes (other.countryCodes),
|
||||
translations (other.translations), fallback (createCopyIfNotNull (other.fallback.get()))
|
||||
{
|
||||
}
|
||||
|
||||
LocalisedStrings& LocalisedStrings::operator= (const LocalisedStrings& other)
|
||||
{
|
||||
languageName = other.languageName;
|
||||
countryCodes = other.countryCodes;
|
||||
translations = other.translations;
|
||||
fallback.reset (createCopyIfNotNull (other.fallback.get()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocalisedStrings::~LocalisedStrings()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String LocalisedStrings::translate (const String& text) const
|
||||
{
|
||||
if (fallback != nullptr && ! translations.containsKey (text))
|
||||
return fallback->translate (text);
|
||||
|
||||
return translations.getValue (text, text);
|
||||
}
|
||||
|
||||
String LocalisedStrings::translate (const String& text, const String& resultIfNotFound) const
|
||||
{
|
||||
if (fallback != nullptr && ! translations.containsKey (text))
|
||||
return fallback->translate (text, resultIfNotFound);
|
||||
|
||||
return translations.getValue (text, resultIfNotFound);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
#if JUCE_CHECK_MEMORY_LEAKS
|
||||
// By using this object to force a LocalisedStrings object to be created
|
||||
// before the currentMappings object, we can force the static order-of-destruction to
|
||||
// delete the currentMappings object first, which avoids a bogus leak warning.
|
||||
// (Oddly, just creating a LocalisedStrings on the stack doesn't work in gcc, it
|
||||
// has to be created with 'new' for this to work..)
|
||||
struct LeakAvoidanceTrick
|
||||
{
|
||||
LeakAvoidanceTrick()
|
||||
{
|
||||
const std::unique_ptr<LocalisedStrings> dummy (new LocalisedStrings (String(), false));
|
||||
}
|
||||
};
|
||||
|
||||
LeakAvoidanceTrick leakAvoidanceTrick;
|
||||
#endif
|
||||
|
||||
SpinLock currentMappingsLock;
|
||||
std::unique_ptr<LocalisedStrings> currentMappings;
|
||||
|
||||
static int findCloseQuote (const String& text, int startPos)
|
||||
{
|
||||
juce_wchar lastChar = 0;
|
||||
auto t = text.getCharPointer() + startPos;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c = t.getAndAdvance();
|
||||
|
||||
if (c == 0 || (c == '"' && lastChar != '\\'))
|
||||
break;
|
||||
|
||||
lastChar = c;
|
||||
++startPos;
|
||||
}
|
||||
|
||||
return startPos;
|
||||
}
|
||||
|
||||
static String unescapeString (const String& s)
|
||||
{
|
||||
return s.replace ("\\\"", "\"")
|
||||
.replace ("\\\'", "\'")
|
||||
.replace ("\\t", "\t")
|
||||
.replace ("\\r", "\r")
|
||||
.replace ("\\n", "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void LocalisedStrings::loadFromText (const String& fileContents, bool ignoreCase)
|
||||
{
|
||||
translations.setIgnoresCase (ignoreCase);
|
||||
|
||||
StringArray lines;
|
||||
lines.addLines (fileContents);
|
||||
|
||||
for (auto& l : lines)
|
||||
{
|
||||
auto line = l.trim();
|
||||
|
||||
if (line.startsWithChar ('"'))
|
||||
{
|
||||
auto closeQuote = findCloseQuote (line, 1);
|
||||
auto originalText = unescapeString (line.substring (1, closeQuote));
|
||||
|
||||
if (originalText.isNotEmpty())
|
||||
{
|
||||
auto openingQuote = findCloseQuote (line, closeQuote + 1);
|
||||
closeQuote = findCloseQuote (line, openingQuote + 1);
|
||||
auto newText = unescapeString (line.substring (openingQuote + 1, closeQuote));
|
||||
|
||||
if (newText.isNotEmpty())
|
||||
translations.set (originalText, newText);
|
||||
}
|
||||
}
|
||||
else if (line.startsWithIgnoreCase ("language:"))
|
||||
{
|
||||
languageName = line.substring (9).trim();
|
||||
}
|
||||
else if (line.startsWithIgnoreCase ("countries:"))
|
||||
{
|
||||
countryCodes.addTokens (line.substring (10).trim(), true);
|
||||
countryCodes.trim();
|
||||
countryCodes.removeEmptyStrings();
|
||||
}
|
||||
}
|
||||
|
||||
translations.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
void LocalisedStrings::addStrings (const LocalisedStrings& other)
|
||||
{
|
||||
jassert (languageName == other.languageName);
|
||||
jassert (countryCodes == other.countryCodes);
|
||||
|
||||
translations.addArray (other.translations);
|
||||
}
|
||||
|
||||
void LocalisedStrings::setFallback (LocalisedStrings* f)
|
||||
{
|
||||
fallback.reset (f);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LocalisedStrings::setCurrentMappings (LocalisedStrings* newTranslations)
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (currentMappingsLock);
|
||||
currentMappings.reset (newTranslations);
|
||||
}
|
||||
|
||||
LocalisedStrings* LocalisedStrings::getCurrentMappings()
|
||||
{
|
||||
return currentMappings.get();
|
||||
}
|
||||
|
||||
String LocalisedStrings::translateWithCurrentMappings (const String& text) { return juce::translate (text); }
|
||||
String LocalisedStrings::translateWithCurrentMappings (const char* text) { return juce::translate (text); }
|
||||
|
||||
JUCE_API String translate (const String& text) { return juce::translate (text, text); }
|
||||
JUCE_API String translate (const char* text) { return juce::translate (String (text)); }
|
||||
JUCE_API String translate (CharPointer_UTF8 text) { return juce::translate (String (text)); }
|
||||
|
||||
JUCE_API String translate (const String& text, const String& resultIfNotFound)
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (currentMappingsLock);
|
||||
|
||||
if (auto* mappings = LocalisedStrings::getCurrentMappings())
|
||||
return mappings->translate (text, resultIfNotFound);
|
||||
|
||||
return resultIfNotFound;
|
||||
}
|
||||
|
||||
} // namespace juce
|
240
deps/juce/modules/juce_core/text/juce_LocalisedStrings.h
vendored
Normal file
240
deps/juce/modules/juce_core/text/juce_LocalisedStrings.h
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used to convert strings to localised foreign-language versions.
|
||||
|
||||
This is basically a look-up table of strings and their translated equivalents.
|
||||
It can be loaded from a text file, so that you can supply a set of localised
|
||||
versions of strings that you use in your app.
|
||||
|
||||
To use it in your code, simply call the translate() method on each string that
|
||||
might have foreign versions, and if none is found, the method will just return
|
||||
the original string.
|
||||
|
||||
The translation file should start with some lines specifying a description of
|
||||
the language it contains, and also a list of ISO country codes where it might
|
||||
be appropriate to use the file. After that, each line of the file should contain
|
||||
a pair of quoted strings with an '=' sign.
|
||||
|
||||
E.g. for a french translation, the file might be:
|
||||
|
||||
@code
|
||||
language: French
|
||||
countries: fr be mc ch lu
|
||||
|
||||
"hello" = "bonjour"
|
||||
"goodbye" = "au revoir"
|
||||
@endcode
|
||||
|
||||
If the strings need to contain a quote character, they can use '\"' instead, and
|
||||
if the first non-whitespace character on a line isn't a quote, then it's ignored,
|
||||
(you can use this to add comments).
|
||||
|
||||
Note that this is a singleton class, so don't create or destroy the object directly.
|
||||
There's also a TRANS(text) macro defined to make it easy to use the this.
|
||||
|
||||
E.g. @code
|
||||
printSomething (TRANS("hello"));
|
||||
@endcode
|
||||
|
||||
This macro is used in the JUCE classes themselves, so your application has a chance to
|
||||
intercept and translate any internal JUCE text strings that might be shown. (You can easily
|
||||
get a list of all the messages by searching for the TRANS() macro in the JUCE source
|
||||
code).
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API LocalisedStrings
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a set of translations from the text of a translation file.
|
||||
|
||||
When you create one of these, you can call setCurrentMappings() to make it
|
||||
the set of mappings that the system's using.
|
||||
*/
|
||||
LocalisedStrings (const String& fileContents, bool ignoreCaseOfKeys);
|
||||
|
||||
/** Creates a set of translations from a file.
|
||||
|
||||
When you create one of these, you can call setCurrentMappings() to make it
|
||||
the set of mappings that the system's using.
|
||||
*/
|
||||
LocalisedStrings (const File& fileToLoad, bool ignoreCaseOfKeys);
|
||||
|
||||
LocalisedStrings (const LocalisedStrings&);
|
||||
LocalisedStrings& operator= (const LocalisedStrings&);
|
||||
|
||||
/** Destructor. */
|
||||
~LocalisedStrings();
|
||||
|
||||
//==============================================================================
|
||||
/** Selects the current set of mappings to be used by the system.
|
||||
|
||||
The object you pass in will be automatically deleted when no longer needed, so
|
||||
don't keep a pointer to it. You can also pass in nullptr to remove the current
|
||||
mappings.
|
||||
|
||||
See also the TRANS() macro, which uses the current set to do its translation.
|
||||
|
||||
@see translateWithCurrentMappings
|
||||
*/
|
||||
static void setCurrentMappings (LocalisedStrings* newTranslations);
|
||||
|
||||
/** Returns the currently selected set of mappings.
|
||||
|
||||
This is the object that was last passed to setCurrentMappings(). It may
|
||||
be nullptr if none has been created.
|
||||
*/
|
||||
static LocalisedStrings* getCurrentMappings();
|
||||
|
||||
/** Tries to translate a string using the currently selected set of mappings.
|
||||
|
||||
If no mapping has been set, or if the mapping doesn't contain a translation
|
||||
for the string, this will just return the original string.
|
||||
|
||||
See also the TRANS() macro, which uses this method to do its translation.
|
||||
|
||||
@see setCurrentMappings, getCurrentMappings
|
||||
*/
|
||||
static String translateWithCurrentMappings (const String& text);
|
||||
|
||||
/** Tries to translate a string using the currently selected set of mappings.
|
||||
|
||||
If no mapping has been set, or if the mapping doesn't contain a translation
|
||||
for the string, this will just return the original string.
|
||||
|
||||
See also the TRANS() macro, which uses this method to do its translation.
|
||||
|
||||
@see setCurrentMappings, getCurrentMappings
|
||||
*/
|
||||
static String translateWithCurrentMappings (const char* text);
|
||||
|
||||
//==============================================================================
|
||||
/** Attempts to look up a string and return its localised version.
|
||||
If the string isn't found in the list, the original string will be returned.
|
||||
*/
|
||||
String translate (const String& text) const;
|
||||
|
||||
/** Attempts to look up a string and return its localised version.
|
||||
If the string isn't found in the list, the resultIfNotFound string will be returned.
|
||||
*/
|
||||
String translate (const String& text, const String& resultIfNotFound) const;
|
||||
|
||||
/** Returns the name of the language specified in the translation file.
|
||||
|
||||
This is specified in the file using a line starting with "language:", e.g.
|
||||
@code
|
||||
language: german
|
||||
@endcode
|
||||
*/
|
||||
String getLanguageName() const { return languageName; }
|
||||
|
||||
/** Returns the list of suitable country codes listed in the translation file.
|
||||
|
||||
These is specified in the file using a line starting with "countries:", e.g.
|
||||
@code
|
||||
countries: fr be mc ch lu
|
||||
@endcode
|
||||
|
||||
The country codes are supposed to be 2-character ISO compliant codes.
|
||||
*/
|
||||
const StringArray& getCountryCodes() const { return countryCodes; }
|
||||
|
||||
/** Provides access to the actual list of mappings. */
|
||||
const StringPairArray& getMappings() const { return translations; }
|
||||
|
||||
//==============================================================================
|
||||
/** Adds and merges another set of translations into this set.
|
||||
|
||||
Note that the language name and country codes of the new LocalisedStrings
|
||||
object must match that of this object - an assertion will be thrown if they
|
||||
don't match.
|
||||
|
||||
Any existing values will have their mappings overwritten by the new ones.
|
||||
*/
|
||||
void addStrings (const LocalisedStrings&);
|
||||
|
||||
/** Gives this object a set of strings to use as a fallback if a string isn't found.
|
||||
The object that is passed-in will be owned and deleted by this object
|
||||
when no longer needed. It can be nullptr to clear the existing fallback object.
|
||||
*/
|
||||
void setFallback (LocalisedStrings* fallbackStrings);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String languageName;
|
||||
StringArray countryCodes;
|
||||
StringPairArray translations;
|
||||
std::unique_ptr<LocalisedStrings> fallback;
|
||||
|
||||
void loadFromText (const String&, bool ignoreCase);
|
||||
|
||||
JUCE_LEAK_DETECTOR (LocalisedStrings)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef TRANS
|
||||
/** Uses the LocalisedStrings class to translate the given string literal.
|
||||
This macro is provided for backwards-compatibility, and just calls the translate()
|
||||
function. In new code, it's recommended that you just call translate() directly
|
||||
instead, and avoid using macros.
|
||||
@see translate(), LocalisedStrings
|
||||
*/
|
||||
#define TRANS(stringLiteral) juce::translate (stringLiteral)
|
||||
#endif
|
||||
|
||||
/** A dummy version of the TRANS macro, used to indicate a string literal that should be
|
||||
added to the translation file by source-code scanner tools.
|
||||
|
||||
Wrapping a string literal in this macro has no effect, but by using it around strings
|
||||
that your app needs to translate at a later stage, it lets automatic code-scanning tools
|
||||
find this string and add it to the list of strings that need translation.
|
||||
*/
|
||||
#define NEEDS_TRANS(stringLiteral) (stringLiteral)
|
||||
|
||||
/** Uses the LocalisedStrings class to translate the given string literal.
|
||||
@see LocalisedStrings
|
||||
*/
|
||||
JUCE_API String translate (const String& stringLiteral);
|
||||
|
||||
/** Uses the LocalisedStrings class to translate the given string literal.
|
||||
@see LocalisedStrings
|
||||
*/
|
||||
JUCE_API String translate (const char* stringLiteral);
|
||||
|
||||
/** Uses the LocalisedStrings class to translate the given string literal.
|
||||
@see LocalisedStrings
|
||||
*/
|
||||
JUCE_API String translate (CharPointer_UTF8 stringLiteral);
|
||||
|
||||
/** Uses the LocalisedStrings class to translate the given string literal.
|
||||
@see LocalisedStrings
|
||||
*/
|
||||
JUCE_API String translate (const String& stringLiteral, const String& resultIfNotFound);
|
||||
|
||||
} // namespace juce
|
82
deps/juce/modules/juce_core/text/juce_NewLine.h
vendored
Normal file
82
deps/juce/modules/juce_core/text/juce_NewLine.h
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** This class is used for represent a new-line character sequence.
|
||||
|
||||
To write a new-line to a stream, you can use the predefined 'newLine' variable, e.g.
|
||||
@code
|
||||
myOutputStream << "Hello World" << newLine << newLine;
|
||||
@endcode
|
||||
|
||||
The exact character sequence that will be used for the new-line can be set and
|
||||
retrieved with OutputStream::setNewLineString() and OutputStream::getNewLineString().
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API NewLine
|
||||
{
|
||||
public:
|
||||
/** Returns the default new-line sequence that the library uses.
|
||||
@see OutputStream::setNewLineString()
|
||||
*/
|
||||
static const char* getDefault() noexcept { return "\r\n"; }
|
||||
|
||||
/** Returns the default new-line sequence that the library uses.
|
||||
@see getDefault()
|
||||
*/
|
||||
operator String() const { return getDefault(); }
|
||||
|
||||
/** Returns the default new-line sequence that the library uses.
|
||||
@see OutputStream::setNewLineString()
|
||||
*/
|
||||
operator StringRef() const noexcept { return getDefault(); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A predefined object representing a new-line, which can be written to a string or stream.
|
||||
|
||||
To write a new-line to a stream, you can use the predefined 'newLine' variable like this:
|
||||
@code
|
||||
myOutputStream << "Hello World" << newLine << newLine;
|
||||
@endcode
|
||||
*/
|
||||
extern NewLine newLine;
|
||||
|
||||
//==============================================================================
|
||||
/** Writes a new-line sequence to a string.
|
||||
You can use the predefined object 'newLine' to invoke this, e.g.
|
||||
@code
|
||||
myString << "Hello World" << newLine << newLine;
|
||||
@endcode
|
||||
*/
|
||||
inline String& operator<< (String& string1, const NewLine&) { return string1 += NewLine::getDefault(); }
|
||||
inline String& operator+= (String& s1, const NewLine&) { return s1 += NewLine::getDefault(); }
|
||||
|
||||
inline String operator+ (const NewLine&, const NewLine&) { return String (NewLine::getDefault()) + NewLine::getDefault(); }
|
||||
inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); }
|
||||
inline String operator+ (const NewLine&, const char* s2) { return String (NewLine::getDefault()) + s2; }
|
||||
|
||||
} // namespace juce
|
2971
deps/juce/modules/juce_core/text/juce_String.cpp
vendored
Normal file
2971
deps/juce/modules/juce_core/text/juce_String.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1507
deps/juce/modules/juce_core/text/juce_String.h
vendored
Normal file
1507
deps/juce/modules/juce_core/text/juce_String.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
478
deps/juce/modules/juce_core/text/juce_StringArray.cpp
vendored
Normal file
478
deps/juce/modules/juce_core/text/juce_StringArray.cpp
vendored
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
StringArray::StringArray() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
StringArray::StringArray (const StringArray& other)
|
||||
: strings (other.strings)
|
||||
{
|
||||
}
|
||||
|
||||
StringArray::StringArray (StringArray&& other) noexcept
|
||||
: strings (std::move (other.strings))
|
||||
{
|
||||
}
|
||||
|
||||
StringArray::StringArray (Array<String>&& other) noexcept
|
||||
: strings (std::move (other))
|
||||
{
|
||||
}
|
||||
|
||||
StringArray::StringArray (const String& firstValue)
|
||||
{
|
||||
strings.add (firstValue);
|
||||
}
|
||||
|
||||
StringArray::StringArray (const String* initialStrings, int numberOfStrings)
|
||||
{
|
||||
strings.addArray (initialStrings, numberOfStrings);
|
||||
}
|
||||
|
||||
StringArray::StringArray (const char* const* initialStrings)
|
||||
{
|
||||
strings.addNullTerminatedArray (initialStrings);
|
||||
}
|
||||
|
||||
StringArray::StringArray (const char* const* initialStrings, int numberOfStrings)
|
||||
{
|
||||
strings.addArray (initialStrings, numberOfStrings);
|
||||
}
|
||||
|
||||
StringArray::StringArray (const wchar_t* const* initialStrings)
|
||||
{
|
||||
strings.addNullTerminatedArray (initialStrings);
|
||||
}
|
||||
|
||||
StringArray::StringArray (const wchar_t* const* initialStrings, int numberOfStrings)
|
||||
{
|
||||
strings.addArray (initialStrings, numberOfStrings);
|
||||
}
|
||||
|
||||
StringArray::StringArray (const std::initializer_list<const char*>& stringList)
|
||||
{
|
||||
strings.addArray (stringList);
|
||||
}
|
||||
|
||||
StringArray& StringArray::operator= (const StringArray& other)
|
||||
{
|
||||
strings = other.strings;
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringArray& StringArray::operator= (StringArray&& other) noexcept
|
||||
{
|
||||
strings = std::move (other.strings);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringArray::~StringArray()
|
||||
{
|
||||
}
|
||||
|
||||
bool StringArray::operator== (const StringArray& other) const noexcept
|
||||
{
|
||||
return strings == other.strings;
|
||||
}
|
||||
|
||||
bool StringArray::operator!= (const StringArray& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
void StringArray::swapWith (StringArray& other) noexcept
|
||||
{
|
||||
strings.swapWith (other.strings);
|
||||
}
|
||||
|
||||
void StringArray::clear()
|
||||
{
|
||||
strings.clear();
|
||||
}
|
||||
|
||||
void StringArray::clearQuick()
|
||||
{
|
||||
strings.clearQuick();
|
||||
}
|
||||
|
||||
const String& StringArray::operator[] (int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, strings.size()))
|
||||
return strings.getReference (index);
|
||||
|
||||
static String empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
String& StringArray::getReference (int index) noexcept
|
||||
{
|
||||
return strings.getReference (index);
|
||||
}
|
||||
|
||||
const String& StringArray::getReference (int index) const noexcept
|
||||
{
|
||||
return strings.getReference (index);
|
||||
}
|
||||
|
||||
void StringArray::add (String newString)
|
||||
{
|
||||
// NB: the local temp copy is to avoid a dangling pointer if the
|
||||
// argument being passed-in is a reference into this array.
|
||||
strings.add (std::move (newString));
|
||||
}
|
||||
|
||||
void StringArray::insert (int index, String newString)
|
||||
{
|
||||
// NB: the local temp copy is to avoid a dangling pointer if the
|
||||
// argument being passed-in is a reference into this array.
|
||||
strings.insert (index, std::move (newString));
|
||||
}
|
||||
|
||||
bool StringArray::addIfNotAlreadyThere (const String& newString, bool ignoreCase)
|
||||
{
|
||||
if (contains (newString, ignoreCase))
|
||||
return false;
|
||||
|
||||
add (newString);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd)
|
||||
{
|
||||
jassert (this != &otherArray); // can't add from our own elements!
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
|
||||
numElementsToAdd = otherArray.size() - startIndex;
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
strings.add (otherArray.strings.getReference (startIndex++));
|
||||
}
|
||||
|
||||
void StringArray::mergeArray (const StringArray& otherArray, bool ignoreCase)
|
||||
{
|
||||
jassert (this != &otherArray); // can't add from our own elements!
|
||||
|
||||
for (auto& s : otherArray)
|
||||
addIfNotAlreadyThere (s, ignoreCase);
|
||||
}
|
||||
|
||||
void StringArray::set (int index, String newString)
|
||||
{
|
||||
strings.set (index, std::move (newString));
|
||||
}
|
||||
|
||||
bool StringArray::contains (StringRef stringToLookFor, bool ignoreCase) const
|
||||
{
|
||||
return indexOf (stringToLookFor, ignoreCase) >= 0;
|
||||
}
|
||||
|
||||
int StringArray::indexOf (StringRef stringToLookFor, bool ignoreCase, int i) const
|
||||
{
|
||||
if (i < 0)
|
||||
i = 0;
|
||||
|
||||
auto numElements = size();
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
for (; i < numElements; ++i)
|
||||
if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; i < numElements; ++i)
|
||||
if (stringToLookFor == strings.getReference (i))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void StringArray::move (int currentIndex, int newIndex) noexcept
|
||||
{
|
||||
strings.move (currentIndex, newIndex);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StringArray::remove (int index)
|
||||
{
|
||||
strings.remove (index);
|
||||
}
|
||||
|
||||
void StringArray::removeString (StringRef stringToRemove, bool ignoreCase)
|
||||
{
|
||||
if (ignoreCase)
|
||||
{
|
||||
for (int i = size(); --i >= 0;)
|
||||
if (strings.getReference(i).equalsIgnoreCase (stringToRemove))
|
||||
strings.remove (i);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = size(); --i >= 0;)
|
||||
if (stringToRemove == strings.getReference (i))
|
||||
strings.remove (i);
|
||||
}
|
||||
}
|
||||
|
||||
void StringArray::removeRange (int startIndex, int numberToRemove)
|
||||
{
|
||||
strings.removeRange (startIndex, numberToRemove);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StringArray::removeEmptyStrings (bool removeWhitespaceStrings)
|
||||
{
|
||||
if (removeWhitespaceStrings)
|
||||
{
|
||||
for (int i = size(); --i >= 0;)
|
||||
if (! strings.getReference(i).containsNonWhitespaceChars())
|
||||
strings.remove (i);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = size(); --i >= 0;)
|
||||
if (strings.getReference(i).isEmpty())
|
||||
strings.remove (i);
|
||||
}
|
||||
}
|
||||
|
||||
void StringArray::trim()
|
||||
{
|
||||
for (auto& s : strings)
|
||||
s = s.trim();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StringArray::sort (bool ignoreCase)
|
||||
{
|
||||
if (ignoreCase)
|
||||
std::sort (strings.begin(), strings.end(),
|
||||
[] (const String& a, const String& b) { return a.compareIgnoreCase (b) < 0; });
|
||||
else
|
||||
std::sort (strings.begin(), strings.end());
|
||||
}
|
||||
|
||||
void StringArray::sortNatural()
|
||||
{
|
||||
std::sort (strings.begin(), strings.end(),
|
||||
[] (const String& a, const String& b) { return a.compareNatural (b) < 0; });
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String StringArray::joinIntoString (StringRef separator, int start, int numberToJoin) const
|
||||
{
|
||||
auto last = (numberToJoin < 0) ? size()
|
||||
: jmin (size(), start + numberToJoin);
|
||||
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
|
||||
if (start >= last)
|
||||
return {};
|
||||
|
||||
if (start == last - 1)
|
||||
return strings.getReference (start);
|
||||
|
||||
auto separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType);
|
||||
auto bytesNeeded = (size_t) (last - start - 1) * separatorBytes;
|
||||
|
||||
for (int i = start; i < last; ++i)
|
||||
bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
|
||||
|
||||
String result;
|
||||
result.preallocateBytes (bytesNeeded);
|
||||
|
||||
auto dest = result.getCharPointer();
|
||||
|
||||
while (start < last)
|
||||
{
|
||||
auto& s = strings.getReference (start);
|
||||
|
||||
if (! s.isEmpty())
|
||||
dest.writeAll (s.getCharPointer());
|
||||
|
||||
if (++start < last && separatorBytes > 0)
|
||||
dest.writeAll (separator.text);
|
||||
}
|
||||
|
||||
dest.writeNull();
|
||||
return result;
|
||||
}
|
||||
|
||||
int StringArray::addTokens (StringRef text, const bool preserveQuotedStrings)
|
||||
{
|
||||
return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : "");
|
||||
}
|
||||
|
||||
int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
if (text.isNotEmpty())
|
||||
{
|
||||
for (auto t = text.text;;)
|
||||
{
|
||||
auto tokenEnd = CharacterFunctions::findEndOfToken (t,
|
||||
breakCharacters.text,
|
||||
quoteCharacters.text);
|
||||
strings.add (String (t, tokenEnd));
|
||||
++num;
|
||||
|
||||
if (tokenEnd.isEmpty())
|
||||
break;
|
||||
|
||||
t = ++tokenEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
int StringArray::addLines (StringRef sourceText)
|
||||
{
|
||||
int numLines = 0;
|
||||
auto text = sourceText.text;
|
||||
bool finished = text.isEmpty();
|
||||
|
||||
while (! finished)
|
||||
{
|
||||
for (auto startOfLine = text;;)
|
||||
{
|
||||
auto endOfLine = text;
|
||||
|
||||
switch (text.getAndAdvance())
|
||||
{
|
||||
case 0: finished = true; break;
|
||||
case '\n': break;
|
||||
case '\r': if (*text == '\n') ++text; break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
strings.add (String (startOfLine, endOfLine));
|
||||
++numLines;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numLines;
|
||||
}
|
||||
|
||||
StringArray StringArray::fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings)
|
||||
{
|
||||
StringArray s;
|
||||
s.addTokens (stringToTokenise, preserveQuotedStrings);
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray StringArray::fromTokens (StringRef stringToTokenise,
|
||||
StringRef breakCharacters,
|
||||
StringRef quoteCharacters)
|
||||
{
|
||||
StringArray s;
|
||||
s.addTokens (stringToTokenise, breakCharacters, quoteCharacters);
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray StringArray::fromLines (StringRef stringToBreakUp)
|
||||
{
|
||||
StringArray s;
|
||||
s.addLines (stringToBreakUp);
|
||||
return s;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StringArray::removeDuplicates (bool ignoreCase)
|
||||
{
|
||||
for (int i = 0; i < size() - 1; ++i)
|
||||
{
|
||||
auto s = strings.getReference(i);
|
||||
|
||||
for (int nextIndex = i + 1;;)
|
||||
{
|
||||
nextIndex = indexOf (s, ignoreCase, nextIndex);
|
||||
|
||||
if (nextIndex < 0)
|
||||
break;
|
||||
|
||||
strings.remove (nextIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StringArray::appendNumbersToDuplicates (bool ignoreCase,
|
||||
bool appendNumberToFirstInstance,
|
||||
CharPointer_UTF8 preNumberString,
|
||||
CharPointer_UTF8 postNumberString)
|
||||
{
|
||||
if (preNumberString.getAddress() == nullptr)
|
||||
preNumberString = CharPointer_UTF8 (" (");
|
||||
|
||||
if (postNumberString.getAddress() == nullptr)
|
||||
postNumberString = CharPointer_UTF8 (")");
|
||||
|
||||
for (int i = 0; i < size() - 1; ++i)
|
||||
{
|
||||
auto& s = strings.getReference(i);
|
||||
auto nextIndex = indexOf (s, ignoreCase, i + 1);
|
||||
|
||||
if (nextIndex >= 0)
|
||||
{
|
||||
auto original = s;
|
||||
int number = 0;
|
||||
|
||||
if (appendNumberToFirstInstance)
|
||||
s = original + String (preNumberString) + String (++number) + String (postNumberString);
|
||||
else
|
||||
++number;
|
||||
|
||||
while (nextIndex >= 0)
|
||||
{
|
||||
set (nextIndex, (*this)[nextIndex] + String (preNumberString) + String (++number) + String (postNumberString));
|
||||
nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StringArray::ensureStorageAllocated (int minNumElements)
|
||||
{
|
||||
strings.ensureStorageAllocated (minNumElements);
|
||||
}
|
||||
|
||||
void StringArray::minimiseStorageOverheads()
|
||||
{
|
||||
strings.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
} // namespace juce
|
477
deps/juce/modules/juce_core/text/juce_StringArray.h
vendored
Normal file
477
deps/juce/modules/juce_core/text/juce_StringArray.h
vendored
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A special array for holding a list of strings.
|
||||
|
||||
@see String, StringPairArray
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API StringArray
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty string array */
|
||||
StringArray() noexcept;
|
||||
|
||||
/** Creates a copy of another string array */
|
||||
StringArray (const StringArray&);
|
||||
|
||||
/** Move constructor */
|
||||
StringArray (StringArray&&) noexcept;
|
||||
|
||||
/** Creates an array containing a single string. */
|
||||
StringArray (const String& firstValue);
|
||||
|
||||
/** Creates an array containing a list of strings. */
|
||||
template <typename... OtherElements>
|
||||
StringArray (StringRef firstValue, OtherElements&&... otherValues)
|
||||
: strings (firstValue, std::forward<OtherElements> (otherValues)...) {}
|
||||
|
||||
/** Creates an array containing a list of strings. */
|
||||
StringArray (const std::initializer_list<const char*>& strings);
|
||||
|
||||
/** Creates a StringArray by moving from an Array<String> */
|
||||
StringArray (Array<String>&&) noexcept;
|
||||
|
||||
/** Creates a StringArray from an array of objects which can be implicitly converted to Strings. */
|
||||
template <typename Type>
|
||||
StringArray (const Array<Type>& stringArray)
|
||||
{
|
||||
addArray (stringArray.begin(), stringArray.end());
|
||||
}
|
||||
|
||||
/** Creates an array from a raw array of strings.
|
||||
@param strings an array of strings to add
|
||||
@param numberOfStrings how many items there are in the array
|
||||
*/
|
||||
StringArray (const String* strings, int numberOfStrings);
|
||||
|
||||
/** Creates a copy of an array of string literals.
|
||||
@param strings an array of strings to add. Null pointers in the array will be
|
||||
treated as empty strings
|
||||
@param numberOfStrings how many items there are in the array
|
||||
*/
|
||||
StringArray (const char* const* strings, int numberOfStrings);
|
||||
|
||||
/** Creates a copy of a null-terminated array of string literals.
|
||||
|
||||
Each item from the array passed-in is added, until it encounters a null pointer,
|
||||
at which point it stops.
|
||||
*/
|
||||
explicit StringArray (const char* const* strings);
|
||||
|
||||
/** Creates a copy of a null-terminated array of string literals.
|
||||
Each item from the array passed-in is added, until it encounters a null pointer,
|
||||
at which point it stops.
|
||||
*/
|
||||
explicit StringArray (const wchar_t* const* strings);
|
||||
|
||||
/** Creates a copy of an array of string literals.
|
||||
@param strings an array of strings to add. Null pointers in the array will be
|
||||
treated as empty strings
|
||||
@param numberOfStrings how many items there are in the array
|
||||
*/
|
||||
StringArray (const wchar_t* const* strings, int numberOfStrings);
|
||||
|
||||
/** Destructor. */
|
||||
~StringArray();
|
||||
|
||||
/** Copies the contents of another string array into this one */
|
||||
StringArray& operator= (const StringArray&);
|
||||
|
||||
/** Move assignment operator */
|
||||
StringArray& operator= (StringArray&&) noexcept;
|
||||
|
||||
/** Copies a StringArray from an array of objects which can be implicitly converted to Strings. */
|
||||
template <typename Type>
|
||||
StringArray& operator= (const Array<Type>& stringArray)
|
||||
{
|
||||
addArray (stringArray.begin(), stringArray.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Swaps the contents of this and another StringArray. */
|
||||
void swapWith (StringArray&) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Compares two arrays.
|
||||
Comparisons are case-sensitive.
|
||||
@returns true only if the other array contains exactly the same strings in the same order
|
||||
*/
|
||||
bool operator== (const StringArray&) const noexcept;
|
||||
|
||||
/** Compares two arrays.
|
||||
Comparisons are case-sensitive.
|
||||
@returns false if the other array contains exactly the same strings in the same order
|
||||
*/
|
||||
bool operator!= (const StringArray&) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of strings in the array */
|
||||
inline int size() const noexcept { return strings.size(); }
|
||||
|
||||
/** Returns true if the array is empty, false otherwise. */
|
||||
inline bool isEmpty() const noexcept { return size() == 0; }
|
||||
|
||||
/** Returns one of the strings from the array.
|
||||
|
||||
If the index is out-of-range, an empty string is returned.
|
||||
|
||||
Obviously the reference returned shouldn't be stored for later use, as the
|
||||
string it refers to may disappear when the array changes.
|
||||
*/
|
||||
const String& operator[] (int index) const noexcept;
|
||||
|
||||
/** Returns a reference to one of the strings in the array.
|
||||
This lets you modify a string in-place in the array, but you must be sure that
|
||||
the index is in-range.
|
||||
*/
|
||||
String& getReference (int index) noexcept;
|
||||
|
||||
/** Returns a reference to one of the strings in the array.
|
||||
This lets you modify a string in-place in the array, but you must be sure that
|
||||
the index is in-range.
|
||||
*/
|
||||
const String& getReference (int index) const noexcept;
|
||||
|
||||
/** Returns a pointer to the first String in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline String* begin() noexcept { return strings.begin(); }
|
||||
|
||||
/** Returns a pointer to the first String in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline const String* begin() const noexcept { return strings.begin(); }
|
||||
|
||||
/** Returns a pointer to the String which follows the last element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline String* end() noexcept { return strings.end(); }
|
||||
|
||||
/** Returns a pointer to the String which follows the last element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline const String* end() const noexcept { return strings.end(); }
|
||||
|
||||
|
||||
/** Searches for a string in the array.
|
||||
|
||||
The comparison will be case-insensitive if the ignoreCase parameter is true.
|
||||
|
||||
@returns true if the string is found inside the array
|
||||
*/
|
||||
bool contains (StringRef stringToLookFor,
|
||||
bool ignoreCase = false) const;
|
||||
|
||||
/** Searches for a string in the array.
|
||||
|
||||
The comparison will be case-insensitive if the ignoreCase parameter is true.
|
||||
|
||||
@param stringToLookFor the string to try to find
|
||||
@param ignoreCase whether the comparison should be case-insensitive
|
||||
@param startIndex the first index to start searching from
|
||||
@returns the index of the first occurrence of the string in this array,
|
||||
or -1 if it isn't found.
|
||||
*/
|
||||
int indexOf (StringRef stringToLookFor,
|
||||
bool ignoreCase = false,
|
||||
int startIndex = 0) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Appends a string at the end of the array. */
|
||||
void add (String stringToAdd);
|
||||
|
||||
/** Inserts a string into the array.
|
||||
|
||||
This will insert a string into the array at the given index, moving
|
||||
up the other elements to make room for it.
|
||||
If the index is less than zero or greater than the size of the array,
|
||||
the new string will be added to the end of the array.
|
||||
*/
|
||||
void insert (int index, String stringToAdd);
|
||||
|
||||
/** Adds a string to the array as long as it's not already in there.
|
||||
The search can optionally be case-insensitive.
|
||||
|
||||
@return true if the string has been added, false otherwise.
|
||||
*/
|
||||
bool addIfNotAlreadyThere (const String& stringToAdd, bool ignoreCase = false);
|
||||
|
||||
/** Replaces one of the strings in the array with another one.
|
||||
|
||||
If the index is higher than the array's size, the new string will be
|
||||
added to the end of the array; if it's less than zero nothing happens.
|
||||
*/
|
||||
void set (int index, String newString);
|
||||
|
||||
/** Appends some strings from another array to the end of this one.
|
||||
|
||||
@param other the array to add
|
||||
@param startIndex the first element of the other array to add
|
||||
@param numElementsToAdd the maximum number of elements to add (if this is
|
||||
less than zero, they are all added)
|
||||
*/
|
||||
void addArray (const StringArray& other,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1);
|
||||
|
||||
/** Adds items from a range of start/end iterators of some kind of objects which
|
||||
can be implicitly converted to Strings.
|
||||
*/
|
||||
template <typename Iterator>
|
||||
void addArray (Iterator&& start, Iterator&& end)
|
||||
{
|
||||
ensureStorageAllocated (size() + (int) static_cast<size_t> (end - start));
|
||||
|
||||
while (start != end)
|
||||
strings.add (*start++);
|
||||
}
|
||||
|
||||
/** Merges the strings from another array into this one.
|
||||
This will not add a string that already exists.
|
||||
|
||||
@param other the array to add
|
||||
@param ignoreCase ignore case when merging
|
||||
*/
|
||||
void mergeArray (const StringArray& other,
|
||||
bool ignoreCase = false);
|
||||
|
||||
/** Breaks up a string into tokens and adds them to this array.
|
||||
|
||||
This will tokenise the given string using whitespace characters as the
|
||||
token delimiters, and will add these tokens to the end of the array.
|
||||
@returns the number of tokens added
|
||||
@see fromTokens
|
||||
*/
|
||||
int addTokens (StringRef stringToTokenise, bool preserveQuotedStrings);
|
||||
|
||||
/** Breaks up a string into tokens and adds them to this array.
|
||||
|
||||
This will tokenise the given string (using the string passed in to define the
|
||||
token delimiters), and will add these tokens to the end of the array.
|
||||
|
||||
@param stringToTokenise the string to tokenise
|
||||
@param breakCharacters a string of characters, any of which will be considered
|
||||
to be a token delimiter.
|
||||
@param quoteCharacters if this string isn't empty, it defines a set of characters
|
||||
which are treated as quotes. Any text occurring
|
||||
between quotes is not broken up into tokens.
|
||||
@returns the number of tokens added
|
||||
@see fromTokens
|
||||
*/
|
||||
int addTokens (StringRef stringToTokenise,
|
||||
StringRef breakCharacters,
|
||||
StringRef quoteCharacters);
|
||||
|
||||
/** Breaks up a string into lines and adds them to this array.
|
||||
|
||||
This breaks a string down into lines separated by \\n or \\r\\n, and adds each line
|
||||
to the array. Line-break characters are omitted from the strings that are added to
|
||||
the array.
|
||||
*/
|
||||
int addLines (StringRef stringToBreakUp);
|
||||
|
||||
/** Returns an array containing the tokens in a given string.
|
||||
|
||||
This will tokenise the given string using whitespace characters as the
|
||||
token delimiters, and return the parsed tokens as an array.
|
||||
@see addTokens
|
||||
*/
|
||||
static StringArray fromTokens (StringRef stringToTokenise,
|
||||
bool preserveQuotedStrings);
|
||||
|
||||
/** Returns an array containing the tokens in a given string.
|
||||
|
||||
This will tokenise the given string using the breakCharacters string to define
|
||||
the token delimiters, and will return the parsed tokens as an array.
|
||||
|
||||
@param stringToTokenise the string to tokenise
|
||||
@param breakCharacters a string of characters, any of which will be considered
|
||||
to be a token delimiter.
|
||||
@param quoteCharacters if this string isn't empty, it defines a set of characters
|
||||
which are treated as quotes. Any text occurring
|
||||
between quotes is not broken up into tokens.
|
||||
@see addTokens
|
||||
*/
|
||||
static StringArray fromTokens (StringRef stringToTokenise,
|
||||
StringRef breakCharacters,
|
||||
StringRef quoteCharacters);
|
||||
|
||||
/** Returns an array containing the lines in a given string.
|
||||
|
||||
This breaks a string down into lines separated by \\n or \\r\\n, and returns an
|
||||
array containing these lines. Line-break characters are omitted from the strings that
|
||||
are added to the array.
|
||||
*/
|
||||
static StringArray fromLines (StringRef stringToBreakUp);
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all elements from the array. */
|
||||
void clear();
|
||||
|
||||
/** Removes all elements from the array without freeing the array's allocated storage.
|
||||
@see clear
|
||||
*/
|
||||
void clearQuick();
|
||||
|
||||
/** Removes a string from the array.
|
||||
If the index is out-of-range, no action will be taken.
|
||||
*/
|
||||
void remove (int index);
|
||||
|
||||
/** Finds a string in the array and removes it.
|
||||
This will remove all occurrences of the given string from the array.
|
||||
The comparison may be case-insensitive depending on the ignoreCase parameter.
|
||||
*/
|
||||
void removeString (StringRef stringToRemove,
|
||||
bool ignoreCase = false);
|
||||
|
||||
/** Removes a range of elements from the array.
|
||||
|
||||
This will remove a set of elements, starting from the given index,
|
||||
and move subsequent elements down to close the gap.
|
||||
|
||||
If the range extends beyond the bounds of the array, it will
|
||||
be safely clipped to the size of the array.
|
||||
|
||||
@param startIndex the index of the first element to remove
|
||||
@param numberToRemove how many elements should be removed
|
||||
*/
|
||||
void removeRange (int startIndex, int numberToRemove);
|
||||
|
||||
/** Removes any duplicated elements from the array.
|
||||
|
||||
If any string appears in the array more than once, only the first occurrence of
|
||||
it will be retained.
|
||||
|
||||
@param ignoreCase whether to use a case-insensitive comparison
|
||||
*/
|
||||
void removeDuplicates (bool ignoreCase);
|
||||
|
||||
/** Removes empty strings from the array.
|
||||
@param removeWhitespaceStrings if true, strings that only contain whitespace
|
||||
characters will also be removed
|
||||
*/
|
||||
void removeEmptyStrings (bool removeWhitespaceStrings = true);
|
||||
|
||||
/** Moves one of the strings to a different position.
|
||||
|
||||
This will move the string to a specified index, shuffling along
|
||||
any intervening elements as required.
|
||||
|
||||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
|
||||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
|
||||
|
||||
@param currentIndex the index of the value to be moved. If this isn't a
|
||||
valid index, then nothing will be done
|
||||
@param newIndex the index at which you'd like this value to end up. If this
|
||||
is less than zero, the value will be moved to the end
|
||||
of the array
|
||||
*/
|
||||
void move (int currentIndex, int newIndex) noexcept;
|
||||
|
||||
/** Deletes any whitespace characters from the starts and ends of all the strings. */
|
||||
void trim();
|
||||
|
||||
/** Adds numbers to the strings in the array, to make each string unique.
|
||||
|
||||
This will add numbers to the ends of groups of similar strings.
|
||||
e.g. if there are two "moose" strings, they will become "moose (1)" and "moose (2)"
|
||||
|
||||
@param ignoreCaseWhenComparing whether the comparison used is case-insensitive
|
||||
@param appendNumberToFirstInstance whether the first of a group of similar strings
|
||||
also has a number appended to it.
|
||||
@param preNumberString when adding a number, this string is added before the number.
|
||||
If you pass nullptr, a default string will be used, which adds
|
||||
brackets around the number.
|
||||
@param postNumberString this string is appended after any numbers that are added.
|
||||
If you pass nullptr, a default string will be used, which adds
|
||||
brackets around the number.
|
||||
*/
|
||||
void appendNumbersToDuplicates (bool ignoreCaseWhenComparing,
|
||||
bool appendNumberToFirstInstance,
|
||||
CharPointer_UTF8 preNumberString = CharPointer_UTF8 (nullptr),
|
||||
CharPointer_UTF8 postNumberString = CharPointer_UTF8 (nullptr));
|
||||
|
||||
//==============================================================================
|
||||
/** Joins the strings in the array together into one string.
|
||||
|
||||
This will join a range of elements from the array into a string, separating
|
||||
them with a given string.
|
||||
|
||||
e.g. joinIntoString (",") will turn an array of "a" "b" and "c" into "a,b,c".
|
||||
|
||||
@param separatorString the string to insert between all the strings
|
||||
@param startIndex the first element to join
|
||||
@param numberOfElements how many elements to join together. If this is less
|
||||
than zero, all available elements will be used.
|
||||
*/
|
||||
String joinIntoString (StringRef separatorString,
|
||||
int startIndex = 0,
|
||||
int numberOfElements = -1) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Sorts the array into alphabetical order.
|
||||
@param ignoreCase if true, the comparisons used will not be case-sensitive.
|
||||
*/
|
||||
void sort (bool ignoreCase);
|
||||
|
||||
/** Sorts the array using extra language-aware rules to do a better job of comparing
|
||||
words containing spaces and numbers.
|
||||
@see String::compareNatural()
|
||||
*/
|
||||
void sortNatural();
|
||||
|
||||
//==============================================================================
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the array won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (int minNumElements);
|
||||
|
||||
/** Reduces the amount of storage being used by the array.
|
||||
|
||||
Arrays typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads();
|
||||
|
||||
/** This is the array holding the actual strings. This is public to allow direct access
|
||||
to array methods that may not already be provided by the StringArray class.
|
||||
*/
|
||||
Array<String> strings;
|
||||
|
||||
private:
|
||||
JUCE_LEAK_DETECTOR (StringArray)
|
||||
};
|
||||
|
||||
} // namespace juce
|
311
deps/juce/modules/juce_core/text/juce_StringPairArray.cpp
vendored
Normal file
311
deps/juce/modules/juce_core/text/juce_StringPairArray.cpp
vendored
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
StringPairArray::StringPairArray (bool shouldIgnoreCase) : ignoreCase (shouldIgnoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
StringPairArray::StringPairArray (const StringPairArray& other)
|
||||
: keys (other.keys),
|
||||
values (other.values),
|
||||
ignoreCase (other.ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
StringPairArray::~StringPairArray()
|
||||
{
|
||||
}
|
||||
|
||||
StringPairArray& StringPairArray::operator= (const StringPairArray& other)
|
||||
{
|
||||
keys = other.keys;
|
||||
values = other.values;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool StringPairArray::operator== (const StringPairArray& other) const
|
||||
{
|
||||
auto num = size();
|
||||
|
||||
if (num != other.size())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
if (keys[i] == other.keys[i]) // optimise for the case where the keys are in the same order
|
||||
{
|
||||
if (values[i] != other.values[i])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we encounter keys that are in a different order, search remaining items by brute force..
|
||||
for (int j = i; j < num; ++j)
|
||||
{
|
||||
auto otherIndex = other.keys.indexOf (keys[j], other.ignoreCase);
|
||||
|
||||
if (otherIndex < 0 || values[j] != other.values[otherIndex])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StringPairArray::operator!= (const StringPairArray& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
const String& StringPairArray::operator[] (StringRef key) const
|
||||
{
|
||||
return values[keys.indexOf (key, ignoreCase)];
|
||||
}
|
||||
|
||||
String StringPairArray::getValue (StringRef key, const String& defaultReturnValue) const
|
||||
{
|
||||
auto i = keys.indexOf (key, ignoreCase);
|
||||
|
||||
if (i >= 0)
|
||||
return values[i];
|
||||
|
||||
return defaultReturnValue;
|
||||
}
|
||||
|
||||
bool StringPairArray::containsKey (StringRef key) const noexcept
|
||||
{
|
||||
return keys.contains (key, ignoreCase);
|
||||
}
|
||||
|
||||
void StringPairArray::set (const String& key, const String& value)
|
||||
{
|
||||
auto i = keys.indexOf (key, ignoreCase);
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
values.set (i, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
keys.add (key);
|
||||
values.add (value);
|
||||
}
|
||||
}
|
||||
|
||||
void StringPairArray::addArray (const StringPairArray& other)
|
||||
{
|
||||
for (int i = 0; i < other.size(); ++i)
|
||||
set (other.keys[i], other.values[i]);
|
||||
}
|
||||
|
||||
void StringPairArray::clear()
|
||||
{
|
||||
keys.clear();
|
||||
values.clear();
|
||||
}
|
||||
|
||||
void StringPairArray::remove (StringRef key)
|
||||
{
|
||||
remove (keys.indexOf (key, ignoreCase));
|
||||
}
|
||||
|
||||
void StringPairArray::remove (int index)
|
||||
{
|
||||
keys.remove (index);
|
||||
values.remove (index);
|
||||
}
|
||||
|
||||
void StringPairArray::setIgnoresCase (bool shouldIgnoreCase)
|
||||
{
|
||||
ignoreCase = shouldIgnoreCase;
|
||||
}
|
||||
|
||||
bool StringPairArray::getIgnoresCase() const noexcept
|
||||
{
|
||||
return ignoreCase;
|
||||
}
|
||||
|
||||
String StringPairArray::getDescription() const
|
||||
{
|
||||
String s;
|
||||
|
||||
for (int i = 0; i < keys.size(); ++i)
|
||||
{
|
||||
s << keys[i] << " = " << values[i];
|
||||
|
||||
if (i < keys.size())
|
||||
s << ", ";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void StringPairArray::minimiseStorageOverheads()
|
||||
{
|
||||
keys.minimiseStorageOverheads();
|
||||
values.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
void StringPairArray::addMap (const std::map<String, String>& toAdd)
|
||||
{
|
||||
// If we just called `set` for each item in `toAdd`, that would
|
||||
// perform badly when adding to large StringPairArrays, as `set`
|
||||
// has to loop through the whole container looking for matching keys.
|
||||
// Instead, we use a temporary map to give us better lookup performance.
|
||||
std::map<String, int> contents;
|
||||
|
||||
const auto normaliseKey = [this] (const String& key)
|
||||
{
|
||||
return ignoreCase ? key.toLowerCase() : key;
|
||||
};
|
||||
|
||||
for (auto i = 0; i != size(); ++i)
|
||||
contents.emplace (normaliseKey (getAllKeys().getReference (i)), i);
|
||||
|
||||
for (const auto& pair : toAdd)
|
||||
{
|
||||
const auto key = normaliseKey (pair.first);
|
||||
const auto it = contents.find (key);
|
||||
|
||||
if (it != contents.cend())
|
||||
{
|
||||
values.getReference (it->second) = pair.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
contents.emplace (key, static_cast<int> (contents.size()));
|
||||
keys.add (pair.first);
|
||||
values.add (pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
static String operator""_S (const char* chars, size_t)
|
||||
{
|
||||
return String { chars };
|
||||
}
|
||||
|
||||
class StringPairArrayTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
StringPairArrayTests()
|
||||
: UnitTest ("StringPairArray", UnitTestCategories::text)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("addMap respects case sensitivity of StringPairArray");
|
||||
{
|
||||
StringPairArray insensitive { true };
|
||||
insensitive.addMap ({ { "duplicate", "a" },
|
||||
{ "Duplicate", "b" } });
|
||||
|
||||
expect (insensitive.size() == 1);
|
||||
expectEquals (insensitive["DUPLICATE"], "a"_S);
|
||||
|
||||
StringPairArray sensitive { false };
|
||||
sensitive.addMap ({ { "duplicate", "a"_S },
|
||||
{ "Duplicate", "b"_S } });
|
||||
|
||||
expect (sensitive.size() == 2);
|
||||
expectEquals (sensitive["duplicate"], "a"_S);
|
||||
expectEquals (sensitive["Duplicate"], "b"_S);
|
||||
expectEquals (sensitive["DUPLICATE"], ""_S);
|
||||
}
|
||||
|
||||
beginTest ("addMap overwrites existing pairs");
|
||||
{
|
||||
StringPairArray insensitive { true };
|
||||
insensitive.set ("key", "value");
|
||||
insensitive.addMap ({ { "KEY", "VALUE" } });
|
||||
|
||||
expect (insensitive.size() == 1);
|
||||
expectEquals (insensitive.getAllKeys()[0], "key"_S);
|
||||
expectEquals (insensitive.getAllValues()[0], "VALUE"_S);
|
||||
|
||||
StringPairArray sensitive { false };
|
||||
sensitive.set ("key", "value");
|
||||
sensitive.addMap ({ { "KEY", "VALUE" },
|
||||
{ "key", "another value" } });
|
||||
|
||||
expect (sensitive.size() == 2);
|
||||
expect (sensitive.getAllKeys() == StringArray { "key", "KEY" });
|
||||
expect (sensitive.getAllValues() == StringArray { "another value", "VALUE" });
|
||||
}
|
||||
|
||||
beginTest ("addMap doesn't change the order of existing keys");
|
||||
{
|
||||
StringPairArray array;
|
||||
array.set ("a", "a");
|
||||
array.set ("z", "z");
|
||||
array.set ("b", "b");
|
||||
array.set ("y", "y");
|
||||
array.set ("c", "c");
|
||||
|
||||
array.addMap ({ { "B", "B" },
|
||||
{ "0", "0" },
|
||||
{ "Z", "Z" } });
|
||||
|
||||
expect (array.getAllKeys() == StringArray { "a", "z", "b", "y", "c", "0" });
|
||||
expect (array.getAllValues() == StringArray { "a", "Z", "B", "y", "c", "0" });
|
||||
}
|
||||
|
||||
beginTest ("addMap has equivalent behaviour to addArray");
|
||||
{
|
||||
StringPairArray initial;
|
||||
initial.set ("aaa", "aaa");
|
||||
initial.set ("zzz", "zzz");
|
||||
initial.set ("bbb", "bbb");
|
||||
|
||||
auto withAddMap = initial;
|
||||
withAddMap.addMap ({ { "ZZZ", "ZZZ" },
|
||||
{ "ddd", "ddd" } });
|
||||
|
||||
auto withAddArray = initial;
|
||||
withAddArray.addArray ([]
|
||||
{
|
||||
StringPairArray toAdd;
|
||||
toAdd.set ("ZZZ", "ZZZ");
|
||||
toAdd.set ("ddd", "ddd");
|
||||
return toAdd;
|
||||
}());
|
||||
|
||||
expect (withAddMap == withAddArray);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static StringPairArrayTests stringPairArrayTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
158
deps/juce/modules/juce_core/text/juce_StringPairArray.h
vendored
Normal file
158
deps/juce/modules/juce_core/text/juce_StringPairArray.h
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A container for holding a set of strings which are keyed by another string.
|
||||
|
||||
@see StringArray
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API StringPairArray
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array */
|
||||
StringPairArray (bool ignoreCaseWhenComparingKeys = true);
|
||||
|
||||
/** Creates a copy of another array */
|
||||
StringPairArray (const StringPairArray& other);
|
||||
|
||||
/** Destructor. */
|
||||
~StringPairArray();
|
||||
|
||||
/** Copies the contents of another string array into this one */
|
||||
StringPairArray& operator= (const StringPairArray& other);
|
||||
|
||||
//==============================================================================
|
||||
/** Compares two arrays.
|
||||
Comparisons are case-sensitive.
|
||||
@returns true only if the other array contains exactly the same strings with the same keys
|
||||
*/
|
||||
bool operator== (const StringPairArray& other) const;
|
||||
|
||||
/** Compares two arrays.
|
||||
Comparisons are case-sensitive.
|
||||
@returns false if the other array contains exactly the same strings with the same keys
|
||||
*/
|
||||
bool operator!= (const StringPairArray& other) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the value corresponding to a key string.
|
||||
|
||||
If no such key is found, this will just return an empty string. To check whether
|
||||
a given key actually exists (because it might actually be paired with an empty string), use
|
||||
the getAllKeys() method to obtain a list.
|
||||
|
||||
Obviously the reference returned shouldn't be stored for later use, as the
|
||||
string it refers to may disappear when the array changes.
|
||||
|
||||
@see getValue
|
||||
*/
|
||||
const String& operator[] (StringRef key) const;
|
||||
|
||||
/** Finds the value corresponding to a key string.
|
||||
If no such key is found, this will just return the value provided as a default.
|
||||
@see operator[]
|
||||
*/
|
||||
String getValue (StringRef, const String& defaultReturnValue) const;
|
||||
|
||||
/** Returns true if the given key exists. */
|
||||
bool containsKey (StringRef key) const noexcept;
|
||||
|
||||
/** Returns a list of all keys in the array. */
|
||||
const StringArray& getAllKeys() const noexcept { return keys; }
|
||||
|
||||
/** Returns a list of all values in the array. */
|
||||
const StringArray& getAllValues() const noexcept { return values; }
|
||||
|
||||
/** Returns the number of strings in the array */
|
||||
inline int size() const noexcept { return keys.size(); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Adds or amends a key/value pair.
|
||||
If a value already exists with this key, its value will be overwritten,
|
||||
otherwise the key/value pair will be added to the array.
|
||||
*/
|
||||
void set (const String& key, const String& value);
|
||||
|
||||
/** Adds the items from another array to this one.
|
||||
This is equivalent to using set() to add each of the pairs from the other array.
|
||||
*/
|
||||
void addArray (const StringPairArray& other);
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all elements from the array. */
|
||||
void clear();
|
||||
|
||||
/** Removes a string from the array based on its key.
|
||||
If the key isn't found, nothing will happen.
|
||||
*/
|
||||
void remove (StringRef key);
|
||||
|
||||
/** Removes a string from the array based on its index.
|
||||
If the index is out-of-range, no action will be taken.
|
||||
*/
|
||||
void remove (int index);
|
||||
|
||||
//==============================================================================
|
||||
/** Indicates whether to use a case-insensitive search when looking up a key string.
|
||||
*/
|
||||
void setIgnoresCase (bool shouldIgnoreCase);
|
||||
|
||||
/** Indicates whether a case-insensitive search is used when looking up a key string.
|
||||
*/
|
||||
bool getIgnoresCase() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a descriptive string containing the items.
|
||||
This is handy for dumping the contents of an array.
|
||||
*/
|
||||
String getDescription() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the array.
|
||||
|
||||
Arrays typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds the contents of a map to this StringPairArray. */
|
||||
void addMap (const std::map<String, String>& mapToAdd);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray keys, values;
|
||||
bool ignoreCase;
|
||||
|
||||
JUCE_LEAK_DETECTOR (StringPairArray)
|
||||
};
|
||||
|
||||
} // namespace juce
|
165
deps/juce/modules/juce_core/text/juce_StringPool.cpp
vendored
Normal file
165
deps/juce/modules/juce_core/text/juce_StringPool.cpp
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
static const int minNumberOfStringsForGarbageCollection = 300;
|
||||
static const uint32 garbageCollectionInterval = 30000;
|
||||
|
||||
|
||||
StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {}
|
||||
StringPool::~StringPool() {}
|
||||
|
||||
struct StartEndString
|
||||
{
|
||||
StartEndString (String::CharPointerType s, String::CharPointerType e) noexcept : start (s), end (e) {}
|
||||
operator String() const { return String (start, end); }
|
||||
|
||||
String::CharPointerType start, end;
|
||||
};
|
||||
|
||||
static int compareStrings (const String& s1, const String& s2) noexcept { return s1.compare (s2); }
|
||||
static int compareStrings (CharPointer_UTF8 s1, const String& s2) noexcept { return s1.compare (s2.getCharPointer()); }
|
||||
|
||||
static int compareStrings (const StartEndString& string1, const String& string2) noexcept
|
||||
{
|
||||
String::CharPointerType s1 (string1.start), s2 (string2.getCharPointer());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const int c1 = s1 < string1.end ? (int) s1.getAndAdvance() : 0;
|
||||
const int c2 = (int) s2.getAndAdvance();
|
||||
const int diff = c1 - c2;
|
||||
|
||||
if (diff != 0) return diff < 0 ? -1 : 1;
|
||||
if (c1 == 0) break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename NewStringType>
|
||||
static String addPooledString (Array<String>& strings, const NewStringType& newString)
|
||||
{
|
||||
int start = 0;
|
||||
int end = strings.size();
|
||||
|
||||
while (start < end)
|
||||
{
|
||||
const String& startString = strings.getReference (start);
|
||||
const int startComp = compareStrings (newString, startString);
|
||||
|
||||
if (startComp == 0)
|
||||
return startString;
|
||||
|
||||
const int halfway = (start + end) / 2;
|
||||
|
||||
if (halfway == start)
|
||||
{
|
||||
if (startComp > 0)
|
||||
++start;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const String& halfwayString = strings.getReference (halfway);
|
||||
const int halfwayComp = compareStrings (newString, halfwayString);
|
||||
|
||||
if (halfwayComp == 0)
|
||||
return halfwayString;
|
||||
|
||||
if (halfwayComp > 0)
|
||||
start = halfway;
|
||||
else
|
||||
end = halfway;
|
||||
}
|
||||
|
||||
strings.insert (start, newString);
|
||||
return strings.getReference (start);
|
||||
}
|
||||
|
||||
String StringPool::getPooledString (const char* const newString)
|
||||
{
|
||||
if (newString == nullptr || *newString == 0)
|
||||
return {};
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
garbageCollectIfNeeded();
|
||||
return addPooledString (strings, CharPointer_UTF8 (newString));
|
||||
}
|
||||
|
||||
String StringPool::getPooledString (String::CharPointerType start, String::CharPointerType end)
|
||||
{
|
||||
if (start.isEmpty() || start == end)
|
||||
return {};
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
garbageCollectIfNeeded();
|
||||
return addPooledString (strings, StartEndString (start, end));
|
||||
}
|
||||
|
||||
String StringPool::getPooledString (StringRef newString)
|
||||
{
|
||||
if (newString.isEmpty())
|
||||
return {};
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
garbageCollectIfNeeded();
|
||||
return addPooledString (strings, newString.text);
|
||||
}
|
||||
|
||||
String StringPool::getPooledString (const String& newString)
|
||||
{
|
||||
if (newString.isEmpty())
|
||||
return {};
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
garbageCollectIfNeeded();
|
||||
return addPooledString (strings, newString);
|
||||
}
|
||||
|
||||
void StringPool::garbageCollectIfNeeded()
|
||||
{
|
||||
if (strings.size() > minNumberOfStringsForGarbageCollection
|
||||
&& Time::getApproximateMillisecondCounter() > lastGarbageCollectionTime + garbageCollectionInterval)
|
||||
garbageCollect();
|
||||
}
|
||||
|
||||
void StringPool::garbageCollect()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = strings.size(); --i >= 0;)
|
||||
if (strings.getReference(i).getReferenceCount() == 1)
|
||||
strings.remove (i);
|
||||
|
||||
lastGarbageCollectionTime = Time::getApproximateMillisecondCounter();
|
||||
}
|
||||
|
||||
StringPool& StringPool::getGlobalPool() noexcept
|
||||
{
|
||||
static StringPool pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
} // namespace juce
|
90
deps/juce/modules/juce_core/text/juce_StringPool.h
vendored
Normal file
90
deps/juce/modules/juce_core/text/juce_StringPool.h
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A StringPool holds a set of shared strings, which reduces storage overheads and improves
|
||||
comparison speed when dealing with many duplicate strings.
|
||||
|
||||
When you add a string to a pool using getPooledString, it'll return a character
|
||||
array containing the same string. This array is owned by the pool, and the same array
|
||||
is returned every time a matching string is asked for. This means that it's trivial to
|
||||
compare two pooled strings for equality, as you can simply compare their pointers. It
|
||||
also cuts down on storage if you're using many copies of the same string.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API StringPool
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty pool. */
|
||||
StringPool() noexcept;
|
||||
|
||||
/** Destructor */
|
||||
~StringPool();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to a shared copy of the string that is passed in.
|
||||
The pool will always return the same String object when asked for a string that matches it.
|
||||
*/
|
||||
String getPooledString (const String& original);
|
||||
|
||||
/** Returns a pointer to a copy of the string that is passed in.
|
||||
The pool will always return the same String object when asked for a string that matches it.
|
||||
*/
|
||||
String getPooledString (const char* original);
|
||||
|
||||
/** Returns a pointer to a shared copy of the string that is passed in.
|
||||
The pool will always return the same String object when asked for a string that matches it.
|
||||
*/
|
||||
String getPooledString (StringRef original);
|
||||
|
||||
/** Returns a pointer to a copy of the string that is passed in.
|
||||
The pool will always return the same String object when asked for a string that matches it.
|
||||
*/
|
||||
String getPooledString (String::CharPointerType start, String::CharPointerType end);
|
||||
|
||||
//==============================================================================
|
||||
/** Scans the pool, and removes any strings that are unreferenced.
|
||||
You don't generally need to call this - it'll be called automatically when the pool grows
|
||||
large enough to warrant it.
|
||||
*/
|
||||
void garbageCollect();
|
||||
|
||||
/** Returns a shared global pool which is used for things like Identifiers, XML parsing. */
|
||||
static StringPool& getGlobalPool() noexcept;
|
||||
|
||||
private:
|
||||
Array<String> strings;
|
||||
CriticalSection lock;
|
||||
uint32 lastGarbageCollectionTime;
|
||||
|
||||
void garbageCollectIfNeeded();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (StringPool)
|
||||
};
|
||||
|
||||
} // namespace juce
|
158
deps/juce/modules/juce_core/text/juce_StringRef.h
vendored
Normal file
158
deps/juce/modules/juce_core/text/juce_StringRef.h
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple class for holding temporary references to a string literal or String.
|
||||
|
||||
Unlike a real String object, the StringRef does not allocate any memory or
|
||||
take ownership of the strings you give to it - it simply holds a reference to
|
||||
a string that has been allocated elsewhere.
|
||||
The main purpose of the class is to be used instead of a const String& as the type
|
||||
of function arguments where the caller may pass either a string literal or a String
|
||||
object. This means that when the called uses a string literal, there's no need
|
||||
for an temporary String object to be allocated, and this cuts down overheads
|
||||
substantially.
|
||||
|
||||
Because the class is simply a wrapper around a pointer, you should always pass
|
||||
it by value, not by reference.
|
||||
|
||||
@code
|
||||
void myStringFunction1 (const String&);
|
||||
void myStringFunction2 (StringRef);
|
||||
|
||||
myStringFunction1 ("abc"); // Implicitly allocates a temporary String object.
|
||||
myStringFunction2 ("abc"); // Much faster, as no local allocations are needed.
|
||||
@endcode
|
||||
|
||||
For examples of it in use, see the XmlElement or StringArray classes.
|
||||
|
||||
Bear in mind that there are still many cases where it's better to use an argument
|
||||
which is a const String&. For example if the function stores the string or needs
|
||||
to internally create a String from the argument, then it's better for the original
|
||||
argument to already be a String.
|
||||
|
||||
@see String
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API StringRef final
|
||||
{
|
||||
public:
|
||||
/** Creates a StringRef from a raw string literal.
|
||||
The StringRef object does NOT take ownership or copy this data, so you must
|
||||
ensure that the data does not change during the lifetime of the StringRef.
|
||||
Note that this pointer cannot be null!
|
||||
*/
|
||||
StringRef (const char* stringLiteral) noexcept;
|
||||
|
||||
/** Creates a StringRef from a raw char pointer.
|
||||
The StringRef object does NOT take ownership or copy this data, so you must
|
||||
ensure that the data does not change during the lifetime of the StringRef.
|
||||
*/
|
||||
StringRef (String::CharPointerType stringLiteral) noexcept;
|
||||
|
||||
/** Creates a StringRef from a String.
|
||||
The StringRef object does NOT take ownership or copy the data from the String,
|
||||
so you must ensure that the String is not modified or deleted during the lifetime
|
||||
of the StringRef.
|
||||
*/
|
||||
StringRef (const String& string) noexcept;
|
||||
|
||||
/** Creates a StringRef from a String.
|
||||
The StringRef object does NOT take ownership or copy the data from the std::string,
|
||||
so you must ensure that the source string object is not modified or deleted during
|
||||
the lifetime of the StringRef.
|
||||
*/
|
||||
StringRef (const std::string& string);
|
||||
|
||||
/** Creates a StringRef pointer to an empty string. */
|
||||
StringRef() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a raw pointer to the underlying string data. */
|
||||
operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); }
|
||||
/** Returns a pointer to the underlying string data as a char pointer object. */
|
||||
operator String::CharPointerType() const noexcept { return text; }
|
||||
|
||||
/** Returns true if the string is empty. */
|
||||
bool isEmpty() const noexcept { return text.isEmpty(); }
|
||||
/** Returns true if the string is not empty. */
|
||||
bool isNotEmpty() const noexcept { return ! text.isEmpty(); }
|
||||
/** Returns the number of characters in the string. */
|
||||
int length() const noexcept { return (int) text.length(); }
|
||||
|
||||
/** Retrieves a character by index. */
|
||||
juce_wchar operator[] (int index) const noexcept { return text[index]; }
|
||||
|
||||
/** Compares this StringRef with a String. */
|
||||
bool operator== (const String& s) const noexcept { return text.compare (s.getCharPointer()) == 0; }
|
||||
/** Compares this StringRef with a String. */
|
||||
bool operator!= (const String& s) const noexcept { return text.compare (s.getCharPointer()) != 0; }
|
||||
/** Compares this StringRef with a String. */
|
||||
bool operator< (const String& s) const noexcept { return text.compare (s.getCharPointer()) < 0; }
|
||||
/** Compares this StringRef with a String. */
|
||||
bool operator<= (const String& s) const noexcept { return text.compare (s.getCharPointer()) <= 0; }
|
||||
/** Compares this StringRef with a String. */
|
||||
bool operator> (const String& s) const noexcept { return text.compare (s.getCharPointer()) > 0; }
|
||||
/** Compares this StringRef with a String. */
|
||||
bool operator>= (const String& s) const noexcept { return text.compare (s.getCharPointer()) >= 0; }
|
||||
|
||||
/** Case-sensitive comparison of two StringRefs. */
|
||||
bool operator== (StringRef s) const noexcept { return text.compare (s.text) == 0; }
|
||||
/** Case-sensitive comparison of two StringRefs. */
|
||||
bool operator!= (StringRef s) const noexcept { return text.compare (s.text) != 0; }
|
||||
|
||||
//==============================================================================
|
||||
/** The text that is referenced. */
|
||||
String::CharPointerType text;
|
||||
|
||||
#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN)
|
||||
// Sorry, non-UTF8 people, you're unable to take advantage of StringRef, because
|
||||
// you've chosen a character encoding that doesn't match C++ string literals.
|
||||
String stringCopy;
|
||||
#endif
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Case-sensitive comparison of two strings. */
|
||||
JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2) noexcept;
|
||||
/** Case-sensitive comparison of two strings. */
|
||||
JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept;
|
||||
/** Case-sensitive comparison of two strings. */
|
||||
JUCE_API bool JUCE_CALLTYPE operator< (const String& string1, StringRef string2) noexcept;
|
||||
/** Case-sensitive comparison of two strings. */
|
||||
JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, StringRef string2) noexcept;
|
||||
/** Case-sensitive comparison of two strings. */
|
||||
JUCE_API bool JUCE_CALLTYPE operator> (const String& string1, StringRef string2) noexcept;
|
||||
/** Case-sensitive comparison of two strings. */
|
||||
JUCE_API bool JUCE_CALLTYPE operator>= (const String& string1, StringRef string2) noexcept;
|
||||
|
||||
inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); }
|
||||
inline String operator+ (StringRef s1, const String& s2) { return String (s1.text) + s2; }
|
||||
inline String operator+ (const char* s1, StringRef s2) { return String (s1) + String (s2.text); }
|
||||
inline String operator+ (StringRef s1, const char* s2) { return String (s1.text) + String (s2); }
|
||||
|
||||
} // namespace juce
|
289
deps/juce/modules/juce_core/text/juce_TextDiff.cpp
vendored
Normal file
289
deps/juce/modules/juce_core/text/juce_TextDiff.cpp
vendored
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
struct TextDiffHelpers
|
||||
{
|
||||
enum { minLengthToMatch = 3,
|
||||
maxComplexity = 16 * 1024 * 1024 };
|
||||
|
||||
struct StringRegion
|
||||
{
|
||||
StringRegion (const String& s) noexcept
|
||||
: text (s.getCharPointer()), start (0), length (s.length()) {}
|
||||
|
||||
StringRegion (String::CharPointerType t, int s, int len) noexcept
|
||||
: text (t), start (s), length (len) {}
|
||||
|
||||
void incrementStart() noexcept { ++text; ++start; --length; }
|
||||
|
||||
String::CharPointerType text;
|
||||
int start, length;
|
||||
};
|
||||
|
||||
static void addInsertion (TextDiff& td, String::CharPointerType text, int index, int length)
|
||||
{
|
||||
TextDiff::Change c;
|
||||
c.insertedText = String (text, (size_t) length);
|
||||
c.start = index;
|
||||
c.length = 0;
|
||||
td.changes.add (c);
|
||||
}
|
||||
|
||||
static void addDeletion (TextDiff& td, int index, int length)
|
||||
{
|
||||
TextDiff::Change c;
|
||||
c.start = index;
|
||||
c.length = length;
|
||||
td.changes.add (c);
|
||||
}
|
||||
|
||||
static void diffSkippingCommonStart (TextDiff& td, StringRegion a, StringRegion b)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto ca = *a.text;
|
||||
auto cb = *b.text;
|
||||
|
||||
if (ca != cb || ca == 0)
|
||||
break;
|
||||
|
||||
a.incrementStart();
|
||||
b.incrementStart();
|
||||
}
|
||||
|
||||
diffRecursively (td, a, b);
|
||||
}
|
||||
|
||||
static void diffRecursively (TextDiff& td, StringRegion a, StringRegion b)
|
||||
{
|
||||
int indexA = 0, indexB = 0;
|
||||
auto len = findLongestCommonSubstring (a.text, a.length, indexA,
|
||||
b.text, b.length, indexB);
|
||||
|
||||
if (len >= minLengthToMatch)
|
||||
{
|
||||
if (indexA > 0 && indexB > 0)
|
||||
diffSkippingCommonStart (td, StringRegion (a.text, a.start, indexA),
|
||||
StringRegion (b.text, b.start, indexB));
|
||||
else if (indexA > 0)
|
||||
addDeletion (td, b.start, indexA);
|
||||
else if (indexB > 0)
|
||||
addInsertion (td, b.text, b.start, indexB);
|
||||
|
||||
diffRecursively (td, StringRegion (a.text + (indexA + len), a.start + indexA + len, a.length - indexA - len),
|
||||
StringRegion (b.text + (indexB + len), b.start + indexB + len, b.length - indexB - len));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.length > 0) addDeletion (td, b.start, a.length);
|
||||
if (b.length > 0) addInsertion (td, b.text, b.start, b.length);
|
||||
}
|
||||
}
|
||||
|
||||
static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, int& indexInA,
|
||||
String::CharPointerType b, const int lenB, int& indexInB) noexcept
|
||||
{
|
||||
if (lenA == 0 || lenB == 0)
|
||||
return 0;
|
||||
|
||||
if (lenA * lenB > maxComplexity)
|
||||
return findCommonSuffix (a, lenA, indexInA,
|
||||
b, lenB, indexInB);
|
||||
|
||||
auto scratchSpace = sizeof (int) * (2 + 2 * (size_t) lenB);
|
||||
|
||||
if (scratchSpace < 4096)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255)
|
||||
auto* scratch = (int*) alloca (scratchSpace);
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
return findLongestCommonSubstring (a, lenA, indexInA, b, lenB, indexInB, scratchSpace, scratch);
|
||||
}
|
||||
|
||||
HeapBlock<int> scratch (scratchSpace);
|
||||
return findLongestCommonSubstring (a, lenA, indexInA, b, lenB, indexInB, scratchSpace, scratch);
|
||||
}
|
||||
|
||||
static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, int& indexInA,
|
||||
String::CharPointerType b, const int lenB, int& indexInB,
|
||||
const size_t scratchSpace, int* const lines) noexcept
|
||||
{
|
||||
zeromem (lines, scratchSpace);
|
||||
|
||||
auto* l0 = lines;
|
||||
auto* l1 = l0 + lenB + 1;
|
||||
|
||||
int loopsWithoutImprovement = 0;
|
||||
int bestLength = 0;
|
||||
|
||||
for (int i = 0; i < lenA; ++i)
|
||||
{
|
||||
auto ca = a.getAndAdvance();
|
||||
auto b2 = b;
|
||||
|
||||
for (int j = 0; j < lenB; ++j)
|
||||
{
|
||||
if (ca != b2.getAndAdvance())
|
||||
{
|
||||
l1[j + 1] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto len = l0[j] + 1;
|
||||
l1[j + 1] = len;
|
||||
|
||||
if (len > bestLength)
|
||||
{
|
||||
loopsWithoutImprovement = 0;
|
||||
bestLength = len;
|
||||
indexInA = i;
|
||||
indexInB = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (++loopsWithoutImprovement > 100)
|
||||
break;
|
||||
|
||||
std::swap (l0, l1);
|
||||
}
|
||||
|
||||
indexInA -= bestLength - 1;
|
||||
indexInB -= bestLength - 1;
|
||||
return bestLength;
|
||||
}
|
||||
|
||||
static int findCommonSuffix (String::CharPointerType a, int lenA, int& indexInA,
|
||||
String::CharPointerType b, int lenB, int& indexInB) noexcept
|
||||
{
|
||||
int length = 0;
|
||||
a += lenA - 1;
|
||||
b += lenB - 1;
|
||||
|
||||
while (length < lenA && length < lenB && *a == *b)
|
||||
{
|
||||
--a;
|
||||
--b;
|
||||
++length;
|
||||
}
|
||||
|
||||
indexInA = lenA - length;
|
||||
indexInB = lenB - length;
|
||||
return length;
|
||||
}
|
||||
};
|
||||
|
||||
TextDiff::TextDiff (const String& original, const String& target)
|
||||
{
|
||||
TextDiffHelpers::diffSkippingCommonStart (*this, original, target);
|
||||
}
|
||||
|
||||
String TextDiff::appliedTo (String text) const
|
||||
{
|
||||
for (auto& c : changes)
|
||||
text = c.appliedTo (text);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
bool TextDiff::Change::isDeletion() const noexcept
|
||||
{
|
||||
return insertedText.isEmpty();
|
||||
}
|
||||
|
||||
String TextDiff::Change::appliedTo (const String& text) const noexcept
|
||||
{
|
||||
return text.replaceSection (start, length, insertedText);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class DiffTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
DiffTests()
|
||||
: UnitTest ("TextDiff class", UnitTestCategories::text)
|
||||
{}
|
||||
|
||||
static String createString (Random& r)
|
||||
{
|
||||
juce_wchar buffer[500] = { 0 };
|
||||
|
||||
for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
|
||||
{
|
||||
if (r.nextInt (10) == 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
|
||||
}
|
||||
while (! CharPointer_UTF16::canRepresent (buffer[i]));
|
||||
}
|
||||
else
|
||||
buffer[i] = (juce_wchar) ('a' + r.nextInt (3));
|
||||
}
|
||||
|
||||
return CharPointer_UTF32 (buffer);
|
||||
}
|
||||
|
||||
void testDiff (const String& a, const String& b)
|
||||
{
|
||||
TextDiff diff (a, b);
|
||||
auto result = diff.appliedTo (a);
|
||||
expectEquals (result, b);
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("TextDiff");
|
||||
|
||||
auto r = getRandom();
|
||||
|
||||
testDiff (String(), String());
|
||||
testDiff ("x", String());
|
||||
testDiff (String(), "x");
|
||||
testDiff ("x", "x");
|
||||
testDiff ("x", "y");
|
||||
testDiff ("xxx", "x");
|
||||
testDiff ("x", "xxx");
|
||||
|
||||
for (int i = 1000; --i >= 0;)
|
||||
{
|
||||
auto s = createString (r);
|
||||
testDiff (s, createString (r));
|
||||
testDiff (s + createString (r), s + createString (r));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static DiffTests diffTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
74
deps/juce/modules/juce_core/text/juce_TextDiff.h
vendored
Normal file
74
deps/juce/modules/juce_core/text/juce_TextDiff.h
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
/**
|
||||
Calculates and applies a sequence of changes to convert one text string into
|
||||
another.
|
||||
|
||||
Once created, the TextDiff object contains an array of change objects, where
|
||||
each change can be either an insertion or a deletion. When applied in order
|
||||
to the original string, these changes will convert it to the target string.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API TextDiff
|
||||
{
|
||||
public:
|
||||
/** Creates a set of diffs for converting the original string into the target. */
|
||||
TextDiff (const String& original,
|
||||
const String& target);
|
||||
|
||||
/** Applies this sequence of changes to the original string, producing the
|
||||
target string that was specified when generating them.
|
||||
|
||||
Obviously it only makes sense to call this function with the string that
|
||||
was originally passed to the constructor. Any other input will produce an
|
||||
undefined result.
|
||||
*/
|
||||
String appliedTo (String text) const;
|
||||
|
||||
/** Describes a change, which can be either an insertion or deletion. */
|
||||
struct Change
|
||||
{
|
||||
String insertedText; /**< If this change is a deletion, this string will be empty; otherwise,
|
||||
it'll be the text that should be inserted at the index specified by start. */
|
||||
int start; /**< Specifies the character index in a string at which text should be inserted or deleted. */
|
||||
int length; /**< If this change is a deletion, this specifies the number of characters to delete. For an
|
||||
insertion, this is the length of the new text being inserted. */
|
||||
|
||||
/** Returns true if this change is a deletion, or false for an insertion. */
|
||||
bool isDeletion() const noexcept;
|
||||
|
||||
/** Returns the result of applying this change to a string. */
|
||||
String appliedTo (const String& original) const noexcept;
|
||||
};
|
||||
|
||||
/** The list of changes required to perform the transformation.
|
||||
Applying each of these, in order, to the original string will produce the target.
|
||||
*/
|
||||
Array<Change> changes;
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user