/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-9 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #ifdef _MSC_VER #pragma warning (disable: 4514) #pragma warning (push) #endif #include #include "../core/juce_StandardHeader.h" #if JUCE_MSVC #include #endif BEGIN_JUCE_NAMESPACE #include "juce_String.h" #include "../core/juce_Atomic.h" #include "../io/streams/juce_OutputStream.h" #ifdef _MSC_VER #pragma warning (pop) #endif #if defined (JUCE_STRINGS_ARE_UNICODE) && ! JUCE_STRINGS_ARE_UNICODE #error "JUCE_STRINGS_ARE_UNICODE is deprecated! All strings are now unicode by default." #endif //============================================================================== class StringHolder { public: //============================================================================== static juce_wchar* create (const size_t numChars) { StringHolder* const s = reinterpret_cast (juce_malloc (sizeof (StringHolder) + numChars * sizeof (juce_wchar))); s->refCount = 0; s->allocatedNumChars = numChars; return &(s->text[0]); } static inline juce_wchar* getEmpty() throw() { return &(empty.text[0]); } //============================================================================== static void retain (juce_wchar* const text) throw() { Atomic::increment (bufferFromText (text)->refCount); } static inline void release (StringHolder* const b) throw() { if (Atomic::decrementAndReturn (b->refCount) == -1 && b != &empty) juce_free (b); } static void release (juce_wchar* const text) throw() { release (bufferFromText (text)); } //============================================================================== static juce_wchar* makeUnique (juce_wchar* const text) { StringHolder* const b = bufferFromText (text); if (b->refCount <= 0) return text; juce_wchar* const newText = create (b->allocatedNumChars); memcpy (newText, text, (b->allocatedNumChars + 1) * sizeof (juce_wchar)); release (b); return newText; } static juce_wchar* makeUniqueWithSize (juce_wchar* const text, size_t numChars) { StringHolder* const b = bufferFromText (text); if (b->refCount <= 0 && b->allocatedNumChars >= numChars) return text; juce_wchar* const newText = create (jmax (b->allocatedNumChars, numChars)); memcpy (newText, text, (b->allocatedNumChars + 1) * sizeof (juce_wchar)); release (b); return newText; } static size_t getAllocatedNumChars (juce_wchar* const text) throw() { return bufferFromText (text)->allocatedNumChars; } //============================================================================== int refCount; size_t allocatedNumChars; juce_wchar text[1]; static StringHolder empty; private: static inline StringHolder* bufferFromText (juce_wchar* const text) throw() { return reinterpret_cast (reinterpret_cast (text) - offsetof (StringHolder, StringHolder::text)); } }; StringHolder StringHolder::empty = { 0x3fffffff, 0, { 0 } }; const String String::empty; //============================================================================== void String::createInternal (const juce_wchar* const t, const size_t numChars) { jassert (t[numChars] == 0); // must have a null terminator text = StringHolder::create (numChars); memcpy (text, t, (numChars + 1) * sizeof (juce_wchar)); } void String::appendInternal (const juce_wchar* const newText, const int numExtraChars) { if (numExtraChars > 0) { const int oldLen = length(); const int newTotalLen = oldLen + numExtraChars; text = StringHolder::makeUniqueWithSize (text, newTotalLen); memcpy (text + oldLen, newText, numExtraChars * sizeof (juce_wchar)); text [newTotalLen] = 0; } } void String::dupeInternalIfMultiplyReferenced() { text = StringHolder::makeUnique (text); } void String::preallocateStorage (const size_t numChars) { text = StringHolder::makeUniqueWithSize (text, numChars); } //============================================================================== String::String() throw() : text (StringHolder::getEmpty()) { } String::~String() throw() { StringHolder::release (text); } String::String (const String& other) throw() : text (other.text) { StringHolder::retain (text); } void String::swapWith (String& other) throw() { swapVariables (text, other.text); } String& String::operator= (const String& other) throw() { juce_wchar* const newText = other.text; StringHolder::retain (newText); StringHolder::release (static_cast (Atomic::swapPointers ((void* volatile*) &text, newText))); return *this; } String::String (const size_t numChars, const int /*dummyVariable*/) : text (StringHolder::create (numChars)) { } String::String (const char* const t) { if (t != 0 && *t != 0) { const int len = CharacterFunctions::length (t); text = StringHolder::create (len); CharacterFunctions::copy (text, t, len + 1); } else { text = StringHolder::getEmpty(); } } String::String (const juce_wchar* const t) { if (t != 0 && *t != 0) { const int len = CharacterFunctions::length (t); text = StringHolder::create (len); memcpy (text, t, (len + 1) * sizeof (juce_wchar)); } else { text = StringHolder::getEmpty(); } } String::String (const char* const t, const size_t maxChars) { int i; for (i = 0; (size_t) i < maxChars; ++i) if (t[i] == 0) break; if (i > 0) { text = StringHolder::create (i); CharacterFunctions::copy (text, t, i); text[i] = 0; } else { text = StringHolder::getEmpty(); } } String::String (const juce_wchar* const t, const size_t maxChars) { int i; for (i = 0; (size_t) i < maxChars; ++i) if (t[i] == 0) break; if (i > 0) { text = StringHolder::create (i); memcpy (text, t, i * sizeof (juce_wchar)); text[i] = 0; } else { text = StringHolder::getEmpty(); } } const String String::charToString (const juce_wchar character) { juce_wchar temp[] = { character, 0 }; return String (temp); } //============================================================================== namespace NumberToStringConverters { // pass in a pointer to the END of a buffer.. static juce_wchar* int64ToString (juce_wchar* t, const int64 n) throw() { *--t = 0; int64 v = (n >= 0) ? n : -n; do { *--t = (juce_wchar) (T('0') + (int) (v % 10)); v /= 10; } while (v > 0); if (n < 0) *--t = T('-'); return t; } static juce_wchar* uint64ToString (juce_wchar* t, int64 v) throw() { *--t = 0; do { *--t = (juce_wchar) (T('0') + (int) (v % 10)); v /= 10; } while (v > 0); return t; } static juce_wchar* intToString (juce_wchar* t, const int n) throw() { if (n == (int) 0x80000000) // (would cause an overflow) return int64ToString (t, n); *--t = 0; int v = abs (n); do { *--t = (juce_wchar) (T('0') + (v % 10)); v /= 10; } while (v > 0); if (n < 0) *--t = T('-'); return t; } static juce_wchar* uintToString (juce_wchar* t, unsigned int v) throw() { *--t = 0; do { *--t = (juce_wchar) (T('0') + (v % 10)); v /= 10; } while (v > 0); return t; } static juce_wchar getDecimalPoint() { static juce_wchar dp = std::use_facet > (std::locale()).decimal_point(); return dp; } static juce_wchar* doubleToString (juce_wchar* buffer, int numChars, double n, int numDecPlaces, size_t& len) throw() { if (numDecPlaces > 0 && n > -1.0e20 && n < 1.0e20) { juce_wchar* const end = buffer + numChars; juce_wchar* t = end; int64 v = (int64) (pow (10.0, numDecPlaces) * fabs (n) + 0.5); *--t = (juce_wchar) 0; while (numDecPlaces >= 0 || v > 0) { if (numDecPlaces == 0) *--t = getDecimalPoint(); *--t = (juce_wchar) (T('0') + (v % 10)); v /= 10; --numDecPlaces; } if (n < 0) *--t = T('-'); len = end - t - 1; return t; } else { #if JUCE_WIN32 #if _MSC_VER <= 1200 len = _snwprintf (buffer, numChars, L"%.9g", n); #else len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n); #endif #else len = swprintf (buffer, numChars, L"%.9g", n); #endif return buffer; } } } //============================================================================== String::String (const int number) { juce_wchar buffer [16]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::intToString (end, number); createInternal (start, end - start - 1); } String::String (const unsigned int number) { juce_wchar buffer [16]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::uintToString (end, number); createInternal (start, end - start - 1); } String::String (const short number) { juce_wchar buffer [16]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::intToString (end, (int) number); createInternal (start, end - start - 1); } String::String (const unsigned short number) { juce_wchar buffer [16]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::uintToString (end, (unsigned int) number); createInternal (start, end - start - 1); } String::String (const int64 number) { juce_wchar buffer [32]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::int64ToString (end, number); createInternal (start, end - start - 1); } String::String (const uint64 number) { juce_wchar buffer [32]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::uint64ToString (end, number); createInternal (start, end - start - 1); } String::String (const float number, const int numberOfDecimalPlaces) { juce_wchar buffer [48]; size_t len; juce_wchar* start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); createInternal (start, len); } String::String (const double number, const int numberOfDecimalPlaces) { juce_wchar buffer [48]; size_t len; juce_wchar* start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), number, numberOfDecimalPlaces, len); createInternal (start, len); } //============================================================================== int String::length() const throw() { return CharacterFunctions::length (text); } int String::hashCode() const throw() { const juce_wchar* t = text; int result = 0; while (*t != (juce_wchar) 0) result = 31 * result + *t++; return result; } int64 String::hashCode64() const throw() { const juce_wchar* t = text; int64 result = 0; while (*t != (juce_wchar) 0) result = 101 * result + *t++; return result; } //============================================================================== bool JUCE_CALLTYPE operator== (const String& string1, const String& string2) throw() { return string1.compare (string2) == 0; } bool JUCE_CALLTYPE operator== (const String& string1, const char* string2) throw() { return string1.compare (string2) == 0; } bool JUCE_CALLTYPE operator== (const String& string1, const juce_wchar* string2) throw() { return string1.compare (string2) == 0; } bool JUCE_CALLTYPE operator!= (const String& string1, const String& string2) throw() { return string1.compare (string2) != 0; } bool JUCE_CALLTYPE operator!= (const String& string1, const char* string2) throw() { return string1.compare (string2) != 0; } bool JUCE_CALLTYPE operator!= (const String& string1, const juce_wchar* string2) throw() { return string1.compare (string2) != 0; } bool JUCE_CALLTYPE operator> (const String& string1, const String& string2) throw() { return string1.compare (string2) > 0; } bool JUCE_CALLTYPE operator< (const String& string1, const String& string2) throw() { return string1.compare (string2) < 0; } bool JUCE_CALLTYPE operator>= (const String& string1, const String& string2) throw() { return string1.compare (string2) >= 0; } bool JUCE_CALLTYPE operator<= (const String& string1, const String& string2) throw() { return string1.compare (string2) <= 0; } bool String::equalsIgnoreCase (const juce_wchar* t) const throw() { return t != 0 ? CharacterFunctions::compareIgnoreCase (text, t) == 0 : isEmpty(); } bool String::equalsIgnoreCase (const String& other) const throw() { return text == other.text || CharacterFunctions::compareIgnoreCase (text, other.text) == 0; } int String::compare (const String& other) const throw() { return (text == other.text) ? 0 : CharacterFunctions::compare (text, other.text); } int String::compare (const char* other) const throw() { return other == 0 ? isEmpty() : CharacterFunctions::compare (text, other); } int String::compare (const juce_wchar* other) const throw() { return other == 0 ? isEmpty() : CharacterFunctions::compare (text, other); } int String::compareIgnoreCase (const String& other) const throw() { return (text == other.text) ? 0 : CharacterFunctions::compareIgnoreCase (text, other.text); } int String::compareLexicographically (const String& other) const throw() { const juce_wchar* s1 = text; while (*s1 != 0 && ! CharacterFunctions::isLetterOrDigit (*s1)) ++s1; const juce_wchar* s2 = other.text; while (*s2 != 0 && ! CharacterFunctions::isLetterOrDigit (*s2)) ++s2; return CharacterFunctions::compareIgnoreCase (s1, s2); } //============================================================================== String& String::operator+= (const juce_wchar* const t) { if (t != 0) appendInternal (t, CharacterFunctions::length (t)); return *this; } String& String::operator+= (const String& other) { if (isEmpty()) operator= (other); else appendInternal (other.text, other.length()); return *this; } String& String::operator+= (const char ch) { const juce_wchar asString[] = { (juce_wchar) ch, 0 }; return operator+= ((const juce_wchar*) asString); } String& String::operator+= (const juce_wchar ch) { const juce_wchar asString[] = { (juce_wchar) ch, 0 }; return operator+= ((const juce_wchar*) asString); } String& String::operator+= (const int number) { juce_wchar buffer [16]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::intToString (end, number); appendInternal (start, (int) (end - start)); return *this; } String& String::operator+= (const unsigned int number) { juce_wchar buffer [16]; juce_wchar* const end = buffer + numElementsInArray (buffer); juce_wchar* const start = NumberToStringConverters::uintToString (end, number); appendInternal (start, (int) (end - start)); return *this; } void String::append (const juce_wchar* const other, const int howMany) { if (howMany > 0) { int i; for (i = 0; i < howMany; ++i) if (other[i] == 0) break; appendInternal (other, i); } } //============================================================================== const String JUCE_CALLTYPE operator+ (const char* const string1, const String& string2) { String s (string1); return s += string2; } const String JUCE_CALLTYPE operator+ (const juce_wchar* const string1, const String& string2) { String s (string1); return s += string2; } const String JUCE_CALLTYPE operator+ (const char string1, const String& string2) { return String::charToString (string1) + string2; } const String JUCE_CALLTYPE operator+ (const juce_wchar string1, const String& string2) { return String::charToString (string1) + string2; } const String JUCE_CALLTYPE operator+ (String string1, const String& string2) { return string1 += string2; } const String JUCE_CALLTYPE operator+ (String string1, const char* const string2) { return string1 += string2; } const String JUCE_CALLTYPE operator+ (String string1, const juce_wchar* const string2) { return string1 += string2; } const String JUCE_CALLTYPE operator+ (String string1, const char string2) { return string1 += string2; } const String JUCE_CALLTYPE operator+ (String string1, const juce_wchar string2) { return string1 += string2; } String& JUCE_CALLTYPE operator<< (String& string1, const char characterToAppend) { return string1 += characterToAppend; } String& JUCE_CALLTYPE operator<< (String& string1, const juce_wchar characterToAppend) { return string1 += characterToAppend; } String& JUCE_CALLTYPE operator<< (String& string1, const char* const string2) { return string1 += string2; } String& JUCE_CALLTYPE operator<< (String& string1, const juce_wchar* const string2) { return string1 += string2; } String& JUCE_CALLTYPE operator<< (String& string1, const String& string2) { return string1 += string2; } String& JUCE_CALLTYPE operator<< (String& string1, const short number) { return string1 += (int) number; } String& JUCE_CALLTYPE operator<< (String& string1, const int number) { return string1 += number; } String& JUCE_CALLTYPE operator<< (String& string1, const unsigned int number) { return string1 += number; } String& JUCE_CALLTYPE operator<< (String& string1, const long number) { return string1 += (int) number; } String& JUCE_CALLTYPE operator<< (String& string1, const unsigned long number) { return string1 += (unsigned int) number; } String& JUCE_CALLTYPE operator<< (String& string1, const float number) { return string1 += String (number); } String& JUCE_CALLTYPE operator<< (String& string1, const double number) { return string1 += String (number); } OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text) { // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind // if lots of large, persistent strings were to be written to streams). const int numBytes = text.getNumBytesAsUTF8(); HeapBlock temp (numBytes + 1); text.copyToUTF8 (temp, numBytes + 1); stream.write (temp, numBytes); return stream; } //============================================================================== int String::indexOfChar (const juce_wchar character) const throw() { const juce_wchar* t = text; for (;;) { if (*t == character) return (int) (t - text); if (*t++ == 0) return -1; } } int String::lastIndexOfChar (const juce_wchar character) const throw() { for (int i = length(); --i >= 0;) if (text[i] == character) return i; return -1; } int String::indexOf (const juce_wchar* const t) const throw() { const juce_wchar* const r = CharacterFunctions::find (text, t); return (r == 0) ? -1 : (int) (r - text); } int String::indexOfChar (const int startIndex, const juce_wchar character) const throw() { if (startIndex >= 0 && startIndex >= length()) return -1; const juce_wchar* t = text + jmax (0, startIndex); for (;;) { if (*t == character) return (int) (t - text); if (*t++ == 0) return -1; } } int String::indexOfAnyOf (const juce_wchar* const charactersToLookFor, const int startIndex, const bool ignoreCase) const throw() { if (charactersToLookFor == 0 || (startIndex >= 0 && startIndex >= length())) return -1; const juce_wchar* t = text + jmax (0, startIndex); while (*t != 0) { if (CharacterFunctions::indexOfChar (charactersToLookFor, *t, ignoreCase) >= 0) return (int) (t - text); ++t; } return -1; } int String::indexOf (const int startIndex, const juce_wchar* const other) const throw() { if (other == 0 || startIndex >= length()) return -1; const juce_wchar* const found = CharacterFunctions::find (text + jmax (0, startIndex), other); return (found == 0) ? -1 : (int) (found - text); } int String::indexOfIgnoreCase (const juce_wchar* const other) const throw() { if (other != 0 && *other != 0) { const int len = CharacterFunctions::length (other); const int end = length() - len; for (int i = 0; i <= end; ++i) if (CharacterFunctions::compareIgnoreCase (text + i, other, len) == 0) return i; } return -1; } int String::indexOfIgnoreCase (const int startIndex, const juce_wchar* const other) const throw() { if (other != 0 && *other != 0) { const int len = CharacterFunctions::length (other); const int end = length() - len; for (int i = jmax (0, startIndex); i <= end; ++i) if (CharacterFunctions::compareIgnoreCase (text + i, other, len) == 0) return i; } return -1; } int String::lastIndexOf (const juce_wchar* const other) const throw() { if (other != 0 && *other != 0) { const int len = CharacterFunctions::length (other); int i = length() - len; if (i >= 0) { const juce_wchar* n = text + i; while (i >= 0) { if (CharacterFunctions::compare (n--, other, len) == 0) return i; --i; } } } return -1; } int String::lastIndexOfIgnoreCase (const juce_wchar* const other) const throw() { if (other != 0 && *other != 0) { const int len = CharacterFunctions::length (other); int i = length() - len; if (i >= 0) { const juce_wchar* n = text + i; while (i >= 0) { if (CharacterFunctions::compareIgnoreCase (n--, other, len) == 0) return i; --i; } } } return -1; } int String::lastIndexOfAnyOf (const juce_wchar* const charactersToLookFor, const bool ignoreCase) const throw() { for (int i = length(); --i >= 0;) if (CharacterFunctions::indexOfChar (charactersToLookFor, text[i], ignoreCase) >= 0) return i; return -1; } bool String::contains (const juce_wchar* const other) const throw() { return indexOf (other) >= 0; } bool String::containsChar (const juce_wchar character) const throw() { return indexOfChar (character) >= 0; } bool String::containsIgnoreCase (const juce_wchar* const t) const throw() { return indexOfIgnoreCase (t) >= 0; } int String::indexOfWholeWord (const juce_wchar* const word) const throw() { if (word != 0 && *word != 0) { const int wordLen = CharacterFunctions::length (word); const int end = length() - wordLen; const juce_wchar* t = text; for (int i = 0; i <= end; ++i) { if (CharacterFunctions::compare (t, word, wordLen) == 0 && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) { return i; } ++t; } } return -1; } int String::indexOfWholeWordIgnoreCase (const juce_wchar* const word) const throw() { if (word != 0 && *word != 0) { const int wordLen = CharacterFunctions::length (word); const int end = length() - wordLen; const juce_wchar* t = text; for (int i = 0; i <= end; ++i) { if (CharacterFunctions::compareIgnoreCase (t, word, wordLen) == 0 && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) { return i; } ++t; } } return -1; } bool String::containsWholeWord (const juce_wchar* const wordToLookFor) const throw() { return indexOfWholeWord (wordToLookFor) >= 0; } bool String::containsWholeWordIgnoreCase (const juce_wchar* const wordToLookFor) const throw() { return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0; } //============================================================================== static int indexOfMatch (const juce_wchar* const wildcard, const juce_wchar* const test, const bool ignoreCase) throw() { int start = 0; while (test [start] != 0) { int i = 0; for (;;) { const juce_wchar wc = wildcard [i]; const juce_wchar c = test [i + start]; if (wc == c || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) || (wc == T('?') && c != 0)) { if (wc == 0) return start; ++i; } else { if (wc == T('*') && (wildcard [i + 1] == 0 || indexOfMatch (wildcard + i + 1, test + start + i, ignoreCase) >= 0)) { return start; } break; } } ++start; } return -1; } bool String::matchesWildcard (const juce_wchar* wildcard, const bool ignoreCase) const throw() { int i = 0; for (;;) { const juce_wchar wc = wildcard [i]; const juce_wchar c = text [i]; if (wc == c || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) || (wc == T('?') && c != 0)) { if (wc == 0) return true; ++i; } else { return wc == T('*') && (wildcard [i + 1] == 0 || indexOfMatch (wildcard + i + 1, text + i, ignoreCase) >= 0); } } } //============================================================================== const String String::repeatedString (const juce_wchar* const stringToRepeat, int numberOfTimesToRepeat) { const int len = CharacterFunctions::length (stringToRepeat); String result ((size_t) (len * numberOfTimesToRepeat + 1), (int) 0); juce_wchar* n = result.text; n[0] = 0; while (--numberOfTimesToRepeat >= 0) { CharacterFunctions::append (n, stringToRepeat); n += len; } return result; } const String String::paddedLeft (const juce_wchar padCharacter, int minimumLength) const { jassert (padCharacter != 0); const int len = length(); if (len >= minimumLength || padCharacter == 0) return *this; String result ((size_t) minimumLength + 1, (int) 0); juce_wchar* n = result.text; minimumLength -= len; while (--minimumLength >= 0) *n++ = padCharacter; *n = 0; CharacterFunctions::append (n, text); return result; } const String String::paddedRight (const juce_wchar padCharacter, int minimumLength) const { jassert (padCharacter != 0); const int paddingNeeded = minimumLength - length(); if (paddingNeeded <= 0 || padCharacter == 0) return *this; return *this + String::empty.paddedLeft (padCharacter, paddingNeeded); } //============================================================================== const String String::replaceSection (int index, int numCharsToReplace, const juce_wchar* const stringToInsert) const { if (index < 0) { // a negative index to replace from? jassertfalse index = 0; } if (numCharsToReplace < 0) { // replacing a negative number of characters? numCharsToReplace = 0; jassertfalse; } const int len = length(); if (index + numCharsToReplace > len) { if (index > len) { // replacing beyond the end of the string? index = len; jassertfalse } numCharsToReplace = len - index; } const int newStringLen = (stringToInsert != 0) ? CharacterFunctions::length (stringToInsert) : 0; const int newTotalLen = len + newStringLen - numCharsToReplace; if (newTotalLen <= 0) return String::empty; String result ((size_t) newTotalLen, (int) 0); memcpy (result.text, text, index * sizeof (juce_wchar)); if (newStringLen > 0) memcpy (result.text + index, stringToInsert, newStringLen * sizeof (juce_wchar)); const int endStringLen = newTotalLen - (index + newStringLen); if (endStringLen > 0) memcpy (result.text + (index + newStringLen), text + (index + numCharsToReplace), endStringLen * sizeof (juce_wchar)); result.text [newTotalLen] = 0; return result; } const String String::replace (const juce_wchar* const stringToReplace, const juce_wchar* const stringToInsert, const bool ignoreCase) const { const int stringToReplaceLen = CharacterFunctions::length (stringToReplace); const int stringToInsertLen = CharacterFunctions::length (stringToInsert); int i = 0; String result (*this); while ((i = (ignoreCase ? result.indexOfIgnoreCase (i, stringToReplace) : result.indexOf (i, stringToReplace))) >= 0) { result = result.replaceSection (i, stringToReplaceLen, stringToInsert); i += stringToInsertLen; } return result; } const String String::replaceCharacter (const juce_wchar charToReplace, const juce_wchar charToInsert) const { const int index = indexOfChar (charToReplace); if (index < 0) return *this; String result (*this); result.dupeInternalIfMultiplyReferenced(); juce_wchar* t = result.text + index; while (*t != 0) { if (*t == charToReplace) *t = charToInsert; ++t; } return result; } const String String::replaceCharacters (const String& charactersToReplace, const juce_wchar* const charactersToInsertInstead) const { String result (*this); result.dupeInternalIfMultiplyReferenced(); juce_wchar* t = result.text; const int len2 = CharacterFunctions::length (charactersToInsertInstead); // the two strings passed in are supposed to be the same length! jassert (len2 == charactersToReplace.length()); while (*t != 0) { const int index = charactersToReplace.indexOfChar (*t); if (((unsigned int) index) < (unsigned int) len2) *t = charactersToInsertInstead [index]; ++t; } return result; } //============================================================================== bool String::startsWith (const juce_wchar* const other) const throw() { return other != 0 && CharacterFunctions::compare (text, other, CharacterFunctions::length (other)) == 0; } bool String::startsWithIgnoreCase (const juce_wchar* const other) const throw() { return other != 0 && CharacterFunctions::compareIgnoreCase (text, other, CharacterFunctions::length (other)) == 0; } bool String::startsWithChar (const juce_wchar character) const throw() { jassert (character != 0); // strings can't contain a null character! return text[0] == character; } bool String::endsWithChar (const juce_wchar character) const throw() { jassert (character != 0); // strings can't contain a null character! return text[0] != 0 && text [length() - 1] == character; } bool String::endsWith (const juce_wchar* const other) const throw() { if (other == 0) return false; const int thisLen = length(); const int otherLen = CharacterFunctions::length (other); return thisLen >= otherLen && CharacterFunctions::compare (text + thisLen - otherLen, other) == 0; } bool String::endsWithIgnoreCase (const juce_wchar* const other) const throw() { if (other == 0) return false; const int thisLen = length(); const int otherLen = CharacterFunctions::length (other); return thisLen >= otherLen && CharacterFunctions::compareIgnoreCase (text + thisLen - otherLen, other) == 0; } //============================================================================== const String String::toUpperCase() const { String result (*this); result.dupeInternalIfMultiplyReferenced(); CharacterFunctions::toUpperCase (result.text); return result; } const String String::toLowerCase() const { String result (*this); result.dupeInternalIfMultiplyReferenced(); CharacterFunctions::toLowerCase (result.text); return result; } //============================================================================== juce_wchar& String::operator[] (const int index) { jassert (((unsigned int) index) <= (unsigned int) length()); dupeInternalIfMultiplyReferenced(); return text [index]; } juce_wchar String::getLastCharacter() const throw() { return isEmpty() ? juce_wchar() : text [length() - 1]; } const String String::substring (int start, int end) const { if (start < 0) start = 0; else if (end <= start) return empty; int len = 0; const juce_wchar* const t = text; while (len <= end && t [len] != 0) ++len; if (end >= len) { if (start == 0) return *this; end = len; } return String (text + start, end - start); } const String String::substring (const int start) const { if (start <= 0) return *this; const int len = length(); if (start >= len) return empty; else return String (text + start, len - start); } const String String::dropLastCharacters (const int numberToDrop) const { return String (text, jmax (0, length() - numberToDrop)); } const String String::getLastCharacters (const int numCharacters) const { return String (text + jmax (0, length() - jmax (0, numCharacters))); } const String String::fromFirstOccurrenceOf (const juce_wchar* const sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? indexOfIgnoreCase (sub) : indexOf (sub); if (i < 0) return empty; else return substring (includeSubString ? i : i + CharacterFunctions::length (sub)); } const String String::fromLastOccurrenceOf (const juce_wchar* const sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? lastIndexOfIgnoreCase (sub) : lastIndexOf (sub); if (i < 0) return *this; else return substring (includeSubString ? i : i + CharacterFunctions::length (sub)); } const String String::upToFirstOccurrenceOf (const juce_wchar* const sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? indexOfIgnoreCase (sub) : indexOf (sub); if (i < 0) return *this; else return substring (0, includeSubString ? i + CharacterFunctions::length (sub) : i); } const String String::upToLastOccurrenceOf (const juce_wchar* const sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? lastIndexOfIgnoreCase (sub) : lastIndexOf (sub); if (i < 0) return *this; return substring (0, includeSubString ? i + CharacterFunctions::length (sub) : i); } bool String::isQuotedString() const { const String trimmed (trimStart()); return trimmed[0] == T('"') || trimmed[0] == T('\''); } const String String::unquoted() const { String s (*this); if (s[0] == T('"') || s[0] == T('\'')) s = s.substring (1); const int lastCharIndex = s.length() - 1; if (lastCharIndex >= 0 && (s [lastCharIndex] == T('"') || s[lastCharIndex] == T('\''))) s [lastCharIndex] = 0; return s; } const String String::quoted (const juce_wchar quoteCharacter) const { if (isEmpty()) return charToString (quoteCharacter) + quoteCharacter; String t (*this); if (! t.startsWithChar (quoteCharacter)) t = charToString (quoteCharacter) + t; if (! t.endsWithChar (quoteCharacter)) t += quoteCharacter; return t; } //============================================================================== const String String::trim() const { if (isEmpty()) return empty; int start = 0; while (CharacterFunctions::isWhitespace (text [start])) ++start; const int len = length(); int end = len - 1; while ((end >= start) && CharacterFunctions::isWhitespace (text [end])) --end; ++end; if (end <= start) return empty; else if (start > 0 || end < len) return String (text + start, end - start); else return *this; } const String String::trimStart() const { if (isEmpty()) return empty; const juce_wchar* t = text; while (CharacterFunctions::isWhitespace (*t)) ++t; if (t == text) return *this; else return String (t); } const String String::trimEnd() const { if (isEmpty()) return empty; const juce_wchar* endT = text + (length() - 1); while ((endT >= text) && CharacterFunctions::isWhitespace (*endT)) --endT; return String (text, (int) (++endT - text)); } const String String::trimCharactersAtStart (const juce_wchar* charactersToTrim) const { jassert (charactersToTrim != 0); if (isEmpty()) return empty; const juce_wchar* t = text; while (CharacterFunctions::indexOfCharFast (charactersToTrim, *t) >= 0) ++t; if (t == text) return *this; else return String (t); } const String String::trimCharactersAtEnd (const juce_wchar* charactersToTrim) const { jassert (charactersToTrim != 0); if (isEmpty()) return empty; const juce_wchar* endT = text + (length() - 1); while ((endT >= text) && CharacterFunctions::indexOfCharFast (charactersToTrim, *endT) >= 0) --endT; return String (text, (int) (++endT - text)); } //============================================================================== const String String::retainCharacters (const juce_wchar* const charactersToRetain) const { jassert (charactersToRetain != 0); if (isEmpty()) return empty; String result (StringHolder::getAllocatedNumChars (text), (int) 0); juce_wchar* dst = result.text; const juce_wchar* src = text; while (*src != 0) { if (CharacterFunctions::indexOfCharFast (charactersToRetain, *src) >= 0) *dst++ = *src; ++src; } *dst = 0; return result; } const String String::removeCharacters (const juce_wchar* const charactersToRemove) const { jassert (charactersToRemove != 0); if (isEmpty()) return empty; String result (StringHolder::getAllocatedNumChars (text), (int) 0); juce_wchar* dst = result.text; const juce_wchar* src = text; while (*src != 0) { if (CharacterFunctions::indexOfCharFast (charactersToRemove, *src) < 0) *dst++ = *src; ++src; } *dst = 0; return result; } const String String::initialSectionContainingOnly (const juce_wchar* const permittedCharacters) const { return substring (0, CharacterFunctions::getIntialSectionContainingOnly (text, permittedCharacters)); } const String String::initialSectionNotContaining (const juce_wchar* const charactersToStopAt) const { jassert (charactersToStopAt != 0); const juce_wchar* const t = text; int i = 0; while (t[i] != 0) { if (CharacterFunctions::indexOfCharFast (charactersToStopAt, t[i]) >= 0) return String (text, i); ++i; } return empty; } bool String::containsOnly (const juce_wchar* const chars) const throw() { jassert (chars != 0); const juce_wchar* t = text; while (*t != 0) if (CharacterFunctions::indexOfCharFast (chars, *t++) < 0) return false; return true; } bool String::containsAnyOf (const juce_wchar* const chars) const throw() { jassert (chars != 0); const juce_wchar* t = text; while (*t != 0) if (CharacterFunctions::indexOfCharFast (chars, *t++) >= 0) return true; return false; } bool String::containsNonWhitespaceChars() const throw() { const juce_wchar* t = text; while (*t != 0) if (! CharacterFunctions::isWhitespace (*t++)) return true; return false; } //============================================================================== int String::getIntValue() const throw() { return CharacterFunctions::getIntValue (text); } int String::getTrailingIntValue() const throw() { int n = 0; int mult = 1; const juce_wchar* t = text + length(); while (--t >= text) { const juce_wchar c = *t; if (! CharacterFunctions::isDigit (c)) { if (c == T('-')) n = -n; break; } n += mult * (c - T('0')); mult *= 10; } return n; } int64 String::getLargeIntValue() const throw() { return CharacterFunctions::getInt64Value (text); } float String::getFloatValue() const throw() { return (float) CharacterFunctions::getDoubleValue (text); } double String::getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (text); } static const juce_wchar* const hexDigits = T("0123456789abcdef"); const String String::toHexString (const int number) { juce_wchar buffer[32]; juce_wchar* const end = buffer + 32; juce_wchar* t = end; *--t = 0; unsigned int v = (unsigned int) number; do { *--t = hexDigits [v & 15]; v >>= 4; } while (v != 0); return String (t, (int) (((char*) end) - (char*) t) - 1); } const String String::toHexString (const int64 number) { juce_wchar buffer[32]; juce_wchar* const end = buffer + 32; juce_wchar* t = end; *--t = 0; uint64 v = (uint64) number; do { *--t = hexDigits [(int) (v & 15)]; v >>= 4; } while (v != 0); return String (t, (int) (((char*) end) - (char*) t)); } const String String::toHexString (const short number) { return toHexString ((int) (unsigned short) number); } const String String::toHexString (const unsigned char* data, const int size, const int groupSize) { if (size <= 0) return empty; int numChars = (size * 2) + 2; if (groupSize > 0) numChars += size / groupSize; String s ((size_t) numChars, (int) 0); juce_wchar* d = s.text; for (int i = 0; i < size; ++i) { *d++ = hexDigits [(*data) >> 4]; *d++ = hexDigits [(*data) & 0xf]; ++data; if (groupSize > 0 && (i % groupSize) == (groupSize - 1) && i < (size - 1)) *d++ = T(' '); } *d = 0; return s; } int String::getHexValue32() const throw() { int result = 0; const juce_wchar* c = text; for (;;) { const int hexValue = CharacterFunctions::getHexDigitValue (*c); if (hexValue >= 0) result = (result << 4) | hexValue; else if (*c == 0) break; ++c; } return result; } int64 String::getHexValue64() const throw() { int64 result = 0; const juce_wchar* c = text; for (;;) { const int hexValue = CharacterFunctions::getHexDigitValue (*c); if (hexValue >= 0) result = (result << 4) | hexValue; else if (*c == 0) break; ++c; } return result; } //============================================================================== const String String::createStringFromData (const void* const data_, const int size) { const char* const data = (const char*) data_; if (size <= 0 || data == 0) { return empty; } else if (size < 2) { return charToString (data[0]); } else if ((data[0] == (char)-2 && data[1] == (char)-1) || (data[0] == (char)-1 && data[1] == (char)-2)) { // assume it's 16-bit unicode const bool bigEndian = (data[0] == (char)-2); const int numChars = size / 2 - 1; String result; result.preallocateStorage (numChars + 2); const uint16* const src = (const uint16*) (data + 2); juce_wchar* const dst = const_cast ((const juce_wchar*) result); if (bigEndian) { for (int i = 0; i < numChars; ++i) dst[i] = (juce_wchar) ByteOrder::swapIfLittleEndian (src[i]); } else { for (int i = 0; i < numChars; ++i) dst[i] = (juce_wchar) ByteOrder::swapIfBigEndian (src[i]); } dst [numChars] = 0; return result; } else { return String::fromUTF8 (data, size); } } //============================================================================== const char* String::toUTF8() const { if (isEmpty()) { return reinterpret_cast (text); } else { const int currentLen = length() + 1; const int utf8BytesNeeded = getNumBytesAsUTF8(); String* const mutableThis = const_cast (this); mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); char* const otherCopy = (char*) (text + currentLen); copyToUTF8 (otherCopy, std::numeric_limits::max()); return otherCopy; } } int String::copyToUTF8 (char* const buffer, const int maxBufferSizeBytes) const throw() { jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! int num = 0, index = 0; for (;;) { const uint32 c = (uint32) text [index++]; if (c >= 0x80) { int numExtraBytes = 1; if (c >= 0x800) { ++numExtraBytes; if (c >= 0x10000) { ++numExtraBytes; if (c >= 0x200000) { ++numExtraBytes; if (c >= 0x4000000) ++numExtraBytes; } } } if (buffer != 0) { if (num + numExtraBytes >= maxBufferSizeBytes) { buffer [num++] = 0; break; } else { buffer [num++] = (uint8) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); while (--numExtraBytes >= 0) buffer [num++] = (uint8) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); } } else { num += numExtraBytes + 1; } } else { if (buffer != 0) { if (num + 1 >= maxBufferSizeBytes) { buffer [num++] = 0; break; } buffer [num] = (uint8) c; } ++num; } if (c == 0) break; } return num; } int String::getNumBytesAsUTF8() const throw() { int num = 0; const juce_wchar* t = text; for (;;) { const uint32 c = (uint32) *t; if (c >= 0x80) { ++num; if (c >= 0x800) { ++num; if (c >= 0x10000) { ++num; if (c >= 0x200000) { ++num; if (c >= 0x4000000) ++num; } } } } else if (c == 0) break; ++num; ++t; } return num; } const String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) { if (buffer == 0) return empty; if (bufferSizeBytes < 0) bufferSizeBytes = std::numeric_limits::max(); size_t numBytes; for (numBytes = 0; numBytes < (size_t) bufferSizeBytes; ++numBytes) if (buffer [numBytes] == 0) break; String result ((size_t) numBytes + 1, (int) 0); juce_wchar* dest = result.text; size_t i = 0; while (i < numBytes) { const char c = buffer [i++]; if (c < 0) { unsigned int mask = 0x7f; int bit = 0x40; int numExtraValues = 0; while (bit != 0 && (c & bit) != 0) { bit >>= 1; mask >>= 1; ++numExtraValues; } int n = (mask & (unsigned char) c); while (--numExtraValues >= 0 && i < (size_t) bufferSizeBytes) { const char nextByte = buffer[i]; if ((nextByte & 0xc0) != 0x80) break; n <<= 6; n |= (nextByte & 0x3f); ++i; } *dest++ = (juce_wchar) n; } else { *dest++ = (juce_wchar) c; } } *dest = 0; return result; } //============================================================================== const char* String::toCString() const { if (isEmpty()) { return reinterpret_cast (text); } else { int len = length(); String* const mutableThis = const_cast (this); mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, (len + 1) * 2); char* otherCopy = (char*) (text + len + 1); CharacterFunctions::copy (otherCopy, text, len); otherCopy [len] = 0; return otherCopy; } } #ifdef _MSC_VER #pragma warning (disable: 4514 4996) #pragma warning (push) #endif int String::getNumBytesAsCString() const throw() { return (int) wcstombs (0, text, 0); } int String::copyToCString (char* destBuffer, const int maxBufferSizeBytes) const throw() { const int numBytes = (int) wcstombs (destBuffer, text, maxBufferSizeBytes); if (destBuffer != 0 && numBytes >= 0) destBuffer [numBytes] = 0; return numBytes; } #ifdef _MSC_VER #pragma warning (pop) #endif //============================================================================== void String::copyToUnicode (juce_wchar* const destBuffer, const int maxCharsToCopy) const throw() { const int len = jmin (maxCharsToCopy, length()); memcpy (destBuffer, text, len * sizeof (juce_wchar)); destBuffer [len] = 0; } //============================================================================== String::Concatenator::Concatenator (String& stringToAppendTo) : result (stringToAppendTo), nextIndex (stringToAppendTo.length()) { } String::Concatenator::~Concatenator() { } void String::Concatenator::append (const String& s) { const int len = s.length(); if (len > 0) { result.preallocateStorage (nextIndex + len); s.copyToUnicode (((juce_wchar*) result) + nextIndex, len); nextIndex += len; } } END_JUCE_NAMESPACE